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