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 as
14 published by the Free Software Foundation; either version 2 of
15 the License, or (at your option) any later version.
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 GNU
20 General Public License for more details.
22 You should have received a copy of the GNU General Public
23 License along with this program; if not, write to the Free
24 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
35 /* Forward referenced subroutines */
37 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass);
39 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass);
40 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass);
41 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass);
42 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass);
43 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass);
44 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass);
45 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass);
46 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass);
47 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass);
48 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass);
49 static void setup_current_opts(void);
52 /* We build the current resource here as we are
53 * scanning the resource configuration definition,
54 * then move it to allocated memory when the resource
58 extern int res_all_size;
60 /* We build the current new Include and Exclude items here */
61 static INCEXE res_incexe;
64 * new Include/Exclude items
65 * name handler value code flags default_value
67 static RES_ITEM newinc_items[] = {
68 {"file", store_fname, NULL, 0, 0, 0},
69 {"options", options_res, NULL, 0, 0, 0},
70 {NULL, NULL, NULL, 0, 0, 0}
74 * Items that are valid in an Options resource
76 static RES_ITEM options_items[] = {
77 {"compression", store_opts, NULL, 0, 0, 0},
78 {"signature", store_opts, NULL, 0, 0, 0},
79 {"verify", store_opts, NULL, 0, 0, 0},
80 {"onefs", store_opts, NULL, 0, 0, 0},
81 {"recurse", store_opts, NULL, 0, 0, 0},
82 {"sparse", store_opts, NULL, 0, 0, 0},
83 {"hardlinks", store_opts, NULL, 0, 0, 0},
84 {"readfifo", store_opts, NULL, 0, 0, 0},
85 {"replace", store_opts, NULL, 0, 0, 0},
86 {"portable", store_opts, NULL, 0, 0, 0},
87 {"mtimeonly", store_opts, NULL, 0, 0, 0},
88 {"keepatime", store_opts, NULL, 0, 0, 0},
89 {"regex", store_regex, NULL, 0, 0, 0},
90 {"regexdir", store_regex, NULL, 1, 0, 0},
91 {"regexfile", store_regex, NULL, 2, 0, 0},
92 {"base", store_base, NULL, 0, 0, 0},
93 {"wild", store_wild, NULL, 0, 0, 0},
94 {"wilddir", store_wild, NULL, 1, 0, 0},
95 {"wildfile", store_wild, NULL, 2, 0, 0},
96 {"exclude", store_opts, NULL, 0, 0, 0},
97 {"aclsupport", store_opts, NULL, 0, 0, 0},
98 {"reader", store_reader, NULL, 0, 0, 0},
99 {"writer", store_writer, NULL, 0, 0, 0},
100 {"ignorecase", store_opts, NULL, 0, 0, 0},
101 {"fstype", store_fstype, NULL, 0, 0, 0},
102 {"hfsplussupport", store_opts, NULL, 0, 0, 0},
103 {NULL, NULL, NULL, 0, 0, 0}
107 /* Define FileSet KeyWord values */
119 INC_KW_REPLACE, /* restore options */
120 INC_KW_READFIFO, /* Causes fifo data to be read */
131 * This is the list of options that can be stored by store_opts
132 * Note, now that the old style Include/Exclude code is gone,
133 * the INC_KW code could be put into the "code" field of the
134 * options given above.
136 static struct s_kw FS_option_kw[] = {
137 {"compression", INC_KW_COMPRESSION},
138 {"signature", INC_KW_SIGNATURE},
139 {"encryption", INC_KW_ENCRYPTION},
140 {"verify", INC_KW_VERIFY},
141 {"onefs", INC_KW_ONEFS},
142 {"recurse", INC_KW_RECURSE},
143 {"sparse", INC_KW_SPARSE},
144 {"hardlinks", INC_KW_HARDLINK},
145 {"replace", INC_KW_REPLACE},
146 {"readfifo", INC_KW_READFIFO},
147 {"portable", INC_KW_PORTABLE},
148 {"mtimeonly", INC_KW_MTIMEONLY},
149 {"keepatime", INC_KW_KEEPATIME},
150 {"exclude", INC_KW_EXCLUDE},
151 {"aclsupport", INC_KW_ACL},
152 {"ignorecase", INC_KW_IGNORECASE},
153 {"hfsplussupport", INC_KW_HFSPLUS},
157 /* Options for FileSet keywords */
166 * Options permitted for each keyword and resulting value.
167 * The output goes into opts, which are then transmitted to
168 * the FD for application as options to the following list of
171 static struct s_fs_opt FS_options[] = {
172 {"md5", INC_KW_SIGNATURE, "M"},
173 {"sha1", INC_KW_SIGNATURE, "S"},
174 {"gzip", INC_KW_COMPRESSION, "Z6"},
175 {"gzip1", INC_KW_COMPRESSION, "Z1"},
176 {"gzip2", INC_KW_COMPRESSION, "Z2"},
177 {"gzip3", INC_KW_COMPRESSION, "Z3"},
178 {"gzip4", INC_KW_COMPRESSION, "Z4"},
179 {"gzip5", INC_KW_COMPRESSION, "Z5"},
180 {"gzip6", INC_KW_COMPRESSION, "Z6"},
181 {"gzip7", INC_KW_COMPRESSION, "Z7"},
182 {"gzip8", INC_KW_COMPRESSION, "Z8"},
183 {"gzip9", INC_KW_COMPRESSION, "Z9"},
184 {"blowfish", INC_KW_ENCRYPTION, "B"}, /* ***FIXME*** not implemented */
185 {"3des", INC_KW_ENCRYPTION, "3"}, /* ***FIXME*** not implemented */
186 {"yes", INC_KW_ONEFS, "0"},
187 {"no", INC_KW_ONEFS, "f"},
188 {"yes", INC_KW_RECURSE, "0"},
189 {"no", INC_KW_RECURSE, "h"},
190 {"yes", INC_KW_SPARSE, "s"},
191 {"no", INC_KW_SPARSE, "0"},
192 {"yes", INC_KW_HARDLINK, "0"},
193 {"no", INC_KW_HARDLINK, "H"},
194 {"always", INC_KW_REPLACE, "a"},
195 {"ifnewer", INC_KW_REPLACE, "w"},
196 {"never", INC_KW_REPLACE, "n"},
197 {"yes", INC_KW_READFIFO, "r"},
198 {"no", INC_KW_READFIFO, "0"},
199 {"yes", INC_KW_PORTABLE, "p"},
200 {"no", INC_KW_PORTABLE, "0"},
201 {"yes", INC_KW_MTIMEONLY, "m"},
202 {"no", INC_KW_MTIMEONLY, "0"},
203 {"yes", INC_KW_KEEPATIME, "k"},
204 {"no", INC_KW_KEEPATIME, "0"},
205 {"yes", INC_KW_EXCLUDE, "e"},
206 {"no", INC_KW_EXCLUDE, "0"},
207 {"yes", INC_KW_ACL, "A"},
208 {"no", INC_KW_ACL, "0"},
209 {"yes", INC_KW_IGNORECASE, "i"},
210 {"no", INC_KW_IGNORECASE, "0"},
211 {"yes", INC_KW_HFSPLUS, "R"}, /* "R" for resource fork */
212 {"no", INC_KW_HFSPLUS, "0"},
219 * Scan for right hand side of Include options (keyword=option) is
220 * converted into one or two characters. Verifyopts=xxxx is Vxxxx:
221 * Whatever is found is concatenated to the opts string.
222 * This code is also used inside an Options resource.
224 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
228 int lcopts = lc->options;
230 option[0] = 0; /* default option = none */
231 option[2] = 0; /* terminate options */
232 lc->options |= LOPT_STRING; /* force string */
233 token = lex_get_token(lc, T_STRING); /* expect at least one option */
234 if (keyword == INC_KW_VERIFY) { /* special case */
235 /* ***FIXME**** ensure these are in permitted set */
236 bstrncat(opts, "V", optlen); /* indicate Verify */
237 bstrncat(opts, lc->str, optlen);
238 bstrncat(opts, ":", optlen); /* terminate it */
239 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
242 * Standard keyword options for Include/Exclude
245 for (i=0; FS_options[i].name; i++) {
246 if (strcasecmp(lc->str, FS_options[i].name) == 0 && FS_options[i].keyword == keyword) {
247 /* NOTE! maximum 2 letters here or increase option[3] */
248 option[0] = FS_options[i].option[0];
249 option[1] = FS_options[i].option[1];
255 scan_err1(lc, "Expected a FileSet option keyword, got:%s:", lc->str);
256 } else { /* add option */
257 bstrncat(opts, option, optlen);
258 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
261 lc->options = lcopts;
263 /* If option terminated by comma, eat it */
265 token = lex_get_token(lc, T_ALL); /* yes, eat comma */
271 * Store FileSet Include/Exclude info
272 * NEW style includes are handled in store_newinc()
274 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass)
279 * Decide if we are doing a new Include or an old include. The
280 * new Include is followed immediately by open brace, whereas the
281 * old include has options following the Include.
283 token = lex_get_token(lc, T_SKIP_EOL);
284 if (token == T_BOB) {
285 store_newinc(lc, item, index, pass);
288 scan_err0(lc, _("Old style Include/Exclude not supported\n"));
293 * Store NEW style FileSet FInclude/FExclude info
295 * Note, when this routine is called, we are inside a FileSet
296 * resource. We treat the Include/Execlude like a sort of
297 * mini-resource within the FileSet resource.
299 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
305 if (!res_all.res_fs.have_MD5) {
306 MD5Init(&res_all.res_fs.md5c);
307 res_all.res_fs.have_MD5 = true;
309 memset(&res_incexe, 0, sizeof(INCEXE));
310 res_all.res_fs.new_include = true;
311 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
312 if (token == T_EOB) {
315 if (token != T_IDENTIFIER) {
316 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
318 for (i=0; newinc_items[i].name; i++) {
319 options = strcasecmp(lc->str, "options") == 0;
320 if (strcasecmp(newinc_items[i].name, lc->str) == 0) {
322 token = lex_get_token(lc, T_SKIP_EOL);
323 if (token != T_EQUALS) {
324 scan_err1(lc, "expected an equals, got: %s", lc->str);
327 /* Call item handler */
328 newinc_items[i].handler(lc, &newinc_items[i], i, pass);
334 scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
338 incexe = (INCEXE *)malloc(sizeof(INCEXE));
339 memcpy(incexe, &res_incexe, sizeof(INCEXE));
340 memset(&res_incexe, 0, sizeof(INCEXE));
341 if (item->code == 0) { /* include */
342 if (res_all.res_fs.num_includes == 0) {
343 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
345 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
346 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
348 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
349 Dmsg1(900, "num_includes=%d\n", res_all.res_fs.num_includes);
350 } else { /* exclude */
351 if (res_all.res_fs.num_excludes == 0) {
352 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
354 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
355 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
357 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
358 Dmsg1(900, "num_excludes=%d\n", res_all.res_fs.num_excludes);
362 set_bit(index, res_all.hdr.item_present);
366 /* Store regex info */
367 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass)
375 token = lex_get_token(lc, T_SKIP_EOL);
377 /* Pickup regex string
381 case T_UNQUOTED_STRING:
382 case T_QUOTED_STRING:
383 rc = regcomp(&preg, lc->str, REG_EXTENDED);
385 regerror(rc, &preg, prbuf, sizeof(prbuf));
387 scan_err1(lc, _("Regex compile error. ERR=%s\n"), prbuf);
391 if (item->code == 1) {
393 res_incexe.current_opts->regexdir.append(bstrdup(lc->str));
394 newsize = res_incexe.current_opts->regexdir.size();
395 } else if (item->code == 2) {
397 res_incexe.current_opts->regexfile.append(bstrdup(lc->str));
398 newsize = res_incexe.current_opts->regexfile.size();
401 res_incexe.current_opts->regex.append(bstrdup(lc->str));
402 newsize = res_incexe.current_opts->regex.size();
404 Dmsg4(900, "set %s %p size=%d %s\n",
405 type, res_incexe.current_opts, newsize, lc->str);
408 scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
414 /* Store Base info */
415 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
419 token = lex_get_token(lc, T_NAME);
422 * Pickup Base Job Name
424 res_incexe.current_opts->base.append(bstrdup(lc->str));
429 /* Store reader info */
430 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass)
434 token = lex_get_token(lc, T_NAME);
437 * Pickup reader command
439 res_incexe.current_opts->reader = bstrdup(lc->str);
444 /* Store writer innfo */
445 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass)
449 token = lex_get_token(lc, T_NAME);
452 * Pickup writer command
454 res_incexe.current_opts->writer = bstrdup(lc->str);
461 /* Store Wild-card info */
462 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass)
468 token = lex_get_token(lc, T_SKIP_EOL);
471 * Pickup Wild-card string
475 case T_UNQUOTED_STRING:
476 case T_QUOTED_STRING:
477 if (item->code == 1) {
479 res_incexe.current_opts->wilddir.append(bstrdup(lc->str));
480 newsize = res_incexe.current_opts->wilddir.size();
481 } else if (item->code == 2) {
483 res_incexe.current_opts->wildfile.append(bstrdup(lc->str));
484 newsize = res_incexe.current_opts->wildfile.size();
487 res_incexe.current_opts->wild.append(bstrdup(lc->str));
488 newsize = res_incexe.current_opts->wild.size();
490 Dmsg4(9, "set %s %p size=%d %s\n",
491 type, res_incexe.current_opts, newsize, lc->str);
494 scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
500 /* Store fstype info */
501 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass)
505 token = lex_get_token(lc, T_SKIP_EOL);
507 /* Pickup fstype string */
510 case T_UNQUOTED_STRING:
511 case T_QUOTED_STRING:
512 res_incexe.current_opts->fstype.append(bstrdup(lc->str));
513 Dmsg3(900, "set fstype %p size=%d %s\n",
514 res_incexe.current_opts, res_incexe.current_opts->fstype.size(), lc->str);
517 scan_err1(lc, _("Expected an fstype string, got: %s\n"), lc->str);
524 * Store Filename info. Note, for minor efficiency reasons, we
525 * always increase the name buffer by 10 items because we expect
526 * to add more entries.
528 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass)
533 token = lex_get_token(lc, T_SKIP_EOL);
535 /* Pickup Filename string
539 case T_UNQUOTED_STRING:
540 case T_QUOTED_STRING:
541 if (res_all.res_fs.have_MD5) {
542 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
544 incexe = &res_incexe;
545 if (incexe->name_list.size() == 0) {
546 incexe->name_list.init(10, true);
548 incexe->name_list.append(bstrdup(lc->str));
549 Dmsg1(900, "Add to name_list %s\n", lc->str);
552 scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
560 * Come here when Options seen in Include/Exclude
562 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass)
566 token = lex_get_token(lc, T_SKIP_EOL);
567 if (token != T_BOB) {
568 scan_err1(lc, "Expecting open brace. Got %s", lc->str);
572 setup_current_opts();
575 while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
576 if (token == T_EOL) {
579 if (token == T_EOB) {
582 if (token != T_IDENTIFIER) {
583 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
585 for (i=0; options_items[i].name; i++) {
586 if (strcasecmp(options_items[i].name, lc->str) == 0) {
587 token = lex_get_token(lc, T_SKIP_EOL);
588 if (token != T_EQUALS) {
589 scan_err1(lc, "expected an equals, got: %s", lc->str);
591 /* Call item handler */
592 options_items[i].handler(lc, &options_items[i], i, pass);
598 scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
605 * New style options come here
607 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass)
614 keyword = INC_KW_NONE;
615 /* Look up the keyword */
616 for (i=0; FS_option_kw[i].name; i++) {
617 if (strcasecmp(item->name, FS_option_kw[i].name) == 0) {
618 keyword = FS_option_kw[i].token;
622 if (keyword == INC_KW_NONE) {
623 scan_err1(lc, "Expected a FileSet keyword, got: %s", lc->str);
625 /* Now scan for the value */
626 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
628 bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
629 Dmsg2(900, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
636 /* If current_opts not defined, create first entry */
637 static void setup_current_opts(void)
639 FOPTS *fo = (FOPTS *)malloc(sizeof(FOPTS));
640 memset(fo, 0, sizeof(FOPTS));
641 fo->regex.init(1, true);
642 fo->regexdir.init(1, true);
643 fo->regexfile.init(1, true);
644 fo->wild.init(1, true);
645 fo->wilddir.init(1, true);
646 fo->wildfile.init(1, true);
647 fo->base.init(1, true);
648 fo->fstype.init(1, true);
649 res_incexe.current_opts = fo;
650 if (res_incexe.num_opts == 0) {
651 res_incexe.opts_list = (FOPTS **)malloc(sizeof(FOPTS *));
653 res_incexe.opts_list = (FOPTS **)realloc(res_incexe.opts_list,
654 sizeof(FOPTS *) * (res_incexe.num_opts + 1));
656 res_incexe.opts_list[res_incexe.num_opts++] = fo;