52c901f4e89e43bfbf45604bd64fb9deb97ed39d
[amitay/samba.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 "  speed                : perform speed tests on the database\n"
186 "  ! command            : execute system command\n"
187 "  1 | first            : print the first record\n"
188 "  n | next             : print the next record\n"
189 "  q | quit             : terminate\n"
190 "  \\n                   : repeat 'next' command\n"
191 "\n");
192 }
193
194 static void terror(const char *why)
195 {
196         printf("%s\n", why);
197 }
198
199 static void create_tdb(const char *tdbname)
200 {
201         if (tdb) tdb_close(tdb);
202         tdb = tdb_open(tdbname, 0, TDB_CLEAR_IF_FIRST | (disable_mmap?TDB_NOMMAP:0),
203                        O_RDWR | O_CREAT | O_TRUNC, 0600);
204         if (!tdb) {
205                 printf("Could not create %s: %s\n", tdbname, strerror(errno));
206         }
207 }
208
209 static void open_tdb(const char *tdbname)
210 {
211         if (tdb) tdb_close(tdb);
212         tdb = tdb_open(tdbname, 0, disable_mmap?TDB_NOMMAP:0, O_RDWR, 0600);
213         if (!tdb) {
214                 printf("Could not open %s: %s\n", tdbname, strerror(errno));
215         }
216 }
217
218 static void insert_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
219 {
220         TDB_DATA key, dbuf;
221
222         if ((keyname == NULL) || (keylen == 0)) {
223                 terror("need key");
224                 return;
225         }
226
227         key.dptr = (unsigned char *)keyname;
228         key.dsize = keylen;
229         dbuf.dptr = (unsigned char *)data;
230         dbuf.dsize = datalen;
231
232         if (tdb_store(tdb, key, dbuf, TDB_INSERT) == -1) {
233                 terror("insert failed");
234         }
235 }
236
237 static void store_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
238 {
239         TDB_DATA key, dbuf;
240
241         if ((keyname == NULL) || (keylen == 0)) {
242                 terror("need key");
243                 return;
244         }
245
246         if ((data == NULL) || (datalen == 0)) {
247                 terror("need data");
248                 return;
249         }
250
251         key.dptr = (unsigned char *)keyname;
252         key.dsize = keylen;
253         dbuf.dptr = (unsigned char *)data;
254         dbuf.dsize = datalen;
255
256         printf("Storing key:\n");
257         print_rec(tdb, key, dbuf, NULL);
258
259         if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
260                 terror("store failed");
261         }
262 }
263
264 static void show_tdb(char *keyname, size_t keylen)
265 {
266         TDB_DATA key, dbuf;
267
268         if ((keyname == NULL) || (keylen == 0)) {
269                 terror("need key");
270                 return;
271         }
272
273         key.dptr = (unsigned char *)keyname;
274         key.dsize = keylen;
275
276         dbuf = tdb_fetch(tdb, key);
277         if (!dbuf.dptr) {
278             terror("fetch failed");
279             return;
280         }
281         
282         print_rec(tdb, key, dbuf, NULL);
283         
284         free( dbuf.dptr );
285         
286         return;
287 }
288
289 static void delete_tdb(char *keyname, size_t keylen)
290 {
291         TDB_DATA key;
292
293         if ((keyname == NULL) || (keylen == 0)) {
294                 terror("need key");
295                 return;
296         }
297
298         key.dptr = (unsigned char *)keyname;
299         key.dsize = keylen;
300
301         if (tdb_delete(tdb, key) != 0) {
302                 terror("delete failed");
303         }
304 }
305
306 static void move_rec(char *keyname, size_t keylen, char* tdbname)
307 {
308         TDB_DATA key, dbuf;
309         TDB_CONTEXT *dst_tdb;
310
311         if ((keyname == NULL) || (keylen == 0)) {
312                 terror("need key");
313                 return;
314         }
315
316         if ( !tdbname ) {
317                 terror("need destination tdb name");
318                 return;
319         }
320
321         key.dptr = (unsigned char *)keyname;
322         key.dsize = keylen;
323
324         dbuf = tdb_fetch(tdb, key);
325         if (!dbuf.dptr) {
326                 terror("fetch failed");
327                 return;
328         }
329         
330         print_rec(tdb, key, dbuf, NULL);
331         
332         dst_tdb = tdb_open(tdbname, 0, 0, O_RDWR, 0600);
333         if ( !dst_tdb ) {
334                 terror("unable to open destination tdb");
335                 return;
336         }
337         
338         if ( tdb_store( dst_tdb, key, dbuf, TDB_REPLACE ) == -1 ) {
339                 terror("failed to move record");
340         }
341         else
342                 printf("record moved\n");
343         
344         tdb_close( dst_tdb );
345         
346         return;
347 }
348
349 static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
350 {
351         printf("\nkey %d bytes\n", (int)key.dsize);
352         print_asc((const char *)key.dptr, key.dsize);
353         printf("\ndata %d bytes\n", (int)dbuf.dsize);
354         print_data((const char *)dbuf.dptr, dbuf.dsize);
355         return 0;
356 }
357
358 static int print_key(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
359 {
360         printf("key %d bytes: ", (int)key.dsize);
361         print_asc((const char *)key.dptr, key.dsize);
362         printf("\n");
363         return 0;
364 }
365
366 static int print_hexkey(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
367 {
368         printf("key %d bytes\n", (int)key.dsize);
369         print_data((const char *)key.dptr, key.dsize);
370         printf("\n");
371         return 0;
372 }
373
374 static int total_bytes;
375
376 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
377 {
378         total_bytes += dbuf.dsize;
379         return 0;
380 }
381
382 static void info_tdb(void)
383 {
384         int count;
385         total_bytes = 0;
386         if ((count = tdb_traverse(tdb, traverse_fn, NULL)) == -1)
387                 printf("Error = %s\n", tdb_errorstr(tdb));
388         else
389                 printf("%d records totalling %d bytes\n", count, total_bytes);
390 }
391
392 static void speed_tdb(const char *tlimit)
393 {
394         unsigned timelimit = tlimit?atoi(tlimit):0;
395         double t;
396         int ops;
397         if (timelimit == 0) timelimit = 5;
398
399         ops = 0;
400         printf("Testing store speed for %u seconds\n", timelimit);
401         _start_timer();
402         do {
403                 long int r = random();
404                 TDB_DATA key, dbuf;
405                 key.dptr = (unsigned char *)"store test";
406                 key.dsize = strlen((char *)key.dptr);
407                 dbuf.dptr = (unsigned char *)&r;
408                 dbuf.dsize = sizeof(r);
409                 tdb_store(tdb, key, dbuf, TDB_REPLACE);
410                 t = _end_timer();
411                 ops++;
412         } while (t < timelimit);
413         printf("%10.3f ops/sec\n", ops/t);
414
415         ops = 0;
416         printf("Testing fetch speed for %u seconds\n", timelimit);
417         _start_timer();
418         do {
419                 long int r = random();
420                 TDB_DATA key, dbuf;
421                 key.dptr = (unsigned char *)"store test";
422                 key.dsize = strlen((char *)key.dptr);
423                 dbuf.dptr = (unsigned char *)&r;
424                 dbuf.dsize = sizeof(r);
425                 tdb_fetch(tdb, key);
426                 t = _end_timer();
427                 ops++;
428         } while (t < timelimit);
429         printf("%10.3f ops/sec\n", ops/t);
430
431         ops = 0;
432         printf("Testing transaction speed for %u seconds\n", timelimit);
433         _start_timer();
434         do {
435                 long int r = random();
436                 TDB_DATA key, dbuf;
437                 key.dptr = (unsigned char *)"transaction test";
438                 key.dsize = strlen((char *)key.dptr);
439                 dbuf.dptr = (unsigned char *)&r;
440                 dbuf.dsize = sizeof(r);
441                 tdb_transaction_start(tdb);
442                 tdb_store(tdb, key, dbuf, TDB_REPLACE);
443                 tdb_transaction_commit(tdb);
444                 t = _end_timer();
445                 ops++;
446         } while (t < timelimit);
447         printf("%10.3f ops/sec\n", ops/t);
448
449         ops = 0;
450         printf("Testing traverse speed for %u seconds\n", timelimit);
451         _start_timer();
452         do {
453                 tdb_traverse(tdb, traverse_fn, NULL);
454                 t = _end_timer();
455                 ops++;
456         } while (t < timelimit);
457         printf("%10.3f ops/sec\n", ops/t);
458 }
459
460 static void toggle_mmap(void)
461 {
462         disable_mmap = !disable_mmap;
463         if (disable_mmap) {
464                 printf("mmap is disabled\n");
465         } else {
466                 printf("mmap is enabled\n");
467         }
468 }
469
470 static char *tdb_getline(const char *prompt)
471 {
472         static char thisline[1024];
473         char *p;
474         fputs(prompt, stdout);
475         thisline[0] = 0;
476         p = fgets(thisline, sizeof(thisline)-1, stdin);
477         if (p) p = strchr(p, '\n');
478         if (p) *p = 0;
479         return p?thisline:NULL;
480 }
481
482 static int do_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf,
483                      void *state)
484 {
485     return tdb_delete(the_tdb, key);
486 }
487
488 static void first_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
489 {
490         TDB_DATA dbuf;
491         *pkey = tdb_firstkey(the_tdb);
492         
493         dbuf = tdb_fetch(the_tdb, *pkey);
494         if (!dbuf.dptr) terror("fetch failed");
495         else {
496                 print_rec(the_tdb, *pkey, dbuf, NULL);
497         }
498 }
499
500 static void next_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
501 {
502         TDB_DATA dbuf;
503         *pkey = tdb_nextkey(the_tdb, *pkey);
504         
505         dbuf = tdb_fetch(the_tdb, *pkey);
506         if (!dbuf.dptr) 
507                 terror("fetch failed");
508         else
509                 print_rec(the_tdb, *pkey, dbuf, NULL);
510 }
511
512 static int test_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
513 {
514         return 0;
515 }
516
517 static void check_db(TDB_CONTEXT *the_tdb)
518 {
519         int tdbcount=-1;
520         if (the_tdb) {
521                 tdbcount = tdb_traverse(the_tdb, test_fn, NULL);
522         } else {
523                 printf("Error: No database opened!\n");
524         }
525
526         if (tdbcount<0) {
527                 printf("Integrity check for the opened database failed.\n");
528         } else {
529                 printf("Database integrity is OK and has %d records.\n", tdbcount);
530         }
531 }
532
533 static int do_command(void)
534 {
535         COMMAND_TABLE *ctp = cmd_table;
536         enum commands mycmd = CMD_HELP;
537         int cmd_len;
538
539         if (cmdname && strlen(cmdname) == 0) {
540             mycmd = CMD_NEXT;
541         } else {
542             while (ctp->name) {
543                 cmd_len = strlen(ctp->name);
544                 if (strncmp(ctp->name,cmdname,cmd_len) == 0) {
545                         mycmd = ctp->cmd;
546                         break;
547                 }
548                 ctp++;
549             }
550         }
551
552         switch (mycmd) {
553         case CMD_CREATE_TDB:
554             bIterate = 0;
555             create_tdb(arg1);
556             return 0;
557         case CMD_OPEN_TDB:
558             bIterate = 0;
559             open_tdb(arg1);
560             return 0;
561         case CMD_SYSTEM:
562             /* Shell command */
563             if (system(arg1) == -1) {
564                 terror("system() call failed\n");
565             }
566             return 0;
567         case CMD_QUIT:
568             return 1;
569         default:
570             /* all the rest require a open database */
571             if (!tdb) {
572                 bIterate = 0;
573                 terror("database not open");
574                 help();
575                 return 0;
576             }
577             switch (mycmd) {
578             case CMD_ERASE:
579                 bIterate = 0;
580                 tdb_traverse(tdb, do_delete_fn, NULL);
581                 return 0;
582             case CMD_DUMP:
583                 bIterate = 0;
584                 tdb_traverse(tdb, print_rec, NULL);
585                 return 0;
586             case CMD_INSERT:
587                 bIterate = 0;
588                 insert_tdb(arg1, arg1len,arg2,arg2len);
589                 return 0;
590             case CMD_MOVE:
591                 bIterate = 0;
592                 move_rec(arg1,arg1len,arg2);
593                 return 0;
594             case CMD_STORE:
595                 bIterate = 0;
596                 store_tdb(arg1,arg1len,arg2,arg2len);
597                 return 0;
598             case CMD_SHOW:
599                 bIterate = 0;
600                 show_tdb(arg1, arg1len);
601                 return 0;
602             case CMD_KEYS:
603                 tdb_traverse(tdb, print_key, NULL);
604                 return 0;
605             case CMD_HEXKEYS:
606                 tdb_traverse(tdb, print_hexkey, NULL);
607                 return 0;
608             case CMD_DELETE:
609                 bIterate = 0;
610                 delete_tdb(arg1,arg1len);
611                 return 0;
612             case CMD_LIST_HASH_FREE:
613                 tdb_dump_all(tdb);
614                 return 0;
615             case CMD_LIST_FREE:
616                 tdb_printfreelist(tdb);
617                 return 0;
618             case CMD_INFO:
619                 info_tdb();
620                 return 0;
621             case CMD_SPEED:
622                 speed_tdb(arg1);
623                 return 0;
624             case CMD_MMAP:
625                 toggle_mmap();
626                 return 0;
627             case CMD_FIRST:
628                 bIterate = 1;
629                 first_record(tdb, &iterate_kbuf);
630                 return 0;
631             case CMD_NEXT:
632                if (bIterate)
633                   next_record(tdb, &iterate_kbuf);
634                 return 0;
635             case CMD_CHECK:
636                 check_db(tdb);
637                 return 0;
638             case CMD_HELP:
639                 help();
640                 return 0;
641             case CMD_CREATE_TDB:
642             case CMD_OPEN_TDB:
643             case CMD_SYSTEM:
644             case CMD_QUIT:
645                 /*
646                  * unhandled commands.  cases included here to avoid compiler
647                  * warnings.
648                  */
649                 return 0;
650             }
651         }
652
653         return 0;
654 }
655
656 static char *convert_string(char *instring, size_t *sizep)
657 {
658     size_t length = 0;
659     char *outp, *inp;
660     char temp[3];
661     
662
663     outp = inp = instring;
664
665     while (*inp) {
666         if (*inp == '\\') {
667             inp++;
668             if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) {
669                 temp[0] = *inp++;
670                 temp[1] = '\0';
671                 if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) {
672                     temp[1] = *inp++;
673                     temp[2] = '\0';
674                 }
675                 *outp++ = (char)strtol((const char *)temp,NULL,16);
676             } else {
677                 *outp++ = *inp++;
678             }
679         } else {
680             *outp++ = *inp++;
681         }
682         length++;
683     }
684     *sizep = length;
685     return instring;
686 }
687
688 int main(int argc, char *argv[])
689 {
690     cmdname = "";
691     arg1 = NULL;
692     arg1len = 0;
693     arg2 = NULL;
694     arg2len = 0;
695
696     if (argv[1]) {
697         cmdname = "open";
698         arg1 = argv[1];
699         do_command();
700         cmdname =  "";
701         arg1 = NULL;
702     }
703
704     switch (argc) {
705         case 1:
706         case 2:
707             /* Interactive mode */
708             while ((cmdname = tdb_getline("tdb> "))) {
709                 arg2 = arg1 = NULL;
710                 if ((arg1 = strchr((const char *)cmdname,' ')) != NULL) {
711                     arg1++;
712                     arg2 = arg1;
713                     while (*arg2) {
714                         if (*arg2 == ' ') {
715                             *arg2++ = '\0';
716                             break;
717                         }
718                         if ((*arg2++ == '\\') && (*arg2 == ' ')) {
719                             arg2++;
720                         }
721                     }
722                 }
723                 if (arg1) arg1 = convert_string(arg1,&arg1len);
724                 if (arg2) arg2 = convert_string(arg2,&arg2len);
725                 if (do_command()) break;
726             }
727             break;
728         case 5:
729             arg2 = convert_string(argv[4],&arg2len);
730         case 4:
731             arg1 = convert_string(argv[3],&arg1len);
732         case 3:
733             cmdname = argv[2];
734         default:
735             do_command();
736             break;
737     }
738
739     if (tdb) tdb_close(tdb);
740
741     return 0;
742 }