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/regex.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_opts(LEX *lc, RES_ITEM *item, int index, int pass);
41 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass);
42 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass);
43 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass);
44 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass);
45 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass);
46 static void setup_current_opts(void);
49 /* We build the current resource here as we are
50 * scanning the resource configuration definition,
51 * then move it to allocated memory when the resource
55 extern "C" { // work around visual compiler mangling variables
61 extern int res_all_size;
63 /* We build the current new Include and Exclude items here */
64 static INCEXE res_incexe;
67 * new Include/Exclude items
68 * name handler value code flags default_value
70 static RES_ITEM newinc_items[] = {
71 {"file", store_fname, {0}, 0, 0, 0},
72 {"options", options_res, {0}, 0, 0, 0},
73 {NULL, NULL, {0}, 0, 0, 0}
77 * Items that are valid in an Options resource
79 static RES_ITEM options_items[] = {
80 {"compression", store_opts, {0}, 0, 0, 0},
81 {"signature", store_opts, {0}, 0, 0, 0},
82 {"verify", store_opts, {0}, 0, 0, 0},
83 {"onefs", store_opts, {0}, 0, 0, 0},
84 {"recurse", store_opts, {0}, 0, 0, 0},
85 {"sparse", store_opts, {0}, 0, 0, 0},
86 {"hardlinks", store_opts, {0}, 0, 0, 0},
87 {"readfifo", store_opts, {0}, 0, 0, 0},
88 {"replace", store_opts, {0}, 0, 0, 0},
89 {"portable", store_opts, {0}, 0, 0, 0},
90 {"mtimeonly", store_opts, {0}, 0, 0, 0},
91 {"keepatime", store_opts, {0}, 0, 0, 0},
92 {"regex", store_regex, {0}, 0, 0, 0},
93 {"regexdir", store_regex, {0}, 1, 0, 0},
94 {"regexfile", store_regex, {0}, 2, 0, 0},
95 {"base", store_base, {0}, 0, 0, 0},
96 {"wild", store_wild, {0}, 0, 0, 0},
97 {"wilddir", store_wild, {0}, 1, 0, 0},
98 {"wildfile", store_wild, {0}, 2, 0, 0},
99 {"exclude", store_opts, {0}, 0, 0, 0},
100 {"aclsupport", store_opts, {0}, 0, 0, 0},
101 {"reader", store_reader, {0}, 0, 0, 0},
102 {"writer", store_writer, {0}, 0, 0, 0},
103 {"ignorecase", store_opts, {0}, 0, 0, 0},
104 {"fstype", store_fstype, {0}, 0, 0, 0},
105 {"hfsplussupport", store_opts, {0}, 0, 0, 0},
106 {"noatime", store_opts, {0}, 0, 0, 0},
107 {NULL, NULL, {0}, 0, 0, 0}
111 /* Define FileSet KeyWord values */
122 INC_KW_REPLACE, /* restore options */
123 INC_KW_READFIFO, /* Causes fifo data to be read */
135 * This is the list of options that can be stored by store_opts
136 * Note, now that the old style Include/Exclude code is gone,
137 * the INC_KW code could be put into the "code" field of the
138 * options given above.
140 static struct s_kw FS_option_kw[] = {
141 {"compression", INC_KW_COMPRESSION},
142 {"signature", INC_KW_DIGEST},
143 {"encryption", INC_KW_ENCRYPTION},
144 {"verify", INC_KW_VERIFY},
145 {"onefs", INC_KW_ONEFS},
146 {"recurse", INC_KW_RECURSE},
147 {"sparse", INC_KW_SPARSE},
148 {"hardlinks", INC_KW_HARDLINK},
149 {"replace", INC_KW_REPLACE},
150 {"readfifo", INC_KW_READFIFO},
151 {"portable", INC_KW_PORTABLE},
152 {"mtimeonly", INC_KW_MTIMEONLY},
153 {"keepatime", INC_KW_KEEPATIME},
154 {"exclude", INC_KW_EXCLUDE},
155 {"aclsupport", INC_KW_ACL},
156 {"ignorecase", INC_KW_IGNORECASE},
157 {"hfsplussupport", INC_KW_HFSPLUS},
158 {"noatime", INC_KW_NOATIME},
162 /* Options for FileSet keywords */
171 * Options permitted for each keyword and resulting value.
172 * The output goes into opts, which are then transmitted to
173 * the FD for application as options to the following list of
176 static struct s_fs_opt FS_options[] = {
177 {"md5", INC_KW_DIGEST, "M"},
178 {"sha1", INC_KW_DIGEST, "S"},
179 {"sha256", INC_KW_DIGEST, "S2"},
180 {"sha512", INC_KW_DIGEST, "S3"},
181 {"gzip", INC_KW_COMPRESSION, "Z6"},
182 {"gzip1", INC_KW_COMPRESSION, "Z1"},
183 {"gzip2", INC_KW_COMPRESSION, "Z2"},
184 {"gzip3", INC_KW_COMPRESSION, "Z3"},
185 {"gzip4", INC_KW_COMPRESSION, "Z4"},
186 {"gzip5", INC_KW_COMPRESSION, "Z5"},
187 {"gzip6", INC_KW_COMPRESSION, "Z6"},
188 {"gzip7", INC_KW_COMPRESSION, "Z7"},
189 {"gzip8", INC_KW_COMPRESSION, "Z8"},
190 {"gzip9", INC_KW_COMPRESSION, "Z9"},
191 {"blowfish", INC_KW_ENCRYPTION, "B"}, /* ***FIXME*** not implemented */
192 {"3des", INC_KW_ENCRYPTION, "3"}, /* ***FIXME*** not implemented */
193 {"yes", INC_KW_ONEFS, "0"},
194 {"no", INC_KW_ONEFS, "f"},
195 {"yes", INC_KW_RECURSE, "0"},
196 {"no", INC_KW_RECURSE, "h"},
197 {"yes", INC_KW_SPARSE, "s"},
198 {"no", INC_KW_SPARSE, "0"},
199 {"yes", INC_KW_HARDLINK, "0"},
200 {"no", INC_KW_HARDLINK, "H"},
201 {"always", INC_KW_REPLACE, "a"},
202 {"ifnewer", INC_KW_REPLACE, "w"},
203 {"never", INC_KW_REPLACE, "n"},
204 {"yes", INC_KW_READFIFO, "r"},
205 {"no", INC_KW_READFIFO, "0"},
206 {"yes", INC_KW_PORTABLE, "p"},
207 {"no", INC_KW_PORTABLE, "0"},
208 {"yes", INC_KW_MTIMEONLY, "m"},
209 {"no", INC_KW_MTIMEONLY, "0"},
210 {"yes", INC_KW_KEEPATIME, "k"},
211 {"no", INC_KW_KEEPATIME, "0"},
212 {"yes", INC_KW_EXCLUDE, "e"},
213 {"no", INC_KW_EXCLUDE, "0"},
214 {"yes", INC_KW_ACL, "A"},
215 {"no", INC_KW_ACL, "0"},
216 {"yes", INC_KW_IGNORECASE, "i"},
217 {"no", INC_KW_IGNORECASE, "0"},
218 {"yes", INC_KW_HFSPLUS, "R"}, /* "R" for resource fork */
219 {"no", INC_KW_HFSPLUS, "0"},
220 {"yes", INC_KW_NOATIME, "K"},
221 {"no", INC_KW_NOATIME, "0"},
228 * Scan for right hand side of Include options (keyword=option) is
229 * converted into one or two characters. Verifyopts=xxxx is Vxxxx:
230 * Whatever is found is concatenated to the opts string.
231 * This code is also used inside an Options resource.
233 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
237 int lcopts = lc->options;
239 option[0] = 0; /* default option = none */
240 option[2] = 0; /* terminate options */
241 lc->options |= LOPT_STRING; /* force string */
242 token = lex_get_token(lc, T_STRING); /* expect at least one option */
243 if (keyword == INC_KW_VERIFY) { /* special case */
244 /* ***FIXME**** ensure these are in permitted set */
245 bstrncat(opts, "V", optlen); /* indicate Verify */
246 bstrncat(opts, lc->str, optlen);
247 bstrncat(opts, ":", optlen); /* terminate it */
248 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
251 * Standard keyword options for Include/Exclude
254 for (i=0; FS_options[i].name; i++) {
255 if (FS_options[i].keyword == keyword && strcasecmp(lc->str, FS_options[i].name) == 0) {
256 /* NOTE! maximum 2 letters here or increase option[3] */
257 option[0] = FS_options[i].option[0];
258 option[1] = FS_options[i].option[1];
264 scan_err1(lc, _("Expected a FileSet option keyword, got:%s:"), lc->str);
265 } else { /* add option */
266 bstrncat(opts, option, optlen);
267 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
270 lc->options = lcopts;
272 /* If option terminated by comma, eat it */
274 token = lex_get_token(lc, T_ALL); /* yes, eat comma */
280 * Store FileSet Include/Exclude info
281 * NEW style includes are handled in store_newinc()
283 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass)
288 * Decide if we are doing a new Include or an old include. The
289 * new Include is followed immediately by open brace, whereas the
290 * old include has options following the Include.
292 token = lex_get_token(lc, T_SKIP_EOL);
293 if (token == T_BOB) {
294 store_newinc(lc, item, index, pass);
297 scan_err0(lc, _("Old style Include/Exclude not supported\n"));
302 * Store NEW style FileSet FInclude/FExclude info
304 * Note, when this routine is called, we are inside a FileSet
305 * resource. We treat the Include/Execlude like a sort of
306 * mini-resource within the FileSet resource.
308 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
314 if (!res_all.res_fs.have_MD5) {
315 MD5Init(&res_all.res_fs.md5c);
316 res_all.res_fs.have_MD5 = true;
318 memset(&res_incexe, 0, sizeof(INCEXE));
319 res_all.res_fs.new_include = true;
320 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
321 if (token == T_EOB) {
324 if (token != T_IDENTIFIER) {
325 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
327 for (i=0; newinc_items[i].name; i++) {
328 options = strcasecmp(lc->str, "options") == 0;
329 if (strcasecmp(newinc_items[i].name, lc->str) == 0) {
331 token = lex_get_token(lc, T_SKIP_EOL);
332 if (token != T_EQUALS) {
333 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
336 /* Call item handler */
337 newinc_items[i].handler(lc, &newinc_items[i], i, pass);
343 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
347 incexe = (INCEXE *)malloc(sizeof(INCEXE));
348 memcpy(incexe, &res_incexe, sizeof(INCEXE));
349 memset(&res_incexe, 0, sizeof(INCEXE));
350 if (item->code == 0) { /* include */
351 if (res_all.res_fs.num_includes == 0) {
352 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
354 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
355 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
357 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
358 Dmsg1(900, "num_includes=%d\n", res_all.res_fs.num_includes);
359 } else { /* exclude */
360 if (res_all.res_fs.num_excludes == 0) {
361 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
363 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
364 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
366 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
367 Dmsg1(900, "num_excludes=%d\n", res_all.res_fs.num_excludes);
371 set_bit(index, res_all.hdr.item_present);
375 /* Store regex info */
376 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass)
384 token = lex_get_token(lc, T_SKIP_EOL);
386 /* Pickup regex string
390 case T_UNQUOTED_STRING:
391 case T_QUOTED_STRING:
392 rc = regcomp(&preg, lc->str, REG_EXTENDED);
394 regerror(rc, &preg, prbuf, sizeof(prbuf));
396 scan_err1(lc, _("Regex compile error. ERR=%s\n"), prbuf);
400 if (item->code == 1) {
402 res_incexe.current_opts->regexdir.append(bstrdup(lc->str));
403 newsize = res_incexe.current_opts->regexdir.size();
404 } else if (item->code == 2) {
406 res_incexe.current_opts->regexfile.append(bstrdup(lc->str));
407 newsize = res_incexe.current_opts->regexfile.size();
410 res_incexe.current_opts->regex.append(bstrdup(lc->str));
411 newsize = res_incexe.current_opts->regex.size();
413 Dmsg4(900, "set %s %p size=%d %s\n",
414 type, res_incexe.current_opts, newsize, lc->str);
417 scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
423 /* Store Base info */
424 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
428 token = lex_get_token(lc, T_NAME);
431 * Pickup Base Job Name
433 res_incexe.current_opts->base.append(bstrdup(lc->str));
438 /* Store reader info */
439 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass)
443 token = lex_get_token(lc, T_NAME);
446 * Pickup reader command
448 res_incexe.current_opts->reader = bstrdup(lc->str);
453 /* Store writer innfo */
454 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass)
458 token = lex_get_token(lc, T_NAME);
461 * Pickup writer command
463 res_incexe.current_opts->writer = bstrdup(lc->str);
470 /* Store Wild-card info */
471 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass)
477 token = lex_get_token(lc, T_SKIP_EOL);
480 * Pickup Wild-card string
484 case T_UNQUOTED_STRING:
485 case T_QUOTED_STRING:
486 if (item->code == 1) {
488 res_incexe.current_opts->wilddir.append(bstrdup(lc->str));
489 newsize = res_incexe.current_opts->wilddir.size();
490 } else if (item->code == 2) {
492 res_incexe.current_opts->wildfile.append(bstrdup(lc->str));
493 newsize = res_incexe.current_opts->wildfile.size();
496 res_incexe.current_opts->wild.append(bstrdup(lc->str));
497 newsize = res_incexe.current_opts->wild.size();
499 Dmsg4(9, "set %s %p size=%d %s\n",
500 type, res_incexe.current_opts, newsize, lc->str);
503 scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
509 /* Store fstype info */
510 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass)
514 token = lex_get_token(lc, T_SKIP_EOL);
516 /* Pickup fstype string */
519 case T_UNQUOTED_STRING:
520 case T_QUOTED_STRING:
521 res_incexe.current_opts->fstype.append(bstrdup(lc->str));
522 Dmsg3(900, "set fstype %p size=%d %s\n",
523 res_incexe.current_opts, res_incexe.current_opts->fstype.size(), lc->str);
526 scan_err1(lc, _("Expected an fstype string, got: %s\n"), lc->str);
533 * Store Filename info. Note, for minor efficiency reasons, we
534 * always increase the name buffer by 10 items because we expect
535 * to add more entries.
537 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass)
542 token = lex_get_token(lc, T_SKIP_EOL);
544 /* Pickup Filename string
548 case T_UNQUOTED_STRING:
549 if (strchr(lc->str, '\\')) {
550 scan_err1(lc, _("Backslash found. Use forward slashes or quote the string.: %s\n"), lc->str);
553 case T_QUOTED_STRING:
554 if (res_all.res_fs.have_MD5) {
555 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
557 incexe = &res_incexe;
558 if (incexe->name_list.size() == 0) {
559 incexe->name_list.init(10, true);
561 incexe->name_list.append(bstrdup(lc->str));
562 Dmsg1(900, "Add to name_list %s\n", lc->str);
565 scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
573 * Come here when Options seen in Include/Exclude
575 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass)
579 token = lex_get_token(lc, T_SKIP_EOL);
580 if (token != T_BOB) {
581 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
585 setup_current_opts();
588 while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
589 if (token == T_EOL) {
592 if (token == T_EOB) {
595 if (token != T_IDENTIFIER) {
596 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
598 for (i=0; options_items[i].name; i++) {
599 if (strcasecmp(options_items[i].name, lc->str) == 0) {
600 token = lex_get_token(lc, T_SKIP_EOL);
601 if (token != T_EQUALS) {
602 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
604 /* Call item handler */
605 options_items[i].handler(lc, &options_items[i], i, pass);
611 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
618 * New style options come here
620 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass)
627 keyword = INC_KW_NONE;
628 /* Look up the keyword */
629 for (i=0; FS_option_kw[i].name; i++) {
630 if (strcasecmp(item->name, FS_option_kw[i].name) == 0) {
631 keyword = FS_option_kw[i].token;
635 if (keyword == INC_KW_NONE) {
636 scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
638 /* Now scan for the value */
639 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
641 bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
642 Dmsg2(900, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
649 /* If current_opts not defined, create first entry */
650 static void setup_current_opts(void)
652 FOPTS *fo = (FOPTS *)malloc(sizeof(FOPTS));
653 memset(fo, 0, sizeof(FOPTS));
654 fo->regex.init(1, true);
655 fo->regexdir.init(1, true);
656 fo->regexfile.init(1, true);
657 fo->wild.init(1, true);
658 fo->wilddir.init(1, true);
659 fo->wildfile.init(1, true);
660 fo->base.init(1, true);
661 fo->fstype.init(1, true);
662 res_incexe.current_opts = fo;
663 if (res_incexe.num_opts == 0) {
664 res_incexe.opts_list = (FOPTS **)malloc(sizeof(FOPTS *));
666 res_incexe.opts_list = (FOPTS **)realloc(res_incexe.opts_list,
667 sizeof(FOPTS *) * (res_incexe.num_opts + 1));
669 res_incexe.opts_list[res_incexe.num_opts++] = fo;