Imported Upstream version 2.5.1
[debian/amanda] / client-src / clientconf.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-2000 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Author: James da Silva, Systems Design and Analysis Group
24  *                         Computer Science Department
25  *                         University of Maryland at College Park
26  */
27 /*
28  * $Id: clientconf.c,v 1.17 2006/07/25 19:36:48 martinea Exp $
29  *
30  * read configuration file
31  */
32 /*
33  *
34  * XXX - I'm not happy *at all* with this implementation, but I don't
35  * think YACC would be any easier.  A more table based implementation
36  * would be better.  Also clean up memory leaks.
37  */
38 #include "amanda.h"
39 #include "arglist.h"
40 #include "util.h"
41 #include "clientconf.h"
42 #include "clock.h"
43
44 /* configuration parameters */
45 static char *cln_config_dir = NULL;
46
47 val_t client_conf[CLN_CLN];
48
49 command_option_t *client_options      = NULL;
50 int               client_options_size = 0;
51
52 /* predeclare local functions */
53
54 static void init_defaults(void);
55 static void read_conffile_recursively(char *filename);
56
57 static int read_confline(void);
58
59 keytab_t client_keytab[] = {
60     { "CONF", CONF_CONF },
61     { "INDEX_SERVER", CONF_INDEX_SERVER },
62     { "TAPE_SERVER", CONF_TAPE_SERVER },
63     { "TAPEDEV", CONF_TAPEDEV },
64     { "AUTH", CONF_AUTH },
65     { "SSH_KEYS", CONF_SSH_KEYS },
66     { "AMANDAD_PATH", CONF_AMANDAD_PATH },
67     { "CLIENT_USERNAME", CONF_CLIENT_USERNAME },
68     { "GNUTAR_LIST_DIR", CONF_GNUTAR_LIST_DIR },
69     { "AMANDATES", CONF_AMANDATES },
70     { "INCLUDEFILE", CONF_INCLUDEFILE },
71     { NULL, CONF_UNKNOWN },
72 };
73
74 t_conf_var client_var [] = {
75    { CONF_CONF           , CONFTYPE_STRING, read_string, CLN_CONF           , NULL },
76    { CONF_INDEX_SERVER   , CONFTYPE_STRING, read_string, CLN_INDEX_SERVER   , NULL },
77    { CONF_TAPE_SERVER    , CONFTYPE_STRING, read_string, CLN_TAPE_SERVER    , NULL },
78    { CONF_TAPEDEV        , CONFTYPE_STRING, read_string, CLN_TAPEDEV        , NULL },
79    { CONF_AUTH           , CONFTYPE_STRING, read_string, CLN_AUTH           , NULL },
80    { CONF_SSH_KEYS       , CONFTYPE_STRING, read_string, CLN_SSH_KEYS       , NULL },
81    { CONF_AMANDAD_PATH   , CONFTYPE_STRING, read_string, CLN_AMANDAD_PATH   , NULL },
82    { CONF_CLIENT_USERNAME, CONFTYPE_STRING, read_string, CLN_CLIENT_USERNAME, NULL },
83    { CONF_GNUTAR_LIST_DIR, CONFTYPE_STRING, read_string, CLN_GNUTAR_LIST_DIR, NULL },
84    { CONF_AMANDATES      , CONFTYPE_STRING, read_string, CLN_AMANDATES      , NULL },
85    { CONF_UNKNOWN        , CONFTYPE_INT   , NULL       , CLN_CLN            , NULL }
86 };
87
88 static int first_file = 1;
89
90 /*
91 ** ------------------------
92 **  External entry points
93 ** ------------------------
94 */
95
96 /* return  0 on success        */
97 /* return  1 on error          */
98 /* return -1 if file not found */
99
100 int read_clientconf(
101     char *filename)
102 {
103     if(first_file == 1) {
104         init_defaults();
105         first_file = 0;
106     } else {
107         allow_overwrites = 1;
108     }
109
110     /* We assume that conf_confname & conf are initialized to NULL above */
111     read_conffile_recursively(filename);
112
113     command_overwrite(client_options, client_var, client_keytab, client_conf,
114                       "");
115
116     return got_parserror;
117 }
118
119
120 char *
121 client_getconf_byname(
122     char *      str)
123 {
124     static char *tmpstr;
125     char number[NUM_STR_SIZE];
126     t_conf_var *np;
127     keytab_t *kt;
128     char *s;
129     char ch;
130
131     tmpstr = stralloc(str);
132     s = tmpstr;
133     while((ch = *s++) != '\0') {
134         if(islower((int)ch))
135             s[-1] = (char)toupper(ch);
136     }
137
138     for(kt = client_keytab; kt->token != CONF_UNKNOWN; kt++)
139         if(kt->keyword && strcmp(kt->keyword, tmpstr) == 0) break;
140
141     if(kt->token == CONF_UNKNOWN) return NULL;
142
143     for(np = client_var; np->token != CONF_UNKNOWN; np++)
144         if(np->token == kt->token) break;
145
146     if(np->type == CONFTYPE_INT) {
147         snprintf(number, SIZEOF(number), "%d", client_getconf_int(np->parm));
148         tmpstr = newstralloc(tmpstr, number);
149     } else if(np->type == CONFTYPE_BOOL) {
150         if(client_getconf_boolean(np->parm) == 0) {
151             tmpstr = newstralloc(tmpstr, "off");
152         }
153         else {
154             tmpstr = newstralloc(tmpstr, "on");
155         }
156     } else if(np->type == CONFTYPE_REAL) {
157         snprintf(number, SIZEOF(number), "%lf", client_getconf_real(np->parm));
158         tmpstr = newstralloc(tmpstr, number);
159     } else {
160         tmpstr = newstralloc(tmpstr, client_getconf_str(np->parm));
161     }
162
163     return tmpstr;
164 }
165
166 int
167 client_getconf_seen(
168     cconfparm_t parm)
169 {
170     t_conf_var *np;
171     np = get_np(client_var, parm);
172     return(client_conf[np->parm].seen);
173 }
174
175 int
176 client_getconf_boolean(
177     cconfparm_t parm)
178 {
179     t_conf_var *np;
180     np = get_np(client_var, parm);
181     if (np->type != CONFTYPE_BOOL) {
182         error("client_getconf_boolean: np is not a CONFTYPE_BOOL");
183         /*NOTREACHED*/
184     }
185     return(client_conf[np->parm].v.i != 0);
186 }
187
188 int
189 client_getconf_int(
190     cconfparm_t parm)
191 {
192     t_conf_var *np;
193     np = get_np(client_var, parm);
194     if (np->type != CONFTYPE_INT) {
195         error("client_getconf_int: np is not a CONFTYPE_INT");
196         /*NOTREACHED*/
197     }
198
199     return(client_conf[np->parm].v.i);
200 }
201
202 off_t
203 client_getconf_am64(
204     cconfparm_t parm)
205 {
206     t_conf_var *np;
207     np = get_np(client_var, parm);
208     if (np->type != CONFTYPE_AM64) {
209         error("client_getconf_am64: np is not a CONFTYPE_AM64");
210         /*NOTREACHED*/
211     }
212     return(client_conf[np->parm].v.am64);
213 }
214
215 double
216 client_getconf_real(
217     cconfparm_t parm)
218 {
219     t_conf_var *np;
220     np = get_np(client_var, parm);
221     if (np->type != CONFTYPE_REAL) {
222         error("client_getconf_real: np is not a CONFTYPE_REAL");
223         /*NOTREACHED*/
224     }
225     return(client_conf[np->parm].v.r);
226 }
227
228 char *
229 client_getconf_str(
230     cconfparm_t parm)
231 {
232     t_conf_var *np;
233     np = get_np(client_var, parm);
234     if (np->type != CONFTYPE_STRING) {
235         error("client_getconf_string: np is not a CONFTYPE_STRING");
236         /*NOTREACHED*/
237     }
238     return(client_conf[np->parm].v.s);
239 }
240
241 /*
242 ** ------------------------
243 **  Internal routines
244 ** ------------------------
245 */
246
247
248 static void
249 init_defaults(void)
250 {
251     char *s;
252
253     /* defaults for exported variables */
254
255 #ifdef DEFAULT_CONFIG
256     s = DEFAULT_CONFIG;
257 #else
258     s = "";
259 #endif
260     conf_init_string(&client_conf[CLN_CONF], s);
261
262 #ifdef DEFAULT_SERVER
263     s = DEFAULT_SERVER;
264 #else
265     s = "";
266 #endif
267     conf_init_string(&client_conf[CLN_INDEX_SERVER], s);
268
269
270 #ifdef DEFAULT_TAPE_SERVER
271     s = DEFAULT_TAPE_SERVER;
272 #else
273 #ifdef DEFAULT_SERVER
274     s = DEFAULT_SERVER;
275 #else
276     s = "";
277 #endif
278 #endif
279     conf_init_string(&client_conf[CLN_TAPE_SERVER], s);
280
281 #ifdef DEFAULT_TAPE_DEVICE
282     s = DEFAULT_TAPE_DEVICE;
283 #else
284     s = NULL;
285 #endif
286     conf_init_string(&client_conf[CLN_TAPEDEV], s);
287
288     conf_init_string(&client_conf[CLN_AUTH], "bsd");
289     conf_init_string(&client_conf[CLN_SSH_KEYS], "");
290     conf_init_string(&client_conf[CLN_AMANDAD_PATH], "");
291     conf_init_string(&client_conf[CLN_CLIENT_USERNAME], "");
292 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR
293     conf_init_string(&client_conf[CLN_GNUTAR_LIST_DIR],
294                      GNUTAR_LISTED_INCREMENTAL_DIR);
295 #else
296     conf_init_string(&client_conf[CLN_GNUTAR_LIST_DIR], NULL);
297 #endif
298     conf_init_string(&client_conf[CLN_AMANDATES], "/etc/amandates");
299     /* defaults for internal variables */
300
301     conf_line_num = got_parserror = 0;
302     allow_overwrites = 0;
303     token_pushed = 0;
304
305 }
306
307 static void
308 read_conffile_recursively(
309     char *      filename)
310 {
311     /* Save globals used in read_confline(), elsewhere. */
312     int  save_line_num  = conf_line_num;
313     FILE *save_conf     = conf_conf;
314     char *save_confname = conf_confname;
315     int rc;
316
317     if (*filename == '/' || cln_config_dir == NULL) {
318         conf_confname = stralloc(filename);
319     } else {
320         conf_confname = stralloc2(cln_config_dir, filename);
321     }
322
323     if((conf_conf = fopen(conf_confname, "r")) == NULL) {
324         dbprintf(("Could not open conf file \"%s\": %s\n", conf_confname,
325                   strerror(errno)));
326         amfree(conf_confname);
327         got_parserror = -1;
328         return;
329     }
330     dbprintf(("Reading conf file \"%s\".\n", conf_confname));
331
332     conf_line_num = 0;
333
334     /* read_confline() can invoke us recursively via "includefile" */
335     do {
336         rc = read_confline();
337     } while (rc != 0);
338     afclose(conf_conf);
339
340     amfree(conf_confname);
341
342     /* Restore globals */
343     conf_line_num = save_line_num;
344     conf_conf     = save_conf;
345     conf_confname = save_confname;
346 }
347
348
349 /* ------------------------ */
350
351
352 static int
353 read_confline(void)
354 {
355     t_conf_var *np;
356
357     keytable = client_keytab;
358
359     conf_line_num += 1;
360     get_conftoken(CONF_ANY);
361     switch(tok) {
362     case CONF_INCLUDEFILE:
363         {
364             char *fn;
365
366             get_conftoken(CONF_STRING);
367             fn = tokenval.v.s;
368             read_conffile_recursively(fn);
369         }
370         break;
371
372     case CONF_NL:       /* empty line */
373         break;
374
375     case CONF_END:      /* end of file */
376         return 0;
377
378     default:
379         {
380             for(np = client_var; np->token != CONF_UNKNOWN; np++)
381                 if(np->token == tok) break;
382
383             if(np->token == CONF_UNKNOWN) {
384                 conf_parserror("configuration keyword expected");
385             } else {
386                 np->read_function(np, &client_conf[np->parm]);
387                 if(np->validate)
388                     np->validate(np, &client_conf[np->parm]);
389             }
390         }
391     }
392     if(tok != CONF_NL)
393         get_conftoken(CONF_NL);
394     return 1;
395 }
396
397
398
399 /* ------------------------ */
400
401 #ifdef TEST
402
403 static char *cln_config_name = NULL;
404 static char *cln_config_dir = NULL;
405
406 void
407 dump_client_configuration(
408     char *filename)
409 {
410     printf("AMANDA CLIENT CONFIGURATION FROM FILE \"%s\":\n\n", filename);
411
412     printf("cln_conf = \"%s\"\n", client_getconf_str(CLN_CONF));
413     printf("cln_index_server = \"%s\"\n", client_getconf_str(CLN_INDEX_SERVER));
414     printf("cln_tape_server = \"%s\"\n", client_getconf_str(CLN_TAPE_SERVER));
415     printf("cln_tapedev = \"%s\"\n", client_getconf_str(CLN_TAPEDEV));
416     printf("cln_auth = \"%s\"\n", client_getconf_str(CLN_AUTH));
417     printf("cln_ssh_keys = \"%s\"\n", client_getconf_str(CLN_SSH_KEYS));
418 }
419
420 int
421 main(
422     int         argc,
423     char **     argv)
424 {
425   char *conffile;
426   char *diskfile;
427   disklist_t lst;
428   int result;
429   unsigned long malloc_hist_1, malloc_size_1;
430   unsigned long malloc_hist_2, malloc_size_2;
431
432   safe_fd(-1, 0);
433
434   set_pname("conffile");
435
436   /* Don't die when child closes pipe */
437   signal(SIGPIPE, SIG_IGN);
438
439   malloc_size_1 = malloc_inuse(&malloc_hist_1);
440
441   startclock();
442
443   if (argc > 1) {
444     if (argv[1][0] == '/') {
445       cln_config_dir = stralloc(argv[1]);
446       cln_config_name = strrchr(cln_config_dir, '/') + 1;
447       cln_config_name[-1] = '\0';
448       cln_config_dir = newstralloc2(cln_config_dir, cln_config_dir, "/");
449     } else {
450       cln_config_name = stralloc(argv[1]);
451       cln_config_dir = vstralloc(CONFIG_DIR, "/", cln_config_name, "/", NULL);
452     }
453   } else {
454     char my_cwd[STR_SIZE];
455
456     if (getcwd(my_cwd, SIZEOF(my_cwd)) == NULL) {
457       error("cannot determine current working directory");
458     }
459     cln_config_dir = stralloc2(my_cwd, "/");
460     if ((cln_config_name = strrchr(my_cwd, '/')) != NULL) {
461       cln_config_name = stralloc(cln_config_name + 1);
462     }
463   }
464
465   conffile = stralloc2(cln_config_dir, CONFFILE_NAME);
466   result = read_conffile(conffile);
467   if (result == 0) {
468       diskfile = client_getconf_str(CNF_DISKFILE);
469       if (diskfile != NULL && access(diskfile, R_OK) == 0) {
470           result = read_diskfile(diskfile, &lst);
471       }
472   }
473   dump_client_configuration(CONFFILE_NAME);
474   amfree(conffile);
475
476   malloc_size_2 = malloc_inuse(&malloc_hist_2);
477
478   if(malloc_size_1 != malloc_size_2) {
479     malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
480   }
481
482   return result;
483 }
484
485 #endif /* TEST */
486
487 char *
488 generic_client_get_security_conf(
489     char *      string,
490     void *      arg)
491 {
492         (void)arg;      /* Quiet unused parameter warning */
493
494         if(!string || !*string)
495                 return(NULL);
496
497         if(strcmp(string, "conf")==0) {
498                 return(client_getconf_str(CLN_CONF));
499         } else if(strcmp(string, "index_server")==0) {
500                 return(client_getconf_str(CLN_INDEX_SERVER));
501         } else if(strcmp(string, "tape_server")==0) {
502                 return(client_getconf_str(CLN_TAPE_SERVER));
503         } else if(strcmp(string, "tapedev")==0) {
504                 return(client_getconf_str(CLN_TAPEDEV));
505         } else if(strcmp(string, "auth")==0) {
506                 return(client_getconf_str(CLN_AUTH));
507         } else if(strcmp(string, "ssh_keys")==0) {
508                 return(client_getconf_str(CLN_SSH_KEYS));
509         } else if(strcmp(string, "amandad_path")==0) {
510                 return(client_getconf_str(CLN_AMANDAD_PATH));
511         } else if(strcmp(string, "client_username")==0) {
512                 return(client_getconf_str(CLN_CLIENT_USERNAME));
513         } else if(strcmp(string, "gnutar_list_dir")==0) {
514                 return(client_getconf_str(CLN_GNUTAR_LIST_DIR));
515         } else if(strcmp(string, "amandates")==0) {
516                 return(client_getconf_str(CLN_AMANDATES));
517 /*
518         } else if(strcmp(string, "krb5principal")==0) {
519                 return(client_getconf_str(CNF_KRB5PRINCIPAL));
520         } else if(strcmp(string, "krb5keytab")==0) {
521                 return(client_getconf_str(CNF_KRB5KEYTAB));
522 */
523         }
524         return(NULL);
525 }
526
527
528 void
529 parse_client_conf(
530     int parse_argc,
531     char **parse_argv,
532     int *new_argc,
533     char ***new_argv)
534 {
535     int i;
536     char **my_argv;
537     char *myarg, *value;
538     command_option_t *client_option;
539
540     client_options = alloc((size_t)(parse_argc+1) * SIZEOF(*client_options));
541     client_options_size = parse_argc+1;
542     client_option = client_options;
543     client_option->name = NULL;
544
545     my_argv = alloc((size_t)parse_argc * SIZEOF(char *));
546     *new_argv = my_argv;
547     *new_argc = 0;
548     i=0;
549     while(i<parse_argc) {
550         if(strncmp(parse_argv[i],"-o",2) == 0) {
551             if(strlen(parse_argv[i]) > 2)
552                 myarg = &parse_argv[i][2];
553             else {
554                 i++;
555                 if(i >= parse_argc)
556                     error("expect something after -o");
557                 myarg = parse_argv[i];
558             }
559             value = index(myarg,'=');
560             if (value == NULL) {
561                 conf_parserror("Must specify a value for %s.\n", myarg);
562             } else {
563                 *value = '\0';
564                 value++;
565                 client_option->used = 0;
566                 client_option->name = stralloc(myarg);
567                 client_option->value = stralloc(value);
568                 client_option++;
569                 client_option->name = NULL;
570             }
571         }
572         else {
573             my_argv[*new_argc] = stralloc(parse_argv[i]);
574             *new_argc += 1;
575         }
576         i++;
577     }
578 }
579
580 /* return  0 on success             */
581 /* return -1 if it is already there */
582 /* return -2 if other failure       */
583 int
584 add_client_conf(
585     cconfparm_t parm,
586     char *value)
587 {
588     t_conf_var *np;
589     keytab_t *kt;
590     command_option_t *command_option;
591     int nb_option;
592
593     for(np = client_var; np->token != CONF_UNKNOWN; np++)
594         if(np->parm == (int)parm) break;
595
596     if(np->token == CONF_UNKNOWN) return -2;
597
598     for(kt = client_keytab; kt->token != CONF_UNKNOWN; kt++)
599         if(kt->token == np->token) break;
600
601     if(kt->token == CONF_UNKNOWN) return -2;
602
603     /* Try to find it */
604     nb_option = 0;
605     for(command_option = client_options; command_option->name != NULL;
606                                                         command_option++) {
607         if(strcasecmp(command_option->name, kt->keyword) == 0) {
608             return -1;
609         }
610         nb_option++;
611     }
612
613     /* Increase size of client_options if needed */
614     if(nb_option >= client_options_size-1) {
615         client_options_size *= 2;
616         client_options = realloc(client_options,
617                                 client_options_size * SIZEOF(*client_options));
618         if (client_options == NULL) {
619             error("Can't realloc client_options: %s\n", strerror(errno));
620             /*NOTREACHED*/
621         }
622         for(command_option = client_options; command_option->name != NULL;
623                                                         command_option++) {
624         }
625     }
626
627     /* add it */
628     command_option->used = 0;
629     command_option->name = stralloc(kt->keyword);
630     command_option->value = stralloc(value);
631     command_option++;
632     command_option->name = NULL;
633     return 0;
634 }
635
636 void
637 report_bad_client_arg(void)
638 {
639     command_option_t *command_option;
640
641     for(command_option = client_options; command_option->name != NULL;
642                                                         command_option++) {
643         if(command_option->used == 0) {
644             fprintf(stderr,"argument -o%s=%s not used\n",
645                     command_option->name, command_option->value);
646         }
647     }
648 }