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.
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_DIGEST},
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_DIGEST, "M"},
167 {"sha1", INC_KW_DIGEST, "S"},
168 {"sha256", INC_KW_DIGEST, "S2"},
169 {"sha512", INC_KW_DIGEST, "S3"},
170 {"gzip", INC_KW_COMPRESSION, "Z6"},
171 {"gzip1", INC_KW_COMPRESSION, "Z1"},
172 {"gzip2", INC_KW_COMPRESSION, "Z2"},
173 {"gzip3", INC_KW_COMPRESSION, "Z3"},
174 {"gzip4", INC_KW_COMPRESSION, "Z4"},
175 {"gzip5", INC_KW_COMPRESSION, "Z5"},
176 {"gzip6", INC_KW_COMPRESSION, "Z6"},
177 {"gzip7", INC_KW_COMPRESSION, "Z7"},
178 {"gzip8", INC_KW_COMPRESSION, "Z8"},
179 {"gzip9", INC_KW_COMPRESSION, "Z9"},
180 {"blowfish", INC_KW_ENCRYPTION, "B"}, /* ***FIXME*** not implemented */
181 {"3des", INC_KW_ENCRYPTION, "3"}, /* ***FIXME*** not implemented */
182 {"yes", INC_KW_ONEFS, "0"},
183 {"no", INC_KW_ONEFS, "f"},
184 {"yes", INC_KW_RECURSE, "0"},
185 {"no", INC_KW_RECURSE, "h"},
186 {"yes", INC_KW_SPARSE, "s"},
187 {"no", INC_KW_SPARSE, "0"},
188 {"yes", INC_KW_HARDLINK, "0"},
189 {"no", INC_KW_HARDLINK, "H"},
190 {"always", INC_KW_REPLACE, "a"},
191 {"ifnewer", INC_KW_REPLACE, "w"},
192 {"never", INC_KW_REPLACE, "n"},
193 {"yes", INC_KW_READFIFO, "r"},
194 {"no", INC_KW_READFIFO, "0"},
195 {"yes", INC_KW_PORTABLE, "p"},
196 {"no", INC_KW_PORTABLE, "0"},
197 {"yes", INC_KW_MTIMEONLY, "m"},
198 {"no", INC_KW_MTIMEONLY, "0"},
199 {"yes", INC_KW_KEEPATIME, "k"},
200 {"no", INC_KW_KEEPATIME, "0"},
201 {"yes", INC_KW_EXCLUDE, "e"},
202 {"no", INC_KW_EXCLUDE, "0"},
203 {"yes", INC_KW_ACL, "A"},
204 {"no", INC_KW_ACL, "0"},
205 {"yes", INC_KW_IGNORECASE, "i"},
206 {"no", INC_KW_IGNORECASE, "0"},
207 {"yes", INC_KW_HFSPLUS, "R"}, /* "R" for resource fork */
208 {"no", INC_KW_HFSPLUS, "0"},
215 * Scan for right hand side of Include options (keyword=option) is
216 * converted into one or two characters. Verifyopts=xxxx is Vxxxx:
217 * Whatever is found is concatenated to the opts string.
218 * This code is also used inside an Options resource.
220 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
224 int lcopts = lc->options;
226 option[0] = 0; /* default option = none */
227 option[2] = 0; /* terminate options */
228 lc->options |= LOPT_STRING; /* force string */
229 token = lex_get_token(lc, T_STRING); /* expect at least one option */
230 if (keyword == INC_KW_VERIFY) { /* special case */
231 /* ***FIXME**** ensure these are in permitted set */
232 bstrncat(opts, "V", optlen); /* indicate Verify */
233 bstrncat(opts, lc->str, optlen);
234 bstrncat(opts, ":", optlen); /* terminate it */
235 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
238 * Standard keyword options for Include/Exclude
241 for (i=0; FS_options[i].name; i++) {
242 if (strcasecmp(lc->str, FS_options[i].name) == 0 && FS_options[i].keyword == keyword) {
243 /* NOTE! maximum 2 letters here or increase option[3] */
244 option[0] = FS_options[i].option[0];
245 option[1] = FS_options[i].option[1];
251 scan_err1(lc, _("Expected a FileSet option keyword, got:%s:"), lc->str);
252 } else { /* add option */
253 bstrncat(opts, option, optlen);
254 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
257 lc->options = lcopts;
259 /* If option terminated by comma, eat it */
261 token = lex_get_token(lc, T_ALL); /* yes, eat comma */
267 * Store FileSet Include/Exclude info
268 * NEW style includes are handled in store_newinc()
270 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass)
275 * Decide if we are doing a new Include or an old include. The
276 * new Include is followed immediately by open brace, whereas the
277 * old include has options following the Include.
279 token = lex_get_token(lc, T_SKIP_EOL);
280 if (token == T_BOB) {
281 store_newinc(lc, item, index, pass);
284 scan_err0(lc, _("Old style Include/Exclude not supported\n"));
289 * Store NEW style FileSet FInclude/FExclude info
291 * Note, when this routine is called, we are inside a FileSet
292 * resource. We treat the Include/Execlude like a sort of
293 * mini-resource within the FileSet resource.
295 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
301 if (!res_all.res_fs.have_MD5) {
302 MD5Init(&res_all.res_fs.md5c);
303 res_all.res_fs.have_MD5 = true;
305 memset(&res_incexe, 0, sizeof(INCEXE));
306 res_all.res_fs.new_include = true;
307 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
308 if (token == T_EOB) {
311 if (token != T_IDENTIFIER) {
312 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
314 for (i=0; newinc_items[i].name; i++) {
315 options = strcasecmp(lc->str, "options") == 0;
316 if (strcasecmp(newinc_items[i].name, lc->str) == 0) {
318 token = lex_get_token(lc, T_SKIP_EOL);
319 if (token != T_EQUALS) {
320 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
323 /* Call item handler */
324 newinc_items[i].handler(lc, &newinc_items[i], i, pass);
330 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
334 incexe = (INCEXE *)malloc(sizeof(INCEXE));
335 memcpy(incexe, &res_incexe, sizeof(INCEXE));
336 memset(&res_incexe, 0, sizeof(INCEXE));
337 if (item->code == 0) { /* include */
338 if (res_all.res_fs.num_includes == 0) {
339 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
341 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
342 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
344 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
345 Dmsg1(900, "num_includes=%d\n", res_all.res_fs.num_includes);
346 } else { /* exclude */
347 if (res_all.res_fs.num_excludes == 0) {
348 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
350 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
351 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
353 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
354 Dmsg1(900, "num_excludes=%d\n", res_all.res_fs.num_excludes);
358 set_bit(index, res_all.hdr.item_present);
362 /* Store regex info */
363 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass)
371 token = lex_get_token(lc, T_SKIP_EOL);
373 /* Pickup regex string
377 case T_UNQUOTED_STRING:
378 case T_QUOTED_STRING:
379 rc = regcomp(&preg, lc->str, REG_EXTENDED);
381 regerror(rc, &preg, prbuf, sizeof(prbuf));
383 scan_err1(lc, _("Regex compile error. ERR=%s\n"), prbuf);
387 if (item->code == 1) {
389 res_incexe.current_opts->regexdir.append(bstrdup(lc->str));
390 newsize = res_incexe.current_opts->regexdir.size();
391 } else if (item->code == 2) {
393 res_incexe.current_opts->regexfile.append(bstrdup(lc->str));
394 newsize = res_incexe.current_opts->regexfile.size();
397 res_incexe.current_opts->regex.append(bstrdup(lc->str));
398 newsize = res_incexe.current_opts->regex.size();
400 Dmsg4(900, "set %s %p size=%d %s\n",
401 type, res_incexe.current_opts, newsize, lc->str);
404 scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
410 /* Store Base info */
411 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
415 token = lex_get_token(lc, T_NAME);
418 * Pickup Base Job Name
420 res_incexe.current_opts->base.append(bstrdup(lc->str));
425 /* Store reader info */
426 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass)
430 token = lex_get_token(lc, T_NAME);
433 * Pickup reader command
435 res_incexe.current_opts->reader = bstrdup(lc->str);
440 /* Store writer innfo */
441 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass)
445 token = lex_get_token(lc, T_NAME);
448 * Pickup writer command
450 res_incexe.current_opts->writer = bstrdup(lc->str);
457 /* Store Wild-card info */
458 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass)
464 token = lex_get_token(lc, T_SKIP_EOL);
467 * Pickup Wild-card string
471 case T_UNQUOTED_STRING:
472 case T_QUOTED_STRING:
473 if (item->code == 1) {
475 res_incexe.current_opts->wilddir.append(bstrdup(lc->str));
476 newsize = res_incexe.current_opts->wilddir.size();
477 } else if (item->code == 2) {
479 res_incexe.current_opts->wildfile.append(bstrdup(lc->str));
480 newsize = res_incexe.current_opts->wildfile.size();
483 res_incexe.current_opts->wild.append(bstrdup(lc->str));
484 newsize = res_incexe.current_opts->wild.size();
486 Dmsg4(9, "set %s %p size=%d %s\n",
487 type, res_incexe.current_opts, newsize, lc->str);
490 scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
496 /* Store fstype info */
497 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass)
501 token = lex_get_token(lc, T_SKIP_EOL);
503 /* Pickup fstype string */
506 case T_UNQUOTED_STRING:
507 case T_QUOTED_STRING:
508 res_incexe.current_opts->fstype.append(bstrdup(lc->str));
509 Dmsg3(900, "set fstype %p size=%d %s\n",
510 res_incexe.current_opts, res_incexe.current_opts->fstype.size(), lc->str);
513 scan_err1(lc, _("Expected an fstype string, got: %s\n"), lc->str);
520 * Store Filename info. Note, for minor efficiency reasons, we
521 * always increase the name buffer by 10 items because we expect
522 * to add more entries.
524 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass)
529 token = lex_get_token(lc, T_SKIP_EOL);
531 /* Pickup Filename string
535 case T_UNQUOTED_STRING:
536 if (strchr(lc->str, '\\')) {
537 scan_err1(lc, _("Backslash found. Use forward slashes or quote the string.: %s\n"), lc->str);
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;