2 * Configuration file parser for new and old Include and
5 * Kern Sibbald, March MMIII
10 Copyright (C) 2003-2004 Kern Sibbald and John Walker
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_opts(LEX *lc, RES_ITEM *item, int index, int pass);
43 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass);
44 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass);
45 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass);
46 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass);
47 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass);
48 static void setup_current_opts(void);
51 /* We build the current resource here as we are
52 * scanning the resource configuration definition,
53 * then move it to allocated memory when the resource
57 extern int res_all_size;
59 /* We build the current new Include and Exclude items here */
60 static INCEXE res_incexe;
63 * new Include/Exclude items
64 * name handler value code flags default_value
66 static RES_ITEM newinc_items[] = {
67 {"file", store_fname, NULL, 0, 0, 0},
68 {"options", options_res, NULL, 0, 0, 0},
69 {NULL, NULL, NULL, 0, 0, 0}
73 * Items that are valid in an Options resource
75 static RES_ITEM options_items[] = {
76 {"compression", store_opts, NULL, 0, 0, 0},
77 {"signature", store_opts, NULL, 0, 0, 0},
78 {"verify", store_opts, NULL, 0, 0, 0},
79 {"onefs", store_opts, NULL, 0, 0, 0},
80 {"recurse", store_opts, NULL, 0, 0, 0},
81 {"sparse", store_opts, NULL, 0, 0, 0},
82 {"hardlinks", store_opts, NULL, 0, 0, 0},
83 {"readfifo", store_opts, NULL, 0, 0, 0},
84 {"replace", store_opts, NULL, 0, 0, 0},
85 {"portable", store_opts, NULL, 0, 0, 0},
86 {"mtimeonly", store_opts, NULL, 0, 0, 0},
87 {"keepatime", store_opts, NULL, 0, 0, 0},
88 {"regex", store_regex, NULL, 0, 0, 0},
89 {"base", store_base, NULL, 0, 0, 0},
90 {"wild", store_wild, NULL, 0, 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 {NULL, NULL, NULL, 0, 0, 0}
99 /* Define FileSet KeyWord values */
111 INC_KW_REPLACE, /* restore options */
112 INC_KW_READFIFO, /* Causes fifo data to be read */
121 * Include keywords -- these are keywords that can appear
122 * in the options lists of an old include ( Include = compression= ...)
124 static struct s_kw FS_option_kw[] = {
125 {"compression", INC_KW_COMPRESSION},
126 {"signature", INC_KW_SIGNATURE},
127 {"encryption", INC_KW_ENCRYPTION},
128 {"verify", INC_KW_VERIFY},
129 {"onefs", INC_KW_ONEFS},
130 {"recurse", INC_KW_RECURSE},
131 {"sparse", INC_KW_SPARSE},
132 {"hardlinks", INC_KW_HARDLINK},
133 {"replace", INC_KW_REPLACE},
134 {"readfifo", INC_KW_READFIFO},
135 {"portable", INC_KW_PORTABLE},
136 {"mtimeonly", INC_KW_MTIMEONLY},
137 {"keepatime", INC_KW_KEEPATIME},
138 {"exclude", INC_KW_EXCLUDE},
139 {"aclsupport", INC_KW_ACL},
143 /* Options for FileSet keywords */
152 * Options permitted for each keyword and resulting value.
153 * The output goes into opts, which are then transmitted to
154 * the FD for application as options to the following list of
157 static struct s_fs_opt FS_options[] = {
158 {"md5", INC_KW_SIGNATURE, "M"},
159 {"sha1", INC_KW_SIGNATURE, "S"},
160 {"gzip", INC_KW_COMPRESSION, "Z6"},
161 {"gzip1", INC_KW_COMPRESSION, "Z1"},
162 {"gzip2", INC_KW_COMPRESSION, "Z2"},
163 {"gzip3", INC_KW_COMPRESSION, "Z3"},
164 {"gzip4", INC_KW_COMPRESSION, "Z4"},
165 {"gzip5", INC_KW_COMPRESSION, "Z5"},
166 {"gzip6", INC_KW_COMPRESSION, "Z6"},
167 {"gzip7", INC_KW_COMPRESSION, "Z7"},
168 {"gzip8", INC_KW_COMPRESSION, "Z8"},
169 {"gzip9", INC_KW_COMPRESSION, "Z9"},
170 {"blowfish", INC_KW_ENCRYPTION, "B"}, /* ***FIXME*** not implemented */
171 {"3des", INC_KW_ENCRYPTION, "3"}, /* ***FIXME*** not implemented */
172 {"yes", INC_KW_ONEFS, "0"},
173 {"no", INC_KW_ONEFS, "f"},
174 {"yes", INC_KW_RECURSE, "0"},
175 {"no", INC_KW_RECURSE, "h"},
176 {"yes", INC_KW_SPARSE, "s"},
177 {"no", INC_KW_SPARSE, "0"},
178 {"yes", INC_KW_HARDLINK, "0"},
179 {"no", INC_KW_HARDLINK, "H"},
180 {"always", INC_KW_REPLACE, "a"},
181 {"ifnewer", INC_KW_REPLACE, "w"},
182 {"never", INC_KW_REPLACE, "n"},
183 {"yes", INC_KW_READFIFO, "r"},
184 {"no", INC_KW_READFIFO, "0"},
185 {"yes", INC_KW_PORTABLE, "p"},
186 {"no", INC_KW_PORTABLE, "0"},
187 {"yes", INC_KW_MTIMEONLY, "m"},
188 {"no", INC_KW_MTIMEONLY, "0"},
189 {"yes", INC_KW_KEEPATIME, "k"},
190 {"no", INC_KW_KEEPATIME, "0"},
191 {"yes", INC_KW_EXCLUDE, "e"},
192 {"no", INC_KW_EXCLUDE, "0"},
193 {"yes", INC_KW_ACL, "A"},
194 {"no", INC_KW_ACL, "0"},
201 * Scan for right hand side of Include options (keyword=option) is
202 * converted into one or two characters. Verifyopts=xxxx is Vxxxx:
203 * Whatever is found is concatenated to the opts string.
204 * This code is also used inside an Options resource.
206 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
211 option[0] = 0; /* default option = none */
212 option[2] = 0; /* terminate options */
213 token = lex_get_token(lc, T_NAME); /* expect at least one option */
214 if (keyword == INC_KW_VERIFY) { /* special case */
215 /* ***FIXME**** ensure these are in permitted set */
216 bstrncat(opts, "V", optlen); /* indicate Verify */
217 bstrncat(opts, lc->str, optlen);
218 bstrncat(opts, ":", optlen); /* terminate it */
219 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
222 * Standard keyword options for Include/Exclude
225 for (i=0; FS_options[i].name; i++) {
226 if (strcasecmp(lc->str, FS_options[i].name) == 0 && FS_options[i].keyword == keyword) {
227 /* NOTE! maximum 2 letters here or increase option[3] */
228 option[0] = FS_options[i].option[0];
229 option[1] = FS_options[i].option[1];
235 scan_err1(lc, "Expected a FileSet option keyword, got:%s:", lc->str);
236 } else { /* add option */
237 bstrncat(opts, option, optlen);
238 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
242 /* If option terminated by comma, eat it */
244 token = lex_get_token(lc, T_ALL); /* yes, eat comma */
250 * Store FileSet Include/Exclude info
251 * NEW style includes are handled in store_newinc()
253 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass)
256 int options = lc->options;
262 * Decide if we are doing a new Include or an old include. The
263 * new Include is followed immediately by open brace, whereas the
264 * old include has options following the Include.
266 token = lex_get_token(lc, T_SKIP_EOL);
267 if (token == T_BOB) {
268 store_newinc(lc, item, index, pass);
272 /* What follows is scanning for the OLD style Include/Exclude */
274 if (token != T_EQUALS) {
275 scan_err1(lc, _("Expecting an equals sign, got: %s\n"), lc->str);
277 lc->options |= LOPT_NO_IDENT; /* make spaces significant */
278 memset(&res_incexe, 0, sizeof(INCEXE));
280 /* Get include options */
282 while ((token=lex_get_token(lc, T_SKIP_EOL)) != T_BOB) {
284 keyword = INC_KW_NONE;
285 for (i=0; FS_option_kw[i].name; i++) {
286 if (strcasecmp(lc->str, FS_option_kw[i].name) == 0) {
287 keyword = FS_option_kw[i].token;
291 if (keyword == INC_KW_NONE) {
292 scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
294 /* Option keyword should be following by = <option> */
295 if ((token=lex_get_token(lc, T_SKIP_EOL)) != T_EQUALS) {
296 scan_err1(lc, _("expected an = following keyword, got: %s"), lc->str);
298 /* Scan right hand side of option */
299 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
301 if (token == T_BOB) {
307 strcat(inc_opts, "0"); /* set no options */
309 inc_opts_len = strlen(inc_opts);
313 if (!res_all.res_fs.have_MD5) {
314 MD5Init(&res_all.res_fs.md5c);
315 res_all.res_fs.have_MD5 = TRUE;
317 setup_current_opts();
318 bstrncpy(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
319 Dmsg2(900, "old pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
321 /* Create incexe structure */
322 Dmsg0(900, "Create INCEXE structure\n");
323 incexe = (INCEXE *)malloc(sizeof(INCEXE));
324 memcpy(incexe, &res_incexe, sizeof(INCEXE));
325 memset(&res_incexe, 0, sizeof(INCEXE));
326 if (item->code == 0) { /* include */
327 if (res_all.res_fs.num_includes == 0) {
328 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
330 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
331 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
333 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
334 Dmsg1(900, "num_includes=%d\n", res_all.res_fs.num_includes);
335 } else { /* exclude */
336 if (res_all.res_fs.num_excludes == 0) {
337 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
339 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
340 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
342 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
343 Dmsg1(900, "num_excludes=%d\n", res_all.res_fs.num_excludes);
346 /* Pickup include/exclude names. They are stored in INCEXE
347 * structures which contains the options and the name.
349 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOB) {
355 case T_UNQUOTED_STRING:
356 case T_QUOTED_STRING:
357 if (res_all.res_fs.have_MD5) {
358 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
360 if (incexe->name_list.size() == 0) {
361 incexe->name_list.init(10, true);
363 incexe->name_list.append(bstrdup(lc->str));
364 Dmsg1(900, "Add to name_list %s\n", lc->str);
367 scan_err1(lc, "Expected a filename, got: %s", lc->str);
370 /* Note, MD5Final is done in backup.c */
371 } else { /* pass 2 */
372 while (lex_get_token(lc, T_ALL) != T_EOB)
376 lc->options = options;
377 set_bit(index, res_all.hdr.item_present);
382 * Store NEW style FileSet FInclude/FExclude info
384 * Note, when this routine is called, we are inside a FileSet
385 * resource. We treat the Include/Execlude like a sort of
386 * mini-resource within the FileSet resource.
388 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
394 if (!res_all.res_fs.have_MD5) {
395 MD5Init(&res_all.res_fs.md5c);
396 res_all.res_fs.have_MD5 = true;
398 memset(&res_incexe, 0, sizeof(INCEXE));
399 res_all.res_fs.new_include = true;
400 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
401 if (token == T_EOB) {
404 if (token != T_IDENTIFIER) {
405 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
407 for (i=0; newinc_items[i].name; i++) {
408 options = strcasecmp(lc->str, "options") == 0;
409 if (strcasecmp(newinc_items[i].name, lc->str) == 0) {
411 token = lex_get_token(lc, T_SKIP_EOL);
412 if (token != T_EQUALS) {
413 scan_err1(lc, "expected an equals, got: %s", lc->str);
416 /* Call item handler */
417 newinc_items[i].handler(lc, &newinc_items[i], i, pass);
423 scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
427 incexe = (INCEXE *)malloc(sizeof(INCEXE));
428 memcpy(incexe, &res_incexe, sizeof(INCEXE));
429 memset(&res_incexe, 0, sizeof(INCEXE));
430 if (item->code == 0) { /* include */
431 if (res_all.res_fs.num_includes == 0) {
432 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
434 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
435 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
437 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
438 Dmsg1(900, "num_includes=%d\n", res_all.res_fs.num_includes);
439 } else { /* exclude */
440 if (res_all.res_fs.num_excludes == 0) {
441 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
443 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
444 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
446 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
447 Dmsg1(900, "num_excludes=%d\n", res_all.res_fs.num_excludes);
451 set_bit(index, res_all.hdr.item_present);
455 /* Store regex info */
456 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass)
462 token = lex_get_token(lc, T_SKIP_EOL);
464 /* Pickup regex string
468 case T_UNQUOTED_STRING:
469 case T_QUOTED_STRING:
470 rc = regcomp(&preg, lc->str, REG_EXTENDED);
472 regerror(rc, &preg, prbuf, sizeof(prbuf));
474 scan_err1(lc, _("Regex compile error. ERR=%s\n"), prbuf);
478 res_incexe.current_opts->regex.append(bstrdup(lc->str));
479 Dmsg3(900, "set regex %p size=%d %s\n",
480 res_incexe.current_opts, res_incexe.current_opts->regex.size(),lc->str);
483 scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
489 /* Store Base info */
490 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
494 token = lex_get_token(lc, T_NAME);
497 * Pickup Base Job Name
499 res_incexe.current_opts->base.append(bstrdup(lc->str));
504 /* Store reader info */
505 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass)
509 token = lex_get_token(lc, T_NAME);
512 * Pickup reader command
514 res_incexe.current_opts->reader = bstrdup(lc->str);
519 /* Store writer innfo */
520 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass)
524 token = lex_get_token(lc, T_NAME);
527 * Pickup writer command
529 res_incexe.current_opts->writer = bstrdup(lc->str);
536 /* Store Wild-card info */
537 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass)
541 token = lex_get_token(lc, T_SKIP_EOL);
544 * Pickup Wild-card string
548 case T_UNQUOTED_STRING:
549 case T_QUOTED_STRING:
550 res_incexe.current_opts->wild.append(bstrdup(lc->str));
551 Dmsg3(900, "set wild %p size=%d %s\n",
552 res_incexe.current_opts, res_incexe.current_opts->wild.size(),lc->str);
555 scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
563 * Store Filename info. Note, for minor efficiency reasons, we
564 * always increase the name buffer by 10 items because we expect
565 * to add more entries.
567 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass)
572 token = lex_get_token(lc, T_SKIP_EOL);
574 /* Pickup Filename string
578 case T_UNQUOTED_STRING:
579 case T_QUOTED_STRING:
580 if (res_all.res_fs.have_MD5) {
581 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
583 incexe = &res_incexe;
584 if (incexe->name_list.size() == 0) {
585 incexe->name_list.init(10, true);
587 incexe->name_list.append(bstrdup(lc->str));
588 Dmsg1(900, "Add to name_list %s\n", lc->str);
591 scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
598 * Come here when Options seen in Include/Exclude
600 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass)
604 token = lex_get_token(lc, T_SKIP_EOL);
605 if (token != T_BOB) {
606 scan_err1(lc, "Expecting open brace. Got %s", lc->str);
610 setup_current_opts();
613 while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
614 if (token == T_EOL) {
617 if (token == T_EOB) {
620 if (token != T_IDENTIFIER) {
621 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
623 for (i=0; options_items[i].name; i++) {
624 if (strcasecmp(options_items[i].name, lc->str) == 0) {
625 token = lex_get_token(lc, T_SKIP_EOL);
626 if (token != T_EQUALS) {
627 scan_err1(lc, "expected an equals, got: %s", lc->str);
629 /* Call item handler */
630 options_items[i].handler(lc, &options_items[i], i, pass);
636 scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
643 * New style options come here
645 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass)
652 keyword = INC_KW_NONE;
653 /* Look up the keyword */
654 for (i=0; FS_option_kw[i].name; i++) {
655 if (strcasecmp(item->name, FS_option_kw[i].name) == 0) {
656 keyword = FS_option_kw[i].token;
660 if (keyword == INC_KW_NONE) {
661 scan_err1(lc, "Expected a FileSet keyword, got: %s", lc->str);
663 /* Now scan for the value */
664 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
666 bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
667 Dmsg2(900, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
674 /* If current_opts not defined, create first entry */
675 static void setup_current_opts(void)
677 FOPTS *fo = (FOPTS *)malloc(sizeof(FOPTS));
678 memset(fo, 0, sizeof(FOPTS));
679 fo->regex.init(1, true);
680 fo->wild.init(1, true);
681 fo->base.init(1, true);
682 res_incexe.current_opts = fo;
683 if (res_incexe.num_opts == 0) {
684 res_incexe.opts_list = (FOPTS **)malloc(sizeof(FOPTS *));
686 res_incexe.opts_list = (FOPTS **)realloc(res_incexe.opts_list,
687 sizeof(FOPTS *) * (res_incexe.num_opts + 1));
689 res_incexe.opts_list[res_incexe.num_opts++] = fo;