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 2 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, write to the Free Software
21   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22*/
23
24#include <errno.h>
25#include <stdlib.h>
26#include <stdio.h>
27#include <fcntl.h>
28#include <unistd.h>
29#include <string.h>
30#include <fcntl.h>
31#include <time.h>
32#include <sys/mman.h>
33#include <sys/stat.h>
34#include <sys/time.h>
35#include <ctype.h>
36#include <signal.h>
37#include "tdb.h"
38
39/* a tdb tool for manipulating a tdb database */
40
41#define FSTRING_LEN 256
42typedef char fstring[FSTRING_LEN];
43
44typedef struct connections_key {
45	pid_t pid;
46	int cnum;
47	fstring name;
48} connections_key;
49
50typedef struct connections_data {
51	int magic;
52	pid_t pid;
53	int cnum;
54	uid_t uid;
55	gid_t gid;
56	char name[24];
57	char addr[24];
58	char machine[128];
59	time_t start;
60} connections_data;
61
62static TDB_CONTEXT *tdb;
63
64static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
65
66static void print_asc(unsigned char *buf,int len)
67{
68	int i;
69
70	/* We're probably printing ASCII strings so don't try to display
71	   the trailing NULL character. */
72
73	if (buf[len - 1] == 0)
74	        len--;
75
76	for (i=0;i<len;i++)
77		printf("%c",isprint(buf[i])?buf[i]:'.');
78}
79
80static void print_data(unsigned char *buf,int len)
81{
82	int i=0;
83	if (len<=0) return;
84	printf("[%03X] ",i);
85	for (i=0;i<len;) {
86		printf("%02X ",(int)buf[i]);
87		i++;
88		if (i%8 == 0) printf(" ");
89		if (i%16 == 0) {
90			print_asc(&buf[i-16],8); printf(" ");
91			print_asc(&buf[i-8],8); printf("\n");
92			if (i<len) printf("[%03X] ",i);
93		}
94	}
95	if (i%16) {
96		int n;
97
98		n = 16 - (i%16);
99		printf(" ");
100		if (n>8) printf(" ");
101		while (n--) printf("   ");
102
103		n = i%16;
104		if (n > 8) n = 8;
105		print_asc(&buf[i-(i%16)],n); printf(" ");
106		n = (i%16) - n;
107		if (n>0) print_asc(&buf[i-n],n);
108		printf("\n");
109	}
110}
111
112static void help(void)
113{
114	printf("\n"
115"tdbtool: \n"
116"  create    dbname     : create a database\n"
117"  open      dbname     : open an existing database\n"
118"  erase                : erase the database\n"
119"  dump                 : dump the database as strings\n"
120"  insert    key  data  : insert a record\n"
121"  move      key  file  : move a record to a destination tdb\n"
122"  store     key  data  : store a record (replace)\n"
123"  show      key        : show a record by key\n"
124"  delete    key        : delete a record by key\n"
125"  list                 : print the database hash table and freelist\n"
126"  free                 : print the database freelist\n"
127"  1 | first            : print the first record\n"
128"  n | next             : print the next record\n"
129"  q | quit             : terminate\n"
130"  \\n                   : repeat 'next' command\n"
131"\n");
132}
133
134static void terror(char *why)
135{
136	printf("%s\n", why);
137}
138
139static char *get_token(int startover)
140{
141	static char tmp[1024];
142	static char *cont = NULL;
143	char *insert, *start;
144	char *k = strtok(NULL, " ");
145
146	if (!k)
147	  return NULL;
148
149	if (startover)
150	  start = tmp;
151	else
152	  start = cont;
153
154	strcpy(start, k);
155	insert = start + strlen(start) - 1;
156	while (*insert == '\\') {
157	  *insert++ = ' ';
158	  k = strtok(NULL, " ");
159	  if (!k)
160	    break;
161	  strcpy(insert, k);
162	  insert = start + strlen(start) - 1;
163	}
164
165	/* Get ready for next call */
166	cont = start + strlen(start) + 1;
167	return start;
168}
169
170static void create_tdb(void)
171{
172	char *tok = get_token(1);
173	if (!tok) {
174		help();
175		return;
176	}
177	if (tdb) tdb_close(tdb);
178	tdb = tdb_open(tok, 0, TDB_CLEAR_IF_FIRST,
179		       O_RDWR | O_CREAT | O_TRUNC, 0600);
180	if (!tdb) {
181		printf("Could not create %s: %s\n", tok, strerror(errno));
182	}
183}
184
185static void open_tdb(void)
186{
187	char *tok = get_token(1);
188	if (!tok) {
189		help();
190		return;
191	}
192	if (tdb) tdb_close(tdb);
193	tdb = tdb_open(tok, 0, 0, O_RDWR, 0600);
194	if (!tdb) {
195		printf("Could not open %s: %s\n", tok, strerror(errno));
196	}
197}
198
199static void insert_tdb(void)
200{
201	char *k = get_token(1);
202	char *d = get_token(0);
203	TDB_DATA key, dbuf;
204
205	if (!k || !d) {
206		help();
207		return;
208	}
209
210	key.dptr = k;
211	key.dsize = strlen(k)+1;
212	dbuf.dptr = d;
213	dbuf.dsize = strlen(d)+1;
214
215	if (tdb_store(tdb, key, dbuf, TDB_INSERT) == -1) {
216		terror("insert failed");
217	}
218}
219
220static void store_tdb(void)
221{
222	char *k = get_token(1);
223	char *d = get_token(0);
224	TDB_DATA key, dbuf;
225
226	if (!k || !d) {
227		help();
228		return;
229	}
230
231	key.dptr = k;
232	key.dsize = strlen(k)+1;
233	dbuf.dptr = d;
234	dbuf.dsize = strlen(d)+1;
235
236	printf("Storing key:\n");
237	print_rec(tdb, key, dbuf, NULL);
238
239	if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
240		terror("store failed");
241	}
242}
243
244static void show_tdb(void)
245{
246	char *k = get_token(1);
247	TDB_DATA key, dbuf;
248
249	if (!k) {
250		help();
251		return;
252	}
253
254	key.dptr = k;
255	key.dsize = strlen(k)+1;
256
257	dbuf = tdb_fetch(tdb, key);
258	if (!dbuf.dptr) {
259		/* maybe it is non-NULL terminated key? */
260		key.dsize = strlen(k);
261		dbuf = tdb_fetch(tdb, key);
262
263		if ( !dbuf.dptr ) {
264			terror("fetch failed");
265			return;
266		}
267	}
268
269	/* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
270	print_rec(tdb, key, dbuf, NULL);
271
272	free( dbuf.dptr );
273
274	return;
275}
276
277static void delete_tdb(void)
278{
279	char *k = get_token(1);
280	TDB_DATA key;
281
282	if (!k) {
283		help();
284		return;
285	}
286
287	key.dptr = k;
288	key.dsize = strlen(k)+1;
289
290	if (tdb_delete(tdb, key) != 0) {
291		terror("delete failed");
292	}
293}
294
295static void move_rec(void)
296{
297	char *k = get_token(1);
298	char *file = get_token(0);
299	TDB_DATA key, dbuf;
300	TDB_CONTEXT *dst_tdb;
301
302	if (!k) {
303		help();
304		return;
305	}
306
307	if ( !file ) {
308		terror("need destination tdb name");
309		return;
310	}
311
312	key.dptr = k;
313	key.dsize = strlen(k)+1;
314
315	dbuf = tdb_fetch(tdb, key);
316	if (!dbuf.dptr) {
317		/* maybe it is non-NULL terminated key? */
318		key.dsize = strlen(k);
319		dbuf = tdb_fetch(tdb, key);
320
321		if ( !dbuf.dptr ) {
322			terror("fetch failed");
323			return;
324		}
325	}
326
327	print_rec(tdb, key, dbuf, NULL);
328
329	dst_tdb = tdb_open(file, 0, 0, O_RDWR, 0600);
330	if ( !dst_tdb ) {
331		terror("unable to open destination tdb");
332		return;
333	}
334
335	if ( tdb_store( dst_tdb, key, dbuf, TDB_REPLACE ) == -1 ) {
336		terror("failed to move record");
337	}
338	else
339		printf("record moved\n");
340
341	tdb_close( dst_tdb );
342
343	return;
344}
345
346#if 0
347static int print_conn_key(TDB_DATA key)
348{
349	printf( "pid    =%5d   ", ((connections_key*)key.dptr)->pid);
350	printf( "cnum   =%10d  ", ((connections_key*)key.dptr)->cnum);
351	printf( "name   =[%s]\n", ((connections_key*)key.dptr)->name);
352	return 0;
353}
354
355static int print_conn_data(TDB_DATA dbuf)
356{
357	printf( "pid    =%5d   ", ((connections_data*)dbuf.dptr)->pid);
358	printf( "cnum   =%10d  ", ((connections_data*)dbuf.dptr)->cnum);
359	printf( "name   =[%s]\n", ((connections_data*)dbuf.dptr)->name);
360
361	printf( "uid    =%5d   ",  ((connections_data*)dbuf.dptr)->uid);
362	printf( "addr   =[%s]\n", ((connections_data*)dbuf.dptr)->addr);
363	printf( "gid    =%5d   ",  ((connections_data*)dbuf.dptr)->gid);
364	printf( "machine=[%s]\n", ((connections_data*)dbuf.dptr)->machine);
365	printf( "start  = %s\n",   ctime(&((connections_data*)dbuf.dptr)->start));
366	return 0;
367}
368#endif
369
370static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
371{
372#if 0
373	print_conn_key(key);
374	print_conn_data(dbuf);
375	return 0;
376#else
377	printf("\nkey %d bytes\n", key.dsize);
378	print_asc(key.dptr, key.dsize);
379	printf("\ndata %d bytes\n", dbuf.dsize);
380	print_data(dbuf.dptr, dbuf.dsize);
381	return 0;
382#endif
383}
384
385static int print_key(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
386{
387	print_asc(key.dptr, key.dsize);
388	printf("\n");
389	return 0;
390}
391
392static int total_bytes;
393
394static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
395{
396	total_bytes += dbuf.dsize;
397	return 0;
398}
399
400static void info_tdb(void)
401{
402	int count;
403	total_bytes = 0;
404	if ((count = tdb_traverse(tdb, traverse_fn, NULL) == -1))
405		printf("Error = %s\n", tdb_errorstr(tdb));
406	else
407		printf("%d records totalling %d bytes\n", count, total_bytes);
408}
409
410static char *tdb_getline(char *prompt)
411{
412	static char line[1024];
413	char *p;
414	fputs(prompt, stdout);
415	line[0] = 0;
416	p = fgets(line, sizeof(line)-1, stdin);
417	if (p) p = strchr(p, '\n');
418	if (p) *p = 0;
419	return p?line:NULL;
420}
421
422static int do_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf,
423                     void *state)
424{
425    return tdb_delete(the_tdb, key);
426}
427
428static void first_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
429{
430	TDB_DATA dbuf;
431	*pkey = tdb_firstkey(the_tdb);
432
433	dbuf = tdb_fetch(the_tdb, *pkey);
434	if (!dbuf.dptr) terror("fetch failed");
435	else {
436		/* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
437		print_rec(the_tdb, *pkey, dbuf, NULL);
438	}
439}
440
441static void next_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
442{
443	TDB_DATA dbuf;
444	*pkey = tdb_nextkey(the_tdb, *pkey);
445
446	dbuf = tdb_fetch(the_tdb, *pkey);
447	if (!dbuf.dptr)
448		terror("fetch failed");
449	else
450		/* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
451		print_rec(the_tdb, *pkey, dbuf, NULL);
452}
453
454int main(int argc, char *argv[])
455{
456    int bIterate = 0;
457    char *line;
458    char *tok;
459	TDB_DATA iterate_kbuf;
460
461    if (argv[1]) {
462	static char tmp[1024];
463        sprintf(tmp, "open %s", argv[1]);
464        tok=strtok(tmp," ");
465        open_tdb();
466    }
467
468    while ((line = tdb_getline("tdb> "))) {
469
470        /* Shell command */
471
472        if (line[0] == '!') {
473            system(line + 1);
474            continue;
475        }
476
477        if ((tok = strtok(line," "))==NULL) {
478           if (bIterate)
479              next_record(tdb, &iterate_kbuf);
480           continue;
481        }
482        if (strcmp(tok,"create") == 0) {
483            bIterate = 0;
484            create_tdb();
485            continue;
486        } else if (strcmp(tok,"open") == 0) {
487            open_tdb();
488            continue;
489        } else if ((strcmp(tok, "q") == 0) ||
490                   (strcmp(tok, "quit") == 0)) {
491            break;
492	}
493
494        /* all the rest require a open database */
495        if (!tdb) {
496            bIterate = 0;
497            terror("database not open");
498            help();
499            continue;
500        }
501
502        if (strcmp(tok,"insert") == 0) {
503            bIterate = 0;
504            insert_tdb();
505        } else if (strcmp(tok,"store") == 0) {
506            bIterate = 0;
507            store_tdb();
508        } else if (strcmp(tok,"show") == 0) {
509            bIterate = 0;
510            show_tdb();
511        } else if (strcmp(tok,"erase") == 0) {
512            bIterate = 0;
513            tdb_traverse(tdb, do_delete_fn, NULL);
514        } else if (strcmp(tok,"delete") == 0) {
515            bIterate = 0;
516            delete_tdb();
517        } else if (strcmp(tok,"dump") == 0) {
518            bIterate = 0;
519            tdb_traverse(tdb, print_rec, NULL);
520        } else if (strcmp(tok,"move") == 0) {
521            bIterate = 0;
522            move_rec();
523        } else if (strcmp(tok,"list") == 0) {
524            tdb_dump_all(tdb);
525        } else if (strcmp(tok, "free") == 0) {
526            tdb_printfreelist(tdb);
527        } else if (strcmp(tok,"info") == 0) {
528            info_tdb();
529        } else if ( (strcmp(tok, "1") == 0) ||
530                    (strcmp(tok, "first") == 0)) {
531            bIterate = 1;
532            first_record(tdb, &iterate_kbuf);
533        } else if ((strcmp(tok, "n") == 0) ||
534                   (strcmp(tok, "next") == 0)) {
535            next_record(tdb, &iterate_kbuf);
536        } else if ((strcmp(tok, "keys") == 0)) {
537                bIterate = 0;
538                tdb_traverse(tdb, print_key, NULL);
539        } else {
540            help();
541        }
542    }
543
544    if (tdb) tdb_close(tdb);
545
546    return 0;
547}
548