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 int res_all_size;
57 /* We build the current new Include and Exclude items here */
58 static INCEXE res_incexe;
61 * new Include/Exclude items
62 * name handler value code flags default_value
64 static RES_ITEM newinc_items[] = {
65 {"file", store_fname, {0}, 0, 0, 0},
66 {"options", options_res, {0}, 0, 0, 0},
67 {NULL, NULL, {0}, 0, 0, 0}
71 * Items that are valid in an Options resource
73 static RES_ITEM options_items[] = {
74 {"compression", store_opts, {0}, 0, 0, 0},
75 {"signature", store_opts, {0}, 0, 0, 0},
76 {"verify", store_opts, {0}, 0, 0, 0},
77 {"onefs", store_opts, {0}, 0, 0, 0},
78 {"recurse", store_opts, {0}, 0, 0, 0},
79 {"sparse", store_opts, {0}, 0, 0, 0},
80 {"hardlinks", store_opts, {0}, 0, 0, 0},
81 {"readfifo", store_opts, {0}, 0, 0, 0},
82 {"replace", store_opts, {0}, 0, 0, 0},
83 {"portable", store_opts, {0}, 0, 0, 0},
84 {"mtimeonly", store_opts, {0}, 0, 0, 0},
85 {"keepatime", store_opts, {0}, 0, 0, 0},
86 {"regex", store_regex, {0}, 0, 0, 0},
87 {"regexdir", store_regex, {0}, 1, 0, 0},
88 {"regexfile", store_regex, {0}, 2, 0, 0},
89 {"base", store_base, {0}, 0, 0, 0},
90 {"wild", store_wild, {0}, 0, 0, 0},
91 {"wilddir", store_wild, {0}, 1, 0, 0},
92 {"wildfile", store_wild, {0}, 2, 0, 0},
93 {"exclude", store_opts, {0}, 0, 0, 0},
94 {"aclsupport", store_opts, {0}, 0, 0, 0},
95 {"reader", store_reader, {0}, 0, 0, 0},
96 {"writer", store_writer, {0}, 0, 0, 0},
97 {"ignorecase", store_opts, {0}, 0, 0, 0},
98 {"fstype", store_fstype, {0}, 0, 0, 0},
99 {"hfsplussupport", store_opts, {0}, 0, 0, 0},
100 {"noatime", store_opts, {0}, 0, 0, 0},
101 {NULL, NULL, {0}, 0, 0, 0}
105 /* Define FileSet KeyWord values */
116 INC_KW_REPLACE, /* restore options */
117 INC_KW_READFIFO, /* Causes fifo data to be read */
129 * This is the list of options that can be stored by store_opts
130 * Note, now that the old style Include/Exclude code is gone,
131 * the INC_KW code could be put into the "code" field of the
132 * options given above.
134 static struct s_kw FS_option_kw[] = {
135 {"compression", INC_KW_COMPRESSION},
136 {"signature", INC_KW_DIGEST},
137 {"encryption", INC_KW_ENCRYPTION},
138 {"verify", INC_KW_VERIFY},
139 {"onefs", INC_KW_ONEFS},
140 {"recurse", INC_KW_RECURSE},
141 {"sparse", INC_KW_SPARSE},
142 {"hardlinks", INC_KW_HARDLINK},
143 {"replace", INC_KW_REPLACE},
144 {"readfifo", INC_KW_READFIFO},
145 {"portable", INC_KW_PORTABLE},
146 {"mtimeonly", INC_KW_MTIMEONLY},
147 {"keepatime", INC_KW_KEEPATIME},
148 {"exclude", INC_KW_EXCLUDE},
149 {"aclsupport", INC_KW_ACL},
150 {"ignorecase", INC_KW_IGNORECASE},
151 {"hfsplussupport", INC_KW_HFSPLUS},
152 {"noatime", INC_KW_NOATIME},
156 /* Options for FileSet keywords */
165 * Options permitted for each keyword and resulting value.
166 * The output goes into opts, which are then transmitted to
167 * the FD for application as options to the following list of
170 static struct s_fs_opt FS_options[] = {
171 {"md5", INC_KW_DIGEST, "M"},
172 {"sha1", INC_KW_DIGEST, "S"},
173 {"sha256", INC_KW_DIGEST, "S2"},
174 {"sha512", INC_KW_DIGEST, "S3"},
175 {"gzip", INC_KW_COMPRESSION, "Z6"},
176 {"gzip1", INC_KW_COMPRESSION, "Z1"},
177 {"gzip2", INC_KW_COMPRESSION, "Z2"},
178 {"gzip3", INC_KW_COMPRESSION, "Z3"},
179 {"gzip4", INC_KW_COMPRESSION, "Z4"},
180 {"gzip5", INC_KW_COMPRESSION, "Z5"},
181 {"gzip6", INC_KW_COMPRESSION, "Z6"},
182 {"gzip7", INC_KW_COMPRESSION, "Z7"},
183 {"gzip8", INC_KW_COMPRESSION, "Z8"},
184 {"gzip9", INC_KW_COMPRESSION, "Z9"},
185 {"blowfish", INC_KW_ENCRYPTION, "B"}, /* ***FIXME*** not implemented */
186 {"3des", INC_KW_ENCRYPTION, "3"}, /* ***FIXME*** not implemented */
187 {"yes", INC_KW_ONEFS, "0"},
188 {"no", INC_KW_ONEFS, "f"},
189 {"yes", INC_KW_RECURSE, "0"},
190 {"no", INC_KW_RECURSE, "h"},
191 {"yes", INC_KW_SPARSE, "s"},
192 {"no", INC_KW_SPARSE, "0"},
193 {"yes", INC_KW_HARDLINK, "0"},
194 {"no", INC_KW_HARDLINK, "H"},
195 {"always", INC_KW_REPLACE, "a"},
196 {"ifnewer", INC_KW_REPLACE, "w"},
197 {"never", INC_KW_REPLACE, "n"},
198 {"yes", INC_KW_READFIFO, "r"},
199 {"no", INC_KW_READFIFO, "0"},
200 {"yes", INC_KW_PORTABLE, "p"},
201 {"no", INC_KW_PORTABLE, "0"},
202 {"yes", INC_KW_MTIMEONLY, "m"},
203 {"no", INC_KW_MTIMEONLY, "0"},
204 {"yes", INC_KW_KEEPATIME, "k"},
205 {"no", INC_KW_KEEPATIME, "0"},
206 {"yes", INC_KW_EXCLUDE, "e"},
207 {"no", INC_KW_EXCLUDE, "0"},
208 {"yes", INC_KW_ACL, "A"},
209 {"no", INC_KW_ACL, "0"},
210 {"yes", INC_KW_IGNORECASE, "i"},
211 {"no", INC_KW_IGNORECASE, "0"},
212 {"yes", INC_KW_HFSPLUS, "R"}, /* "R" for resource fork */
213 {"no", INC_KW_HFSPLUS, "0"},
214 {"yes", INC_KW_NOATIME, "K"},
215 {"no", INC_KW_NOATIME, "0"},
222 * Scan for right hand side of Include options (keyword=option) is
223 * converted into one or two characters. Verifyopts=xxxx is Vxxxx:
224 * Whatever is found is concatenated to the opts string.
225 * This code is also used inside an Options resource.
227 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
231 int lcopts = lc->options;
233 option[0] = 0; /* default option = none */
234 option[2] = 0; /* terminate options */
235 lc->options |= LOPT_STRING; /* force string */
236 token = lex_get_token(lc, T_STRING); /* expect at least one option */
237 if (keyword == INC_KW_VERIFY) { /* special case */
238 /* ***FIXME**** ensure these are in permitted set */
239 bstrncat(opts, "V", optlen); /* indicate Verify */
240 bstrncat(opts, lc->str, optlen);
241 bstrncat(opts, ":", optlen); /* terminate it */
242 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
245 * Standard keyword options for Include/Exclude
248 for (i=0; FS_options[i].name; i++) {
249 if (strcasecmp(lc->str, FS_options[i].name) == 0 && FS_options[i].keyword == keyword) {
250 /* NOTE! maximum 2 letters here or increase option[3] */
251 option[0] = FS_options[i].option[0];
252 option[1] = FS_options[i].option[1];
258 scan_err1(lc, _("Expected a FileSet option keyword, got:%s:"), lc->str);
259 } else { /* add option */
260 bstrncat(opts, option, optlen);
261 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
264 lc->options = lcopts;
266 /* If option terminated by comma, eat it */
268 token = lex_get_token(lc, T_ALL); /* yes, eat comma */
274 * Store FileSet Include/Exclude info
275 * NEW style includes are handled in store_newinc()
277 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass)
282 * Decide if we are doing a new Include or an old include. The
283 * new Include is followed immediately by open brace, whereas the
284 * old include has options following the Include.
286 token = lex_get_token(lc, T_SKIP_EOL);
287 if (token == T_BOB) {
288 store_newinc(lc, item, index, pass);
291 scan_err0(lc, _("Old style Include/Exclude not supported\n"));
296 * Store NEW style FileSet FInclude/FExclude info
298 * Note, when this routine is called, we are inside a FileSet
299 * resource. We treat the Include/Execlude like a sort of
300 * mini-resource within the FileSet resource.
302 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
308 if (!res_all.res_fs.have_MD5) {
309 MD5Init(&res_all.res_fs.md5c);
310 res_all.res_fs.have_MD5 = true;
312 memset(&res_incexe, 0, sizeof(INCEXE));
313 res_all.res_fs.new_include = true;
314 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
315 if (token == T_EOB) {
318 if (token != T_IDENTIFIER) {
319 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
321 for (i=0; newinc_items[i].name; i++) {
322 options = strcasecmp(lc->str, "options") == 0;
323 if (strcasecmp(newinc_items[i].name, lc->str) == 0) {
325 token = lex_get_token(lc, T_SKIP_EOL);
326 if (token != T_EQUALS) {
327 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
330 /* Call item handler */
331 newinc_items[i].handler(lc, &newinc_items[i], i, pass);
337 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
341 incexe = (INCEXE *)malloc(sizeof(INCEXE));
342 memcpy(incexe, &res_incexe, sizeof(INCEXE));
343 memset(&res_incexe, 0, sizeof(INCEXE));
344 if (item->code == 0) { /* include */
345 if (res_all.res_fs.num_includes == 0) {
346 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
348 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
349 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
351 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
352 Dmsg1(900, "num_includes=%d\n", res_all.res_fs.num_includes);
353 } else { /* exclude */
354 if (res_all.res_fs.num_excludes == 0) {
355 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
357 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
358 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
360 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
361 Dmsg1(900, "num_excludes=%d\n", res_all.res_fs.num_excludes);
365 set_bit(index, res_all.hdr.item_present);
369 /* Store regex info */
370 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass)
378 token = lex_get_token(lc, T_SKIP_EOL);
380 /* Pickup regex string
384 case T_UNQUOTED_STRING:
385 case T_QUOTED_STRING:
386 rc = regcomp(&preg, lc->str, REG_EXTENDED);
388 regerror(rc, &preg, prbuf, sizeof(prbuf));
390 scan_err1(lc, _("Regex compile error. ERR=%s\n"), prbuf);
394 if (item->code == 1) {
396 res_incexe.current_opts->regexdir.append(bstrdup(lc->str));
397 newsize = res_incexe.current_opts->regexdir.size();
398 } else if (item->code == 2) {
400 res_incexe.current_opts->regexfile.append(bstrdup(lc->str));
401 newsize = res_incexe.current_opts->regexfile.size();
404 res_incexe.current_opts->regex.append(bstrdup(lc->str));
405 newsize = res_incexe.current_opts->regex.size();
407 Dmsg4(900, "set %s %p size=%d %s\n",
408 type, res_incexe.current_opts, newsize, lc->str);
411 scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
417 /* Store Base info */
418 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
422 token = lex_get_token(lc, T_NAME);
425 * Pickup Base Job Name
427 res_incexe.current_opts->base.append(bstrdup(lc->str));
432 /* Store reader info */
433 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass)
437 token = lex_get_token(lc, T_NAME);
440 * Pickup reader command
442 res_incexe.current_opts->reader = bstrdup(lc->str);
447 /* Store writer innfo */
448 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass)
452 token = lex_get_token(lc, T_NAME);
455 * Pickup writer command
457 res_incexe.current_opts->writer = bstrdup(lc->str);
464 /* Store Wild-card info */
465 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass)
471 token = lex_get_token(lc, T_SKIP_EOL);
474 * Pickup Wild-card string
478 case T_UNQUOTED_STRING:
479 case T_QUOTED_STRING:
480 if (item->code == 1) {
482 res_incexe.current_opts->wilddir.append(bstrdup(lc->str));
483 newsize = res_incexe.current_opts->wilddir.size();
484 } else if (item->code == 2) {
486 res_incexe.current_opts->wildfile.append(bstrdup(lc->str));
487 newsize = res_incexe.current_opts->wildfile.size();
490 res_incexe.current_opts->wild.append(bstrdup(lc->str));
491 newsize = res_incexe.current_opts->wild.size();
493 Dmsg4(9, "set %s %p size=%d %s\n",
494 type, res_incexe.current_opts, newsize, lc->str);
497 scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
503 /* Store fstype info */
504 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass)
508 token = lex_get_token(lc, T_SKIP_EOL);
510 /* Pickup fstype string */
513 case T_UNQUOTED_STRING:
514 case T_QUOTED_STRING:
515 res_incexe.current_opts->fstype.append(bstrdup(lc->str));
516 Dmsg3(900, "set fstype %p size=%d %s\n",
517 res_incexe.current_opts, res_incexe.current_opts->fstype.size(), lc->str);
520 scan_err1(lc, _("Expected an fstype string, got: %s\n"), lc->str);
527 * Store Filename info. Note, for minor efficiency reasons, we
528 * always increase the name buffer by 10 items because we expect
529 * to add more entries.
531 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass)
536 token = lex_get_token(lc, T_SKIP_EOL);
538 /* Pickup Filename string
542 case T_UNQUOTED_STRING:
543 if (strchr(lc->str, '\\')) {
544 scan_err1(lc, _("Backslash found. Use forward slashes or quote the string.: %s\n"), lc->str);
547 case T_QUOTED_STRING:
548 if (res_all.res_fs.have_MD5) {
549 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
551 incexe = &res_incexe;
552 if (incexe->name_list.size() == 0) {
553 incexe->name_list.init(10, true);
555 incexe->name_list.append(bstrdup(lc->str));
556 Dmsg1(900, "Add to name_list %s\n", lc->str);
559 scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
567 * Come here when Options seen in Include/Exclude
569 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass)
573 token = lex_get_token(lc, T_SKIP_EOL);
574 if (token != T_BOB) {
575 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
579 setup_current_opts();
582 while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
583 if (token == T_EOL) {
586 if (token == T_EOB) {
589 if (token != T_IDENTIFIER) {
590 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
592 for (i=0; options_items[i].name; i++) {
593 if (strcasecmp(options_items[i].name, lc->str) == 0) {
594 token = lex_get_token(lc, T_SKIP_EOL);
595 if (token != T_EQUALS) {
596 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
598 /* Call item handler */
599 options_items[i].handler(lc, &options_items[i], i, pass);
605 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
612 * New style options come here
614 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass)
621 keyword = INC_KW_NONE;
622 /* Look up the keyword */
623 for (i=0; FS_option_kw[i].name; i++) {
624 if (strcasecmp(item->name, FS_option_kw[i].name) == 0) {
625 keyword = FS_option_kw[i].token;
629 if (keyword == INC_KW_NONE) {
630 scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
632 /* Now scan for the value */
633 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
635 bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
636 Dmsg2(900, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
643 /* If current_opts not defined, create first entry */
644 static void setup_current_opts(void)
646 FOPTS *fo = (FOPTS *)malloc(sizeof(FOPTS));
647 memset(fo, 0, sizeof(FOPTS));
648 fo->regex.init(1, true);
649 fo->regexdir.init(1, true);
650 fo->regexfile.init(1, true);
651 fo->wild.init(1, true);
652 fo->wilddir.init(1, true);
653 fo->wildfile.init(1, true);
654 fo->base.init(1, true);
655 fo->fstype.init(1, true);
656 res_incexe.current_opts = fo;
657 if (res_incexe.num_opts == 0) {
658 res_incexe.opts_list = (FOPTS **)malloc(sizeof(FOPTS *));
660 res_incexe.opts_list = (FOPTS **)realloc(res_incexe.opts_list,
661 sizeof(FOPTS *) * (res_incexe.num_opts + 1));
663 res_incexe.opts_list[res_incexe.num_opts++] = fo;