Git init
[pkgs/e/elektra.git] / src / kdb / kdb-tool.c
1 /***************************************************************************
2                 kdb-tool.c  -  Tool for the kdb administration
3                              -------------------
4     begin                : Mon Mar 02 2003
5     copyright            : (C) 2003 by Avi Alkalay
6     email                : avi@unix.sh
7  ***************************************************************************/
8
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the BSD License (revised).                      *
13  *                                                                         *
14  ***************************************************************************/
15
16 #include <kdb-tool.h>
17
18 /*
19  * @defgroup libexample  The kdb Command Source Code: Example of Full Library Utilization
20  * @{
21  */
22
23 char *argComment=0;
24 char *argFile=0;
25 char *argData=0;
26 char *argKeyName=0;
27 char *argDomain=0;
28 uid_t *argUID=0;
29 uid_t *argGID=0;
30 int argCommand=0;
31 int argNoRecursive=KDB_O_NORECURSIVE;
32 int argLong=0;
33 int argValue=0;
34 int argAll=0;
35 int argSort=0;
36 int argDescriptive=0;
37 int argFullName=0;
38 int argShell=0;
39 int argGenerate=0;
40 int argOutput=0;
41 int argXML=0;
42 int argDir=0;
43 int argHelp=0;
44 mode_t argMode=0;
45 int argType=KEY_TYPE_UNDEFINED;
46
47
48 /* We'll load this methods dynamically to avoid libxml dependencies */
49 KSFromXMLfile ksFromXMLfile;
50 KSFromXML ksFromXML;
51
52 output ksToStream;
53 output ksOutput;
54 output ksGenerate;
55
56
57
58 /*
59  * Prints an error message related to @c errno on standard error, prefixed by @p msg.
60  * @see kdbStrError()
61  * @ingroup kdb
62  */
63 int kdbPrintError(const char * msg) {
64         fprintf (stderr, "%s\n", msg);
65         return 0;
66 }
67
68 /**
69  * Uses getopt to parse commandline options
70  **/
71 int parseCommandLine(int argc, char *argv[]) {
72         char sargType[ARGSIZE],argUser[ARGSIZE],argGroup[ARGSIZE];
73         char sargMode[ARGSIZE],sargCommand[ARGSIZE];
74         char * keyEnv;
75         size_t keyEnvLength=0, keyOptLength=0, keyOldLength;
76
77         int opt;
78
79         *sargType=*argUser=*argGroup=*sargCommand=*sargMode=0;
80
81         while ((opt=getopt(argc,argv,"ab:c:dfg:Ghilm:nOrRst:u:vXx"))!=-1)
82         {
83                 if (opt == EOF)
84                         break;
85                 switch (opt)
86                 {
87                 case 'a':
88                         argAll=1;
89                         break;
90                 case 'b':
91                         argFile=realloc(argFile,strlen(optarg)+1);
92                         assert(argFile!=NULL);
93                         strcpy(argFile,optarg);
94                         break;
95                 case 'c':
96                         argComment=realloc(argComment,strlen(optarg)+1);
97                         assert(argComment!=NULL);
98                         strcpy(argComment,optarg);
99                         break;
100                 case 'd':
101                         argDescriptive=1;
102                         argLong=1;
103                         argDir=1;
104                         break;
105                 case 'f':
106                         argFullName=1;
107                         break;
108                 case 'g':
109                         strncpy(argGroup,optarg,ARGSIZE);
110                         break;
111                 case 'G':
112                         argGenerate=1;
113                         break;
114                 case 'h':
115                         argHelp=1;
116                         break;
117                 case 'l':
118                         argLong=1;
119                         break;
120                 case 'm':
121                         strncpy(sargMode,optarg,ARGSIZE);
122                         break;
123                 case 'n':
124                         argSort=1;
125                         break;
126                 case 'O':
127                         argOutput=1;
128                         break;
129                 case 'R':
130                 case 'r':
131                         argNoRecursive=0;
132                         break;
133                 case 's':
134                         argShell=1;
135                         break;
136                 case 't':
137                         strncpy(sargType,optarg,ARGSIZE);
138                         break;
139                 case 'u':
140                         strncpy(argUser,optarg,ARGSIZE);
141                         break;
142                 case 'v':
143                         argValue=1;
144                         break;
145                 case 'X':
146                 case 'x':
147                         argXML=1;
148                         break;
149                 default:
150                         fprintf(stderr, "Unknown error (%d %c) in parsing arguments\n",
151                                         opt,opt);
152                         break;
153                 }
154         }
155
156         if (optind < argc) /*parse command*/
157         {
158                 strncpy(sargCommand,argv[optind],ARGSIZE);
159                 optind ++;
160         } else {
161                 commandHelp();
162                 exit(0);
163         }
164
165         keyEnv = getenv ("KDB_ROOT");
166         if (keyEnv) keyEnvLength = strlen (keyEnv) + 1;
167         else keyEnvLength = 0;
168         if (optind < argc) /*parse key name*/
169         {
170                 keyOptLength = strlen (argv[optind]) + 1;
171                 argKeyName=realloc(argKeyName,
172                         keyEnvLength + keyOptLength + 1);
173                 assert(argKeyName!=NULL);
174                 if (keyEnv) strncpy (argKeyName, keyEnv,   keyEnvLength);
175                 strncpy(argKeyName + keyEnvLength, argv[optind], keyOptLength);
176                 if (keyEnv) *(argKeyName+keyEnvLength-1) = '/';
177                 optind ++;
178         } else if (keyEnv) {
179                 argKeyName=realloc(argKeyName, keyEnvLength + 1);
180                 assert(argKeyName!=NULL);
181                 if (keyEnv) strncpy (argKeyName, keyEnv, keyEnvLength);
182         }
183
184         keyOptLength = 0;
185         keyOldLength = 0;
186         while (optind < argc) /* parse value (rest of arguments) */
187         {
188                 keyOptLength += strlen(argv[optind]) + 1;
189                 argData=realloc(argData,keyOptLength + 1);
190                 assert(argData!=NULL);
191                 if (keyOldLength > 0) *(argData+keyOldLength-1) = ' ';
192                 strcpy(argData+keyOldLength, argv[optind]);
193                 optind ++;
194                 keyOldLength = keyOptLength;
195         }
196
197         /* see if parsing worked:
198         fprintf (stderr, "command: %s\n", sargCommand);
199         fprintf (stderr, "key name: %s\n", argKeyName);
200         fprintf (stderr, "value: %s\n", argData);
201         exit (0);
202         */
203                 
204         /* End of command line argument reading. Now parse and finalize */
205
206         /* Check parsed command */
207         if (!strcmp(sargCommand,"ls"))           argCommand=CMD_LIST;
208         else if (!strcmp(sargCommand,"set"))     argCommand=CMD_SET;
209         else if (!strcmp(sargCommand,"get"))     argCommand=CMD_GET;
210         else if (!strcmp(sargCommand,"rm"))      argCommand=CMD_REMOVE;
211         else if (!strcmp(sargCommand,"vi"))      argCommand=CMD_EDIT;
212         else if (!strcmp(sargCommand,"edit"))    argCommand=CMD_EDIT;
213         else if (!strcmp(sargCommand,"load"))    argCommand=CMD_LOAD;
214         else if (!strcmp(sargCommand,"import"))  argCommand=CMD_LOAD;
215         else if (!strcmp(sargCommand,"save"))    argCommand=CMD_SAVE;
216         else if (!strcmp(sargCommand,"export"))  argCommand=CMD_SAVE;
217         else if (!strcmp(sargCommand,"mon"))     argCommand=CMD_MONITOR;
218         else if (!strcmp(sargCommand,"monitor")) argCommand=CMD_MONITOR;
219         else if (!strcmp(sargCommand,"mv"))      argCommand=CMD_MOVE;
220         else if (!strcmp(sargCommand,"info"))    argCommand=CMD_INFO;
221         else if (!strcmp(sargCommand,"help"))    argCommand=CMD_HELP;
222         else {
223                 fprintf(stderr,"kdb: Invalid subcommand.\n");
224                 return 1;
225         }
226
227         /* Parse type */
228         if (*sargType!=0) {
229                 /* TODO: use regex */
230                 if      (!strcmp(sargType,"string")) argType=KEY_TYPE_STRING;
231 #ifdef TUNNING_ELEKTRA_0_7
232       else if (!strcmp(sargType,"int"))    argType=KEY_TYPE_STRING+1;
233       else if (!strcmp(sargType,"double")) argType=KEY_TYPE_STRING+2;
234       else if (!strcmp(sargType,"bool")) argType=KEY_TYPE_STRING+3;
235 #endif
236                 else if (!strcmp(sargType,"bin"))    argType=KEY_TYPE_BINARY;
237                 else if (!strcmp(sargType,"binary")) argType=KEY_TYPE_BINARY;
238                 else if (!strcmp(sargType,"dir"))    argDir=1; /* bkwrds compatibility */
239                 else {
240                         argType=strtol(sargType,0,10);
241                         if (errno == ERANGE || errno == EINVAL)
242                                 /* handle undefined later */
243                                 argType=KEY_TYPE_UNDEFINED;
244                 }
245         } else if (argCommand==CMD_SET) { /* We must have a type */
246                 argType=KEY_TYPE_STRING;
247         }
248
249 #ifdef HAVE_PWD_H
250         /* Parse UID */
251         if (*argUser) {
252                 if (isdigit(*argUser)) {
253                         argUID=malloc(sizeof(uid_t));
254                         *argUID=atoi(argUser);
255                 } else {
256                         struct passwd *pwd;
257                         pwd=getpwnam(argUser);
258                         if (pwd) {
259                                 argUID=malloc(sizeof(uid_t));
260                                 *argUID=pwd->pw_uid;
261                         } else {
262                                 fprintf(stderr,"kdb: Invalid user \'%s\'. Ignoring\n", argUser);
263                         }
264                 }
265         }
266 #endif
267 #ifdef HAVE_GRP_H
268         /* Parse GID */
269         if (*argGroup) {
270                 if (isdigit(*argGroup)) {
271                         argGID=malloc(sizeof(gid_t));
272                         *argGID=atoi(argGroup);
273                 } else {
274                         struct group *grp;
275                         grp=getgrnam(argGroup);
276                         if (grp) {
277                                 argGID=malloc(sizeof(gid_t));
278                                 *argGID=grp->gr_gid;
279                         } else {
280                                 fprintf(stderr,"kdb: Invalid group \'%s\'. Ignoring\n",argGroup);
281                         }
282                 }
283         }
284 #endif
285
286
287         /* Parse permissions */
288         if (*sargMode!=0) argMode=strtol(sargMode,0,8);
289
290         return argCommand;
291 }
292
293
294
295
296
297
298 /*
299  * Helper for the 'kdb ls' command
300  *
301  */
302 int listMode(Key *key,char *readable) {
303         mode_t mode=keyGetMode(key);
304
305         if (keyIsDir(key)) readable[0]='d';
306         else readable[0]='-';
307
308         readable[1] = mode & S_IRUSR ? 'r' : '-';
309         readable[2] = mode & S_IWUSR ? 'w' : '-';
310         readable[3] = mode & S_IXUSR ? 'x' : '-';
311         #ifdef HAVE_WIN32
312         return 3;
313         #else
314         readable[4] = mode & S_IRGRP ? 'r' : '-';
315         readable[5] = mode & S_IWGRP ? 'w' : '-';
316         readable[6] = mode & S_IXGRP ? 'x' : '-';
317         readable[7] = mode & S_IROTH ? 'r' : '-';
318         readable[8] = mode & S_IWOTH ? 'w' : '-';
319         readable[9] = mode & S_IXOTH ? 'x' : '-';
320         return 9;
321         #endif
322 }
323
324
325
326
327 /*
328  * Helper for the 'kdb ls' command
329  *
330  */
331 int listTime(time_t when,char *readable) {
332         time_t current_time=time(0);
333         char buf[26];
334         #ifndef HAVE_CTIME_R
335         char *ctimep = NULL;
336         #endif
337         time_t six_months_ago;
338         int recent;
339
340         /* If the file appears to be in the future, update the current
341            time, in case the file happens to have been modified since
342            the last time we checked the clock.  */
343
344         /* Consider a time to be recent if it is within the past six
345            months.  A Gregorian year has 365.2425 * 24 * 60 * 60 ==
346            31556952 seconds on the average.  Write this value as an
347            integer constant to avoid floating point hassles.  */
348         six_months_ago = current_time - 31556952 / 2;
349         recent = (six_months_ago <= when) && (when <= current_time);
350 #ifdef HAVE_CTIME_R
351         ctime_r(&when,buf);
352 #else
353         ctimep = ctime(&when);
354         strncpy(buf, ctimep, sizeof(buf));
355 #endif
356         /*
357         buf now is like "Wed Jun 30 21:49:08 1993\n"
358         fprintf (stderr, "(%d), recent: %d buf: %s", strlen(buf), recent, buf);
359         */
360
361         if (recent) {
362                 memcpy(readable,buf+4,12); // copy "Jun 30 21:49"
363                 /* in readable are now the parts "Jun 30 21:49" */
364         } else {
365                 memcpy(readable,buf+4,7); // copy "Jun 30 "
366                 /* in readable are now the parts "Jun 30 " */
367                 readable[7]=' ';
368                 memcpy(readable+8,buf+20,4); // copy date
369         }
370         if (readable[4] == 32) readable[4] = '0'; // prefix date
371         return 12;
372 }
373
374
375 /*
376  * Helper for the 'kdb ls' command
377  *
378  */
379 void listSingleKey(Key *key) {
380         char buffer[400];
381         char *p=buffer;
382         char *unknown = "<unknown>";
383         int unknown_length= strlen (unknown);
384         char *binary = "<binary>";
385         int binary_length = strlen (binary);
386         struct passwd *pwd;
387
388
389         if (argLong) {
390                 p+=listMode(key,p);
391                 *p=' '; p++;
392                 *p=' '; p++;
393                 *p=' '; p++;
394 #ifdef HAVE_PWD_H
395                 if ( (pwd=getpwuid(keyGetUID(key))) != NULL ) {
396                         strcpy(p,pwd->pw_name);
397                         p+=strlen(pwd->pw_name);
398                         *p=' '; p++;
399                         *p=' '; p++;
400                 } else {
401                         strcpy(p, unknown);
402                         p+=unknown_length;
403                         *p=' '; p++;
404                         *p=' '; p++;
405                 }
406 #endif
407 #ifdef HAVE_GRP_H
408                 if ( (grp=getgrgid(keyGetGID(key))) != NULL ) {
409                         strcpy(p,grp->gr_name);
410                         p+=strlen(grp->gr_name);
411                         *p=' '; p++;
412                 } else {
413                         strcpy(p, unknown);
414                         p+=unknown_length;
415                         *p=' '; p++;
416                         *p=' '; p++;
417                 }
418 #endif
419                 /*sprintf(p,"%*d ",5,keyGetRecordSize(key));
420                 p+=strlen(p);*/
421
422                 p+=listTime(keyGetMTime(key),p);
423                 *p=' '; p++;
424         }
425
426         if (argFullName)
427                 p+=keyGetFullName(key,p,sizeof(buffer)-(p-buffer))-1;
428         else
429                 p+=keyGetName(key,p,sizeof(buffer)-(p-buffer))-1;
430
431         if (argValue && (keyGetValueSize(key)>1))
432         {
433                 *p='='; p++;
434
435                 if (keyIsString (key))
436                 {
437                         p+=keyGetString(key,p,sizeof(buffer)-(p-buffer))-1;
438                 } else if (keyIsBinary(key)) {
439                         strcpy (p, binary);
440                         p+=binary_length;
441                 }
442         }
443
444         puts(buffer);
445 }
446
447
448
449
450 /*
451  * Helper for the 'kdb ls' command
452  *
453  */
454 void listAllKeys (KeySet * ks) {
455         size_t listSize=ksGetSize(ks);
456         Key *walker;
457
458         if (listSize == 1) listSingleKey(ksHead(ks));
459         else if (listSize > 1) {
460                 ksRewind(ks);
461                 while ((walker=ksNext(ks)))
462                         listSingleKey(walker);
463         }
464 }
465
466
467
468 /*
469  * Helper for the 'kdb ls -s' command
470  *
471  */
472 void listAllKeysForShell(KeySet *ks) {
473         Key *walker=0;
474         char *isParent=0;
475         size_t parentNameSize=strlen(argKeyName);
476         const char *keyname=0;
477         const void *keyvalue=0;
478         char *child=0;
479         
480         ksRewind(ks);
481         while ((walker=ksNext(ks))) {
482                 keyname=keyName(walker);
483                 isParent=strstr(keyname,argKeyName);
484                 
485                 if (isParent) {
486                         keyname+=parentNameSize;
487                         while (*keyname && *keyname == '/') keyname++;
488                 } else {
489                         keyname=0;
490                 }
491                 
492                 /* At this point keyname points to the begining of the
493                    trailing key name after stripping argKeyName */
494                 if (keyname) {
495                         /* output key name */
496                         while ((child=strchr(keyname,'/'))) {
497                                 fwrite(keyname,child-keyname,1,stdout);
498                                 fputc('_',stdout);
499                                 keyname=child+1;
500                         }
501                         fputs(keyname,stdout); /* writes remaining stuff */
502                         
503                         /* now output the value surrounded by '"' */
504                         fputs("=\"",stdout);
505                         keyvalue=keyValue(walker);
506                         if (keyvalue) fputs(keyvalue,stdout);
507                         fputs("\";\n",stdout);
508                 }
509         }
510 }
511
512
513
514
515 /**
516  * The business logic behind 'kdb rm' command
517  * @par Example:
518  * @code
519  * bash$ kdb rm user/env/alias/ls   # get rid to the ls alias
520  * @endcode
521  *
522  * @see kdbRemove()
523  * @param argKeyName name of the key that will be removed
524  */
525 int commandRemove(KDB *handle)
526 {
527         if (!argKeyName) {
528                 fprintf(stderr,"kdb rm: No key name\n");
529                 return -1;
530         }
531
532         if (argNoRecursive)
533         {
534                 if (kdbRemove(handle,argKeyName) == -1)
535                 {
536                         char error[300];
537
538                         sprintf(error,"kdb rm: \'%s\'",argKeyName);
539                         kdbPrintError(error);
540                         return -1;
541                 }
542         } else { /* -R set */
543                 Key *key;
544                 KeySet *ks = ksNew(0);
545
546                 key=keyNew(argKeyName,KEY_END);
547
548                 if (!key) {
549                         fprintf(stderr,"kdb rm: No valid key name\n");
550                         return -1;
551                 }
552
553                 if (kdbGet(handle, ks, key, KDB_O_INACTIVE) == -1)
554                 {
555                         char error[500];
556
557                         if (ksCurrent(ks))
558                                 sprintf(error,"kdb rm: Could not get keys, problem appeared at \'%s\'",
559                                                 keyName(ksCurrent(ks)));
560                         else
561                                 sprintf(error,"kdb rm: Could not get keys below \'%s\'", argKeyName);
562                         kdbPrintError(error);
563                         keyDel (key);
564                         ksDel (ks);
565                         return -1;
566                 }
567                 if (kdbSet(handle, ks, key, KDB_O_REMOVEONLY) == -1)
568                 {
569                         char error[500];
570
571                         if (ksCurrent(ks))
572                                 sprintf(error,"kdb rm: Could not rm keys, problem appeared at \'%s\'", keyName(ksCurrent(ks)));
573                         else
574                                 sprintf(error,"kdb rm: Could not remove keys below \'%s\'", argKeyName);
575                         kdbPrintError(error);
576                         keyDel (key);
577                         ksDel (ks);
578                         return -1;
579                 }
580                 ksDel (ks);
581                 keyDel (key);
582         }
583
584         return 0;
585 }
586
587
588 /*
589  * Renames the key to a given newname in backend in an atomic way.
590  *
591  * @note the key will not exist afterwards in database
592  *
593  * @param handle contains internal information of @link kdbOpen() opened @endlink key database
594  * @param key an initialized Key retrieved from backend
595  * @param newname the new name which the name should have in backend
596  * @return 0 on success
597  * @return -1 on failure and @c errno is propagated
598  * @see kdbSet()
599  * @ingroup kdb
600  */
601 int kdbRename(KDB *handle, const Key *key, const char *newname) {
602         KeySet * ks = ksNew(0);
603         Key * toRename = keyDup(key);
604         Key * toRemove = keyDup(key);
605         keyRemove (toRemove);
606         ksAppendKey(ks, toRemove);
607
608         if (keySetName (toRename, newname) == -1)
609         {
610                 ksDel (ks);
611                 return -1;
612         }
613         ksAppendKey(ks, toRename);
614
615         if (kdbSet (handle, ks,0,0) == -1)
616         {
617 #if DEBUG
618                 printf ("kdbRename: kdbSet failed\n");
619 #endif
620                 ksDel (ks);
621                 return -1;
622         }
623
624         ksDel (ks);
625         return 0;
626 }
627
628
629
630 /**
631  * The business logic behind 'kdb mv' command.
632  * The central method used is kdbRename() but this function is
633  * way more robust, and is an example on how to handle errors.
634  * @par Example:
635  * @code
636  * bash# kdb mv user/env  user:tatiana/env
637  * @endcode
638  *
639  * @see kdbRename()
640  * @param argKeyName name of the source key
641  * @param argData name of the target key
642  */
643 int commandMove(KDB *handle) {
644         Key *key;
645         size_t size=0;
646         int rc;
647         
648         /* Consistency */
649         if (!argKeyName) {
650                 fprintf(stderr,"kdb mv: No target specified\n");
651                 return -1;
652         }
653
654         if (!argData) {
655                 fprintf(stderr,"kdb mv: \'%s\': No destination specified\n",argKeyName);
656                 return -1;
657         }
658         
659         key=keyNew(argKeyName,KEY_END);
660         size=keyGetNameSize(key);
661         
662         if (size == 0) {
663                 char error[100];
664                 
665                 sprintf(error,"kdb mv: \'%s\'", argKeyName);
666                 kdbPrintError(error);
667                 
668                 keyDel(key);
669                 return 1;
670         }
671         
672         rc=kdbRename(handle,key,argData);
673         if (rc != 0) {
674                 /* Handle a non-zero rc, with same behavior of Unix mv command */
675                 switch (errno) {
676                         
677                 }
678         }
679         
680         keyDel(key);
681         
682         return rc;
683 }
684
685
686
687 /**
688  * The business logic behind 'kdb set' command.
689  * Sets value to a single key.
690  *
691  * @par Example:
692  * @code
693  * bash$ kdb set -c "My shell prompt" user/env/env1/PS1 '\h:\w\$'
694  * @endcode
695  *
696  * @param argKeyName name of the key that will be set
697  * @param argComment comment to be set to key (-c)
698  * @param argType type of the key (-t)
699  * @param argMode mode permissions that will be set to sey (-m)
700  * @param argUID UID to be set to sey
701  * @param argGID GID to be set to sey
702  * @param argData the value to the key
703  * @param argFile a filename to use as the input for the value
704  * @see kdbSetKey()
705  */
706 int commandSet(KDB *handle) {
707         Key *key=0;
708         int ret=0;
709         char error[200];
710         size_t offset=0;
711
712
713         /* Consistency */
714         if (!argKeyName) {
715                 fprintf(stderr,"kdb set: No key name\n");
716                 return -1;
717         }
718
719         key=keyNew(argKeyName,KEY_END);
720
721         if (!key) {
722                 fprintf(stderr,"kdb set: No key name\n");
723                 return -1;
724         }
725         ret=kdbGetKey(handle,key);
726         if (ret == 0) { /* Key already exists. Good. */
727                 /* Use existed key type if user didn't give us one */
728                 if (argType==KEY_TYPE_UNDEFINED) argType=keyGetType(key);
729         }
730
731         /* Set or overwrite everything else... */
732         
733         if (argUID) keySetUID(key,*argUID);
734         if (argGID) keySetGID(key,*argGID);
735         if (argMode) keySetMode(key,argMode);
736         if (argDir) {
737                 keySetDir(key);
738         }
739
740         if (argComment) keySetComment(key,argComment);
741         
742         if (argFile) {
743                 FILE *f;
744                 int end=0;
745                 
746                 if (argData) free(argData);
747                 argData=0;
748                 f=fopen(argFile,"r");
749                 
750                 if (!f) {
751                         sprintf(error,"kdb set: \'%s\'",argFile);
752                         kdbPrintError(error);
753                         return -1;
754                 }
755                 while (! end) {
756                         char buffer[100];
757                         ssize_t r;
758                         
759                         r=read(fileno(f),buffer,sizeof(buffer));
760                         switch (r) {
761                                 case 0:
762                                         r=lseek(fileno(f),0,SEEK_END)-offset;
763                                         end=1;
764                                         break;
765                                 case -1:
766                                         /* those bizarre errors */
767                                         fprintf(stderr,"kdb set: \'%s\': problem reading file\n",argFile);
768                                         fclose(f);
769                                         return -1;
770                         }
771                         argData=realloc(argData,offset+r);
772                         assert(argData!=NULL);
773                         memcpy(argData+offset,buffer,r);
774                         offset+=r;
775                 }
776                 fclose(f);
777         }
778
779
780         /* Set key value . . . */
781         if (argType == KEY_TYPE_UNDEFINED)
782                 keySetString(key,argData); /* the most common here */
783         else if (argData) { /* Handle special type values . . . */
784         
785                 /* set raw data */
786                 if (offset) keySetRaw(key,argData,offset);
787                 else if (KEY_TYPE_BINARY <= argType && argType < KEY_TYPE_STRING)
788                          /* command-line-passed bin values have unwanted \0 in the end */
789                          keySetRaw(key,argData,strlen(argData));
790                 else keySetRaw(key,argData,strlen(argData)+1);
791                 
792                 /* set type explicitly */
793                 keySetType(key,argType);
794         }
795
796
797         ret=kdbSetKey(handle,key);
798         if (ret) {
799                 sprintf(error,"kdb set: \'%s\'",argKeyName);
800                 kdbPrintError(error);
801         }
802         
803         keyDel(key);
804         
805         return ret;
806 }
807
808
809
810
811 /**
812  * The business logic behind 'kdb ls' command.
813  * @param argKeyName key name to be listed
814  * @param argNoRecursive whether to act recursively (-R)
815  * @param argValue whether to show key values or not (-v)
816  * @param argAll whether to list also inactive keys (-a)
817  * @param argShell operate in a shell script friendly mode (-s)
818  * @param argXML whether to create XML output (-x)
819  *
820  * @par Example:
821  * @code
822  * bash$ kdb ls -R   # list all keys from system and user trees
823  * bash$ kdb ls -Ra  # list them all plus the hidden/inactive keys
824  * bash$ kdb ls -Rav # list all showing value
825  * bash# kdb ls -Rxv # equivalent to 'kdb export'
826  * bash$ kdb ls -Rv user/env # list my aliases and environment vars
827  * @endcode
828  *
829  * @see keyToStream(), ksToStream()
830  * @see commandExport() for the 'kdb export' command
831  */
832 int commandList(KDB *handle) {
833         KeySet *ks; /* this is the container for all keys we'll collect bellow */
834         ssize_t ret;
835         unsigned long options=0;
836
837         /* Build our option set */
838         
839         if (argSort)                options |= KDB_O_SORT;
840         if (argNoRecursive)         options |= KDB_O_NORECURSIVE;
841         if (argAll)                 options |= KDB_O_INACTIVE;
842         if (!argValue)              options |= KDB_O_STATONLY;
843         
844         /* these options make sense only to ksToStream() */
845         if (argFullName)            options |= KDB_O_FULLNAME;
846         /* ksToStream() defaults */ options |= KDB_O_HEADER | KDB_O_HIER;
847
848         ks=ksNew(0);
849
850         if (!argKeyName || strcmp(argKeyName, "/") == 0)
851         {
852                 Key *walker=0;
853                 KeySet *tmp = ksNew(0);
854
855                 /* User don't want a specific key, so list the root keys */
856                 kdbGet(handle,tmp,0,0);
857
858                 if (! argNoRecursive) {
859                         
860                         while ((walker=ksPop(tmp))) {
861                                 /* walk root by root, retrieve entire subtree
862                                  * and append it to ks
863                                  */
864                                 KeySet *thisRoot=ksNew(0);
865                                 
866                                 ret=kdbGet(handle,thisRoot,walker,options | KDB_O_POP);
867                                 
868                                 /* A hack to transfer a key from a keyset to another.
869                                  * Don't do this at home.
870                                  */
871                                 ksAppendKey(ks,walker);
872                                 ksAppend(ks,thisRoot);
873                                 ksDel(thisRoot); /* we don't need the container anymore */
874                         }
875                 } else ksAppend(ks,tmp);
876                 ksDel(tmp);
877         } else {
878                 /* User gave us a specific key to start with */
879
880                 ret=kdbGetByName (handle,ks,argKeyName,options);
881
882                 if (ret<0) {
883                         /* We got an error. Check if it is because its not a folder key */
884                         if (errno==ENOTDIR) {
885                                 /* We still have a chance, since there is something there */
886                                 Key *key=keyNew(argKeyName,KEY_END);
887
888                                 if (!key)
889                                 {
890                                         char error[200];
891
892                                         keyDel(key);
893                                         ksDel(ks);
894                                         
895                                         sprintf(error,"kdb ls: %s",argKeyName);
896                                         kdbPrintError(error);
897                                         return ret;
898                                 }
899
900                                 if (argValue)
901                                         ret=kdbGetKey(handle,key);
902                                 else ret=kdbStatKey(handle,key);
903                                 
904                                 if (ret == 0) ksAppendKey(ks,key);
905                                 else {
906                                         /* There is absolutely nothing there */
907                                         char error[200];
908
909                                         keyDel(key);
910                                         ksDel(ks);
911                                         
912                                         sprintf(error,"kdb ls: %s",argKeyName);
913                                         kdbPrintError(error);
914                                         return ret;
915                                 }
916                                 keyDel (key);
917                         } else { /* A real error */
918                                 char error[200];
919
920                                 ksDel(ks);
921
922                                 sprintf(error,"kdb ls: %s",argKeyName);
923                                 kdbPrintError(error);
924                                 return ret;
925                         }
926                 }
927         }
928
929         /* Better give it a sort */
930         if (ksNeedSort (ks)) ksSort (ks);
931
932         if (argXML) ksToStream(ks,stdout,options);
933         else if (argShell) listAllKeysForShell(ks);
934         else if (argGenerate) ksGenerate(ks,stdout, 0);
935         else if (argOutput) ksOutput(ks,stdout, KEY_VALUE|KEY_COMMENT);
936         else listAllKeys(ks);
937
938         ksDel(ks);
939         return 0;
940 }
941
942
943
944
945
946
947
948 /**
949  * Business logic behind the 'kdb get' command.
950  * Get a key and return its value to you.
951  *
952  * @par Example:
953  * @code
954  * bash$ kdb get user/env/alias/ls
955  * ls -Fh --color=tty
956  * @endcode
957  *
958  * @param argKeyName key to get value
959  * @param argDescriptive show also the key comment (-d)
960  * @param argShell output suitable for shell scripts (-s)
961  * @param argLong show also the key name (-l)
962  * @param argFullName with @p argLong, show the user domain too (-f)
963  *
964  * @see kdbGetKey(), kdbGetBaseName(), keyGetComment(), keyGetString()
965  *
966  */
967 int commandGet(KDB *handle) {
968         int ret;
969         Key *key = 0;
970         char *buffer = 0; // used two times
971         char *p;
972         size_t size,cs=0;
973         type_t keyType;
974         char error[200];
975
976
977         if (!argKeyName) {
978                 fprintf(stderr,"kdb get: No key name\n");
979                 fprintf(stderr,"run kdb get -h for more info\n");
980                 return -1;
981         }
982         
983         key=keyNew(argKeyName,KEY_END);
984
985         if (argKeyName[0] == '/')
986         {
987                 buffer = malloc (strlen (argKeyName)+sizeof("system\0"));
988                 
989                 strcpy (buffer, "user\0");
990                 keySetName(key, strcat (buffer, argKeyName));
991                 ret=kdbGetKey(handle,key);
992                 if (ret == 0) goto done;
993                 
994                 strcpy (buffer, "system\0");
995                 keySetName(key, strcat (buffer, argKeyName));
996                 ret=kdbGetKey(handle,key);
997                 if (ret == 0) goto done;
998         } else {        
999                 ret=kdbGetKey(handle,key);
1000         }
1001
1002         if (ret) {
1003                 sprintf(error,"kdb get: %s",argKeyName);
1004                 kdbPrintError(error);
1005                 goto cleanup;
1006         }
1007 done:
1008         size=keyGetValueSize(key);
1009         if (argDescriptive) {
1010                 cs=keyGetCommentSize(key);
1011                 if (cs) size+=cs+3;
1012         }
1013         if (argShell) {
1014                 size+=keyGetBaseNameSize(key);
1015                 size+=2; /* for 2 '"' to wrap the value */
1016         } else if (argLong) {
1017                 if (argFullName) size+=keyGetFullNameSize(key);
1018                 else size+=keyGetNameSize(key);
1019         }
1020
1021
1022         if (buffer) free (buffer);
1023         p=buffer=malloc(size);
1024
1025
1026         if (argDescriptive) {
1027                 if (cs>1) {
1028                         p+=sprintf(p,"# ");
1029                         p+=keyGetComment(key,p,size-(p-buffer));
1030                         *--p='\n'; p++;
1031                 }
1032         }
1033         if (argShell) {
1034                 p+=keyGetBaseName(key,p,size-(p-buffer));
1035                 *--p='='; p++;
1036                 *p='\"'; p++;
1037         } else if (argLong) {
1038                 if (argFullName) p+=keyGetFullName(key,p,size-(p-buffer));
1039                 else p+=keyGetName(key,p,size-(p-buffer));
1040                 *--p='='; p++;
1041         }
1042         
1043         keyType=keyGetType(key);
1044
1045         if (keyIsBinary(key)) p+=keyGetBinary(key,p,size-(p-buffer));
1046         else p+=keyGetString(key,p,size-(p-buffer));
1047         if (argShell) {
1048                 *--p='\"'; p++;
1049                 *p=0;
1050         }
1051         if (keyIsBinary(key)) fwrite(buffer,size,1,stdout);
1052         else printf("%s\n",buffer);
1053
1054         ret = 0;
1055
1056 cleanup:
1057         free(buffer);
1058         keyDel(key);
1059
1060         return ret;
1061 }
1062
1063
1064
1065
1066
1067
1068 /**
1069  * Opens an editor to edit an XML representation of the keys.
1070  * This is one of the most complex commands of the kdb program.
1071  * It will
1072  * -# retrieve the desired keys
1073  * -# put them inside an editor in an XML format to let the user change them
1074  * -# wait for the editor to finish
1075  * -# reread the edited XML, converting to an internal KeySet
1076  * -# compare original and edited KeySets, with ksCompare(), to detect
1077  *    differences
1078  * -# remove removed keys
1079  * -# update updated keys
1080  * -# add added keys
1081  * -# leave untouched the not-changed keys
1082  *
1083  * @par Example:
1084  * @code
1085  * bash$ EDITOR=kedit kdb edit -R user/env # edit with kedit
1086  * bash# kdb edit -R system/sw/MyApp       # defaults to vi editor
1087  * @endcode
1088  *
1089  * @param argKeyName the parent key name (and children) that will be edited
1090  * @param argRecursive whether to act recursivelly or not
1091  * @param argAll whether to edit inactive keys or not
1092  * @param EDITOR environment var that defines editor to use, or @p vi
1093  * @see keyCompare(), ksCompare(), kdbGetByName()
1094  *      ksToStream(), kdbRemoveKey()
1095  */
1096 int commandEdit(KDB *handle) {
1097         KeySet *ks;
1098         KeySet *ksEdited;
1099         KeySet *toRemove;
1100         Key *current;
1101         int ret;
1102         char filename[]="/var/tmp/kdbeditXXXXXX";
1103         char command[300];
1104         //FILE *xmlfile=0;
1105         char choice[5];
1106
1107         if (!ksFromXMLfile) return 1;
1108         
1109         ks=ksNew(0);
1110
1111         kdbGetByName(handle, ks, argKeyName, (argAll?KDB_O_INACTIVE:0) | (argNoRecursive?KDB_O_NORECURSIVE:0));
1112
1113         if (! ksGetSize(ks)) {
1114                 /* Maybe the user parameter is not a parent key, but a single key */
1115                 current=keyNew(argKeyName,KEY_END);
1116                 if (kdbGetKey(handle,current)) {
1117                         /* Failed. Cleanup */
1118                         keyDel(current);
1119                         current=0;
1120                 } else {
1121                         /* We have something. */
1122                         ksAppendKey(ks,current);
1123                         current=0;
1124                 }
1125         }
1126
1127 /*
1128         for (current=ks.start; current; current=current->next) {
1129                 if (keyNeedSync(current)) {
1130                         printf("%s needs sync\n",current->key);
1131                 }
1132         }
1133 */
1134
1135         /*
1136         TODO
1137         xmlfile=fdopen(mkstemp(filename),"rw+");
1138
1139         ksToStream(ks,xmlfile,KDB_O_XMLHEADERS | KDB_O_HIER |
1140                 KDB_O_FULLNAME | KDB_O_FULLUGID);
1141         fclose(xmlfile);
1142         */
1143
1144         do {
1145                 /* execute the editor and wait for it to finish */
1146                 sprintf(command,"[ -z \"$EDITOR\" ] && EDITOR=vi; $EDITOR %s",filename);
1147                 system(command);
1148
1149                 toRemove=ksNew(0);
1150                 ksEdited=ksNew(0);
1151
1152                 /* ksFromXML is not a library function.
1153                  * It is implemented in and for this program only.
1154                  * It is pretty reusable code, though.
1155                  */
1156                 ret=ksFromXMLfile(ksEdited,filename);
1157                 if (ret!=0) {
1158                         printf("kdb cannot import this file, because it is not valid !\n");
1159                         strcpy(choice,"");
1160                         while (choice[0]!='E' && choice[0]!='C') {
1161                                 printf("Do you want to edit it again or to cancel ? (E/C) : ");
1162                                 fgets(choice,4, stdin );
1163                         }
1164                 }
1165         } while (ret!=0 && choice[0]=='E');
1166         remove(filename);
1167         
1168         if (ret==0) {
1169                 /*TODO ksCompare
1170                 ksCompare(ks,ksEdited,toRemove);
1171                 */
1172         
1173                 /* Discard ksEdited because there is nothing else here
1174                 * after keyCompare() */
1175                 ksDel(ksEdited);
1176                 
1177                 /* Commit changed keys */
1178                 ksRewind(ks);
1179                 while ((ret=kdbSet(handle,ks,0,0))) {
1180                         /* We got an error. Warn user. */
1181                         Key *problem;
1182                         char error[500];
1183                         char keyname[300]="";
1184                         
1185                         problem=ksCurrent(ks);
1186                         if (problem) keyGetFullName(problem,keyname,sizeof(keyname));
1187                         sprintf(error,"kdb edit: while setting/updating %s", keyname);
1188                         kdbPrintError(error);
1189                         
1190                         /* And try to set keys again starting from the next key,
1191                         * unless we reached the end of the KeySet */
1192                         if (ksNext(ks) == 0) break;
1193                 }
1194                 
1195                 ksDel(ks); /* Finished with this KeySet */
1196         
1197                 /* Remove removed keys */
1198                 ksRewind(toRemove);
1199                 while ((current=ksNext(toRemove))) {
1200                         char keyname[800];
1201         
1202                         keyGetFullName(current,keyname,sizeof(keyname));
1203                         ret=kdbRemove(handle,keyname);
1204                         if (ret != 0) {
1205                                 char error[850];
1206                                 
1207                                 sprintf(error,"kdb edit: while removing %s",keyname);
1208                                 kdbPrintError(error);
1209                         }
1210                 }
1211         
1212                 /* Finished with this KeySet too */
1213                 ksDel(toRemove);
1214                 }
1215         
1216         return 0;
1217 }
1218
1219 ssize_t kdbbGetFullFilename(KDB *handle, const Key *forKey,char *returned,size_t maxSize);
1220
1221 /**
1222  * Business logic behind the 'kdb info' command.
1223  * Displays some information about the Elektra library, version, backend, etc.
1224  *
1225  * @par Example:
1226  * @code
1227  * bash$ kdb info
1228  * @endcode
1229  * 
1230  * @see kdbGetInfo(), kdbInfoToString(), kdbFreeInfo()
1231  */
1232 int commandInfo(KDB *handle)
1233 {
1234         char buffer [MAX_PATH_LENGTH];
1235         Key *k;
1236         int rc;
1237
1238         if (!argKeyName)
1239         {
1240                 printf ("Supply a keyname as second argument to deduce where the key will be stored on disk.\n");
1241                 return 1;
1242         }
1243
1244         k = keyNew (argKeyName, KEY_END);
1245
1246         if (!k)
1247         {
1248                 printf ("Supply a valid keyname.\n");
1249                 return 1;
1250         }
1251
1252         rc = kdbbGetFullFilename(handle, k, buffer, MAX_PATH_LENGTH);
1253
1254         if (rc == -1)
1255         {
1256                 printf ("failure\n");
1257                 return 1;
1258         } else {
1259                 printf ("%s\n", buffer);
1260                 return 0;
1261         }
1262 }
1263
1264
1265 /**
1266  * Business logic behind the 'kdb import' command.
1267  * Import an XML file (or standard input) into the key database.
1268  * This is usefull to import full application's keys, or restore backups.
1269  *
1270  * @par Example:
1271  * @code
1272  * bash$ kdb import myAppDefaultKeys.xml
1273  * bash$ generateKeys | kdb import
1274  * @endcode
1275  * 
1276  * @see commandExport()
1277  */
1278 int commandImport(KDB *handle) {
1279         KeySet *ks;
1280         int ret;
1281
1282         if (!ksFromXMLfile || !ksFromXML) return 1;
1283         
1284         
1285         ks=ksNew(0);
1286         /* The command line parsing function will put the XML filename
1287            in the argKeyName global. */
1288         if (argKeyName) ksFromXMLfile(ks,argKeyName);
1289         else ksFromXML(ks,fileno(stdin) /* more elegant then just '0' */);
1290
1291         ksRewind(ks);
1292         while ((ret=kdbSet(handle,ks,0,0))==-1) {
1293                 /* We got an error. Warn user. */
1294                 Key *problem;
1295                 char error[500]="";
1296                 char keyname[300]="";
1297
1298                 problem=ksCurrent(ks);
1299                 if (problem) keyGetFullName(problem,keyname,sizeof(keyname));
1300                 sprintf(error,"kdb import: while importing %s", keyname);
1301                 kdbPrintError(error);
1302                 
1303                 /* And try to set keys again starting from the next key,
1304                  *  unless we reached the end of KeySet */
1305                 if (ksNext(ks) == 0) break;
1306         }
1307         
1308         ksDel(ks);
1309         
1310         return ret;
1311 }
1312
1313
1314
1315
1316
1317 /**
1318  * Business logic behind the 'kdb export' command.
1319  * Export a set of keys to an XML format. Usefull to make backups or copy
1320  * keys to other machine or user.
1321  * Equivalent to 'kdb ls -xRv base/key/name'
1322  *
1323  * @par Example:
1324  * @code
1325  * bash# kdb export system > systemConfigurationBackup.xml
1326  * bash# kdb export system/sw/MyApp > myAppConfiguration.xml
1327  * bash$ kdb export system/sw/MyApp | sed -e 's|system/sw|user/sw|g' | kdb import
1328  * @endcode
1329  *
1330  * @see commandList(), commandImport()
1331  *
1332  */
1333 int commandExport(KDB *handle)
1334 {
1335         KeySet *ks;
1336         ssize_t ret;
1337
1338         if (!argKeyName)
1339         {
1340                 printf ("You have to pass a keyname as argument\n");
1341                 return 1;
1342         }
1343
1344         ks = ksNew (0);
1345         ret = kdbGetByName(handle, ks, argKeyName, KDB_O_INACTIVE);
1346
1347         if (ret == 0)
1348         {
1349                 fprintf (stderr, "You try to print in the middle of a backend which does not support\n");
1350                 fprintf (stderr, "getting only some keys.\n");
1351                 return 1;
1352         }
1353         else if (ret == -1)
1354         {
1355                 kdbPrintError ("Could not get keys, reason:");
1356                 ksDel (ks);
1357                 return 2;
1358         }
1359
1360         else if (argGenerate) ksGenerate(ks,stdout, 0);
1361         else if (argOutput) ksOutput(ks,stdout, KEY_VALUE|KEY_COMMENT);
1362         else ksToStream(ks,stdout,0);
1363
1364         ksDel (ks);
1365
1366         return 0;
1367 }
1368
1369
1370 /**
1371  * Business logic behind 'kdb mon' command.
1372  *
1373  * Will block your command line until some change happens to the
1374  * interested key.
1375  *
1376  * @par Example:
1377  * @code
1378  * bash$ kdb mon system/sw/MyApp/someKey
1379  * @endcode
1380  *
1381  * @see kdbMonitorKey(), kdbMonitorKeys()
1382  */
1383 int commandMonitor(KDB *handle) {
1384 #if 0
1385         Key *toMonitor;
1386         uint32_t diff;
1387         
1388
1389         toMonitor=keyNew(argKeyName,KEY_NEEDSYNC,handle,KEY_END);
1390         
1391         diff=kdbMonitorKey(handle,
1392                 toMonitor,           /* key to monitor */
1393                 KEY_VALUE,    /* key info we are interested in */
1394                 0,                   /* how many times to poll. 0 = ad-infinitum */
1395                 500                  /* usecs between polls. 0 defaults to 1 second */);
1396
1397         /*
1398          * Since in our case we'll hang completelly until we get a key's
1399          * value change, we don't have to check diff.
1400          * So if method returned, the value has changed, and toMonitor has it.
1401          */
1402         printf("New value is %s\n",(char *)keyValue(toMonitor));
1403         
1404         keyDel(toMonitor);
1405
1406 #endif
1407
1408         return 0;
1409 }
1410
1411 kdbLibHandle dlhandle=0;
1412
1413 void closeToolsLib(void)
1414 {
1415         kdbLibClose (dlhandle);
1416 }
1417
1418 int loadToolsLib(void)
1419 {
1420
1421         kdbLibInit();
1422
1423         dlhandle=kdbLibLoad("libelektratools");
1424         if (dlhandle == 0) {
1425                 return 1;
1426         }
1427
1428         ksFromXMLfile=(KSFromXMLfile)kdbLibSym(dlhandle,"ksFromXMLfile");
1429         ksFromXML=(KSFromXML)kdbLibSym(dlhandle,"ksFromXML");
1430
1431         ksToStream = (output) kdbLibSym (dlhandle, "ksToStream");
1432         ksOutput   = (output) kdbLibSym (dlhandle, "ksOutput");
1433         ksGenerate = (output) kdbLibSym (dlhandle, "ksGenerate");
1434
1435         atexit(closeToolsLib);
1436
1437         return 0;
1438 }
1439
1440
1441 int doCommand(int command, KDB *handle) {
1442         switch (command) {
1443                 case CMD_SET:             return commandSet(handle);
1444                 case CMD_LIST:            return commandList(handle);
1445                 case CMD_GET:             return commandGet(handle);
1446                 case CMD_REMOVE:          return commandRemove(handle);
1447                 case CMD_EDIT:            return commandEdit(handle);
1448                 case CMD_LOAD:            return commandImport(handle);
1449                 case CMD_SAVE:            return commandExport(handle);
1450                 case CMD_MONITOR:         return commandMonitor(handle);
1451                 case CMD_MOVE:            return commandMove(handle);
1452                 case CMD_INFO:            return commandInfo(handle);
1453                 case CMD_HELP:            return commandHelp(handle);
1454         }
1455         return 0;
1456 }
1457
1458
1459 int main(int argc, char **argv) {
1460         KDB *handle=0;
1461         int command=0;
1462         int ret=0;
1463
1464
1465         if (loadToolsLib())
1466                 fprintf(stderr,"kdb: XML importing and editing disabled\n");
1467         
1468         /* Parse the command line */
1469         command=parseCommandLine(argc,argv);
1470
1471         /* Check if user only wants some help (kdb -h {command})*/
1472         if (argHelp) helpCommand(command);
1473
1474         /* Open key database */
1475         handle = kdbOpen();
1476
1477         if (handle == 0) {
1478                 fprintf (stderr, "kdb: could not open key database\n");
1479                 exit (1);
1480         }
1481
1482         /* Execute command with parameters from command line and exit
1483          * program afterwards.*/
1484         ret = doCommand(command,handle);
1485
1486         kdbClose(handle);
1487         return ret;
1488 }
1489
1490 /*
1491  * @}
1492  */