2 * Configuration file parser for new and old Include and
5 * Kern Sibbald, March MMIII
10 Copyright (C) 2003-2005 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.
30 /* Forward referenced subroutines */
32 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass);
34 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass);
35 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass);
36 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass);
37 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass);
38 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass);
39 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass);
40 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass);
41 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass);
42 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass);
43 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass);
44 static void setup_current_opts(void);
47 /* We build the current resource here as we are
48 * scanning the resource configuration definition,
49 * then move it to allocated memory when the resource
53 extern int res_all_size;
55 /* We build the current new Include and Exclude items here */
56 static INCEXE res_incexe;
59 * new Include/Exclude items
60 * name handler value code flags default_value
62 static RES_ITEM newinc_items[] = {
63 {"file", store_fname, NULL, 0, 0, 0},
64 {"options", options_res, NULL, 0, 0, 0},
65 {NULL, NULL, NULL, 0, 0, 0}
69 * Items that are valid in an Options resource
71 static RES_ITEM options_items[] = {
72 {"compression", store_opts, NULL, 0, 0, 0},
73 {"signature", store_opts, NULL, 0, 0, 0},
74 {"verify", store_opts, NULL, 0, 0, 0},
75 {"onefs", store_opts, NULL, 0, 0, 0},
76 {"recurse", store_opts, NULL, 0, 0, 0},
77 {"sparse", store_opts, NULL, 0, 0, 0},
78 {"hardlinks", store_opts, NULL, 0, 0, 0},
79 {"readfifo", store_opts, NULL, 0, 0, 0},
80 {"replace", store_opts, NULL, 0, 0, 0},
81 {"portable", store_opts, NULL, 0, 0, 0},
82 {"mtimeonly", store_opts, NULL, 0, 0, 0},
83 {"keepatime", store_opts, NULL, 0, 0, 0},
84 {"regex", store_regex, NULL, 0, 0, 0},
85 {"regexdir", store_regex, NULL, 1, 0, 0},
86 {"regexfile", store_regex, NULL, 2, 0, 0},
87 {"base", store_base, NULL, 0, 0, 0},
88 {"wild", store_wild, NULL, 0, 0, 0},
89 {"wilddir", store_wild, NULL, 1, 0, 0},
90 {"wildfile", store_wild, NULL, 2, 0, 0},
91 {"exclude", store_opts, NULL, 0, 0, 0},
92 {"aclsupport", store_opts, NULL, 0, 0, 0},
93 {"reader", store_reader, NULL, 0, 0, 0},
94 {"writer", store_writer, NULL, 0, 0, 0},
95 {"ignorecase", store_opts, NULL, 0, 0, 0},
96 {"fstype", store_fstype, NULL, 0, 0, 0},
97 {"hfsplussupport", store_opts, NULL, 0, 0, 0},
98 {NULL, NULL, NULL, 0, 0, 0}
102 /* Define FileSet KeyWord values */
113 INC_KW_REPLACE, /* restore options */
114 INC_KW_READFIFO, /* Causes fifo data to be read */
125 * This is the list of options that can be stored by store_opts
126 * Note, now that the old style Include/Exclude code is gone,
127 * the INC_KW code could be put into the "code" field of the
128 * options given above.
130 static struct s_kw FS_option_kw[] = {
131 {"compression", INC_KW_COMPRESSION},
132 {"signature", INC_KW_SIGNATURE},
133 {"encryption", INC_KW_ENCRYPTION},
134 {"verify", INC_KW_VERIFY},
135 {"onefs", INC_KW_ONEFS},
136 {"recurse", INC_KW_RECURSE},
137 {"sparse", INC_KW_SPARSE},
138 {"hardlinks", INC_KW_HARDLINK},
139 {"replace", INC_KW_REPLACE},
140 {"readfifo", INC_KW_READFIFO},
141 {"portable", INC_KW_PORTABLE},
142 {"mtimeonly", INC_KW_MTIMEONLY},
143 {"keepatime", INC_KW_KEEPATIME},
144 {"exclude", INC_KW_EXCLUDE},
145 {"aclsupport", INC_KW_ACL},
146 {"ignorecase", INC_KW_IGNORECASE},
147 {"hfsplussupport", INC_KW_HFSPLUS},
151 /* Options for FileSet keywords */
160 * Options permitted for each keyword and resulting value.
161 * The output goes into opts, which are then transmitted to
162 * the FD for application as options to the following list of
165 static struct s_fs_opt FS_options[] = {
166 {"md5", INC_KW_SIGNATURE, "M"},
167 {"sha1", INC_KW_SIGNATURE, "S"},
168 {"gzip", INC_KW_COMPRESSION, "Z6"},
169 {"gzip1", INC_KW_COMPRESSION, "Z1"},
170 {"gzip2", INC_KW_COMPRESSION, "Z2"},
171 {"gzip3", INC_KW_COMPRESSION, "Z3"},
172 {"gzip4", INC_KW_COMPRESSION, "Z4"},
173 {"gzip5", INC_KW_COMPRESSION, "Z5"},
174 {"gzip6", INC_KW_COMPRESSION, "Z6"},
175 {"gzip7", INC_KW_COMPRESSION, "Z7"},
176 {"gzip8", INC_KW_COMPRESSION, "Z8"},
177 {"gzip9", INC_KW_COMPRESSION, "Z9"},
178 {"blowfish", INC_KW_ENCRYPTION, "B"}, /* ***FIXME*** not implemented */
179 {"3des", INC_KW_ENCRYPTION, "3"}, /* ***FIXME*** not implemented */
180 {"yes", INC_KW_ONEFS, "0"},
181 {"no", INC_KW_ONEFS, "f"},
182 {"yes", INC_KW_RECURSE, "0"},
183 {"no", INC_KW_RECURSE, "h"},
184 {"yes", INC_KW_SPARSE, "s"},
185 {"no", INC_KW_SPARSE, "0"},
186 {"yes", INC_KW_HARDLINK, "0"},
187 {"no", INC_KW_HARDLINK, "H"},
188 {"always", INC_KW_REPLACE, "a"},
189 {"ifnewer", INC_KW_REPLACE, "w"},
190 {"never", INC_KW_REPLACE, "n"},
191 {"yes", INC_KW_READFIFO, "r"},
192 {"no", INC_KW_READFIFO, "0"},
193 {"yes", INC_KW_PORTABLE, "p"},
194 {"no", INC_KW_PORTABLE, "0"},
195 {"yes", INC_KW_MTIMEONLY, "m"},
196 {"no", INC_KW_MTIMEONLY, "0"},
197 {"yes", INC_KW_KEEPATIME, "k"},
198 {"no", INC_KW_KEEPATIME, "0"},
199 {"yes", INC_KW_EXCLUDE, "e"},
200 {"no", INC_KW_EXCLUDE, "0"},
201 {"yes", INC_KW_ACL, "A"},
202 {"no", INC_KW_ACL, "0"},
203 {"yes", INC_KW_IGNORECASE, "i"},
204 {"no", INC_KW_IGNORECASE, "0"},
205 {"yes", INC_KW_HFSPLUS, "R"}, /* "R" for resource fork */
206 {"no", INC_KW_HFSPLUS, "0"},
213 * Scan for right hand side of Include options (keyword=option) is
214 * converted into one or two characters. Verifyopts=xxxx is Vxxxx:
215 * Whatever is found is concatenated to the opts string.
216 * This code is also used inside an Options resource.
218 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
222 int lcopts = lc->options;
224 option[0] = 0; /* default option = none */
225 option[2] = 0; /* terminate options */
226 lc->options |= LOPT_STRING; /* force string */
227 token = lex_get_token(lc, T_STRING); /* expect at least one option */
228 if (keyword == INC_KW_VERIFY) { /* special case */
229 /* ***FIXME**** ensure these are in permitted set */
230 bstrncat(opts, "V", optlen); /* indicate Verify */
231 bstrncat(opts, lc->str, optlen);
232 bstrncat(opts, ":", optlen); /* terminate it */
233 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
236 * Standard keyword options for Include/Exclude
239 for (i=0; FS_options[i].name; i++) {
240 if (strcasecmp(lc->str, FS_options[i].name) == 0 && FS_options[i].keyword == keyword) {
241 /* NOTE! maximum 2 letters here or increase option[3] */
242 option[0] = FS_options[i].option[0];
243 option[1] = FS_options[i].option[1];
249 scan_err1(lc, "Expected a FileSet option keyword, got:%s:", lc->str);
250 } else { /* add option */
251 bstrncat(opts, option, optlen);
252 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
255 lc->options = lcopts;
257 /* If option terminated by comma, eat it */
259 token = lex_get_token(lc, T_ALL); /* yes, eat comma */
265 * Store FileSet Include/Exclude info
266 * NEW style includes are handled in store_newinc()
268 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass)
273 * Decide if we are doing a new Include or an old include. The
274 * new Include is followed immediately by open brace, whereas the
275 * old include has options following the Include.
277 token = lex_get_token(lc, T_SKIP_EOL);
278 if (token == T_BOB) {
279 store_newinc(lc, item, index, pass);
282 scan_err0(lc, _("Old style Include/Exclude not supported\n"));
287 * Store NEW style FileSet FInclude/FExclude info
289 * Note, when this routine is called, we are inside a FileSet
290 * resource. We treat the Include/Execlude like a sort of
291 * mini-resource within the FileSet resource.
293 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
299 if (!res_all.res_fs.have_MD5) {
300 MD5Init(&res_all.res_fs.md5c);
301 res_all.res_fs.have_MD5 = true;
303 memset(&res_incexe, 0, sizeof(INCEXE));
304 res_all.res_fs.new_include = true;
305 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
306 if (token == T_EOB) {
309 if (token != T_IDENTIFIER) {
310 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
312 for (i=0; newinc_items[i].name; i++) {
313 options = strcasecmp(lc->str, "options") == 0;
314 if (strcasecmp(newinc_items[i].name, lc->str) == 0) {
316 token = lex_get_token(lc, T_SKIP_EOL);
317 if (token != T_EQUALS) {
318 scan_err1(lc, "expected an equals, got: %s", lc->str);
321 /* Call item handler */
322 newinc_items[i].handler(lc, &newinc_items[i], i, pass);
328 scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
332 incexe = (INCEXE *)malloc(sizeof(INCEXE));
333 memcpy(incexe, &res_incexe, sizeof(INCEXE));
334 memset(&res_incexe, 0, sizeof(INCEXE));
335 if (item->code == 0) { /* include */
336 if (res_all.res_fs.num_includes == 0) {
337 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
339 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
340 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
342 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
343 Dmsg1(900, "num_includes=%d\n", res_all.res_fs.num_includes);
344 } else { /* exclude */
345 if (res_all.res_fs.num_excludes == 0) {
346 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
348 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
349 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
351 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
352 Dmsg1(900, "num_excludes=%d\n", res_all.res_fs.num_excludes);
356 set_bit(index, res_all.hdr.item_present);
360 /* Store regex info */
361 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass)
369 token = lex_get_token(lc, T_SKIP_EOL);
371 /* Pickup regex string
375 case T_UNQUOTED_STRING:
376 case T_QUOTED_STRING:
377 rc = regcomp(&preg, lc->str, REG_EXTENDED);
379 regerror(rc, &preg, prbuf, sizeof(prbuf));
381 scan_err1(lc, _("Regex compile error. ERR=%s\n"), prbuf);
385 if (item->code == 1) {
387 res_incexe.current_opts->regexdir.append(bstrdup(lc->str));
388 newsize = res_incexe.current_opts->regexdir.size();
389 } else if (item->code == 2) {
391 res_incexe.current_opts->regexfile.append(bstrdup(lc->str));
392 newsize = res_incexe.current_opts->regexfile.size();
395 res_incexe.current_opts->regex.append(bstrdup(lc->str));
396 newsize = res_incexe.current_opts->regex.size();
398 Dmsg4(900, "set %s %p size=%d %s\n",
399 type, res_incexe.current_opts, newsize, lc->str);
402 scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
408 /* Store Base info */
409 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
413 token = lex_get_token(lc, T_NAME);
416 * Pickup Base Job Name
418 res_incexe.current_opts->base.append(bstrdup(lc->str));
423 /* Store reader info */
424 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass)
428 token = lex_get_token(lc, T_NAME);
431 * Pickup reader command
433 res_incexe.current_opts->reader = bstrdup(lc->str);
438 /* Store writer innfo */
439 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass)
443 token = lex_get_token(lc, T_NAME);
446 * Pickup writer command
448 res_incexe.current_opts->writer = bstrdup(lc->str);
455 /* Store Wild-card info */
456 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass)
462 token = lex_get_token(lc, T_SKIP_EOL);
465 * Pickup Wild-card string
469 case T_UNQUOTED_STRING:
470 case T_QUOTED_STRING:
471 if (item->code == 1) {
473 res_incexe.current_opts->wilddir.append(bstrdup(lc->str));
474 newsize = res_incexe.current_opts->wilddir.size();
475 } else if (item->code == 2) {
477 res_incexe.current_opts->wildfile.append(bstrdup(lc->str));
478 newsize = res_incexe.current_opts->wildfile.size();
481 res_incexe.current_opts->wild.append(bstrdup(lc->str));
482 newsize = res_incexe.current_opts->wild.size();
484 Dmsg4(9, "set %s %p size=%d %s\n",
485 type, res_incexe.current_opts, newsize, lc->str);
488 scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
494 /* Store fstype info */
495 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass)
499 token = lex_get_token(lc, T_SKIP_EOL);
501 /* Pickup fstype string */
504 case T_UNQUOTED_STRING:
505 case T_QUOTED_STRING:
506 res_incexe.current_opts->fstype.append(bstrdup(lc->str));
507 Dmsg3(900, "set fstype %p size=%d %s\n",
508 res_incexe.current_opts, res_incexe.current_opts->fstype.size(), lc->str);
511 scan_err1(lc, _("Expected an fstype string, got: %s\n"), lc->str);
518 * Store Filename info. Note, for minor efficiency reasons, we
519 * always increase the name buffer by 10 items because we expect
520 * to add more entries.
522 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass)
527 token = lex_get_token(lc, T_SKIP_EOL);
529 /* Pickup Filename string
533 case T_UNQUOTED_STRING:
534 case T_QUOTED_STRING:
535 if (res_all.res_fs.have_MD5) {
536 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
538 incexe = &res_incexe;
539 if (incexe->name_list.size() == 0) {
540 incexe->name_list.init(10, true);
542 incexe->name_list.append(bstrdup(lc->str));
543 Dmsg1(900, "Add to name_list %s\n", lc->str);
546 scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
554 * Come here when Options seen in Include/Exclude
556 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass)
560 token = lex_get_token(lc, T_SKIP_EOL);
561 if (token != T_BOB) {
562 scan_err1(lc, "Expecting open brace. Got %s", lc->str);
566 setup_current_opts();
569 while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
570 if (token == T_EOL) {
573 if (token == T_EOB) {
576 if (token != T_IDENTIFIER) {
577 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
579 for (i=0; options_items[i].name; i++) {
580 if (strcasecmp(options_items[i].name, lc->str) == 0) {
581 token = lex_get_token(lc, T_SKIP_EOL);
582 if (token != T_EQUALS) {
583 scan_err1(lc, "expected an equals, got: %s", lc->str);
585 /* Call item handler */
586 options_items[i].handler(lc, &options_items[i], i, pass);
592 scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
599 * New style options come here
601 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass)
608 keyword = INC_KW_NONE;
609 /* Look up the keyword */
610 for (i=0; FS_option_kw[i].name; i++) {
611 if (strcasecmp(item->name, FS_option_kw[i].name) == 0) {
612 keyword = FS_option_kw[i].token;
616 if (keyword == INC_KW_NONE) {
617 scan_err1(lc, "Expected a FileSet keyword, got: %s", lc->str);
619 /* Now scan for the value */
620 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
622 bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
623 Dmsg2(900, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
630 /* If current_opts not defined, create first entry */
631 static void setup_current_opts(void)
633 FOPTS *fo = (FOPTS *)malloc(sizeof(FOPTS));
634 memset(fo, 0, sizeof(FOPTS));
635 fo->regex.init(1, true);
636 fo->regexdir.init(1, true);
637 fo->regexfile.init(1, true);
638 fo->wild.init(1, true);
639 fo->wilddir.init(1, true);
640 fo->wildfile.init(1, true);
641 fo->base.init(1, true);
642 fo->fstype.init(1, true);
643 res_incexe.current_opts = fo;
644 if (res_incexe.num_opts == 0) {
645 res_incexe.opts_list = (FOPTS **)malloc(sizeof(FOPTS *));
647 res_incexe.opts_list = (FOPTS **)realloc(res_incexe.opts_list,
648 sizeof(FOPTS *) * (res_incexe.num_opts + 1));
650 res_incexe.opts_list[res_incexe.num_opts++] = fo;