2a11cdaef3d0e22ab9a8739ac2d3c59637a32882
[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 "  ! 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=0;
396         if (timelimit == 0) timelimit = 10;
397         printf("Testing traverse speed for %u seconds\n", timelimit);
398         _start_timer();
399         while ((t=_end_timer()) < timelimit) {
400                 tdb_traverse(tdb, traverse_fn, NULL);
401                 printf("%10.3f ops/sec\r", (++ops)/t);
402         }
403         printf("\n");
404 }
405
406 static void toggle_mmap(void)
407 {
408         disable_mmap = !disable_mmap;
409         if (disable_mmap) {
410                 printf("mmap is disabled\n");
411         } else {
412                 printf("mmap is enabled\n");
413         }
414 }
415
416 static char *tdb_getline(const char *prompt)
417 {
418         static char thisline[1024];
419         char *p;
420         fputs(prompt, stdout);
421         thisline[0] = 0;
422         p = fgets(thisline, sizeof(thisline)-1, stdin);
423         if (p) p = strchr(p, '\n');
424         if (p) *p = 0;
425         return p?thisline:NULL;
426 }
427
428 static int do_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf,
429                      void *state)
430 {
431     return tdb_delete(the_tdb, key);
432 }
433
434 static void first_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
435 {
436         TDB_DATA dbuf;
437         *pkey = tdb_firstkey(the_tdb);
438         
439         dbuf = tdb_fetch(the_tdb, *pkey);
440         if (!dbuf.dptr) terror("fetch failed");
441         else {
442                 print_rec(the_tdb, *pkey, dbuf, NULL);
443         }
444 }
445
446 static void next_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
447 {
448         TDB_DATA dbuf;
449         *pkey = tdb_nextkey(the_tdb, *pkey);
450         
451         dbuf = tdb_fetch(the_tdb, *pkey);
452         if (!dbuf.dptr) 
453                 terror("fetch failed");
454         else
455                 print_rec(the_tdb, *pkey, dbuf, NULL);
456 }
457
458 static int test_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
459 {
460         return 0;
461 }
462
463 static void check_db(TDB_CONTEXT *the_tdb)
464 {
465         int tdbcount=-1;
466         if (the_tdb) {
467                 tdbcount = tdb_traverse(the_tdb, test_fn, NULL);
468         } else {
469                 printf("Error: No database opened!\n");
470         }
471
472         if (tdbcount<0) {
473                 printf("Integrity check for the opened database failed.\n");
474         } else {
475                 printf("Database integrity is OK and has %d records.\n", tdbcount);
476         }
477 }
478
479 static int do_command(void)
480 {
481         COMMAND_TABLE *ctp = cmd_table;
482         enum commands mycmd = CMD_HELP;
483         int cmd_len;
484
485         if (cmdname && strlen(cmdname) == 0) {
486             mycmd = CMD_NEXT;
487         } else {
488             while (ctp->name) {
489                 cmd_len = strlen(ctp->name);
490                 if (strncmp(ctp->name,cmdname,cmd_len) == 0) {
491                         mycmd = ctp->cmd;
492                         break;
493                 }
494                 ctp++;
495             }
496         }
497
498         switch (mycmd) {
499         case CMD_CREATE_TDB:
500             bIterate = 0;
501             create_tdb(arg1);
502             return 0;
503         case CMD_OPEN_TDB:
504             bIterate = 0;
505             open_tdb(arg1);
506             return 0;
507         case CMD_SYSTEM:
508             /* Shell command */
509             system(arg1);
510             return 0;
511         case CMD_QUIT:
512             return 1;
513         default:
514             /* all the rest require a open database */
515             if (!tdb) {
516                 bIterate = 0;
517                 terror("database not open");
518                 help();
519                 return 0;
520             }
521             switch (mycmd) {
522             case CMD_ERASE:
523                 bIterate = 0;
524                 tdb_traverse(tdb, do_delete_fn, NULL);
525                 return 0;
526             case CMD_DUMP:
527                 bIterate = 0;
528                 tdb_traverse(tdb, print_rec, NULL);
529                 return 0;
530             case CMD_INSERT:
531                 bIterate = 0;
532                 insert_tdb(arg1, arg1len,arg2,arg2len);
533                 return 0;
534             case CMD_MOVE:
535                 bIterate = 0;
536                 move_rec(arg1,arg1len,arg2);
537                 return 0;
538             case CMD_STORE:
539                 bIterate = 0;
540                 store_tdb(arg1,arg1len,arg2,arg2len);
541                 return 0;
542             case CMD_SHOW:
543                 bIterate = 0;
544                 show_tdb(arg1, arg1len);
545                 return 0;
546             case CMD_KEYS:
547                 tdb_traverse(tdb, print_key, NULL);
548                 return 0;
549             case CMD_HEXKEYS:
550                 tdb_traverse(tdb, print_hexkey, NULL);
551                 return 0;
552             case CMD_DELETE:
553                 bIterate = 0;
554                 delete_tdb(arg1,arg1len);
555                 return 0;
556             case CMD_LIST_HASH_FREE:
557                 tdb_dump_all(tdb);
558                 return 0;
559             case CMD_LIST_FREE:
560                 tdb_printfreelist(tdb);
561                 return 0;
562             case CMD_INFO:
563                 info_tdb();
564                 return 0;
565             case CMD_SPEED:
566                 speed_tdb(arg1);
567                 return 0;
568             case CMD_MMAP:
569                 toggle_mmap();
570                 return 0;
571             case CMD_FIRST:
572                 bIterate = 1;
573                 first_record(tdb, &iterate_kbuf);
574                 return 0;
575             case CMD_NEXT:
576                if (bIterate)
577                   next_record(tdb, &iterate_kbuf);
578                 return 0;
579             case CMD_CHECK:
580                 check_db(tdb);
581                 return 0;
582             case CMD_HELP:
583                 help();
584                 return 0;
585             case CMD_CREATE_TDB:
586             case CMD_OPEN_TDB:
587             case CMD_SYSTEM:
588             case CMD_QUIT:
589                 /*
590                  * unhandled commands.  cases included here to avoid compiler
591                  * warnings.
592                  */
593                 return 0;
594             }
595         }
596
597         return 0;
598 }
599
600 static char *convert_string(char *instring, size_t *sizep)
601 {
602     size_t length = 0;
603     char *outp, *inp;
604     char temp[3];
605     
606
607     outp = inp = instring;
608
609     while (*inp) {
610         if (*inp == '\\') {
611             inp++;
612             if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) {
613                 temp[0] = *inp++;
614                 temp[1] = '\0';
615                 if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) {
616                     temp[1] = *inp++;
617                     temp[2] = '\0';
618                 }
619                 *outp++ = (char)strtol((const char *)temp,NULL,16);
620             } else {
621                 *outp++ = *inp++;
622             }
623         } else {
624             *outp++ = *inp++;
625         }
626         length++;
627     }
628     *sizep = length;
629     return instring;
630 }
631
632 int main(int argc, char *argv[])
633 {
634     cmdname = "";
635     arg1 = NULL;
636     arg1len = 0;
637     arg2 = NULL;
638     arg2len = 0;
639
640     if (argv[1]) {
641         cmdname = "open";
642         arg1 = argv[1];
643         do_command();
644         cmdname =  "";
645         arg1 = NULL;
646     }
647
648     switch (argc) {
649         case 1:
650         case 2:
651             /* Interactive mode */
652             while ((cmdname = tdb_getline("tdb> "))) {
653                 arg2 = arg1 = NULL;
654                 if ((arg1 = strchr((const char *)cmdname,' ')) != NULL) {
655                     arg1++;
656                     arg2 = arg1;
657                     while (*arg2) {
658                         if (*arg2 == ' ') {
659                             *arg2++ = '\0';
660                             break;
661                         }
662                         if ((*arg2++ == '\\') && (*arg2 == ' ')) {
663                             arg2++;
664                         }
665                     }
666                 }
667                 if (arg1) arg1 = convert_string(arg1,&arg1len);
668                 if (arg2) arg2 = convert_string(arg2,&arg2len);
669                 if (do_command()) break;
670             }
671             break;
672         case 5:
673             arg2 = convert_string(argv[4],&arg2len);
674         case 4:
675             arg1 = convert_string(argv[3],&arg1len);
676         case 3:
677             cmdname = argv[2];
678         default:
679             do_command();
680             break;
681     }
682
683     if (tdb) tdb_close(tdb);
684
685     return 0;
686 }