]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/inc_conf.c
2c1413217ef0673124b582fbbf8ae7ec47164b47
[bacula/bacula] / bacula / src / dird / inc_conf.c
1 /*
2  *   Configuration file parser for new and old Include and
3  *      Exclude records
4  *
5  *     Kern Sibbald, March MMIII
6  *
7  *     Version $Id$
8  */
9 /*
10    Copyright (C) 2003-2004 Kern Sibbald and John Walker
11
12    This program is free software; you can redistribute it and/or
13    modify it under the terms of the GNU General Public License as
14    published by the Free Software Foundation; either version 2 of
15    the License, or (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20    General Public License for more details.
21
22    You should have received a copy of the GNU General Public
23    License along with this program; if not, write to the Free
24    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25    MA 02111-1307, USA.
26
27  */
28
29 #include "bacula.h"
30 #include "dird.h"
31
32 /* Forward referenced subroutines */
33
34 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass);
35
36 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass);
37 static void store_match(LEX *lc, RES_ITEM *item, int index, int pass);
38 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass);
39 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass);
40 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass);
41 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass);
42 static void setup_current_opts(void);
43
44
45 /* We build the current resource here as we are
46  * scanning the resource configuration definition,
47  * then move it to allocated memory when the resource
48  * scan is complete.
49  */
50 extern URES res_all;
51 extern int  res_all_size;
52
53 /* We build the current new Include and Exclude items here */
54 static INCEXE res_incexe;
55
56 /* 
57  * new Include/Exclude items
58  *   name             handler     value    code flags default_value
59  */
60 static RES_ITEM newinc_items[] = {
61    {"file",            store_fname,   NULL,     0, 0, 0},
62    {"options",         options_res,   NULL,     0, 0, 0},
63    {NULL, NULL, NULL, 0, 0, 0} 
64 };
65
66 /*
67  * Items that are valid in an Options resource
68  */
69 static RES_ITEM options_items[] = {
70    {"compression",     store_opts,    NULL,     0, 0, 0},
71    {"signature",       store_opts,    NULL,     0, 0, 0},
72    {"verify",          store_opts,    NULL,     0, 0, 0},
73    {"onefs",           store_opts,    NULL,     0, 0, 0},
74    {"recurse",         store_opts,    NULL,     0, 0, 0},
75    {"sparse",          store_opts,    NULL,     0, 0, 0},
76    {"readfifo",        store_opts,    NULL,     0, 0, 0},
77    {"replace",         store_opts,    NULL,     0, 0, 0},
78    {"portable",        store_opts,    NULL,     0, 0, 0},
79    {"match",           store_match,   NULL,     0, 0, 0},
80    {"base",            store_base,    NULL,     0, 0, 0},
81    {NULL, NULL, NULL, 0, 0, 0} 
82 };
83
84
85 /* Define FileSet KeyWord values */
86
87 #define INC_KW_NONE         0
88 #define INC_KW_COMPRESSION  1
89 #define INC_KW_SIGNATURE    2
90 #define INC_KW_ENCRYPTION   3
91 #define INC_KW_VERIFY       4
92 #define INC_KW_ONEFS        5
93 #define INC_KW_RECURSE      6
94 #define INC_KW_SPARSE       7
95 #define INC_KW_REPLACE      8         /* restore options */
96 #define INC_KW_READFIFO     9         /* Causes fifo data to be read */
97 #define INC_KW_PORTABLE    10
98 #define INC_KW_MTIMEONLY   11
99 #define INC_KW_KEEPATIME   12
100
101 /* Include keywords -- these are keywords that can appear
102  *    in the options lists of an old include ( Include = compression= ...)
103  */
104 static struct s_kw FS_option_kw[] = {
105    {"compression", INC_KW_COMPRESSION},
106    {"signature",   INC_KW_SIGNATURE},
107    {"encryption",  INC_KW_ENCRYPTION},
108    {"verify",      INC_KW_VERIFY},
109    {"onefs",       INC_KW_ONEFS},
110    {"recurse",     INC_KW_RECURSE},
111    {"sparse",      INC_KW_SPARSE},
112    {"replace",     INC_KW_REPLACE},
113    {"readfifo",    INC_KW_READFIFO},
114    {"portable",    INC_KW_PORTABLE},
115    {"mtimeonly",   INC_KW_MTIMEONLY},
116    {"keepatime",   INC_KW_KEEPATIME},
117    {NULL,          0}
118 };
119
120 /* Options for FileSet keywords */
121
122 struct s_fs_opt {
123    char *name;
124    int keyword;
125    char *option;
126 };
127
128 /*
129  * Options permitted for each keyword and resulting value.     
130  * The output goes into opts, which are then transmitted to
131  * the FD for application as options to the following list of
132  * included files.
133  */
134 static struct s_fs_opt FS_options[] = {
135    {"md5",      INC_KW_SIGNATURE,    "M"},
136    {"sha1",     INC_KW_SIGNATURE,    "S"},
137    {"gzip",     INC_KW_COMPRESSION,  "Z6"},
138    {"gzip1",    INC_KW_COMPRESSION,  "Z1"},
139    {"gzip2",    INC_KW_COMPRESSION,  "Z2"},
140    {"gzip3",    INC_KW_COMPRESSION,  "Z3"},
141    {"gzip4",    INC_KW_COMPRESSION,  "Z4"},
142    {"gzip5",    INC_KW_COMPRESSION,  "Z5"},
143    {"gzip6",    INC_KW_COMPRESSION,  "Z6"},
144    {"gzip7",    INC_KW_COMPRESSION,  "Z7"},
145    {"gzip8",    INC_KW_COMPRESSION,  "Z8"},
146    {"gzip9",    INC_KW_COMPRESSION,  "Z9"},
147    {"blowfish", INC_KW_ENCRYPTION,    "B"},   /* ***FIXME*** not implemented */
148    {"3des",     INC_KW_ENCRYPTION,    "3"},   /* ***FIXME*** not implemented */
149    {"yes",      INC_KW_ONEFS,         "0"},
150    {"no",       INC_KW_ONEFS,         "f"},
151    {"yes",      INC_KW_RECURSE,       "0"},
152    {"no",       INC_KW_RECURSE,       "h"},
153    {"yes",      INC_KW_SPARSE,        "s"},
154    {"no",       INC_KW_SPARSE,        "0"},
155    {"always",   INC_KW_REPLACE,       "a"},
156    {"ifnewer",  INC_KW_REPLACE,       "w"},
157    {"never",    INC_KW_REPLACE,       "n"},
158    {"yes",      INC_KW_READFIFO,      "r"},
159    {"no",       INC_KW_READFIFO,      "0"},
160    {"yes",      INC_KW_PORTABLE,      "p"},
161    {"no",       INC_KW_PORTABLE,      "0"},
162    {"yes",      INC_KW_MTIMEONLY,     "m"},
163    {"no",       INC_KW_MTIMEONLY,     "0"},
164    {"yes",      INC_KW_KEEPATIME,     "k"},
165    {"no",       INC_KW_KEEPATIME,     "0"},
166    {NULL,       0,                   0}
167 };
168
169
170
171 /* 
172  * Scan for right hand side of Include options (keyword=option) is 
173  *    converted into one or two characters. Verifyopts=xxxx is Vxxxx:
174  *    Whatever is found is concatenated to the opts string.
175  * This code is also used inside an Options resource.
176  */
177 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
178 {
179    int token, i;
180    char option[3];
181
182    option[0] = 0;                     /* default option = none */
183    option[2] = 0;                     /* terminate options */
184    token = lex_get_token(lc, T_NAME);             /* expect at least one option */       
185    if (keyword == INC_KW_VERIFY) { /* special case */
186       /* ***FIXME**** ensure these are in permitted set */
187       bstrncat(opts, "V", optlen);         /* indicate Verify */
188       bstrncat(opts, lc->str, optlen);
189       bstrncat(opts, ":", optlen);         /* terminate it */
190       Dmsg3(100, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
191
192    /*
193     * Standard keyword options for Include/Exclude 
194     */
195    } else {
196       for (i=0; FS_options[i].name; i++) {
197          if (strcasecmp(lc->str, FS_options[i].name) == 0 && FS_options[i].keyword == keyword) {
198             /* NOTE! maximum 2 letters here or increase option[3] */
199             option[0] = FS_options[i].option[0];
200             option[1] = FS_options[i].option[1];
201             i = 0;
202             break;
203          }
204       }
205       if (i != 0) {
206          scan_err1(lc, "Expected a FileSet option keyword, got:%s:", lc->str);
207       } else { /* add option */
208          bstrncat(opts, option, optlen);
209          Dmsg3(100, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
210       }
211    }
212
213    /* If option terminated by comma, eat it */
214    if (lc->ch == ',') {
215       token = lex_get_token(lc, T_ALL);      /* yes, eat comma */
216    }
217 }
218
219 /* 
220  * 
221  * Store FileSet Include/Exclude info   
222  */
223 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass)
224 {
225    int token, i;
226    int options = lc->options;
227    int keyword;
228    char inc_opts[100];
229    int inc_opts_len;
230
231    /*
232     * Decide if we are doing a new Include or an old include. The
233     *  new Include is followed immediately by {, whereas the
234     *  old include has options following the Include.
235     */
236    token = lex_get_token(lc, T_ALL);            
237    if (token == T_BOB) {
238       store_newinc(lc, item, index, pass);
239       return;
240    }
241    if (token != T_EQUALS) {
242       scan_err1(lc, _("Expecting an equals sign, got: %s\n"), lc->str);
243    }
244    lc->options |= LOPT_NO_IDENT;      /* make spaces significant */
245    memset(&res_incexe, 0, sizeof(INCEXE));
246
247    /* Get include options */
248    inc_opts[0] = 0;
249    while ((token=lex_get_token(lc, T_ALL)) != T_BOB) {
250          
251       keyword = INC_KW_NONE;
252       for (i=0; FS_option_kw[i].name; i++) {
253          if (strcasecmp(lc->str, FS_option_kw[i].name) == 0) {
254             keyword = FS_option_kw[i].token;
255             break;
256          }
257       }
258       if (keyword == INC_KW_NONE) {
259          scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
260       }
261       /* Option keyword should be following by = <option> */
262       if ((token=lex_get_token(lc, T_ALL)) != T_EQUALS) {
263          scan_err1(lc, _("expected an = following keyword, got: %s"), lc->str);
264       } else {
265          /* Scan right hand side of option */
266          scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
267       }
268       if (token == T_BOB) {
269          break;
270       }
271    }
272
273    if (!inc_opts[0]) {
274       strcat(inc_opts, "0");          /* set no options */
275    }
276    inc_opts_len = strlen(inc_opts);
277
278    if (pass == 1) {
279       INCEXE *incexe;
280       if (!res_all.res_fs.have_MD5) {
281          MD5Init(&res_all.res_fs.md5c);
282          res_all.res_fs.have_MD5 = TRUE;
283       }
284       setup_current_opts();
285       bstrncpy(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
286       Dmsg2(100, "old pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
287
288       /* Create incexe structure */
289       Dmsg0(200, "Create INCEXE structure\n");
290       incexe = (INCEXE *)malloc(sizeof(INCEXE));
291       memcpy(incexe, &res_incexe, sizeof(INCEXE));
292       memset(&res_incexe, 0, sizeof(INCEXE));
293       if (item->code == 0) { /* include */
294          if (res_all.res_fs.num_includes == 0) {
295             res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
296           } else {
297             res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
298                            sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
299           }
300           res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
301           Dmsg1(200, "num_includes=%d\n", res_all.res_fs.num_includes);
302       } else {    /* exclude */
303          if (res_all.res_fs.num_excludes == 0) {
304             res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
305           } else {
306             res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
307                            sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
308           }
309           res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
310           Dmsg1(200, "num_excludes=%d\n", res_all.res_fs.num_excludes);
311       }
312
313       /* Pickup include/exclude names.  They are stored in INCEXE
314        *  structures which contains the options and the name.
315        */
316       while ((token = lex_get_token(lc, T_ALL)) != T_EOB) {
317          switch (token) {
318          case T_COMMA:
319          case T_EOL:
320             continue;
321
322          case T_IDENTIFIER:
323          case T_UNQUOTED_STRING:
324          case T_QUOTED_STRING:
325             if (res_all.res_fs.have_MD5) {
326                MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
327             }
328             if (incexe->name_list.size() == 0) {
329                incexe->name_list.init(10, true);
330             }
331             incexe->name_list.append(bstrdup(lc->str));
332             Dmsg1(200, "Add to name_list %s\n", lc->str);
333             break;
334          default:
335             scan_err1(lc, "Expected a filename, got: %s", lc->str);
336          }                                 
337       }
338       /* Note, MD5Final is done in backup.c */
339    } else { /* pass 2 */
340       while (lex_get_token(lc, T_ALL) != T_EOB) 
341          {}
342    }
343    scan_to_eol(lc);
344    lc->options = options;
345    set_bit(index, res_all.hdr.item_present);
346 }
347
348
349 /*
350  * Store FileSet FInclude/FExclude info   
351  *  Note, when this routine is called, we are inside a FileSet
352  *  resource.  We treat the Finclude/Fexeclude like a sort of
353  *  mini-resource within the FileSet resource.
354  */
355 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
356 {
357    int token, i;
358    INCEXE *incexe;
359    bool options;  
360
361    if (!res_all.res_fs.have_MD5) {
362       MD5Init(&res_all.res_fs.md5c);
363       res_all.res_fs.have_MD5 = TRUE;
364    }
365    memset(&res_incexe, 0, sizeof(INCEXE));
366    res_all.res_fs.new_include = TRUE;
367    while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
368       if (token == T_EOL) {
369          continue;
370       }
371       if (token == T_EOB) {
372          break;
373       }
374       if (token != T_IDENTIFIER) {
375          scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
376       }
377       for (i=0; newinc_items[i].name; i++) {
378          options = strcasecmp(lc->str, "options") == 0;
379          if (strcasecmp(newinc_items[i].name, lc->str) == 0) {
380             if (!options) {
381                token = lex_get_token(lc, T_ALL);
382                if (token != T_EQUALS) {
383                   scan_err1(lc, "expected an equals, got: %s", lc->str);
384                }
385             }
386             /* Call item handler */
387             newinc_items[i].handler(lc, &newinc_items[i], i, pass);
388             i = -1;
389             break;
390          }
391       }
392       if (i >=0) {
393          scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
394       }
395    }
396    if (pass == 1) {
397       incexe = (INCEXE *)malloc(sizeof(INCEXE));
398       memcpy(incexe, &res_incexe, sizeof(INCEXE));
399       memset(&res_incexe, 0, sizeof(INCEXE));
400       if (item->code == 0) { /* include */
401          if (res_all.res_fs.num_includes == 0) {
402             res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
403          } else {
404             res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
405                            sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
406          }
407          res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
408          Dmsg1(200, "num_includes=%d\n", res_all.res_fs.num_includes);
409       } else {    /* exclude */
410          if (res_all.res_fs.num_excludes == 0) {
411             res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
412          } else {
413             res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
414                            sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
415          }
416          res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
417          Dmsg1(200, "num_excludes=%d\n", res_all.res_fs.num_excludes);
418       }
419    }
420    scan_to_eol(lc);
421    set_bit(index, res_all.hdr.item_present);
422 }
423
424
425 /* Store Match info */
426 static void store_match(LEX *lc, RES_ITEM *item, int index, int pass)
427 {
428    int token;
429
430    if (pass == 1) {
431       /* Pickup Match string
432        */
433       token = lex_get_token(lc, T_ALL);            
434       switch (token) {
435       case T_IDENTIFIER:
436       case T_UNQUOTED_STRING:
437       case T_QUOTED_STRING:
438          setup_current_opts();
439          res_incexe.current_opts->match.append(bstrdup(lc->str));
440          break;
441       default:
442          scan_err1(lc, _("Expected a filename, got: %s\n"), lc->str);
443       }                                 
444    } else { /* pass 2 */
445       lex_get_token(lc, T_ALL);          
446    }
447    scan_to_eol(lc);
448 }
449
450 /* Store Base info */
451 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
452 {
453    int token;
454
455    if (pass == 1) {
456       setup_current_opts();
457       /*
458        * Pickup Base Job Name
459        */
460       token = lex_get_token(lc, T_NAME);           
461       res_incexe.current_opts->base_list.append(bstrdup(lc->str));
462    } else { /* pass 2 */
463       lex_get_token(lc, T_ALL);          
464    }
465    scan_to_eol(lc);
466 }
467 /*
468  * Store Filename info. Note, for minor efficiency reasons, we
469  * always increase the name buffer by 10 items because we expect
470  * to add more entries.
471  */
472 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass)
473 {
474    int token;
475    INCEXE *incexe;
476
477    if (pass == 1) {
478       /* Pickup Filename string
479        */
480       token = lex_get_token(lc, T_ALL);            
481       switch (token) {
482          case T_IDENTIFIER:
483          case T_UNQUOTED_STRING:
484          case T_QUOTED_STRING:
485             if (res_all.res_fs.have_MD5) {
486                MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
487             }
488             incexe = &res_incexe;
489             if (incexe->name_list.size() == 0) {
490                incexe->name_list.init(10, true);
491             }
492             incexe->name_list.append(bstrdup(lc->str));
493             Dmsg1(200, "Add to name_list %s\n", lc->str);
494             break;
495          default:
496             scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
497       }                                 
498    } else { /* pass 2 */
499       lex_get_token(lc, T_ALL);          
500    }
501    scan_to_eol(lc);
502 }
503
504 /*
505  * Come here when Options seen in Include/Exclude
506  */
507 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass)
508 {
509    int token, i;
510
511    token = lex_get_token(lc, T_ALL);            
512    if (token != T_BOB) {
513       scan_err1(lc, "Expecting open brace. Got %s", lc->str);
514    }
515
516    while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
517       if (token == T_EOL) {
518          continue;
519       }
520       if (token == T_EOB) {
521          break;
522       }
523       if (token != T_IDENTIFIER) {
524          scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
525       }
526       for (i=0; options_items[i].name; i++) {
527          if (strcasecmp(options_items[i].name, lc->str) == 0) {
528             token = lex_get_token(lc, T_ALL);
529             if (token != T_EQUALS) {
530                scan_err1(lc, "expected an equals, got: %s", lc->str);
531             }
532             /* Call item handler */
533             options_items[i].handler(lc, &options_items[i], i, pass);
534             i = -1;
535             break;
536          }
537       }
538       if (i >=0) {
539          scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
540       }
541    }
542    scan_to_eol(lc);
543 }
544
545
546 /*
547  * New style options come here
548  */
549 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass)
550 {
551    int i;
552    int keyword;
553    char inc_opts[100];
554
555    inc_opts[0] = 0;
556    keyword = INC_KW_NONE;
557    /* Look up the keyword */
558    for (i=0; FS_option_kw[i].name; i++) {
559       if (strcasecmp(item->name, FS_option_kw[i].name) == 0) {
560          keyword = FS_option_kw[i].token;
561          break;
562       }
563    }
564    if (keyword == INC_KW_NONE) {
565       scan_err1(lc, "Expected a FileSet keyword, got: %s", lc->str);
566    }
567    /* Now scan for the value */
568    scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
569    if (pass == 1) {
570       setup_current_opts();
571       bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
572       Dmsg2(100, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
573    }
574    scan_to_eol(lc);
575 }
576
577
578
579 /* If current_opts not defined, create first entry */
580 static void setup_current_opts(void)
581 {
582    if (res_incexe.current_opts == NULL) {
583       res_incexe.current_opts = (FOPTS *)malloc(sizeof(FOPTS));
584       memset(res_incexe.current_opts, 0, sizeof(FOPTS));
585       res_incexe.current_opts->match.init(1, true);
586       res_incexe.current_opts->base_list.init(1, true);
587       res_incexe.num_opts = 1;
588       res_incexe.opts_list = (FOPTS **)malloc(sizeof(FOPTS *));
589       res_incexe.opts_list[0] = res_incexe.current_opts;
590    }
591 }