2 * Configuration file parser for new and old Include and
5 * Kern Sibbald, March MMIII
10 Copyright (C) 2003-2004 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 * Include keywords -- these are keywords that can appear
132 * in the options lists of an old include ( Include = compression= ...)
134 static struct s_kw FS_option_kw[] = {
135 {"compression", INC_KW_COMPRESSION},
136 {"signature", INC_KW_SIGNATURE},
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},
155 /* Options for FileSet keywords */
164 * Options permitted for each keyword and resulting value.
165 * The output goes into opts, which are then transmitted to
166 * the FD for application as options to the following list of
169 static struct s_fs_opt FS_options[] = {
170 {"md5", INC_KW_SIGNATURE, "M"},
171 {"sha1", INC_KW_SIGNATURE, "S"},
172 {"gzip", INC_KW_COMPRESSION, "Z6"},
173 {"gzip1", INC_KW_COMPRESSION, "Z1"},
174 {"gzip2", INC_KW_COMPRESSION, "Z2"},
175 {"gzip3", INC_KW_COMPRESSION, "Z3"},
176 {"gzip4", INC_KW_COMPRESSION, "Z4"},
177 {"gzip5", INC_KW_COMPRESSION, "Z5"},
178 {"gzip6", INC_KW_COMPRESSION, "Z6"},
179 {"gzip7", INC_KW_COMPRESSION, "Z7"},
180 {"gzip8", INC_KW_COMPRESSION, "Z8"},
181 {"gzip9", INC_KW_COMPRESSION, "Z9"},
182 {"blowfish", INC_KW_ENCRYPTION, "B"}, /* ***FIXME*** not implemented */
183 {"3des", INC_KW_ENCRYPTION, "3"}, /* ***FIXME*** not implemented */
184 {"yes", INC_KW_ONEFS, "0"},
185 {"no", INC_KW_ONEFS, "f"},
186 {"yes", INC_KW_RECURSE, "0"},
187 {"no", INC_KW_RECURSE, "h"},
188 {"yes", INC_KW_SPARSE, "s"},
189 {"no", INC_KW_SPARSE, "0"},
190 {"yes", INC_KW_HARDLINK, "0"},
191 {"no", INC_KW_HARDLINK, "H"},
192 {"always", INC_KW_REPLACE, "a"},
193 {"ifnewer", INC_KW_REPLACE, "w"},
194 {"never", INC_KW_REPLACE, "n"},
195 {"yes", INC_KW_READFIFO, "r"},
196 {"no", INC_KW_READFIFO, "0"},
197 {"yes", INC_KW_PORTABLE, "p"},
198 {"no", INC_KW_PORTABLE, "0"},
199 {"yes", INC_KW_MTIMEONLY, "m"},
200 {"no", INC_KW_MTIMEONLY, "0"},
201 {"yes", INC_KW_KEEPATIME, "k"},
202 {"no", INC_KW_KEEPATIME, "0"},
203 {"yes", INC_KW_EXCLUDE, "e"},
204 {"no", INC_KW_EXCLUDE, "0"},
205 {"yes", INC_KW_ACL, "A"},
206 {"no", INC_KW_ACL, "0"},
207 {"yes", INC_KW_IGNORECASE, "i"},
208 {"no", INC_KW_IGNORECASE, "0"},
209 {"yes", INC_KW_HFSPLUS, "R"}, /* "R" for resource fork */
210 {"no", INC_KW_HFSPLUS, "0"},
217 * Scan for right hand side of Include options (keyword=option) is
218 * converted into one or two characters. Verifyopts=xxxx is Vxxxx:
219 * Whatever is found is concatenated to the opts string.
220 * This code is also used inside an Options resource.
222 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
226 int lcopts = lc->options;
228 option[0] = 0; /* default option = none */
229 option[2] = 0; /* terminate options */
230 lc->options |= LOPT_STRING; /* force string */
231 token = lex_get_token(lc, T_STRING); /* expect at least one option */
232 if (keyword == INC_KW_VERIFY) { /* special case */
233 /* ***FIXME**** ensure these are in permitted set */
234 bstrncat(opts, "V", optlen); /* indicate Verify */
235 bstrncat(opts, lc->str, optlen);
236 bstrncat(opts, ":", optlen); /* terminate it */
237 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
240 * Standard keyword options for Include/Exclude
243 for (i=0; FS_options[i].name; i++) {
244 if (strcasecmp(lc->str, FS_options[i].name) == 0 && FS_options[i].keyword == keyword) {
245 /* NOTE! maximum 2 letters here or increase option[3] */
246 option[0] = FS_options[i].option[0];
247 option[1] = FS_options[i].option[1];
253 scan_err1(lc, "Expected a FileSet option keyword, got:%s:", lc->str);
254 } else { /* add option */
255 bstrncat(opts, option, optlen);
256 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
259 lc->options = lcopts;
261 /* If option terminated by comma, eat it */
263 token = lex_get_token(lc, T_ALL); /* yes, eat comma */
269 * Store FileSet Include/Exclude info
270 * NEW style includes are handled in store_newinc()
272 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass)
275 int options = lc->options;
281 * Decide if we are doing a new Include or an old include. The
282 * new Include is followed immediately by open brace, whereas the
283 * old include has options following the Include.
285 token = lex_get_token(lc, T_SKIP_EOL);
286 if (token == T_BOB) {
287 store_newinc(lc, item, index, pass);
291 /* What follows is scanning for the OLD style Include/Exclude */
293 if (token != T_EQUALS) {
294 scan_err1(lc, _("Expecting an equals sign, got: %s\n"), lc->str);
296 lc->options |= LOPT_NO_IDENT; /* make spaces significant */
297 memset(&res_incexe, 0, sizeof(INCEXE));
299 /* Get include options */
301 while ((token=lex_get_token(lc, T_SKIP_EOL)) != T_BOB) {
303 keyword = INC_KW_NONE;
304 for (i=0; FS_option_kw[i].name; i++) {
305 if (strcasecmp(lc->str, FS_option_kw[i].name) == 0) {
306 keyword = FS_option_kw[i].token;
310 if (keyword == INC_KW_NONE) {
311 scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
313 /* Option keyword should be following by = <option> */
314 if ((token=lex_get_token(lc, T_SKIP_EOL)) != T_EQUALS) {
315 scan_err1(lc, _("expected an = following keyword, got: %s"), lc->str);
317 /* Scan right hand side of option */
318 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
320 if (token == T_BOB) {
326 strcat(inc_opts, "0"); /* set no options */
328 inc_opts_len = strlen(inc_opts);
332 if (!res_all.res_fs.have_MD5) {
333 MD5Init(&res_all.res_fs.md5c);
334 res_all.res_fs.have_MD5 = TRUE;
336 setup_current_opts();
337 bstrncpy(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
338 Dmsg2(900, "old pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
340 /* Create incexe structure */
341 Dmsg0(900, "Create INCEXE structure\n");
342 incexe = (INCEXE *)malloc(sizeof(INCEXE));
343 memcpy(incexe, &res_incexe, sizeof(INCEXE));
344 memset(&res_incexe, 0, sizeof(INCEXE));
345 if (item->code == 0) { /* include */
346 if (res_all.res_fs.num_includes == 0) {
347 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
349 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
350 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
352 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
353 Dmsg1(900, "num_includes=%d\n", res_all.res_fs.num_includes);
354 } else { /* exclude */
355 if (res_all.res_fs.num_excludes == 0) {
356 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
358 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
359 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
361 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
362 Dmsg1(900, "num_excludes=%d\n", res_all.res_fs.num_excludes);
365 /* Pickup include/exclude names. They are stored in INCEXE
366 * structures which contains the options and the name.
368 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOB) {
374 case T_UNQUOTED_STRING:
375 case T_QUOTED_STRING:
376 if (res_all.res_fs.have_MD5) {
377 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
379 if (incexe->name_list.size() == 0) {
380 incexe->name_list.init(10, true);
382 incexe->name_list.append(bstrdup(lc->str));
383 Dmsg1(900, "Add to name_list %s\n", lc->str);
386 scan_err1(lc, "Expected a filename, got: %s", lc->str);
389 /* Note, MD5Final is done in backup.c */
390 } else { /* pass 2 */
391 while (lex_get_token(lc, T_ALL) != T_EOB)
395 lc->options = options;
396 set_bit(index, res_all.hdr.item_present);
401 * Store NEW style FileSet FInclude/FExclude info
403 * Note, when this routine is called, we are inside a FileSet
404 * resource. We treat the Include/Execlude like a sort of
405 * mini-resource within the FileSet resource.
407 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
413 if (!res_all.res_fs.have_MD5) {
414 MD5Init(&res_all.res_fs.md5c);
415 res_all.res_fs.have_MD5 = true;
417 memset(&res_incexe, 0, sizeof(INCEXE));
418 res_all.res_fs.new_include = true;
419 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
420 if (token == T_EOB) {
423 if (token != T_IDENTIFIER) {
424 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
426 for (i=0; newinc_items[i].name; i++) {
427 options = strcasecmp(lc->str, "options") == 0;
428 if (strcasecmp(newinc_items[i].name, lc->str) == 0) {
430 token = lex_get_token(lc, T_SKIP_EOL);
431 if (token != T_EQUALS) {
432 scan_err1(lc, "expected an equals, got: %s", lc->str);
435 /* Call item handler */
436 newinc_items[i].handler(lc, &newinc_items[i], i, pass);
442 scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
446 incexe = (INCEXE *)malloc(sizeof(INCEXE));
447 memcpy(incexe, &res_incexe, sizeof(INCEXE));
448 memset(&res_incexe, 0, sizeof(INCEXE));
449 if (item->code == 0) { /* include */
450 if (res_all.res_fs.num_includes == 0) {
451 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
453 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
454 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
456 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
457 Dmsg1(900, "num_includes=%d\n", res_all.res_fs.num_includes);
458 } else { /* exclude */
459 if (res_all.res_fs.num_excludes == 0) {
460 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
462 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
463 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
465 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
466 Dmsg1(900, "num_excludes=%d\n", res_all.res_fs.num_excludes);
470 set_bit(index, res_all.hdr.item_present);
474 /* Store regex info */
475 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass)
483 token = lex_get_token(lc, T_SKIP_EOL);
485 /* Pickup regex string
489 case T_UNQUOTED_STRING:
490 case T_QUOTED_STRING:
491 rc = regcomp(&preg, lc->str, REG_EXTENDED);
493 regerror(rc, &preg, prbuf, sizeof(prbuf));
495 scan_err1(lc, _("Regex compile error. ERR=%s\n"), prbuf);
499 if (item->code == 1) {
501 res_incexe.current_opts->regexdir.append(bstrdup(lc->str));
502 newsize = res_incexe.current_opts->regexdir.size();
503 } else if (item->code == 2) {
505 res_incexe.current_opts->regexfile.append(bstrdup(lc->str));
506 newsize = res_incexe.current_opts->regexfile.size();
509 res_incexe.current_opts->regex.append(bstrdup(lc->str));
510 newsize = res_incexe.current_opts->regex.size();
512 Dmsg4(900, "set %s %p size=%d %s\n",
513 type, res_incexe.current_opts, newsize, lc->str);
516 scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
522 /* Store Base info */
523 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
527 token = lex_get_token(lc, T_NAME);
530 * Pickup Base Job Name
532 res_incexe.current_opts->base.append(bstrdup(lc->str));
537 /* Store reader info */
538 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass)
542 token = lex_get_token(lc, T_NAME);
545 * Pickup reader command
547 res_incexe.current_opts->reader = bstrdup(lc->str);
552 /* Store writer innfo */
553 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass)
557 token = lex_get_token(lc, T_NAME);
560 * Pickup writer command
562 res_incexe.current_opts->writer = bstrdup(lc->str);
569 /* Store Wild-card info */
570 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass)
576 token = lex_get_token(lc, T_SKIP_EOL);
579 * Pickup Wild-card string
583 case T_UNQUOTED_STRING:
584 case T_QUOTED_STRING:
585 if (item->code == 1) {
587 res_incexe.current_opts->wilddir.append(bstrdup(lc->str));
588 newsize = res_incexe.current_opts->wilddir.size();
589 } else if (item->code == 2) {
591 res_incexe.current_opts->wildfile.append(bstrdup(lc->str));
592 newsize = res_incexe.current_opts->wildfile.size();
595 res_incexe.current_opts->wild.append(bstrdup(lc->str));
596 newsize = res_incexe.current_opts->wild.size();
598 Dmsg4(9, "set %s %p size=%d %s\n",
599 type, res_incexe.current_opts, newsize, lc->str);
602 scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
608 /* Store fstype info */
609 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass)
613 token = lex_get_token(lc, T_SKIP_EOL);
615 /* Pickup fstype string */
618 case T_UNQUOTED_STRING:
619 case T_QUOTED_STRING:
620 res_incexe.current_opts->fstype.append(bstrdup(lc->str));
621 Dmsg3(900, "set fstype %p size=%d %s\n",
622 res_incexe.current_opts, res_incexe.current_opts->fstype.size(), lc->str);
625 scan_err1(lc, _("Expected an fstype string, got: %s\n"), lc->str);
632 * Store Filename info. Note, for minor efficiency reasons, we
633 * always increase the name buffer by 10 items because we expect
634 * to add more entries.
636 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass)
641 token = lex_get_token(lc, T_SKIP_EOL);
643 /* Pickup Filename string
647 case T_UNQUOTED_STRING:
648 case T_QUOTED_STRING:
649 if (res_all.res_fs.have_MD5) {
650 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
652 incexe = &res_incexe;
653 if (incexe->name_list.size() == 0) {
654 incexe->name_list.init(10, true);
656 incexe->name_list.append(bstrdup(lc->str));
657 Dmsg1(900, "Add to name_list %s\n", lc->str);
660 scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
668 * Come here when Options seen in Include/Exclude
670 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass)
674 token = lex_get_token(lc, T_SKIP_EOL);
675 if (token != T_BOB) {
676 scan_err1(lc, "Expecting open brace. Got %s", lc->str);
680 setup_current_opts();
683 while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
684 if (token == T_EOL) {
687 if (token == T_EOB) {
690 if (token != T_IDENTIFIER) {
691 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
693 for (i=0; options_items[i].name; i++) {
694 if (strcasecmp(options_items[i].name, lc->str) == 0) {
695 token = lex_get_token(lc, T_SKIP_EOL);
696 if (token != T_EQUALS) {
697 scan_err1(lc, "expected an equals, got: %s", lc->str);
699 /* Call item handler */
700 options_items[i].handler(lc, &options_items[i], i, pass);
706 scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
713 * New style options come here
715 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass)
722 keyword = INC_KW_NONE;
723 /* Look up the keyword */
724 for (i=0; FS_option_kw[i].name; i++) {
725 if (strcasecmp(item->name, FS_option_kw[i].name) == 0) {
726 keyword = FS_option_kw[i].token;
730 if (keyword == INC_KW_NONE) {
731 scan_err1(lc, "Expected a FileSet keyword, got: %s", lc->str);
733 /* Now scan for the value */
734 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
736 bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
737 Dmsg2(900, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
744 /* If current_opts not defined, create first entry */
745 static void setup_current_opts(void)
747 FOPTS *fo = (FOPTS *)malloc(sizeof(FOPTS));
748 memset(fo, 0, sizeof(FOPTS));
749 fo->regex.init(1, true);
750 fo->regexdir.init(1, true);
751 fo->regexfile.init(1, true);
752 fo->wild.init(1, true);
753 fo->wilddir.init(1, true);
754 fo->wildfile.init(1, true);
755 fo->base.init(1, true);
756 fo->fstype.init(1, true);
757 res_incexe.current_opts = fo;
758 if (res_incexe.num_opts == 0) {
759 res_incexe.opts_list = (FOPTS **)malloc(sizeof(FOPTS *));
761 res_incexe.opts_list = (FOPTS **)realloc(res_incexe.opts_list,
762 sizeof(FOPTS *) * (res_incexe.num_opts + 1));
764 res_incexe.opts_list[res_incexe.num_opts++] = fo;