Merge branch 'master' of ssh://git.samba.org/data/git/samba
[ira/wip.git] / lib / tdb / tools / tdbtool.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Samba database functions
4    Copyright (C) Andrew Tridgell              1999-2000
5    Copyright (C) Paul `Rusty' Russell              2000
6    Copyright (C) Jeremy Allison                    2000
7    Copyright (C) Andrew Esh                        2001
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "replace.h"
24 #include "system/locale.h"
25 #include "system/time.h"
26 #include "system/filesys.h"
27 #include "system/wait.h"
28 #include "tdb.h"
29
30 static int do_command(void);
31 const char *cmdname;
32 char *arg1, *arg2;
33 size_t arg1len, arg2len;
34 int bIterate = 0;
35 char *line;
36 TDB_DATA iterate_kbuf;
37 char cmdline[1024];
38 static int disable_mmap;
39
40 enum commands {
41         CMD_CREATE_TDB,
42         CMD_OPEN_TDB,
43         CMD_ERASE,
44         CMD_DUMP,
45         CMD_INSERT,
46         CMD_MOVE,
47         CMD_STORE,
48         CMD_SHOW,
49         CMD_KEYS,
50         CMD_HEXKEYS,
51         CMD_DELETE,
52         CMD_LIST_HASH_FREE,
53         CMD_LIST_FREE,
54         CMD_INFO,
55         CMD_MMAP,
56         CMD_SPEED,
57         CMD_FIRST,
58         CMD_NEXT,
59         CMD_SYSTEM,
60         CMD_CHECK,
61         CMD_QUIT,
62         CMD_HELP
63 };
64
65 typedef struct {
66         const char *name;
67         enum commands cmd;
68 } COMMAND_TABLE;
69
70 COMMAND_TABLE cmd_table[] = {
71         {"create",      CMD_CREATE_TDB},
72         {"open",        CMD_OPEN_TDB},
73         {"erase",       CMD_ERASE},
74         {"dump",        CMD_DUMP},
75         {"insert",      CMD_INSERT},
76         {"move",        CMD_MOVE},
77         {"store",       CMD_STORE},
78         {"show",        CMD_SHOW},
79         {"keys",        CMD_KEYS},
80         {"hexkeys",     CMD_HEXKEYS},
81         {"delete",      CMD_DELETE},
82         {"list",        CMD_LIST_HASH_FREE},
83         {"free",        CMD_LIST_FREE},
84         {"info",        CMD_INFO},
85         {"speed",       CMD_SPEED},
86         {"mmap",        CMD_MMAP},
87         {"first",       CMD_FIRST},
88         {"1",           CMD_FIRST},
89         {"next",        CMD_NEXT},
90         {"n",           CMD_NEXT},
91         {"check",       CMD_CHECK},
92         {"quit",        CMD_QUIT},
93         {"q",           CMD_QUIT},
94         {"!",           CMD_SYSTEM},
95         {NULL,          CMD_HELP}
96 };
97
98 struct timeval tp1,tp2;
99
100 static void _start_timer(void)
101 {
102         gettimeofday(&tp1,NULL);
103 }
104
105 static double _end_timer(void)
106 {
107         gettimeofday(&tp2,NULL);
108         return((tp2.tv_sec - tp1.tv_sec) + 
109                (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
110 }
111
112 /* a tdb tool for manipulating a tdb database */
113
114 static TDB_CONTEXT *tdb;
115
116 static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
117 static int print_key(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
118 static int print_hexkey(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
119
120 static void print_asc(const char *buf,int len)
121 {
122         int i;
123
124         /* We're probably printing ASCII strings so don't try to display
125            the trailing NULL character. */
126
127         if (buf[len - 1] == 0)
128                 len--;
129
130         for (i=0;i<len;i++)
131                 printf("%c",isprint(buf[i])?buf[i]:'.');
132 }
133
134 static void print_data(const char *buf,int len)
135 {
136         int i=0;
137         if (len<=0) return;
138         printf("[%03X] ",i);
139         for (i=0;i<len;) {
140                 printf("%02X ",(int)((unsigned char)buf[i]));
141                 i++;
142                 if (i%8 == 0) printf(" ");
143                 if (i%16 == 0) {      
144                         print_asc(&buf[i-16],8); printf(" ");
145                         print_asc(&buf[i-8],8); printf("\n");
146                         if (i<len) printf("[%03X] ",i);
147                 }
148         }
149         if (i%16) {
150                 int n;
151                 
152                 n = 16 - (i%16);
153                 printf(" ");
154                 if (n>8) printf(" ");
155                 while (n--) printf("   ");
156                 
157                 n = i%16;
158                 if (n > 8) n = 8;
159                 print_asc(&buf[i-(i%16)],n); printf(" ");
160                 n = (i%16) - n;
161                 if (n>0) print_asc(&buf[i-n],n); 
162                 printf("\n");    
163         }
164 }
165
166 static void help(void)
167 {
168         printf("\n"
169 "tdbtool: \n"
170 "  create    dbname     : create a database\n"
171 "  open      dbname     : open an existing database\n"
172 "  erase                : erase the database\n"
173 "  dump                 : dump the database as strings\n"
174 "  keys                 : dump the database keys as strings\n"
175 "  hexkeys              : dump the database keys as hex values\n"
176 "  info                 : print summary info about the database\n"
177 "  insert    key  data  : insert a record\n"
178 "  move      key  file  : move a record to a destination tdb\n"
179 "  store     key  data  : store a record (replace)\n"
180 "  show      key        : show a record by key\n"
181 "  delete    key        : delete a record by key\n"
182 "  list                 : print the database hash table and freelist\n"
183 "  free                 : print the database freelist\n"
184 "  check                : check the integrity of an opened database\n"
185 "  ! command            : execute system command\n"
186 "  1 | first            : print the first record\n"
187 "  n | next             : print the next record\n"
188 "  q | quit             : terminate\n"
189 "  \\n                   : repeat 'next' command\n"
190 "\n");
191 }
192
193 static void terror(const char *why)
194 {
195         printf("%s\n", why);
196 }
197
198 static void create_tdb(const char *tdbname)
199 {
200         if (tdb) tdb_close(tdb);
201         tdb = tdb_open(tdbname, 0, TDB_CLEAR_IF_FIRST | (disable_mmap?TDB_NOMMAP:0),
202                        O_RDWR | O_CREAT | O_TRUNC, 0600);
203         if (!tdb) {
204                 printf("Could not create %s: %s\n", tdbname, strerror(errno));
205         }
206 }
207
208 static void open_tdb(const char *tdbname)
209 {
210         if (tdb) tdb_close(tdb);
211         tdb = tdb_open(tdbname, 0, disable_mmap?TDB_NOMMAP:0, O_RDWR, 0600);
212         if (!tdb) {
213                 printf("Could not open %s: %s\n", tdbname, strerror(errno));
214         }
215 }
216
217 static void insert_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
218 {
219         TDB_DATA key, dbuf;
220
221         if ((keyname == NULL) || (keylen == 0)) {
222                 terror("need key");
223                 return;
224         }
225
226         key.dptr = (unsigned char *)keyname;
227         key.dsize = keylen;
228         dbuf.dptr = (unsigned char *)data;
229         dbuf.dsize = datalen;
230
231         if (tdb_store(tdb, key, dbuf, TDB_INSERT) == -1) {
232                 terror("insert failed");
233         }
234 }
235
236 static void store_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
237 {
238         TDB_DATA key, dbuf;
239
240         if ((keyname == NULL) || (keylen == 0)) {
241                 terror("need key");
242                 return;
243         }
244
245         if ((data == NULL) || (datalen == 0)) {
246                 terror("need data");
247                 return;
248         }
249
250         key.dptr = (unsigned char *)keyname;
251         key.dsize = keylen;
252         dbuf.dptr = (unsigned char *)data;
253         dbuf.dsize = datalen;
254
255         printf("Storing key:\n");
256         print_rec(tdb, key, dbuf, NULL);
257
258         if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
259                 terror("store failed");
260         }
261 }
262
263 static void show_tdb(char *keyname, size_t keylen)
264 {
265         TDB_DATA key, dbuf;
266
267         if ((keyname == NULL) || (keylen == 0)) {
268                 terror("need key");
269                 return;
270         }
271
272         key.dptr = (unsigned char *)keyname;
273         key.dsize = keylen;
274
275         dbuf = tdb_fetch(tdb, key);
276         if (!dbuf.dptr) {
277             terror("fetch failed");
278             return;
279         }
280         
281         print_rec(tdb, key, dbuf, NULL);
282         
283         free( dbuf.dptr );
284         
285         return;
286 }
287
288 static void delete_tdb(char *keyname, size_t keylen)
289 {
290         TDB_DATA key;
291
292         if ((keyname == NULL) || (keylen == 0)) {
293                 terror("need key");
294                 return;
295         }
296
297         key.dptr = (unsigned char *)keyname;
298         key.dsize = keylen;
299
300         if (tdb_delete(tdb, key) != 0) {
301                 terror("delete failed");
302         }
303 }
304
305 static void move_rec(char *keyname, size_t keylen, char* tdbname)
306 {
307         TDB_DATA key, dbuf;
308         TDB_CONTEXT *dst_tdb;
309
310         if ((keyname == NULL) || (keylen == 0)) {
311                 terror("need key");
312                 return;
313         }
314
315         if ( !tdbname ) {
316                 terror("need destination tdb name");
317                 return;
318         }
319
320         key.dptr = (unsigned char *)keyname;
321         key.dsize = keylen;
322
323         dbuf = tdb_fetch(tdb, key);
324         if (!dbuf.dptr) {
325                 terror("fetch failed");
326                 return;
327         }
328         
329         print_rec(tdb, key, dbuf, NULL);
330         
331         dst_tdb = tdb_open(tdbname, 0, 0, O_RDWR, 0600);
332         if ( !dst_tdb ) {
333                 terror("unable to open destination tdb");
334                 return;
335         }
336         
337         if ( tdb_store( dst_tdb, key, dbuf, TDB_REPLACE ) == -1 ) {
338                 terror("failed to move record");
339         }
340         else
341                 printf("record moved\n");
342         
343         tdb_close( dst_tdb );
344         
345         return;
346 }
347
348 static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
349 {
350         printf("\nkey %d bytes\n", (int)key.dsize);
351         print_asc((const char *)key.dptr, key.dsize);
352         printf("\ndata %d bytes\n", (int)dbuf.dsize);
353         print_data((const char *)dbuf.dptr, dbuf.dsize);
354         return 0;
355 }
356
357 static int print_key(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
358 {
359         printf("key %d bytes: ", (int)key.dsize);
360         print_asc((const char *)key.dptr, key.dsize);
361         printf("\n");
362         return 0;
363 }
364
365 static int print_hexkey(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
366 {
367         printf("key %d bytes\n", (int)key.dsize);
368         print_data((const char *)key.dptr, key.dsize);
369         printf("\n");
370         return 0;
371 }
372
373 static int total_bytes;
374
375 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
376 {
377         total_bytes += dbuf.dsize;
378         return 0;
379 }
380
381 static void info_tdb(void)
382 {
383         int count;
384         total_bytes = 0;
385         if ((count = tdb_traverse(tdb, traverse_fn, NULL)) == -1)
386                 printf("Error = %s\n", tdb_errorstr(tdb));
387         else
388                 printf("%d records totalling %d bytes\n", count, total_bytes);
389 }
390
391 static void speed_tdb(const char *tlimit)
392 {
393         unsigned timelimit = tlimit?atoi(tlimit):0;
394         double t;
395         int ops;
396         if (timelimit == 0) timelimit = 5;
397
398         ops = 0;
399         printf("Testing store speed for %u seconds\n", timelimit);
400         _start_timer();
401         do {
402                 long int r = random();
403                 TDB_DATA key, dbuf;
404                 key.dptr = "store test";
405                 key.dsize = strlen(key.dptr);
406                 dbuf.dptr = (unsigned char *)&r;
407                 dbuf.dsize = sizeof(r);
408                 tdb_store(tdb, key, dbuf, TDB_REPLACE);
409                 t = _end_timer();
410                 ops++;
411         } while (t < timelimit);
412         printf("%10.3f ops/sec\n", ops/t);
413
414         ops = 0;
415         printf("Testing fetch speed for %u seconds\n", timelimit);
416         _start_timer();
417         do {
418                 long int r = random();
419                 TDB_DATA key, dbuf;
420                 key.dptr = "store test";
421                 key.dsize = strlen(key.dptr);
422                 dbuf.dptr = (unsigned char *)&r;
423                 dbuf.dsize = sizeof(r);
424                 tdb_fetch(tdb, key);
425                 t = _end_timer();
426                 ops++;
427         } while (t < timelimit);
428         printf("%10.3f ops/sec\n", ops/t);
429
430         ops = 0;
431         printf("Testing transaction speed for %u seconds\n", timelimit);
432         _start_timer();
433         do {
434                 long int r = random();
435                 TDB_DATA key, dbuf;
436                 key.dptr = "transaction test";
437                 key.dsize = strlen(key.dptr);
438                 dbuf.dptr = (unsigned char *)&r;
439                 dbuf.dsize = sizeof(r);
440                 tdb_transaction_start(tdb);
441                 tdb_store(tdb, key, dbuf, TDB_REPLACE);
442                 tdb_transaction_commit(tdb);
443                 t = _end_timer();
444                 ops++;
445         } while (t < timelimit);
446         printf("%10.3f ops/sec\n", ops/t);
447
448         ops = 0;
449         printf("Testing traverse speed for %u seconds\n", timelimit);
450         _start_timer();
451         do {
452                 tdb_traverse(tdb, traverse_fn, NULL);
453                 t = _end_timer();
454                 ops++;
455         } while (t < timelimit);
456         printf("%10.3f ops/sec\n", ops/t);
457 }
458
459 static void toggle_mmap(void)
460 {
461         disable_mmap = !disable_mmap;
462         if (disable_mmap) {
463                 printf("mmap is disabled\n");
464         } else {
465                 printf("mmap is enabled\n");
466         }
467 }
468
469 static char *tdb_getline(const char *prompt)
470 {
471         static char thisline[1024];
472         char *p;
473         fputs(prompt, stdout);
474         thisline[0] = 0;
475         p = fgets(thisline, sizeof(thisline)-1, stdin);
476         if (p) p = strchr(p, '\n');
477         if (p) *p = 0;
478         return p?thisline:NULL;
479 }
480
481 static int do_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf,
482                      void *state)
483 {
484     return tdb_delete(the_tdb, key);
485 }
486
487 static void first_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
488 {
489         TDB_DATA dbuf;
490         *pkey = tdb_firstkey(the_tdb);
491         
492         dbuf = tdb_fetch(the_tdb, *pkey);
493         if (!dbuf.dptr) terror("fetch failed");
494         else {
495                 print_rec(the_tdb, *pkey, dbuf, NULL);
496         }
497 }
498
499 static void next_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
500 {
501         TDB_DATA dbuf;
502         *pkey = tdb_nextkey(the_tdb, *pkey);
503         
504         dbuf = tdb_fetch(the_tdb, *pkey);
505         if (!dbuf.dptr) 
506                 terror("fetch failed");
507         else
508                 print_rec(the_tdb, *pkey, dbuf, NULL);
509 }
510
511 static int test_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
512 {
513         return 0;
514 }
515
516 static void check_db(TDB_CONTEXT *the_tdb)
517 {
518         int tdbcount=-1;
519         if (the_tdb) {
520                 tdbcount = tdb_traverse(the_tdb, test_fn, NULL);
521         } else {
522                 printf("Error: No database opened!\n");
523         }
524
525         if (tdbcount<0) {
526                 printf("Integrity check for the opened database failed.\n");
527         } else {
528                 printf("Database integrity is OK and has %d records.\n", tdbcount);
529         }
530 }
531
532 static int do_command(void)
533 {
534         COMMAND_TABLE *ctp = cmd_table;
535         enum commands mycmd = CMD_HELP;
536         int cmd_len;
537
538         if (cmdname && strlen(cmdname) == 0) {
539             mycmd = CMD_NEXT;
540         } else {
541             while (ctp->name) {
542                 cmd_len = strlen(ctp->name);
543                 if (strncmp(ctp->name,cmdname,cmd_len) == 0) {
544                         mycmd = ctp->cmd;
545                         break;
546                 }
547                 ctp++;
548             }
549         }
550
551         switch (mycmd) {
552         case CMD_CREATE_TDB:
553             bIterate = 0;
554             create_tdb(arg1);
555             return 0;
556         case CMD_OPEN_TDB:
557             bIterate = 0;
558             open_tdb(arg1);
559             return 0;
560         case CMD_SYSTEM:
561             /* Shell command */
562             if (system(arg1) == -1) {
563                 terror("system() call failed\n");
564             }
565             return 0;
566         case CMD_QUIT:
567             return 1;
568         default:
569             /* all the rest require a open database */
570             if (!tdb) {
571                 bIterate = 0;
572                 terror("database not open");
573                 help();
574                 return 0;
575             }
576             switch (mycmd) {
577             case CMD_ERASE:
578                 bIterate = 0;
579                 tdb_traverse(tdb, do_delete_fn, NULL);
580                 return 0;
581             case CMD_DUMP:
582                 bIterate = 0;
583                 tdb_traverse(tdb, print_rec, NULL);
584                 return 0;
585             case CMD_INSERT:
586                 bIterate = 0;
587                 insert_tdb(arg1, arg1len,arg2,arg2len);
588                 return 0;
589             case CMD_MOVE:
590                 bIterate = 0;
591                 move_rec(arg1,arg1len,arg2);
592                 return 0;
593             case CMD_STORE:
594                 bIterate = 0;
595                 store_tdb(arg1,arg1len,arg2,arg2len);
596                 return 0;
597             case CMD_SHOW:
598                 bIterate = 0;
599                 show_tdb(arg1, arg1len);
600                 return 0;
601             case CMD_KEYS:
602                 tdb_traverse(tdb, print_key, NULL);
603                 return 0;
604             case CMD_HEXKEYS:
605                 tdb_traverse(tdb, print_hexkey, NULL);
606                 return 0;
607             case CMD_DELETE:
608                 bIterate = 0;
609                 delete_tdb(arg1,arg1len);
610                 return 0;
611             case CMD_LIST_HASH_FREE:
612                 tdb_dump_all(tdb);
613                 return 0;
614             case CMD_LIST_FREE:
615                 tdb_printfreelist(tdb);
616                 return 0;
617             case CMD_INFO:
618                 info_tdb();
619                 return 0;
620             case CMD_SPEED:
621                 speed_tdb(arg1);
622                 return 0;
623             case CMD_MMAP:
624                 toggle_mmap();
625                 return 0;
626             case CMD_FIRST:
627                 bIterate = 1;
628                 first_record(tdb, &iterate_kbuf);
629                 return 0;
630             case CMD_NEXT:
631                if (bIterate)
632                   next_record(tdb, &iterate_kbuf);
633                 return 0;
634             case CMD_CHECK:
635                 check_db(tdb);
636                 return 0;
637             case CMD_HELP:
638                 help();
639                 return 0;
640             case CMD_CREATE_TDB:
641             case CMD_OPEN_TDB:
642             case CMD_SYSTEM:
643             case CMD_QUIT:
644                 /*
645                  * unhandled commands.  cases included here to avoid compiler
646                  * warnings.
647                  */
648                 return 0;
649             }
650         }
651
652         return 0;
653 }
654
655 static char *convert_string(char *instring, size_t *sizep)
656 {
657     size_t length = 0;
658     char *outp, *inp;
659     char temp[3];
660     
661
662     outp = inp = instring;
663
664     while (*inp) {
665         if (*inp == '\\') {
666             inp++;
667             if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) {
668                 temp[0] = *inp++;
669                 temp[1] = '\0';
670                 if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) {
671                     temp[1] = *inp++;
672                     temp[2] = '\0';
673                 }
674                 *outp++ = (char)strtol((const char *)temp,NULL,16);
675             } else {
676                 *outp++ = *inp++;
677             }
678         } else {
679             *outp++ = *inp++;
680         }
681         length++;
682     }
683     *sizep = length;
684     return instring;
685 }
686
687 int main(int argc, char *argv[])
688 {
689     cmdname = "";
690     arg1 = NULL;
691     arg1len = 0;
692     arg2 = NULL;
693     arg2len = 0;
694
695     if (argv[1]) {
696         cmdname = "open";
697         arg1 = argv[1];
698         do_command();
699         cmdname =  "";
700         arg1 = NULL;
701     }
702
703     switch (argc) {
704         case 1:
705         case 2:
706             /* Interactive mode */
707             while ((cmdname = tdb_getline("tdb> "))) {
708                 arg2 = arg1 = NULL;
709                 if ((arg1 = strchr((const char *)cmdname,' ')) != NULL) {
710                     arg1++;
711                     arg2 = arg1;
712                     while (*arg2) {
713                         if (*arg2 == ' ') {
714                             *arg2++ = '\0';
715                             break;
716                         }
717                         if ((*arg2++ == '\\') && (*arg2 == ' ')) {
718                             arg2++;
719                         }
720                     }
721                 }
722                 if (arg1) arg1 = convert_string(arg1,&arg1len);
723                 if (arg2) arg2 = convert_string(arg2,&arg2len);
724                 if (do_command()) break;
725             }
726             break;
727         case 5:
728             arg2 = convert_string(argv[4],&arg2len);
729         case 4:
730             arg1 = convert_string(argv[3],&arg1len);
731         case 3:
732             cmdname = argv[2];
733         default:
734             do_command();
735             break;
736     }
737
738     if (tdb) tdb_close(tdb);
739
740     return 0;
741 }