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