2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * Configuration file parser for new and old Include and
32 * Kern Sibbald, March MMIII
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 {"checkfilechanges",store_opts, {0}, 0, 0, 0},
124 {"strippath", store_opts, {0}, 0, 0, 0},
125 {NULL, NULL, {0}, 0, 0, 0}
129 /* Define FileSet KeyWord values */
140 INC_KW_REPLACE, /* restore options */
141 INC_KW_READFIFO, /* Causes fifo data to be read */
156 * This is the list of options that can be stored by store_opts
157 * Note, now that the old style Include/Exclude code is gone,
158 * the INC_KW code could be put into the "code" field of the
159 * options given above.
161 static struct s_kw FS_option_kw[] = {
162 {"compression", INC_KW_COMPRESSION},
163 {"signature", INC_KW_DIGEST},
164 {"encryption", INC_KW_ENCRYPTION},
165 {"verify", INC_KW_VERIFY},
166 {"onefs", INC_KW_ONEFS},
167 {"recurse", INC_KW_RECURSE},
168 {"sparse", INC_KW_SPARSE},
169 {"hardlinks", INC_KW_HARDLINK},
170 {"replace", INC_KW_REPLACE},
171 {"readfifo", INC_KW_READFIFO},
172 {"portable", INC_KW_PORTABLE},
173 {"mtimeonly", INC_KW_MTIMEONLY},
174 {"keepatime", INC_KW_KEEPATIME},
175 {"exclude", INC_KW_EXCLUDE},
176 {"aclsupport", INC_KW_ACL},
177 {"ignorecase", INC_KW_IGNORECASE},
178 {"hfsplussupport", INC_KW_HFSPLUS},
179 {"noatime", INC_KW_NOATIME},
180 {"enhancedwild", INC_KW_ENHANCEDWILD},
181 {"checkfilechanges", INC_KW_CHKCHANGES},
182 {"strippath", INC_KW_STRIPPATH},
186 /* Options for FileSet keywords */
195 * Options permitted for each keyword and resulting value.
196 * The output goes into opts, which are then transmitted to
197 * the FD for application as options to the following list of
200 static struct s_fs_opt FS_options[] = {
201 {"md5", INC_KW_DIGEST, "M"},
202 {"sha1", INC_KW_DIGEST, "S"},
203 {"sha256", INC_KW_DIGEST, "S2"},
204 {"sha512", INC_KW_DIGEST, "S3"},
205 {"gzip", INC_KW_COMPRESSION, "Z6"},
206 {"gzip1", INC_KW_COMPRESSION, "Z1"},
207 {"gzip2", INC_KW_COMPRESSION, "Z2"},
208 {"gzip3", INC_KW_COMPRESSION, "Z3"},
209 {"gzip4", INC_KW_COMPRESSION, "Z4"},
210 {"gzip5", INC_KW_COMPRESSION, "Z5"},
211 {"gzip6", INC_KW_COMPRESSION, "Z6"},
212 {"gzip7", INC_KW_COMPRESSION, "Z7"},
213 {"gzip8", INC_KW_COMPRESSION, "Z8"},
214 {"gzip9", INC_KW_COMPRESSION, "Z9"},
215 {"blowfish", INC_KW_ENCRYPTION, "B"}, /* ***FIXME*** not implemented */
216 {"3des", INC_KW_ENCRYPTION, "3"}, /* ***FIXME*** not implemented */
217 {"yes", INC_KW_ONEFS, "0"},
218 {"no", INC_KW_ONEFS, "f"},
219 {"yes", INC_KW_RECURSE, "0"},
220 {"no", INC_KW_RECURSE, "h"},
221 {"yes", INC_KW_SPARSE, "s"},
222 {"no", INC_KW_SPARSE, "0"},
223 {"yes", INC_KW_HARDLINK, "0"},
224 {"no", INC_KW_HARDLINK, "H"},
225 {"always", INC_KW_REPLACE, "a"},
226 {"ifnewer", INC_KW_REPLACE, "w"},
227 {"never", INC_KW_REPLACE, "n"},
228 {"yes", INC_KW_READFIFO, "r"},
229 {"no", INC_KW_READFIFO, "0"},
230 {"yes", INC_KW_PORTABLE, "p"},
231 {"no", INC_KW_PORTABLE, "0"},
232 {"yes", INC_KW_MTIMEONLY, "m"},
233 {"no", INC_KW_MTIMEONLY, "0"},
234 {"yes", INC_KW_KEEPATIME, "k"},
235 {"no", INC_KW_KEEPATIME, "0"},
236 {"yes", INC_KW_EXCLUDE, "e"},
237 {"no", INC_KW_EXCLUDE, "0"},
238 {"yes", INC_KW_ACL, "A"},
239 {"no", INC_KW_ACL, "0"},
240 {"yes", INC_KW_IGNORECASE, "i"},
241 {"no", INC_KW_IGNORECASE, "0"},
242 {"yes", INC_KW_HFSPLUS, "R"}, /* "R" for resource fork */
243 {"no", INC_KW_HFSPLUS, "0"},
244 {"yes", INC_KW_NOATIME, "K"},
245 {"no", INC_KW_NOATIME, "0"},
246 {"yes", INC_KW_ENHANCEDWILD, "K"},
247 {"no", INC_KW_ENHANCEDWILD, "0"},
248 {"yes", INC_KW_CHKCHANGES, "c"},
249 {"no", INC_KW_CHKCHANGES, "0"},
256 * Scan for right hand side of Include options (keyword=option) is
257 * converted into one or two characters. Verifyopts=xxxx is Vxxxx:
258 * Whatever is found is concatenated to the opts string.
259 * This code is also used inside an Options resource.
261 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
265 int lcopts = lc->options;
267 option[0] = 0; /* default option = none */
268 option[2] = 0; /* terminate options */
269 lc->options |= LOPT_STRING; /* force string */
270 token = lex_get_token(lc, T_STRING); /* expect at least one option */
271 if (keyword == INC_KW_VERIFY) { /* special case */
272 /* ***FIXME**** ensure these are in permitted set */
273 bstrncat(opts, "V", optlen); /* indicate Verify */
274 bstrncat(opts, lc->str, optlen);
275 bstrncat(opts, ":", optlen); /* terminate it */
276 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
277 } else if (keyword == INC_KW_STRIPPATH) { /* another special case */
278 if (!is_an_integer(lc->str)) {
279 scan_err1(lc, _("Expected a strip path positive integer, got:%s:"), lc->str);
281 bstrncat(opts, "P", optlen); /* indicate strip path */
282 bstrncat(opts, lc->str, optlen);
283 bstrncat(opts, ":", optlen); /* terminate it */
284 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
286 * Standard keyword options for Include/Exclude
289 for (i=0; FS_options[i].name; i++) {
290 if (FS_options[i].keyword == keyword && strcasecmp(lc->str, FS_options[i].name) == 0) {
291 /* NOTE! maximum 2 letters here or increase option[3] */
292 option[0] = FS_options[i].option[0];
293 option[1] = FS_options[i].option[1];
299 scan_err1(lc, _("Expected a FileSet option keyword, got:%s:"), lc->str);
300 } else { /* add option */
301 bstrncat(opts, option, optlen);
302 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
305 lc->options = lcopts;
307 /* If option terminated by comma, eat it */
309 token = lex_get_token(lc, T_ALL); /* yes, eat comma */
315 * Store FileSet Include/Exclude info
316 * NEW style includes are handled in store_newinc()
318 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass)
323 * Decide if we are doing a new Include or an old include. The
324 * new Include is followed immediately by open brace, whereas the
325 * old include has options following the Include.
327 token = lex_get_token(lc, T_SKIP_EOL);
328 if (token == T_BOB) {
329 store_newinc(lc, item, index, pass);
332 scan_err0(lc, _("Old style Include/Exclude not supported\n"));
337 * Store NEW style FileSet FInclude/FExclude info
339 * Note, when this routine is called, we are inside a FileSet
340 * resource. We treat the Include/Execlude like a sort of
341 * mini-resource within the FileSet resource.
343 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
349 if (!res_all.res_fs.have_MD5) {
350 MD5Init(&res_all.res_fs.md5c);
351 res_all.res_fs.have_MD5 = true;
353 memset(&res_incexe, 0, sizeof(INCEXE));
354 res_all.res_fs.new_include = true;
355 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
356 if (token == T_EOB) {
359 if (token != T_IDENTIFIER) {
360 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
362 for (i=0; newinc_items[i].name; i++) {
363 options = strcasecmp(lc->str, "options") == 0;
364 if (strcasecmp(newinc_items[i].name, lc->str) == 0) {
366 token = lex_get_token(lc, T_SKIP_EOL);
367 if (token != T_EQUALS) {
368 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
371 /* Call item handler */
372 newinc_items[i].handler(lc, &newinc_items[i], i, pass);
378 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
382 incexe = (INCEXE *)malloc(sizeof(INCEXE));
383 memcpy(incexe, &res_incexe, sizeof(INCEXE));
384 memset(&res_incexe, 0, sizeof(INCEXE));
385 if (item->code == 0) { /* include */
386 if (res_all.res_fs.num_includes == 0) {
387 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
389 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
390 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
392 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
393 Dmsg1(900, "num_includes=%d\n", res_all.res_fs.num_includes);
394 } else { /* exclude */
395 if (res_all.res_fs.num_excludes == 0) {
396 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
398 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
399 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
401 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
402 Dmsg1(900, "num_excludes=%d\n", res_all.res_fs.num_excludes);
406 set_bit(index, res_all.hdr.item_present);
410 /* Store regex info */
411 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass)
419 token = lex_get_token(lc, T_SKIP_EOL);
421 /* Pickup regex string
425 case T_UNQUOTED_STRING:
426 case T_QUOTED_STRING:
427 rc = regcomp(&preg, lc->str, REG_EXTENDED);
429 regerror(rc, &preg, prbuf, sizeof(prbuf));
431 scan_err1(lc, _("Regex compile error. ERR=%s\n"), prbuf);
435 if (item->code == 1) {
437 res_incexe.current_opts->regexdir.append(bstrdup(lc->str));
438 newsize = res_incexe.current_opts->regexdir.size();
439 } else if (item->code == 2) {
441 res_incexe.current_opts->regexfile.append(bstrdup(lc->str));
442 newsize = res_incexe.current_opts->regexfile.size();
445 res_incexe.current_opts->regex.append(bstrdup(lc->str));
446 newsize = res_incexe.current_opts->regex.size();
448 Dmsg4(900, "set %s %p size=%d %s\n",
449 type, res_incexe.current_opts, newsize, lc->str);
452 scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
458 /* Store Base info */
459 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
463 token = lex_get_token(lc, T_NAME);
466 * Pickup Base Job Name
468 res_incexe.current_opts->base.append(bstrdup(lc->str));
473 /* Store reader info */
474 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass)
478 token = lex_get_token(lc, T_NAME);
481 * Pickup reader command
483 res_incexe.current_opts->reader = bstrdup(lc->str);
488 /* Store writer innfo */
489 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass)
493 token = lex_get_token(lc, T_NAME);
496 * Pickup writer command
498 res_incexe.current_opts->writer = bstrdup(lc->str);
505 /* Store Wild-card info */
506 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass)
512 token = lex_get_token(lc, T_SKIP_EOL);
515 * Pickup Wild-card string
519 case T_UNQUOTED_STRING:
520 case T_QUOTED_STRING:
521 if (item->code == 1) {
523 res_incexe.current_opts->wilddir.append(bstrdup(lc->str));
524 newsize = res_incexe.current_opts->wilddir.size();
525 } else if (item->code == 2) {
526 if (strpbrk(lc->str, "/\\") != NULL) {
528 res_incexe.current_opts->wildfile.append(bstrdup(lc->str));
529 newsize = res_incexe.current_opts->wildfile.size();
532 res_incexe.current_opts->wildbase.append(bstrdup(lc->str));
533 newsize = res_incexe.current_opts->wildbase.size();
537 res_incexe.current_opts->wild.append(bstrdup(lc->str));
538 newsize = res_incexe.current_opts->wild.size();
540 Dmsg4(9, "set %s %p size=%d %s\n",
541 type, res_incexe.current_opts, newsize, lc->str);
544 scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
550 /* Store fstype info */
551 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass)
555 token = lex_get_token(lc, T_SKIP_EOL);
557 /* Pickup fstype string */
560 case T_UNQUOTED_STRING:
561 case T_QUOTED_STRING:
562 res_incexe.current_opts->fstype.append(bstrdup(lc->str));
563 Dmsg3(900, "set fstype %p size=%d %s\n",
564 res_incexe.current_opts, res_incexe.current_opts->fstype.size(), lc->str);
567 scan_err1(lc, _("Expected an fstype string, got: %s\n"), lc->str);
573 /* Store drivetype info */
574 static void store_drivetype(LEX *lc, RES_ITEM *item, int index, int pass)
578 token = lex_get_token(lc, T_SKIP_EOL);
580 /* Pickup drivetype string */
583 case T_UNQUOTED_STRING:
584 case T_QUOTED_STRING:
585 res_incexe.current_opts->drivetype.append(bstrdup(lc->str));
586 Dmsg3(900, "set drivetype %p size=%d %s\n",
587 res_incexe.current_opts, res_incexe.current_opts->drivetype.size(), lc->str);
590 scan_err1(lc, _("Expected an drivetype string, got: %s\n"), lc->str);
597 * Store Filename info. Note, for minor efficiency reasons, we
598 * always increase the name buffer by 10 items because we expect
599 * to add more entries.
601 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass)
606 token = lex_get_token(lc, T_SKIP_EOL);
608 /* Pickup Filename string
612 case T_UNQUOTED_STRING:
613 if (strchr(lc->str, '\\')) {
614 scan_err1(lc, _("Backslash found. Use forward slashes or quote the string.: %s\n"), lc->str);
617 case T_QUOTED_STRING:
618 if (res_all.res_fs.have_MD5) {
619 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
621 incexe = &res_incexe;
622 if (incexe->name_list.size() == 0) {
623 incexe->name_list.init(10, true);
625 incexe->name_list.append(bstrdup(lc->str));
626 Dmsg1(900, "Add to name_list %s\n", lc->str);
629 scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
637 * Come here when Options seen in Include/Exclude
639 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass)
643 token = lex_get_token(lc, T_SKIP_EOL);
644 if (token != T_BOB) {
645 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
649 setup_current_opts();
652 while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
653 if (token == T_EOL) {
656 if (token == T_EOB) {
659 if (token != T_IDENTIFIER) {
660 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
662 for (i=0; options_items[i].name; i++) {
663 if (strcasecmp(options_items[i].name, lc->str) == 0) {
664 token = lex_get_token(lc, T_SKIP_EOL);
665 if (token != T_EQUALS) {
666 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
668 /* Call item handler */
669 options_items[i].handler(lc, &options_items[i], i, pass);
675 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
682 * New style options come here
684 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass)
691 keyword = INC_KW_NONE;
692 /* Look up the keyword */
693 for (i=0; FS_option_kw[i].name; i++) {
694 if (strcasecmp(item->name, FS_option_kw[i].name) == 0) {
695 keyword = FS_option_kw[i].token;
699 if (keyword == INC_KW_NONE) {
700 scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
702 /* Now scan for the value */
703 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
705 bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
706 Dmsg2(900, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
713 /* If current_opts not defined, create first entry */
714 static void setup_current_opts(void)
716 FOPTS *fo = (FOPTS *)malloc(sizeof(FOPTS));
717 memset(fo, 0, sizeof(FOPTS));
718 fo->regex.init(1, true);
719 fo->regexdir.init(1, true);
720 fo->regexfile.init(1, true);
721 fo->wild.init(1, true);
722 fo->wilddir.init(1, true);
723 fo->wildfile.init(1, true);
724 fo->wildbase.init(1, true);
725 fo->base.init(1, true);
726 fo->fstype.init(1, true);
727 fo->drivetype.init(1, true);
728 res_incexe.current_opts = fo;
729 if (res_incexe.num_opts == 0) {
730 res_incexe.opts_list = (FOPTS **)malloc(sizeof(FOPTS *));
732 res_incexe.opts_list = (FOPTS **)realloc(res_incexe.opts_list,
733 sizeof(FOPTS *) * (res_incexe.num_opts + 1));
735 res_incexe.opts_list[res_incexe.num_opts++] = fo;