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 {"readfifo", store_opts, NULL, 0, 0, 0},
83 {"replace", store_opts, NULL, 0, 0, 0},
84 {"portable", store_opts, NULL, 0, 0, 0},
85 {"mtimeonly", store_opts, NULL, 0, 0, 0},
86 {"keepatime", store_opts, NULL, 0, 0, 0},
87 {"regex", store_regex, NULL, 0, 0, 0},
88 {"base", store_base, NULL, 0, 0, 0},
89 {"wild", store_wild, NULL, 0, 0, 0},
90 {"exclude", store_opts, NULL, 0, 0, 0},
91 {"aclsupport", store_opts, NULL, 0, 0, 0},
92 {"reader", store_reader, NULL, 0, 0, 0},
93 {"writer", store_writer, NULL, 0, 0, 0},
94 {NULL, NULL, NULL, 0, 0, 0}
98 /* Define FileSet KeyWord values */
109 INC_KW_REPLACE, /* restore options */
110 INC_KW_READFIFO, /* Causes fifo data to be read */
119 * Include keywords -- these are keywords that can appear
120 * in the options lists of an old include ( Include = compression= ...)
122 static struct s_kw FS_option_kw[] = {
123 {"compression", INC_KW_COMPRESSION},
124 {"signature", INC_KW_SIGNATURE},
125 {"encryption", INC_KW_ENCRYPTION},
126 {"verify", INC_KW_VERIFY},
127 {"onefs", INC_KW_ONEFS},
128 {"recurse", INC_KW_RECURSE},
129 {"sparse", INC_KW_SPARSE},
130 {"replace", INC_KW_REPLACE},
131 {"readfifo", INC_KW_READFIFO},
132 {"portable", INC_KW_PORTABLE},
133 {"mtimeonly", INC_KW_MTIMEONLY},
134 {"keepatime", INC_KW_KEEPATIME},
135 {"exclude", INC_KW_EXCLUDE},
136 {"aclsupport", INC_KW_ACL},
140 /* Options for FileSet keywords */
149 * Options permitted for each keyword and resulting value.
150 * The output goes into opts, which are then transmitted to
151 * the FD for application as options to the following list of
154 static struct s_fs_opt FS_options[] = {
155 {"md5", INC_KW_SIGNATURE, "M"},
156 {"sha1", INC_KW_SIGNATURE, "S"},
157 {"gzip", INC_KW_COMPRESSION, "Z6"},
158 {"gzip1", INC_KW_COMPRESSION, "Z1"},
159 {"gzip2", INC_KW_COMPRESSION, "Z2"},
160 {"gzip3", INC_KW_COMPRESSION, "Z3"},
161 {"gzip4", INC_KW_COMPRESSION, "Z4"},
162 {"gzip5", INC_KW_COMPRESSION, "Z5"},
163 {"gzip6", INC_KW_COMPRESSION, "Z6"},
164 {"gzip7", INC_KW_COMPRESSION, "Z7"},
165 {"gzip8", INC_KW_COMPRESSION, "Z8"},
166 {"gzip9", INC_KW_COMPRESSION, "Z9"},
167 {"blowfish", INC_KW_ENCRYPTION, "B"}, /* ***FIXME*** not implemented */
168 {"3des", INC_KW_ENCRYPTION, "3"}, /* ***FIXME*** not implemented */
169 {"yes", INC_KW_ONEFS, "0"},
170 {"no", INC_KW_ONEFS, "f"},
171 {"yes", INC_KW_RECURSE, "0"},
172 {"no", INC_KW_RECURSE, "h"},
173 {"yes", INC_KW_SPARSE, "s"},
174 {"no", INC_KW_SPARSE, "0"},
175 {"always", INC_KW_REPLACE, "a"},
176 {"ifnewer", INC_KW_REPLACE, "w"},
177 {"never", INC_KW_REPLACE, "n"},
178 {"yes", INC_KW_READFIFO, "r"},
179 {"no", INC_KW_READFIFO, "0"},
180 {"yes", INC_KW_PORTABLE, "p"},
181 {"no", INC_KW_PORTABLE, "0"},
182 {"yes", INC_KW_MTIMEONLY, "m"},
183 {"no", INC_KW_MTIMEONLY, "0"},
184 {"yes", INC_KW_KEEPATIME, "k"},
185 {"no", INC_KW_KEEPATIME, "0"},
186 {"yes", INC_KW_EXCLUDE, "e"},
187 {"no", INC_KW_EXCLUDE, "0"},
188 {"yes", INC_KW_ACL, "A"},
189 {"no", INC_KW_ACL, "0"},
196 * Scan for right hand side of Include options (keyword=option) is
197 * converted into one or two characters. Verifyopts=xxxx is Vxxxx:
198 * Whatever is found is concatenated to the opts string.
199 * This code is also used inside an Options resource.
201 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
206 option[0] = 0; /* default option = none */
207 option[2] = 0; /* terminate options */
208 token = lex_get_token(lc, T_NAME); /* expect at least one option */
209 if (keyword == INC_KW_VERIFY) { /* special case */
210 /* ***FIXME**** ensure these are in permitted set */
211 bstrncat(opts, "V", optlen); /* indicate Verify */
212 bstrncat(opts, lc->str, optlen);
213 bstrncat(opts, ":", optlen); /* terminate it */
214 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
217 * Standard keyword options for Include/Exclude
220 for (i=0; FS_options[i].name; i++) {
221 if (strcasecmp(lc->str, FS_options[i].name) == 0 && FS_options[i].keyword == keyword) {
222 /* NOTE! maximum 2 letters here or increase option[3] */
223 option[0] = FS_options[i].option[0];
224 option[1] = FS_options[i].option[1];
230 scan_err1(lc, "Expected a FileSet option keyword, got:%s:", lc->str);
231 } else { /* add option */
232 bstrncat(opts, option, optlen);
233 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
237 /* If option terminated by comma, eat it */
239 token = lex_get_token(lc, T_ALL); /* yes, eat comma */
245 * Store FileSet Include/Exclude info
246 * NEW style includes are handled in store_newinc()
248 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass)
251 int options = lc->options;
257 * Decide if we are doing a new Include or an old include. The
258 * new Include is followed immediately by open brace, whereas the
259 * old include has options following the Include.
261 token = lex_get_token(lc, T_SKIP_EOL);
262 if (token == T_BOB) {
263 store_newinc(lc, item, index, pass);
267 /* What follows is scanning for the OLD style Include/Exclude */
269 if (token != T_EQUALS) {
270 scan_err1(lc, _("Expecting an equals sign, got: %s\n"), lc->str);
272 lc->options |= LOPT_NO_IDENT; /* make spaces significant */
273 memset(&res_incexe, 0, sizeof(INCEXE));
275 /* Get include options */
277 while ((token=lex_get_token(lc, T_SKIP_EOL)) != T_BOB) {
279 keyword = INC_KW_NONE;
280 for (i=0; FS_option_kw[i].name; i++) {
281 if (strcasecmp(lc->str, FS_option_kw[i].name) == 0) {
282 keyword = FS_option_kw[i].token;
286 if (keyword == INC_KW_NONE) {
287 scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
289 /* Option keyword should be following by = <option> */
290 if ((token=lex_get_token(lc, T_SKIP_EOL)) != T_EQUALS) {
291 scan_err1(lc, _("expected an = following keyword, got: %s"), lc->str);
293 /* Scan right hand side of option */
294 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
296 if (token == T_BOB) {
302 strcat(inc_opts, "0"); /* set no options */
304 inc_opts_len = strlen(inc_opts);
308 if (!res_all.res_fs.have_MD5) {
309 MD5Init(&res_all.res_fs.md5c);
310 res_all.res_fs.have_MD5 = TRUE;
312 setup_current_opts();
313 bstrncpy(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
314 Dmsg2(900, "old pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
316 /* Create incexe structure */
317 Dmsg0(900, "Create INCEXE structure\n");
318 incexe = (INCEXE *)malloc(sizeof(INCEXE));
319 memcpy(incexe, &res_incexe, sizeof(INCEXE));
320 memset(&res_incexe, 0, sizeof(INCEXE));
321 if (item->code == 0) { /* include */
322 if (res_all.res_fs.num_includes == 0) {
323 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
325 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
326 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
328 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
329 Dmsg1(900, "num_includes=%d\n", res_all.res_fs.num_includes);
330 } else { /* exclude */
331 if (res_all.res_fs.num_excludes == 0) {
332 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
334 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
335 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
337 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
338 Dmsg1(900, "num_excludes=%d\n", res_all.res_fs.num_excludes);
341 /* Pickup include/exclude names. They are stored in INCEXE
342 * structures which contains the options and the name.
344 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOB) {
350 case T_UNQUOTED_STRING:
351 case T_QUOTED_STRING:
352 if (res_all.res_fs.have_MD5) {
353 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
355 if (incexe->name_list.size() == 0) {
356 incexe->name_list.init(10, true);
358 incexe->name_list.append(bstrdup(lc->str));
359 Dmsg1(900, "Add to name_list %s\n", lc->str);
362 scan_err1(lc, "Expected a filename, got: %s", lc->str);
365 /* Note, MD5Final is done in backup.c */
366 } else { /* pass 2 */
367 while (lex_get_token(lc, T_ALL) != T_EOB)
371 lc->options = options;
372 set_bit(index, res_all.hdr.item_present);
377 * Store NEW style FileSet FInclude/FExclude info
379 * Note, when this routine is called, we are inside a FileSet
380 * resource. We treat the Include/Execlude like a sort of
381 * mini-resource within the FileSet resource.
383 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
389 if (!res_all.res_fs.have_MD5) {
390 MD5Init(&res_all.res_fs.md5c);
391 res_all.res_fs.have_MD5 = true;
393 memset(&res_incexe, 0, sizeof(INCEXE));
394 res_all.res_fs.new_include = true;
395 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
396 if (token == T_EOB) {
399 if (token != T_IDENTIFIER) {
400 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
402 for (i=0; newinc_items[i].name; i++) {
403 options = strcasecmp(lc->str, "options") == 0;
404 if (strcasecmp(newinc_items[i].name, lc->str) == 0) {
406 token = lex_get_token(lc, T_SKIP_EOL);
407 if (token != T_EQUALS) {
408 scan_err1(lc, "expected an equals, got: %s", lc->str);
411 /* Call item handler */
412 newinc_items[i].handler(lc, &newinc_items[i], i, pass);
418 scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
422 incexe = (INCEXE *)malloc(sizeof(INCEXE));
423 memcpy(incexe, &res_incexe, sizeof(INCEXE));
424 memset(&res_incexe, 0, sizeof(INCEXE));
425 if (item->code == 0) { /* include */
426 if (res_all.res_fs.num_includes == 0) {
427 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
429 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
430 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
432 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
433 Dmsg1(900, "num_includes=%d\n", res_all.res_fs.num_includes);
434 } else { /* exclude */
435 if (res_all.res_fs.num_excludes == 0) {
436 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
438 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
439 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
441 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
442 Dmsg1(900, "num_excludes=%d\n", res_all.res_fs.num_excludes);
446 set_bit(index, res_all.hdr.item_present);
450 /* Store regex info */
451 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass)
457 token = lex_get_token(lc, T_SKIP_EOL);
459 /* Pickup regex string
463 case T_UNQUOTED_STRING:
464 case T_QUOTED_STRING:
465 rc = regcomp(&preg, lc->str, REG_EXTENDED);
467 regerror(rc, &preg, prbuf, sizeof(prbuf));
469 scan_err1(lc, _("Regex compile error. ERR=%s\n"), prbuf);
473 res_incexe.current_opts->regex.append(bstrdup(lc->str));
474 Dmsg3(900, "set regex %p size=%d %s\n",
475 res_incexe.current_opts, res_incexe.current_opts->regex.size(),lc->str);
478 scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
484 /* Store Base info */
485 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
489 token = lex_get_token(lc, T_NAME);
492 * Pickup Base Job Name
494 res_incexe.current_opts->base.append(bstrdup(lc->str));
499 /* Store reader info */
500 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass)
504 token = lex_get_token(lc, T_NAME);
507 * Pickup reader command
509 res_incexe.current_opts->reader = bstrdup(lc->str);
514 /* Store writer innfo */
515 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass)
519 token = lex_get_token(lc, T_NAME);
522 * Pickup writer command
524 res_incexe.current_opts->writer = bstrdup(lc->str);
531 /* Store Wild-card info */
532 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass)
536 token = lex_get_token(lc, T_SKIP_EOL);
539 * Pickup Wild-card string
543 case T_UNQUOTED_STRING:
544 case T_QUOTED_STRING:
545 res_incexe.current_opts->wild.append(bstrdup(lc->str));
546 Dmsg3(900, "set wild %p size=%d %s\n",
547 res_incexe.current_opts, res_incexe.current_opts->wild.size(),lc->str);
550 scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
558 * Store Filename info. Note, for minor efficiency reasons, we
559 * always increase the name buffer by 10 items because we expect
560 * to add more entries.
562 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass)
567 token = lex_get_token(lc, T_SKIP_EOL);
569 /* Pickup Filename string
573 case T_UNQUOTED_STRING:
574 case T_QUOTED_STRING:
575 if (res_all.res_fs.have_MD5) {
576 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
578 incexe = &res_incexe;
579 if (incexe->name_list.size() == 0) {
580 incexe->name_list.init(10, true);
582 incexe->name_list.append(bstrdup(lc->str));
583 Dmsg1(900, "Add to name_list %s\n", lc->str);
586 scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
593 * Come here when Options seen in Include/Exclude
595 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass)
599 token = lex_get_token(lc, T_SKIP_EOL);
600 if (token != T_BOB) {
601 scan_err1(lc, "Expecting open brace. Got %s", lc->str);
605 setup_current_opts();
608 while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
609 if (token == T_EOL) {
612 if (token == T_EOB) {
615 if (token != T_IDENTIFIER) {
616 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
618 for (i=0; options_items[i].name; i++) {
619 if (strcasecmp(options_items[i].name, lc->str) == 0) {
620 token = lex_get_token(lc, T_SKIP_EOL);
621 if (token != T_EQUALS) {
622 scan_err1(lc, "expected an equals, got: %s", lc->str);
624 /* Call item handler */
625 options_items[i].handler(lc, &options_items[i], i, pass);
631 scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
638 * New style options come here
640 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass)
647 keyword = INC_KW_NONE;
648 /* Look up the keyword */
649 for (i=0; FS_option_kw[i].name; i++) {
650 if (strcasecmp(item->name, FS_option_kw[i].name) == 0) {
651 keyword = FS_option_kw[i].token;
655 if (keyword == INC_KW_NONE) {
656 scan_err1(lc, "Expected a FileSet keyword, got: %s", lc->str);
658 /* Now scan for the value */
659 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
661 bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
662 Dmsg2(900, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
669 /* If current_opts not defined, create first entry */
670 static void setup_current_opts(void)
672 FOPTS *fo = (FOPTS *)malloc(sizeof(FOPTS));
673 memset(fo, 0, sizeof(FOPTS));
674 fo->regex.init(1, true);
675 fo->wild.init(1, true);
676 fo->base.init(1, true);
677 res_incexe.current_opts = fo;
678 if (res_incexe.num_opts == 0) {
679 res_incexe.opts_list = (FOPTS **)malloc(sizeof(FOPTS *));
681 res_incexe.opts_list = (FOPTS **)realloc(res_incexe.opts_list,
682 sizeof(FOPTS *) * (res_incexe.num_opts + 1));
684 res_incexe.opts_list[res_incexe.num_opts++] = fo;