1 /* $OpenBSD: dbtest.c,v 1.11 2003/07/31 21:48:02 deraadt Exp $ */ 2 /* $NetBSD: dbtest.c,v 1.8 1996/05/03 21:57:48 cgd Exp $ */ 3 4 /*- 5 * Copyright (c) 1992, 1993, 1994 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #ifndef lint 34 static char copyright[] = 35 "@(#) Copyright (c) 1992, 1993, 1994\n\ 36 The Regents of the University of California. All rights reserved.\n"; 37 #endif /* not lint */ 38 39 #ifndef lint 40 #if 0 41 static char sccsid[] = "@(#)dbtest.c 8.17 (Berkeley) 9/1/94"; 42 #else 43 static char rcsid[] = "$OpenBSD: dbtest.c,v 1.11 2003/07/31 21:48:02 deraadt Exp $"; 44 #endif 45 #endif /* not lint */ 46 47 #include <sys/param.h> 48 #include <sys/stat.h> 49 50 #include <ctype.h> 51 #include <errno.h> 52 #include <fcntl.h> 53 #include <limits.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <stdarg.h> 58 #include <unistd.h> 59 60 #include <db.h> 61 62 enum S { COMMAND, COMPARE, GET, PUT, REMOVE, SEQ, SEQFLAG, KEY, DATA }; 63 64 void compare(DBT *, DBT *); 65 DBTYPE dbtype(char *); 66 void dump(DB *, int); 67 void dberr(const char *, ...); 68 void get(DB *, DBT *); 69 void getdata(DB *, DBT *, DBT *); 70 void put(DB *, DBT *, DBT *); 71 void rem(DB *, DBT *); 72 char *sflags(int); 73 void synk(DB *); 74 void *rfile(char *, size_t *); 75 void seq(DB *, DBT *); 76 u_int setflags(char *); 77 void *setinfo(DBTYPE, char *); 78 void usage(void); 79 void *xmalloc(char *, size_t); 80 81 DBTYPE type; /* Database type. */ 82 void *infop; /* Iflags. */ 83 u_long lineno; /* Current line in test script. */ 84 u_int flags; /* Current DB flags. */ 85 int ofd = STDOUT_FILENO; /* Standard output fd. */ 86 87 DB *XXdbp; /* Global for gdb. */ 88 int XXlineno; /* Fast breakpoint for gdb. */ 89 90 int 91 main(int argc, char *argv[]) 92 { 93 extern int optind; 94 extern char *optarg; 95 enum S command, state; 96 DB *dbp; 97 DBT data, key, keydata; 98 size_t len; 99 int ch, oflags, sflag; 100 char *fname, *infoarg, *p, *t, buf[8 * 1024]; 101 102 infoarg = NULL; 103 fname = NULL; 104 oflags = O_CREAT | O_RDWR; 105 sflag = 0; 106 while ((ch = getopt(argc, argv, "f:i:lo:s")) != -1) 107 switch (ch) { 108 case 'f': 109 fname = optarg; 110 break; 111 case 'i': 112 infoarg = optarg; 113 break; 114 case 'l': 115 oflags |= DB_LOCK; 116 break; 117 case 'o': 118 if ((ofd = open(optarg, 119 O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) 120 dberr("%s: %s", optarg, strerror(errno)); 121 break; 122 case 's': 123 sflag = 1; 124 break; 125 case '?': 126 default: 127 usage(); 128 } 129 argc -= optind; 130 argv += optind; 131 132 if (argc != 2) 133 usage(); 134 135 /* Set the type. */ 136 type = dbtype(*argv++); 137 138 /* Open the descriptor file. */ 139 if (strcmp(*argv, "-") && freopen(*argv, "r", stdin) == NULL) 140 dberr("%s: %s", *argv, strerror(errno)); 141 142 /* Set up the db structure as necessary. */ 143 if (infoarg == NULL) 144 infop = NULL; 145 else 146 for (p = strtok(infoarg, ",\t "); p != NULL; 147 p = strtok(0, ",\t ")) 148 if (*p != '\0') 149 infop = setinfo(type, p); 150 151 /* 152 * Open the DB. Delete any preexisting copy, you almost never 153 * want it around, and it often screws up tests. 154 */ 155 if (fname == NULL) { 156 p = getenv("TMPDIR"); 157 if (p == NULL) 158 p = "/var/tmp"; 159 (void)snprintf(buf, sizeof buf, "%s/__dbtest", p); 160 fname = buf; 161 (void)unlink(buf); 162 } else if (!sflag) 163 (void)unlink(fname); 164 165 if ((dbp = dbopen(fname, 166 oflags, S_IRUSR | S_IWUSR, type, infop)) == NULL) 167 dberr("dbopen: %s", strerror(errno)); 168 XXdbp = dbp; 169 170 state = COMMAND; 171 for (lineno = 1; 172 (p = fgets(buf, sizeof(buf), stdin)) != NULL; ++lineno) { 173 /* Delete the newline, displaying the key/data is easier. */ 174 if (ofd == STDOUT_FILENO && (t = strchr(p, '\n')) != NULL) 175 *t = '\0'; 176 if ((len = strlen(buf)) == 0 || isspace(*p) || *p == '#') 177 continue; 178 179 /* Convenient gdb break point. */ 180 if (XXlineno == lineno) 181 XXlineno = 1; 182 switch (*p) { 183 case 'c': /* compare */ 184 if (state != COMMAND) 185 dberr("line %lu: not expecting command", 186 lineno); 187 state = KEY; 188 command = COMPARE; 189 break; 190 case 'e': /* echo */ 191 if (state != COMMAND) 192 dberr("line %lu: not expecting command", 193 lineno); 194 /* Don't display the newline, if CR at EOL. */ 195 if (p[len - 2] == '\r') 196 --len; 197 if (write(ofd, p + 1, len - 1) != len - 1 || 198 write(ofd, "\n", 1) != 1) 199 dberr("write: %s", strerror(errno)); 200 break; 201 case 'g': /* get */ 202 if (state != COMMAND) 203 dberr("line %lu: not expecting command", 204 lineno); 205 state = KEY; 206 command = GET; 207 break; 208 case 'p': /* put */ 209 if (state != COMMAND) 210 dberr("line %lu: not expecting command", 211 lineno); 212 state = KEY; 213 command = PUT; 214 break; 215 case 'r': /* remove */ 216 if (state != COMMAND) 217 dberr("line %lu: not expecting command", 218 lineno); 219 if (flags == R_CURSOR) { 220 rem(dbp, &key); 221 state = COMMAND; 222 } else { 223 state = KEY; 224 command = REMOVE; 225 } 226 break; 227 case 'S': /* sync */ 228 if (state != COMMAND) 229 dberr("line %lu: not expecting command", 230 lineno); 231 synk(dbp); 232 state = COMMAND; 233 break; 234 case 's': /* seq */ 235 if (state != COMMAND) 236 dberr("line %lu: not expecting command", 237 lineno); 238 if (flags == R_CURSOR) { 239 state = KEY; 240 command = SEQ; 241 } else 242 seq(dbp, &key); 243 break; 244 case 'f': 245 flags = setflags(p + 1); 246 break; 247 case 'D': /* data file */ 248 if (state != DATA) 249 dberr("line %lu: not expecting data", lineno); 250 data.data = rfile(p + 1, &data.size); 251 goto ldata; 252 case 'd': /* data */ 253 if (state != DATA) 254 dberr("line %lu: not expecting data", lineno); 255 data.data = xmalloc(p + 1, len - 1); 256 data.size = len - 1; 257 ldata: switch (command) { 258 case COMPARE: 259 compare(&keydata, &data); 260 break; 261 case PUT: 262 put(dbp, &key, &data); 263 break; 264 default: 265 dberr("line %lu: command doesn't take data", 266 lineno); 267 } 268 if (type != DB_RECNO) 269 free(key.data); 270 free(data.data); 271 state = COMMAND; 272 break; 273 case 'K': /* key file */ 274 if (state != KEY) 275 dberr("line %lu: not expecting a key", lineno); 276 if (type == DB_RECNO) 277 dberr("line %lu: 'K' not available for recno", 278 lineno); 279 key.data = rfile(p + 1, &key.size); 280 goto lkey; 281 case 'k': /* key */ 282 if (state != KEY) 283 dberr("line %lu: not expecting a key", lineno); 284 if (type == DB_RECNO) { 285 static recno_t recno; 286 recno = atoi(p + 1); 287 key.data = &recno; 288 key.size = sizeof(recno); 289 } else { 290 key.data = xmalloc(p + 1, len - 1); 291 key.size = len - 1; 292 } 293 lkey: switch (command) { 294 case COMPARE: 295 getdata(dbp, &key, &keydata); 296 state = DATA; 297 break; 298 case GET: 299 get(dbp, &key); 300 if (type != DB_RECNO) 301 free(key.data); 302 state = COMMAND; 303 break; 304 case PUT: 305 state = DATA; 306 break; 307 case REMOVE: 308 rem(dbp, &key); 309 if ((type != DB_RECNO) && (flags != R_CURSOR)) 310 free(key.data); 311 state = COMMAND; 312 break; 313 case SEQ: 314 seq(dbp, &key); 315 if ((type != DB_RECNO) && (flags != R_CURSOR)) 316 free(key.data); 317 state = COMMAND; 318 break; 319 default: 320 dberr("line %lu: command doesn't take a key", 321 lineno); 322 } 323 break; 324 case 'o': 325 dump(dbp, p[1] == 'r'); 326 break; 327 default: 328 dberr("line %lu: %s: unknown command character", 329 lineno, p); 330 } 331 } 332 #ifdef STATISTICS 333 /* 334 * -l must be used (DB_LOCK must be set) for this to be 335 * used, otherwise a page will be locked and it will fail. 336 */ 337 if (type == DB_BTREE && oflags & DB_LOCK) 338 __bt_stat(dbp); 339 #endif 340 if (dbp->close(dbp)) 341 dberr("db->close: %s", strerror(errno)); 342 (void)close(ofd); 343 exit(0); 344 } 345 346 #define NOOVERWRITE "put failed, would overwrite key\n" 347 348 void 349 compare(db1, db2) 350 DBT *db1, *db2; 351 { 352 register size_t len; 353 register u_char *p1, *p2; 354 355 if (db1->size != db2->size) 356 printf("compare failed: key->data len %lu != data len %lu\n", 357 db1->size, db2->size); 358 359 len = MIN(db1->size, db2->size); 360 for (p1 = db1->data, p2 = db2->data; len--;) 361 if (*p1++ != *p2++) { 362 printf("compare failed at offset %d\n", 363 p1 - (u_char *)db1->data); 364 break; 365 } 366 } 367 368 void 369 get(dbp, kp) 370 DB *dbp; 371 DBT *kp; 372 { 373 DBT data; 374 375 switch (dbp->get(dbp, kp, &data, flags)) { 376 case 0: 377 (void)write(ofd, data.data, data.size); 378 if (ofd == STDOUT_FILENO) 379 (void)write(ofd, "\n", 1); 380 break; 381 case -1: 382 dberr("line %lu: get: %s", lineno, strerror(errno)); 383 /* NOTREACHED */ 384 case 1: 385 #define NOSUCHKEY "get failed, no such key\n" 386 if (ofd != STDOUT_FILENO) 387 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); 388 else 389 (void)fprintf(stderr, "%d: %.*s: %s", 390 lineno, MIN(kp->size, 20), kp->data, NOSUCHKEY); 391 #undef NOSUCHKEY 392 break; 393 } 394 } 395 396 void 397 getdata(dbp, kp, dp) 398 DB *dbp; 399 DBT *kp, *dp; 400 { 401 switch (dbp->get(dbp, kp, dp, flags)) { 402 case 0: 403 return; 404 case -1: 405 dberr("line %lu: getdata: %s", lineno, strerror(errno)); 406 /* NOTREACHED */ 407 case 1: 408 dberr("line %lu: getdata failed, no such key", lineno); 409 /* NOTREACHED */ 410 } 411 } 412 413 void 414 put(dbp, kp, dp) 415 DB *dbp; 416 DBT *kp, *dp; 417 { 418 switch (dbp->put(dbp, kp, dp, flags)) { 419 case 0: 420 break; 421 case -1: 422 dberr("line %lu: put: %s", lineno, strerror(errno)); 423 /* NOTREACHED */ 424 case 1: 425 (void)write(ofd, NOOVERWRITE, sizeof(NOOVERWRITE) - 1); 426 break; 427 } 428 } 429 430 void 431 rem(dbp, kp) 432 DB *dbp; 433 DBT *kp; 434 { 435 switch (dbp->del(dbp, kp, flags)) { 436 case 0: 437 break; 438 case -1: 439 dberr("line %lu: rem: %s", lineno, strerror(errno)); 440 /* NOTREACHED */ 441 case 1: 442 #define NOSUCHKEY "rem failed, no such key\n" 443 if (ofd != STDOUT_FILENO) 444 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); 445 else if (flags != R_CURSOR) 446 (void)fprintf(stderr, "%d: %.*s: %s", 447 lineno, MIN(kp->size, 20), kp->data, NOSUCHKEY); 448 else 449 (void)fprintf(stderr, 450 "%d: rem of cursor failed\n", lineno); 451 #undef NOSUCHKEY 452 break; 453 } 454 } 455 456 void 457 synk(dbp) 458 DB *dbp; 459 { 460 switch (dbp->sync(dbp, flags)) { 461 case 0: 462 break; 463 case -1: 464 dberr("line %lu: synk: %s", lineno, strerror(errno)); 465 /* NOTREACHED */ 466 } 467 } 468 469 void 470 seq(dbp, kp) 471 DB *dbp; 472 DBT *kp; 473 { 474 DBT data; 475 476 switch (dbp->seq(dbp, kp, &data, flags)) { 477 case 0: 478 (void)write(ofd, data.data, data.size); 479 if (ofd == STDOUT_FILENO) 480 (void)write(ofd, "\n", 1); 481 break; 482 case -1: 483 dberr("line %lu: seq: %s", lineno, strerror(errno)); 484 /* NOTREACHED */ 485 case 1: 486 #define NOSUCHKEY "seq failed, no such key\n" 487 if (ofd != STDOUT_FILENO) 488 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); 489 else if (flags == R_CURSOR) 490 (void)fprintf(stderr, "%d: %.*s: %s", 491 lineno, MIN(kp->size, 20), kp->data, NOSUCHKEY); 492 else 493 (void)fprintf(stderr, 494 "%d: seq (%s) failed\n", lineno, sflags(flags)); 495 #undef NOSUCHKEY 496 break; 497 } 498 } 499 500 void 501 dump(dbp, rev) 502 DB *dbp; 503 int rev; 504 { 505 DBT key, data; 506 int flags, nflags; 507 508 if (rev) { 509 flags = R_LAST; 510 nflags = R_PREV; 511 } else { 512 flags = R_FIRST; 513 nflags = R_NEXT; 514 } 515 for (;; flags = nflags) 516 switch (dbp->seq(dbp, &key, &data, flags)) { 517 case 0: 518 (void)write(ofd, data.data, data.size); 519 if (ofd == STDOUT_FILENO) 520 (void)write(ofd, "\n", 1); 521 break; 522 case 1: 523 goto done; 524 case -1: 525 dberr("line %lu: (dump) seq: %s", 526 lineno, strerror(errno)); 527 /* NOTREACHED */ 528 } 529 done: return; 530 } 531 532 u_int 533 setflags(s) 534 char *s; 535 { 536 char *p; 537 538 for (; isspace(*s); ++s); 539 if (*s == '\n' || *s == '\0') 540 return (0); 541 if ((p = strchr(s, '\n')) != NULL) 542 *p = '\0'; 543 if (!strcmp(s, "R_CURSOR")) return (R_CURSOR); 544 if (!strcmp(s, "R_FIRST")) return (R_FIRST); 545 if (!strcmp(s, "R_IAFTER")) return (R_IAFTER); 546 if (!strcmp(s, "R_IBEFORE")) return (R_IBEFORE); 547 if (!strcmp(s, "R_LAST")) return (R_LAST); 548 if (!strcmp(s, "R_NEXT")) return (R_NEXT); 549 if (!strcmp(s, "R_NOOVERWRITE")) return (R_NOOVERWRITE); 550 if (!strcmp(s, "R_PREV")) return (R_PREV); 551 if (!strcmp(s, "R_SETCURSOR")) return (R_SETCURSOR); 552 553 dberr("line %lu: %s: unknown flag", lineno, s); 554 /* NOTREACHED */ 555 } 556 557 char * 558 sflags(flags) 559 int flags; 560 { 561 switch (flags) { 562 case R_CURSOR: return ("R_CURSOR"); 563 case R_FIRST: return ("R_FIRST"); 564 case R_IAFTER: return ("R_IAFTER"); 565 case R_IBEFORE: return ("R_IBEFORE"); 566 case R_LAST: return ("R_LAST"); 567 case R_NEXT: return ("R_NEXT"); 568 case R_NOOVERWRITE: return ("R_NOOVERWRITE"); 569 case R_PREV: return ("R_PREV"); 570 case R_SETCURSOR: return ("R_SETCURSOR"); 571 } 572 573 return ("UNKNOWN!"); 574 } 575 576 DBTYPE 577 dbtype(s) 578 char *s; 579 { 580 if (!strcmp(s, "btree")) 581 return (DB_BTREE); 582 if (!strcmp(s, "hash")) 583 return (DB_HASH); 584 if (!strcmp(s, "recno")) 585 return (DB_RECNO); 586 dberr("%s: unknown type (use btree, hash or recno)", s); 587 /* NOTREACHED */ 588 } 589 590 void * 591 setinfo(type, s) 592 DBTYPE type; 593 char *s; 594 { 595 static BTREEINFO ib; 596 static HASHINFO ih; 597 static RECNOINFO rh; 598 char *eq; 599 600 if ((eq = strchr(s, '=')) == NULL) 601 dberr("%s: illegal structure set statement", s); 602 *eq++ = '\0'; 603 if (!isdigit(*eq)) 604 dberr("%s: structure set statement must be a number", s); 605 606 switch (type) { 607 case DB_BTREE: 608 if (!strcmp("flags", s)) { 609 ib.flags = atoi(eq); 610 return (&ib); 611 } 612 if (!strcmp("cachesize", s)) { 613 ib.cachesize = atoi(eq); 614 return (&ib); 615 } 616 if (!strcmp("maxkeypage", s)) { 617 ib.maxkeypage = atoi(eq); 618 return (&ib); 619 } 620 if (!strcmp("minkeypage", s)) { 621 ib.minkeypage = atoi(eq); 622 return (&ib); 623 } 624 if (!strcmp("lorder", s)) { 625 ib.lorder = atoi(eq); 626 return (&ib); 627 } 628 if (!strcmp("psize", s)) { 629 ib.psize = atoi(eq); 630 return (&ib); 631 } 632 break; 633 case DB_HASH: 634 if (!strcmp("bsize", s)) { 635 ih.bsize = atoi(eq); 636 return (&ih); 637 } 638 if (!strcmp("ffactor", s)) { 639 ih.ffactor = atoi(eq); 640 return (&ih); 641 } 642 if (!strcmp("nelem", s)) { 643 ih.nelem = atoi(eq); 644 return (&ih); 645 } 646 if (!strcmp("cachesize", s)) { 647 ih.cachesize = atoi(eq); 648 return (&ih); 649 } 650 if (!strcmp("lorder", s)) { 651 ih.lorder = atoi(eq); 652 return (&ih); 653 } 654 break; 655 case DB_RECNO: 656 if (!strcmp("flags", s)) { 657 rh.flags = atoi(eq); 658 return (&rh); 659 } 660 if (!strcmp("cachesize", s)) { 661 rh.cachesize = atoi(eq); 662 return (&rh); 663 } 664 if (!strcmp("lorder", s)) { 665 rh.lorder = atoi(eq); 666 return (&rh); 667 } 668 if (!strcmp("reclen", s)) { 669 rh.reclen = atoi(eq); 670 return (&rh); 671 } 672 if (!strcmp("bval", s)) { 673 rh.bval = atoi(eq); 674 return (&rh); 675 } 676 if (!strcmp("psize", s)) { 677 rh.psize = atoi(eq); 678 return (&rh); 679 } 680 break; 681 } 682 dberr("%s: unknown structure value", s); 683 /* NOTREACHED */ 684 } 685 686 void * 687 rfile(name, lenp) 688 char *name; 689 size_t *lenp; 690 { 691 struct stat sb; 692 void *p; 693 int fd; 694 char *np; 695 696 for (; isspace(*name); ++name); 697 if ((np = strchr(name, '\n')) != NULL) 698 *np = '\0'; 699 if ((fd = open(name, O_RDONLY, 0)) < 0 || 700 fstat(fd, &sb)) 701 dberr("%s: %s\n", name, strerror(errno)); 702 #ifdef NOT_PORTABLE 703 if (sb.st_size > (off_t)SIZE_T_MAX) 704 dberr("%s: %s\n", name, strerror(E2BIG)); 705 #endif 706 if ((p = (void *)malloc((u_int)sb.st_size)) == NULL) 707 dberr("%s", strerror(errno)); 708 (void)read(fd, p, (int)sb.st_size); 709 *lenp = sb.st_size; 710 (void)close(fd); 711 return (p); 712 } 713 714 void * 715 xmalloc(text, len) 716 char *text; 717 size_t len; 718 { 719 void *p; 720 721 if ((p = (void *)malloc(len)) == NULL) 722 dberr("%s", strerror(errno)); 723 memmove(p, text, len); 724 return (p); 725 } 726 727 void 728 usage() 729 { 730 (void)fprintf(stderr, 731 "usage: dbtest [-l] [-f file] [-i info] [-o file] type script\n"); 732 exit(1); 733 } 734 735 void 736 dberr(const char *fmt, ...) 737 { 738 va_list ap; 739 740 va_start(ap, fmt); 741 (void)fprintf(stderr, "dbtest: "); 742 (void)vfprintf(stderr, fmt, ap); 743 va_end(ap); 744 (void)fprintf(stderr, "\n"); 745 exit(1); 746 /* NOTREACHED */ 747 } 748