2 * Configuration file parser for new and old Include and
5 * Kern Sibbald, March MMIII
10 Bacula® - The Network Backup Solution
12 Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
14 The main author of Bacula is Kern Sibbald, with contributions from
15 many others, a complete list can be found in the file AUTHORS.
16 This program is Free Software; you can redistribute it and/or
17 modify it under the terms of version two of the GNU General Public
18 License as published by the Free Software Foundation plus additions
19 that are listed in the file LICENSE.
21 This program is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
31 Bacula® is a registered trademark of John Walker.
32 The licensor of Bacula is the Free Software Foundation Europe
33 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
34 Switzerland, email:ftf@fsfeurope.org.
40 #include "lib/bregex.h"
45 /* Forward referenced subroutines */
47 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass);
49 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass);
50 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass);
51 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass);
52 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass);
53 static void store_drivetype(LEX *lc, RES_ITEM *item, int index, int pass);
54 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass);
55 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass);
56 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass);
57 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass);
58 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass);
59 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass);
60 static void setup_current_opts(void);
63 /* We build the current resource here as we are
64 * scanning the resource configuration definition,
65 * then move it to allocated memory when the resource
69 extern "C" { // work around visual compiler mangling variables
75 extern int res_all_size;
77 /* We build the current new Include and Exclude items here */
78 static INCEXE res_incexe;
81 * new Include/Exclude items
82 * name handler value code flags default_value
84 static RES_ITEM newinc_items[] = {
85 {"file", store_fname, {0}, 0, 0, 0},
86 {"options", options_res, {0}, 0, 0, 0},
87 {NULL, NULL, {0}, 0, 0, 0}
91 * Items that are valid in an Options resource
93 static RES_ITEM options_items[] = {
94 {"compression", store_opts, {0}, 0, 0, 0},
95 {"signature", store_opts, {0}, 0, 0, 0},
96 {"verify", store_opts, {0}, 0, 0, 0},
97 {"onefs", store_opts, {0}, 0, 0, 0},
98 {"recurse", store_opts, {0}, 0, 0, 0},
99 {"sparse", store_opts, {0}, 0, 0, 0},
100 {"hardlinks", store_opts, {0}, 0, 0, 0},
101 {"readfifo", store_opts, {0}, 0, 0, 0},
102 {"replace", store_opts, {0}, 0, 0, 0},
103 {"portable", store_opts, {0}, 0, 0, 0},
104 {"mtimeonly", store_opts, {0}, 0, 0, 0},
105 {"keepatime", store_opts, {0}, 0, 0, 0},
106 {"regex", store_regex, {0}, 0, 0, 0},
107 {"regexdir", store_regex, {0}, 1, 0, 0},
108 {"regexfile", store_regex, {0}, 2, 0, 0},
109 {"base", store_base, {0}, 0, 0, 0},
110 {"wild", store_wild, {0}, 0, 0, 0},
111 {"wilddir", store_wild, {0}, 1, 0, 0},
112 {"wildfile", store_wild, {0}, 2, 0, 0},
113 {"exclude", store_opts, {0}, 0, 0, 0},
114 {"aclsupport", store_opts, {0}, 0, 0, 0},
115 {"reader", store_reader, {0}, 0, 0, 0},
116 {"writer", store_writer, {0}, 0, 0, 0},
117 {"ignorecase", store_opts, {0}, 0, 0, 0},
118 {"fstype", store_fstype, {0}, 0, 0, 0},
119 {"hfsplussupport", store_opts, {0}, 0, 0, 0},
120 {"noatime", store_opts, {0}, 0, 0, 0},
121 {"enhancedwild", store_opts, {0}, 0, 0, 0},
122 {"drivetype", store_drivetype, {0}, 0, 0, 0},
123 {NULL, NULL, {0}, 0, 0, 0}
127 /* Define FileSet KeyWord values */
138 INC_KW_REPLACE, /* restore options */
139 INC_KW_READFIFO, /* Causes fifo data to be read */
152 * This is the list of options that can be stored by store_opts
153 * Note, now that the old style Include/Exclude code is gone,
154 * the INC_KW code could be put into the "code" field of the
155 * options given above.
157 static struct s_kw FS_option_kw[] = {
158 {"compression", INC_KW_COMPRESSION},
159 {"signature", INC_KW_DIGEST},
160 {"encryption", INC_KW_ENCRYPTION},
161 {"verify", INC_KW_VERIFY},
162 {"onefs", INC_KW_ONEFS},
163 {"recurse", INC_KW_RECURSE},
164 {"sparse", INC_KW_SPARSE},
165 {"hardlinks", INC_KW_HARDLINK},
166 {"replace", INC_KW_REPLACE},
167 {"readfifo", INC_KW_READFIFO},
168 {"portable", INC_KW_PORTABLE},
169 {"mtimeonly", INC_KW_MTIMEONLY},
170 {"keepatime", INC_KW_KEEPATIME},
171 {"exclude", INC_KW_EXCLUDE},
172 {"aclsupport", INC_KW_ACL},
173 {"ignorecase", INC_KW_IGNORECASE},
174 {"hfsplussupport", INC_KW_HFSPLUS},
175 {"noatime", INC_KW_NOATIME},
176 {"enhancedwild", INC_KW_ENHANCEDWILD},
180 /* Options for FileSet keywords */
189 * Options permitted for each keyword and resulting value.
190 * The output goes into opts, which are then transmitted to
191 * the FD for application as options to the following list of
194 static struct s_fs_opt FS_options[] = {
195 {"md5", INC_KW_DIGEST, "M"},
196 {"sha1", INC_KW_DIGEST, "S"},
197 {"sha256", INC_KW_DIGEST, "S2"},
198 {"sha512", INC_KW_DIGEST, "S3"},
199 {"gzip", INC_KW_COMPRESSION, "Z6"},
200 {"gzip1", INC_KW_COMPRESSION, "Z1"},
201 {"gzip2", INC_KW_COMPRESSION, "Z2"},
202 {"gzip3", INC_KW_COMPRESSION, "Z3"},
203 {"gzip4", INC_KW_COMPRESSION, "Z4"},
204 {"gzip5", INC_KW_COMPRESSION, "Z5"},
205 {"gzip6", INC_KW_COMPRESSION, "Z6"},
206 {"gzip7", INC_KW_COMPRESSION, "Z7"},
207 {"gzip8", INC_KW_COMPRESSION, "Z8"},
208 {"gzip9", INC_KW_COMPRESSION, "Z9"},
209 {"blowfish", INC_KW_ENCRYPTION, "B"}, /* ***FIXME*** not implemented */
210 {"3des", INC_KW_ENCRYPTION, "3"}, /* ***FIXME*** not implemented */
211 {"yes", INC_KW_ONEFS, "0"},
212 {"no", INC_KW_ONEFS, "f"},
213 {"yes", INC_KW_RECURSE, "0"},
214 {"no", INC_KW_RECURSE, "h"},
215 {"yes", INC_KW_SPARSE, "s"},
216 {"no", INC_KW_SPARSE, "0"},
217 {"yes", INC_KW_HARDLINK, "0"},
218 {"no", INC_KW_HARDLINK, "H"},
219 {"always", INC_KW_REPLACE, "a"},
220 {"ifnewer", INC_KW_REPLACE, "w"},
221 {"never", INC_KW_REPLACE, "n"},
222 {"yes", INC_KW_READFIFO, "r"},
223 {"no", INC_KW_READFIFO, "0"},
224 {"yes", INC_KW_PORTABLE, "p"},
225 {"no", INC_KW_PORTABLE, "0"},
226 {"yes", INC_KW_MTIMEONLY, "m"},
227 {"no", INC_KW_MTIMEONLY, "0"},
228 {"yes", INC_KW_KEEPATIME, "k"},
229 {"no", INC_KW_KEEPATIME, "0"},
230 {"yes", INC_KW_EXCLUDE, "e"},
231 {"no", INC_KW_EXCLUDE, "0"},
232 {"yes", INC_KW_ACL, "A"},
233 {"no", INC_KW_ACL, "0"},
234 {"yes", INC_KW_IGNORECASE, "i"},
235 {"no", INC_KW_IGNORECASE, "0"},
236 {"yes", INC_KW_HFSPLUS, "R"}, /* "R" for resource fork */
237 {"no", INC_KW_HFSPLUS, "0"},
238 {"yes", INC_KW_NOATIME, "K"},
239 {"no", INC_KW_NOATIME, "0"},
240 {"yes", INC_KW_ENHANCEDWILD, "K"},
241 {"no", INC_KW_ENHANCEDWILD, "0"},
248 * Scan for right hand side of Include options (keyword=option) is
249 * converted into one or two characters. Verifyopts=xxxx is Vxxxx:
250 * Whatever is found is concatenated to the opts string.
251 * This code is also used inside an Options resource.
253 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
257 int lcopts = lc->options;
259 option[0] = 0; /* default option = none */
260 option[2] = 0; /* terminate options */
261 lc->options |= LOPT_STRING; /* force string */
262 token = lex_get_token(lc, T_STRING); /* expect at least one option */
263 if (keyword == INC_KW_VERIFY) { /* special case */
264 /* ***FIXME**** ensure these are in permitted set */
265 bstrncat(opts, "V", optlen); /* indicate Verify */
266 bstrncat(opts, lc->str, optlen);
267 bstrncat(opts, ":", optlen); /* terminate it */
268 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
271 * Standard keyword options for Include/Exclude
274 for (i=0; FS_options[i].name; i++) {
275 if (FS_options[i].keyword == keyword && strcasecmp(lc->str, FS_options[i].name) == 0) {
276 /* NOTE! maximum 2 letters here or increase option[3] */
277 option[0] = FS_options[i].option[0];
278 option[1] = FS_options[i].option[1];
284 scan_err1(lc, _("Expected a FileSet option keyword, got:%s:"), lc->str);
285 } else { /* add option */
286 bstrncat(opts, option, optlen);
287 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
290 lc->options = lcopts;
292 /* If option terminated by comma, eat it */
294 token = lex_get_token(lc, T_ALL); /* yes, eat comma */
300 * Store FileSet Include/Exclude info
301 * NEW style includes are handled in store_newinc()
303 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass)
308 * Decide if we are doing a new Include or an old include. The
309 * new Include is followed immediately by open brace, whereas the
310 * old include has options following the Include.
312 token = lex_get_token(lc, T_SKIP_EOL);
313 if (token == T_BOB) {
314 store_newinc(lc, item, index, pass);
317 scan_err0(lc, _("Old style Include/Exclude not supported\n"));
322 * Store NEW style FileSet FInclude/FExclude info
324 * Note, when this routine is called, we are inside a FileSet
325 * resource. We treat the Include/Execlude like a sort of
326 * mini-resource within the FileSet resource.
328 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
334 if (!res_all.res_fs.have_MD5) {
335 MD5Init(&res_all.res_fs.md5c);
336 res_all.res_fs.have_MD5 = true;
338 memset(&res_incexe, 0, sizeof(INCEXE));
339 res_all.res_fs.new_include = true;
340 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
341 if (token == T_EOB) {
344 if (token != T_IDENTIFIER) {
345 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
347 for (i=0; newinc_items[i].name; i++) {
348 options = strcasecmp(lc->str, "options") == 0;
349 if (strcasecmp(newinc_items[i].name, lc->str) == 0) {
351 token = lex_get_token(lc, T_SKIP_EOL);
352 if (token != T_EQUALS) {
353 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
356 /* Call item handler */
357 newinc_items[i].handler(lc, &newinc_items[i], i, pass);
363 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
367 incexe = (INCEXE *)malloc(sizeof(INCEXE));
368 memcpy(incexe, &res_incexe, sizeof(INCEXE));
369 memset(&res_incexe, 0, sizeof(INCEXE));
370 if (item->code == 0) { /* include */
371 if (res_all.res_fs.num_includes == 0) {
372 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
374 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
375 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
377 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
378 Dmsg1(900, "num_includes=%d\n", res_all.res_fs.num_includes);
379 } else { /* exclude */
380 if (res_all.res_fs.num_excludes == 0) {
381 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
383 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
384 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
386 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
387 Dmsg1(900, "num_excludes=%d\n", res_all.res_fs.num_excludes);
391 set_bit(index, res_all.hdr.item_present);
395 /* Store regex info */
396 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass)
404 token = lex_get_token(lc, T_SKIP_EOL);
406 /* Pickup regex string
410 case T_UNQUOTED_STRING:
411 case T_QUOTED_STRING:
412 rc = regcomp(&preg, lc->str, REG_EXTENDED);
414 regerror(rc, &preg, prbuf, sizeof(prbuf));
416 scan_err1(lc, _("Regex compile error. ERR=%s\n"), prbuf);
420 if (item->code == 1) {
422 res_incexe.current_opts->regexdir.append(bstrdup(lc->str));
423 newsize = res_incexe.current_opts->regexdir.size();
424 } else if (item->code == 2) {
426 res_incexe.current_opts->regexfile.append(bstrdup(lc->str));
427 newsize = res_incexe.current_opts->regexfile.size();
430 res_incexe.current_opts->regex.append(bstrdup(lc->str));
431 newsize = res_incexe.current_opts->regex.size();
433 Dmsg4(900, "set %s %p size=%d %s\n",
434 type, res_incexe.current_opts, newsize, lc->str);
437 scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
443 /* Store Base info */
444 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
448 token = lex_get_token(lc, T_NAME);
451 * Pickup Base Job Name
453 res_incexe.current_opts->base.append(bstrdup(lc->str));
458 /* Store reader info */
459 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass)
463 token = lex_get_token(lc, T_NAME);
466 * Pickup reader command
468 res_incexe.current_opts->reader = bstrdup(lc->str);
473 /* Store writer innfo */
474 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass)
478 token = lex_get_token(lc, T_NAME);
481 * Pickup writer command
483 res_incexe.current_opts->writer = bstrdup(lc->str);
490 /* Store Wild-card info */
491 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass)
497 token = lex_get_token(lc, T_SKIP_EOL);
500 * Pickup Wild-card string
504 case T_UNQUOTED_STRING:
505 case T_QUOTED_STRING:
506 if (item->code == 1) {
508 res_incexe.current_opts->wilddir.append(bstrdup(lc->str));
509 newsize = res_incexe.current_opts->wilddir.size();
510 } else if (item->code == 2) {
511 if (strpbrk(lc->str, "/\\") != NULL) {
513 res_incexe.current_opts->wildfile.append(bstrdup(lc->str));
514 newsize = res_incexe.current_opts->wildfile.size();
517 res_incexe.current_opts->wildbase.append(bstrdup(lc->str));
518 newsize = res_incexe.current_opts->wildbase.size();
522 res_incexe.current_opts->wild.append(bstrdup(lc->str));
523 newsize = res_incexe.current_opts->wild.size();
525 Dmsg4(9, "set %s %p size=%d %s\n",
526 type, res_incexe.current_opts, newsize, lc->str);
529 scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
535 /* Store fstype info */
536 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass)
540 token = lex_get_token(lc, T_SKIP_EOL);
542 /* Pickup fstype string */
545 case T_UNQUOTED_STRING:
546 case T_QUOTED_STRING:
547 res_incexe.current_opts->fstype.append(bstrdup(lc->str));
548 Dmsg3(900, "set fstype %p size=%d %s\n",
549 res_incexe.current_opts, res_incexe.current_opts->fstype.size(), lc->str);
552 scan_err1(lc, _("Expected an fstype string, got: %s\n"), lc->str);
558 /* Store drivetype info */
559 static void store_drivetype(LEX *lc, RES_ITEM *item, int index, int pass)
563 token = lex_get_token(lc, T_SKIP_EOL);
565 /* Pickup drivetype string */
568 case T_UNQUOTED_STRING:
569 case T_QUOTED_STRING:
570 res_incexe.current_opts->drivetype.append(bstrdup(lc->str));
571 Dmsg3(900, "set drivetype %p size=%d %s\n",
572 res_incexe.current_opts, res_incexe.current_opts->drivetype.size(), lc->str);
575 scan_err1(lc, _("Expected an drivetype string, got: %s\n"), lc->str);
582 * Store Filename info. Note, for minor efficiency reasons, we
583 * always increase the name buffer by 10 items because we expect
584 * to add more entries.
586 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass)
591 token = lex_get_token(lc, T_SKIP_EOL);
593 /* Pickup Filename string
597 case T_UNQUOTED_STRING:
598 if (strchr(lc->str, '\\')) {
599 scan_err1(lc, _("Backslash found. Use forward slashes or quote the string.: %s\n"), lc->str);
602 case T_QUOTED_STRING:
603 if (res_all.res_fs.have_MD5) {
604 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
606 incexe = &res_incexe;
607 if (incexe->name_list.size() == 0) {
608 incexe->name_list.init(10, true);
610 incexe->name_list.append(bstrdup(lc->str));
611 Dmsg1(900, "Add to name_list %s\n", lc->str);
614 scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
622 * Come here when Options seen in Include/Exclude
624 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass)
628 token = lex_get_token(lc, T_SKIP_EOL);
629 if (token != T_BOB) {
630 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
634 setup_current_opts();
637 while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
638 if (token == T_EOL) {
641 if (token == T_EOB) {
644 if (token != T_IDENTIFIER) {
645 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
647 for (i=0; options_items[i].name; i++) {
648 if (strcasecmp(options_items[i].name, lc->str) == 0) {
649 token = lex_get_token(lc, T_SKIP_EOL);
650 if (token != T_EQUALS) {
651 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
653 /* Call item handler */
654 options_items[i].handler(lc, &options_items[i], i, pass);
660 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
667 * New style options come here
669 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass)
676 keyword = INC_KW_NONE;
677 /* Look up the keyword */
678 for (i=0; FS_option_kw[i].name; i++) {
679 if (strcasecmp(item->name, FS_option_kw[i].name) == 0) {
680 keyword = FS_option_kw[i].token;
684 if (keyword == INC_KW_NONE) {
685 scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
687 /* Now scan for the value */
688 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
690 bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
691 Dmsg2(900, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
698 /* If current_opts not defined, create first entry */
699 static void setup_current_opts(void)
701 FOPTS *fo = (FOPTS *)malloc(sizeof(FOPTS));
702 memset(fo, 0, sizeof(FOPTS));
703 fo->regex.init(1, true);
704 fo->regexdir.init(1, true);
705 fo->regexfile.init(1, true);
706 fo->wild.init(1, true);
707 fo->wilddir.init(1, true);
708 fo->wildfile.init(1, true);
709 fo->wildbase.init(1, true);
710 fo->base.init(1, true);
711 fo->fstype.init(1, true);
712 fo->drivetype.init(1, true);
713 res_incexe.current_opts = fo;
714 if (res_incexe.num_opts == 0) {
715 res_incexe.opts_list = (FOPTS **)malloc(sizeof(FOPTS *));
717 res_incexe.opts_list = (FOPTS **)realloc(res_incexe.opts_list,
718 sizeof(FOPTS *) * (res_incexe.num_opts + 1));
720 res_incexe.opts_list[res_incexe.num_opts++] = fo;