2 * Configuration file parser for new and old Include and
5 * Kern Sibbald, March MMIII
10 Copyright (C) 2003-2006 Kern Sibbald
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 version 2 as amended with additional clauses defined in the
15 file LICENSE in the main source directory.
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
20 the file LICENSE for additional details.
27 #include "lib/bregex.h"
32 /* Forward referenced subroutines */
34 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass);
36 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass);
37 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass);
38 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass);
39 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass);
40 static void store_drivetype(LEX *lc, RES_ITEM *item, int index, int pass);
41 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass);
42 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass);
43 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass);
44 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass);
45 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass);
46 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass);
47 static void setup_current_opts(void);
50 /* We build the current resource here as we are
51 * scanning the resource configuration definition,
52 * then move it to allocated memory when the resource
56 extern "C" { // work around visual compiler mangling variables
62 extern int res_all_size;
64 /* We build the current new Include and Exclude items here */
65 static INCEXE res_incexe;
68 * new Include/Exclude items
69 * name handler value code flags default_value
71 static RES_ITEM newinc_items[] = {
72 {"file", store_fname, {0}, 0, 0, 0},
73 {"options", options_res, {0}, 0, 0, 0},
74 {NULL, NULL, {0}, 0, 0, 0}
78 * Items that are valid in an Options resource
80 static RES_ITEM options_items[] = {
81 {"compression", store_opts, {0}, 0, 0, 0},
82 {"signature", store_opts, {0}, 0, 0, 0},
83 {"verify", store_opts, {0}, 0, 0, 0},
84 {"onefs", store_opts, {0}, 0, 0, 0},
85 {"recurse", store_opts, {0}, 0, 0, 0},
86 {"sparse", store_opts, {0}, 0, 0, 0},
87 {"hardlinks", store_opts, {0}, 0, 0, 0},
88 {"readfifo", store_opts, {0}, 0, 0, 0},
89 {"replace", store_opts, {0}, 0, 0, 0},
90 {"portable", store_opts, {0}, 0, 0, 0},
91 {"mtimeonly", store_opts, {0}, 0, 0, 0},
92 {"keepatime", store_opts, {0}, 0, 0, 0},
93 {"regex", store_regex, {0}, 0, 0, 0},
94 {"regexdir", store_regex, {0}, 1, 0, 0},
95 {"regexfile", store_regex, {0}, 2, 0, 0},
96 {"base", store_base, {0}, 0, 0, 0},
97 {"wild", store_wild, {0}, 0, 0, 0},
98 {"wilddir", store_wild, {0}, 1, 0, 0},
99 {"wildfile", store_wild, {0}, 2, 0, 0},
100 {"exclude", store_opts, {0}, 0, 0, 0},
101 {"aclsupport", store_opts, {0}, 0, 0, 0},
102 {"reader", store_reader, {0}, 0, 0, 0},
103 {"writer", store_writer, {0}, 0, 0, 0},
104 {"ignorecase", store_opts, {0}, 0, 0, 0},
105 {"fstype", store_fstype, {0}, 0, 0, 0},
106 {"hfsplussupport", store_opts, {0}, 0, 0, 0},
107 {"noatime", store_opts, {0}, 0, 0, 0},
108 {"enhancedwild", store_opts, {0}, 0, 0, 0},
109 {"drivetype", store_drivetype, {0}, 0, 0, 0},
110 {NULL, NULL, {0}, 0, 0, 0}
114 /* Define FileSet KeyWord values */
125 INC_KW_REPLACE, /* restore options */
126 INC_KW_READFIFO, /* Causes fifo data to be read */
139 * This is the list of options that can be stored by store_opts
140 * Note, now that the old style Include/Exclude code is gone,
141 * the INC_KW code could be put into the "code" field of the
142 * options given above.
144 static struct s_kw FS_option_kw[] = {
145 {"compression", INC_KW_COMPRESSION},
146 {"signature", INC_KW_DIGEST},
147 {"encryption", INC_KW_ENCRYPTION},
148 {"verify", INC_KW_VERIFY},
149 {"onefs", INC_KW_ONEFS},
150 {"recurse", INC_KW_RECURSE},
151 {"sparse", INC_KW_SPARSE},
152 {"hardlinks", INC_KW_HARDLINK},
153 {"replace", INC_KW_REPLACE},
154 {"readfifo", INC_KW_READFIFO},
155 {"portable", INC_KW_PORTABLE},
156 {"mtimeonly", INC_KW_MTIMEONLY},
157 {"keepatime", INC_KW_KEEPATIME},
158 {"exclude", INC_KW_EXCLUDE},
159 {"aclsupport", INC_KW_ACL},
160 {"ignorecase", INC_KW_IGNORECASE},
161 {"hfsplussupport", INC_KW_HFSPLUS},
162 {"noatime", INC_KW_NOATIME},
163 {"enhancedwild", INC_KW_ENHANCEDWILD},
167 /* Options for FileSet keywords */
176 * Options permitted for each keyword and resulting value.
177 * The output goes into opts, which are then transmitted to
178 * the FD for application as options to the following list of
181 static struct s_fs_opt FS_options[] = {
182 {"md5", INC_KW_DIGEST, "M"},
183 {"sha1", INC_KW_DIGEST, "S"},
184 {"sha256", INC_KW_DIGEST, "S2"},
185 {"sha512", INC_KW_DIGEST, "S3"},
186 {"gzip", INC_KW_COMPRESSION, "Z6"},
187 {"gzip1", INC_KW_COMPRESSION, "Z1"},
188 {"gzip2", INC_KW_COMPRESSION, "Z2"},
189 {"gzip3", INC_KW_COMPRESSION, "Z3"},
190 {"gzip4", INC_KW_COMPRESSION, "Z4"},
191 {"gzip5", INC_KW_COMPRESSION, "Z5"},
192 {"gzip6", INC_KW_COMPRESSION, "Z6"},
193 {"gzip7", INC_KW_COMPRESSION, "Z7"},
194 {"gzip8", INC_KW_COMPRESSION, "Z8"},
195 {"gzip9", INC_KW_COMPRESSION, "Z9"},
196 {"blowfish", INC_KW_ENCRYPTION, "B"}, /* ***FIXME*** not implemented */
197 {"3des", INC_KW_ENCRYPTION, "3"}, /* ***FIXME*** not implemented */
198 {"yes", INC_KW_ONEFS, "0"},
199 {"no", INC_KW_ONEFS, "f"},
200 {"yes", INC_KW_RECURSE, "0"},
201 {"no", INC_KW_RECURSE, "h"},
202 {"yes", INC_KW_SPARSE, "s"},
203 {"no", INC_KW_SPARSE, "0"},
204 {"yes", INC_KW_HARDLINK, "0"},
205 {"no", INC_KW_HARDLINK, "H"},
206 {"always", INC_KW_REPLACE, "a"},
207 {"ifnewer", INC_KW_REPLACE, "w"},
208 {"never", INC_KW_REPLACE, "n"},
209 {"yes", INC_KW_READFIFO, "r"},
210 {"no", INC_KW_READFIFO, "0"},
211 {"yes", INC_KW_PORTABLE, "p"},
212 {"no", INC_KW_PORTABLE, "0"},
213 {"yes", INC_KW_MTIMEONLY, "m"},
214 {"no", INC_KW_MTIMEONLY, "0"},
215 {"yes", INC_KW_KEEPATIME, "k"},
216 {"no", INC_KW_KEEPATIME, "0"},
217 {"yes", INC_KW_EXCLUDE, "e"},
218 {"no", INC_KW_EXCLUDE, "0"},
219 {"yes", INC_KW_ACL, "A"},
220 {"no", INC_KW_ACL, "0"},
221 {"yes", INC_KW_IGNORECASE, "i"},
222 {"no", INC_KW_IGNORECASE, "0"},
223 {"yes", INC_KW_HFSPLUS, "R"}, /* "R" for resource fork */
224 {"no", INC_KW_HFSPLUS, "0"},
225 {"yes", INC_KW_NOATIME, "K"},
226 {"no", INC_KW_NOATIME, "0"},
227 {"yes", INC_KW_ENHANCEDWILD, "K"},
228 {"no", INC_KW_ENHANCEDWILD, "0"},
235 * Scan for right hand side of Include options (keyword=option) is
236 * converted into one or two characters. Verifyopts=xxxx is Vxxxx:
237 * Whatever is found is concatenated to the opts string.
238 * This code is also used inside an Options resource.
240 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
244 int lcopts = lc->options;
246 option[0] = 0; /* default option = none */
247 option[2] = 0; /* terminate options */
248 lc->options |= LOPT_STRING; /* force string */
249 token = lex_get_token(lc, T_STRING); /* expect at least one option */
250 if (keyword == INC_KW_VERIFY) { /* special case */
251 /* ***FIXME**** ensure these are in permitted set */
252 bstrncat(opts, "V", optlen); /* indicate Verify */
253 bstrncat(opts, lc->str, optlen);
254 bstrncat(opts, ":", optlen); /* terminate it */
255 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
258 * Standard keyword options for Include/Exclude
261 for (i=0; FS_options[i].name; i++) {
262 if (FS_options[i].keyword == keyword && strcasecmp(lc->str, FS_options[i].name) == 0) {
263 /* NOTE! maximum 2 letters here or increase option[3] */
264 option[0] = FS_options[i].option[0];
265 option[1] = FS_options[i].option[1];
271 scan_err1(lc, _("Expected a FileSet option keyword, got:%s:"), lc->str);
272 } else { /* add option */
273 bstrncat(opts, option, optlen);
274 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
277 lc->options = lcopts;
279 /* If option terminated by comma, eat it */
281 token = lex_get_token(lc, T_ALL); /* yes, eat comma */
287 * Store FileSet Include/Exclude info
288 * NEW style includes are handled in store_newinc()
290 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass)
295 * Decide if we are doing a new Include or an old include. The
296 * new Include is followed immediately by open brace, whereas the
297 * old include has options following the Include.
299 token = lex_get_token(lc, T_SKIP_EOL);
300 if (token == T_BOB) {
301 store_newinc(lc, item, index, pass);
304 scan_err0(lc, _("Old style Include/Exclude not supported\n"));
309 * Store NEW style FileSet FInclude/FExclude info
311 * Note, when this routine is called, we are inside a FileSet
312 * resource. We treat the Include/Execlude like a sort of
313 * mini-resource within the FileSet resource.
315 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
321 if (!res_all.res_fs.have_MD5) {
322 MD5Init(&res_all.res_fs.md5c);
323 res_all.res_fs.have_MD5 = true;
325 memset(&res_incexe, 0, sizeof(INCEXE));
326 res_all.res_fs.new_include = true;
327 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
328 if (token == T_EOB) {
331 if (token != T_IDENTIFIER) {
332 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
334 for (i=0; newinc_items[i].name; i++) {
335 options = strcasecmp(lc->str, "options") == 0;
336 if (strcasecmp(newinc_items[i].name, lc->str) == 0) {
338 token = lex_get_token(lc, T_SKIP_EOL);
339 if (token != T_EQUALS) {
340 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
343 /* Call item handler */
344 newinc_items[i].handler(lc, &newinc_items[i], i, pass);
350 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
354 incexe = (INCEXE *)malloc(sizeof(INCEXE));
355 memcpy(incexe, &res_incexe, sizeof(INCEXE));
356 memset(&res_incexe, 0, sizeof(INCEXE));
357 if (item->code == 0) { /* include */
358 if (res_all.res_fs.num_includes == 0) {
359 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
361 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
362 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
364 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
365 Dmsg1(900, "num_includes=%d\n", res_all.res_fs.num_includes);
366 } else { /* exclude */
367 if (res_all.res_fs.num_excludes == 0) {
368 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
370 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
371 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
373 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
374 Dmsg1(900, "num_excludes=%d\n", res_all.res_fs.num_excludes);
378 set_bit(index, res_all.hdr.item_present);
382 /* Store regex info */
383 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass)
391 token = lex_get_token(lc, T_SKIP_EOL);
393 /* Pickup regex string
397 case T_UNQUOTED_STRING:
398 case T_QUOTED_STRING:
399 rc = regcomp(&preg, lc->str, REG_EXTENDED);
401 regerror(rc, &preg, prbuf, sizeof(prbuf));
403 scan_err1(lc, _("Regex compile error. ERR=%s\n"), prbuf);
407 if (item->code == 1) {
409 res_incexe.current_opts->regexdir.append(bstrdup(lc->str));
410 newsize = res_incexe.current_opts->regexdir.size();
411 } else if (item->code == 2) {
413 res_incexe.current_opts->regexfile.append(bstrdup(lc->str));
414 newsize = res_incexe.current_opts->regexfile.size();
417 res_incexe.current_opts->regex.append(bstrdup(lc->str));
418 newsize = res_incexe.current_opts->regex.size();
420 Dmsg4(900, "set %s %p size=%d %s\n",
421 type, res_incexe.current_opts, newsize, lc->str);
424 scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
430 /* Store Base info */
431 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
435 token = lex_get_token(lc, T_NAME);
438 * Pickup Base Job Name
440 res_incexe.current_opts->base.append(bstrdup(lc->str));
445 /* Store reader info */
446 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass)
450 token = lex_get_token(lc, T_NAME);
453 * Pickup reader command
455 res_incexe.current_opts->reader = bstrdup(lc->str);
460 /* Store writer innfo */
461 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass)
465 token = lex_get_token(lc, T_NAME);
468 * Pickup writer command
470 res_incexe.current_opts->writer = bstrdup(lc->str);
477 /* Store Wild-card info */
478 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass)
484 token = lex_get_token(lc, T_SKIP_EOL);
487 * Pickup Wild-card string
491 case T_UNQUOTED_STRING:
492 case T_QUOTED_STRING:
493 if (item->code == 1) {
495 res_incexe.current_opts->wilddir.append(bstrdup(lc->str));
496 newsize = res_incexe.current_opts->wilddir.size();
497 } else if (item->code == 2) {
498 if (strchr(lc->str, '/') != NULL) {
500 res_incexe.current_opts->wildfile.append(bstrdup(lc->str));
501 newsize = res_incexe.current_opts->wildfile.size();
504 res_incexe.current_opts->wildbase.append(bstrdup(lc->str));
505 newsize = res_incexe.current_opts->wildbase.size();
509 res_incexe.current_opts->wild.append(bstrdup(lc->str));
510 newsize = res_incexe.current_opts->wild.size();
512 Dmsg4(9, "set %s %p size=%d %s\n",
513 type, res_incexe.current_opts, newsize, lc->str);
516 scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
522 /* Store fstype info */
523 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass)
527 token = lex_get_token(lc, T_SKIP_EOL);
529 /* Pickup fstype string */
532 case T_UNQUOTED_STRING:
533 case T_QUOTED_STRING:
534 res_incexe.current_opts->fstype.append(bstrdup(lc->str));
535 Dmsg3(900, "set fstype %p size=%d %s\n",
536 res_incexe.current_opts, res_incexe.current_opts->fstype.size(), lc->str);
539 scan_err1(lc, _("Expected an fstype string, got: %s\n"), lc->str);
545 /* Store drivetype info */
546 static void store_drivetype(LEX *lc, RES_ITEM *item, int index, int pass)
550 token = lex_get_token(lc, T_SKIP_EOL);
552 /* Pickup drivetype string */
555 case T_UNQUOTED_STRING:
556 case T_QUOTED_STRING:
557 res_incexe.current_opts->drivetype.append(bstrdup(lc->str));
558 Dmsg3(900, "set drivetype %p size=%d %s\n",
559 res_incexe.current_opts, res_incexe.current_opts->drivetype.size(), lc->str);
562 scan_err1(lc, _("Expected an drivetype string, got: %s\n"), lc->str);
569 * Store Filename info. Note, for minor efficiency reasons, we
570 * always increase the name buffer by 10 items because we expect
571 * to add more entries.
573 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass)
578 token = lex_get_token(lc, T_SKIP_EOL);
580 /* Pickup Filename string
584 case T_UNQUOTED_STRING:
585 if (strchr(lc->str, '\\')) {
586 scan_err1(lc, _("Backslash found. Use forward slashes or quote the string.: %s\n"), lc->str);
589 case T_QUOTED_STRING:
590 if (res_all.res_fs.have_MD5) {
591 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
593 incexe = &res_incexe;
594 if (incexe->name_list.size() == 0) {
595 incexe->name_list.init(10, true);
597 incexe->name_list.append(bstrdup(lc->str));
598 Dmsg1(900, "Add to name_list %s\n", lc->str);
601 scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
609 * Come here when Options seen in Include/Exclude
611 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass)
615 token = lex_get_token(lc, T_SKIP_EOL);
616 if (token != T_BOB) {
617 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
621 setup_current_opts();
624 while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
625 if (token == T_EOL) {
628 if (token == T_EOB) {
631 if (token != T_IDENTIFIER) {
632 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
634 for (i=0; options_items[i].name; i++) {
635 if (strcasecmp(options_items[i].name, lc->str) == 0) {
636 token = lex_get_token(lc, T_SKIP_EOL);
637 if (token != T_EQUALS) {
638 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
640 /* Call item handler */
641 options_items[i].handler(lc, &options_items[i], i, pass);
647 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
654 * New style options come here
656 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass)
663 keyword = INC_KW_NONE;
664 /* Look up the keyword */
665 for (i=0; FS_option_kw[i].name; i++) {
666 if (strcasecmp(item->name, FS_option_kw[i].name) == 0) {
667 keyword = FS_option_kw[i].token;
671 if (keyword == INC_KW_NONE) {
672 scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
674 /* Now scan for the value */
675 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
677 bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
678 Dmsg2(900, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
685 /* If current_opts not defined, create first entry */
686 static void setup_current_opts(void)
688 FOPTS *fo = (FOPTS *)malloc(sizeof(FOPTS));
689 memset(fo, 0, sizeof(FOPTS));
690 fo->regex.init(1, true);
691 fo->regexdir.init(1, true);
692 fo->regexfile.init(1, true);
693 fo->wild.init(1, true);
694 fo->wilddir.init(1, true);
695 fo->wildfile.init(1, true);
696 fo->wildbase.init(1, true);
697 fo->base.init(1, true);
698 fo->fstype.init(1, true);
699 fo->drivetype.init(1, true);
700 res_incexe.current_opts = fo;
701 if (res_incexe.num_opts == 0) {
702 res_incexe.opts_list = (FOPTS **)malloc(sizeof(FOPTS *));
704 res_incexe.opts_list = (FOPTS **)realloc(res_incexe.opts_list,
705 sizeof(FOPTS *) * (res_incexe.num_opts + 1));
707 res_incexe.opts_list[res_incexe.num_opts++] = fo;