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