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,
32 /* Forward referenced subroutines */
34 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass);
36 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass);
37 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass);
38 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass);
39 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass);
40 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass);
41 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass);
42 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass);
43 static void setup_current_opts(void);
46 /* We build the current resource here as we are
47 * scanning the resource configuration definition,
48 * then move it to allocated memory when the resource
52 extern int res_all_size;
54 /* We build the current new Include and Exclude items here */
55 static INCEXE res_incexe;
58 * new Include/Exclude items
59 * name handler value code flags default_value
61 static RES_ITEM newinc_items[] = {
62 {"file", store_fname, NULL, 0, 0, 0},
63 {"include", 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 {"readfifo", store_opts, NULL, 0, 0, 0},
79 {"replace", store_opts, NULL, 0, 0, 0},
80 {"portable", store_opts, NULL, 0, 0, 0},
81 {"regex", store_regex, NULL, 0, 0, 0},
82 {"base", store_base, NULL, 0, 0, 0},
83 {"wild", store_wild, NULL, 0, 0, 0},
84 {NULL, NULL, NULL, 0, 0, 0}
88 /* Define FileSet KeyWord values */
91 #define INC_KW_COMPRESSION 1
92 #define INC_KW_SIGNATURE 2
93 #define INC_KW_ENCRYPTION 3
94 #define INC_KW_VERIFY 4
95 #define INC_KW_ONEFS 5
96 #define INC_KW_RECURSE 6
97 #define INC_KW_SPARSE 7
98 #define INC_KW_REPLACE 8 /* restore options */
99 #define INC_KW_READFIFO 9 /* Causes fifo data to be read */
100 #define INC_KW_PORTABLE 10
101 #define INC_KW_MTIMEONLY 11
102 #define INC_KW_KEEPATIME 12
104 /* Include keywords -- these are keywords that can appear
105 * in the options lists of an old include ( Include = compression= ...)
107 static struct s_kw FS_option_kw[] = {
108 {"compression", INC_KW_COMPRESSION},
109 {"signature", INC_KW_SIGNATURE},
110 {"encryption", INC_KW_ENCRYPTION},
111 {"verify", INC_KW_VERIFY},
112 {"onefs", INC_KW_ONEFS},
113 {"recurse", INC_KW_RECURSE},
114 {"sparse", INC_KW_SPARSE},
115 {"replace", INC_KW_REPLACE},
116 {"readfifo", INC_KW_READFIFO},
117 {"portable", INC_KW_PORTABLE},
118 {"mtimeonly", INC_KW_MTIMEONLY},
119 {"keepatime", INC_KW_KEEPATIME},
123 /* Options for FileSet keywords */
132 * Options permitted for each keyword and resulting value.
133 * The output goes into opts, which are then transmitted to
134 * the FD for application as options to the following list of
137 static struct s_fs_opt FS_options[] = {
138 {"md5", INC_KW_SIGNATURE, "M"},
139 {"sha1", INC_KW_SIGNATURE, "S"},
140 {"gzip", INC_KW_COMPRESSION, "Z6"},
141 {"gzip1", INC_KW_COMPRESSION, "Z1"},
142 {"gzip2", INC_KW_COMPRESSION, "Z2"},
143 {"gzip3", INC_KW_COMPRESSION, "Z3"},
144 {"gzip4", INC_KW_COMPRESSION, "Z4"},
145 {"gzip5", INC_KW_COMPRESSION, "Z5"},
146 {"gzip6", INC_KW_COMPRESSION, "Z6"},
147 {"gzip7", INC_KW_COMPRESSION, "Z7"},
148 {"gzip8", INC_KW_COMPRESSION, "Z8"},
149 {"gzip9", INC_KW_COMPRESSION, "Z9"},
150 {"blowfish", INC_KW_ENCRYPTION, "B"}, /* ***FIXME*** not implemented */
151 {"3des", INC_KW_ENCRYPTION, "3"}, /* ***FIXME*** not implemented */
152 {"yes", INC_KW_ONEFS, "0"},
153 {"no", INC_KW_ONEFS, "f"},
154 {"yes", INC_KW_RECURSE, "0"},
155 {"no", INC_KW_RECURSE, "h"},
156 {"yes", INC_KW_SPARSE, "s"},
157 {"no", INC_KW_SPARSE, "0"},
158 {"always", INC_KW_REPLACE, "a"},
159 {"ifnewer", INC_KW_REPLACE, "w"},
160 {"never", INC_KW_REPLACE, "n"},
161 {"yes", INC_KW_READFIFO, "r"},
162 {"no", INC_KW_READFIFO, "0"},
163 {"yes", INC_KW_PORTABLE, "p"},
164 {"no", INC_KW_PORTABLE, "0"},
165 {"yes", INC_KW_MTIMEONLY, "m"},
166 {"no", INC_KW_MTIMEONLY, "0"},
167 {"yes", INC_KW_KEEPATIME, "k"},
168 {"no", INC_KW_KEEPATIME, "0"},
175 * Scan for right hand side of Include options (keyword=option) is
176 * converted into one or two characters. Verifyopts=xxxx is Vxxxx:
177 * Whatever is found is concatenated to the opts string.
178 * This code is also used inside an Options resource.
180 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
185 option[0] = 0; /* default option = none */
186 option[2] = 0; /* terminate options */
187 token = lex_get_token(lc, T_NAME); /* expect at least one option */
188 if (keyword == INC_KW_VERIFY) { /* special case */
189 /* ***FIXME**** ensure these are in permitted set */
190 bstrncat(opts, "V", optlen); /* indicate Verify */
191 bstrncat(opts, lc->str, optlen);
192 bstrncat(opts, ":", optlen); /* terminate it */
193 Dmsg3(100, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
196 * Standard keyword options for Include/Exclude
199 for (i=0; FS_options[i].name; i++) {
200 if (strcasecmp(lc->str, FS_options[i].name) == 0 && FS_options[i].keyword == keyword) {
201 /* NOTE! maximum 2 letters here or increase option[3] */
202 option[0] = FS_options[i].option[0];
203 option[1] = FS_options[i].option[1];
209 scan_err1(lc, "Expected a FileSet option keyword, got:%s:", lc->str);
210 } else { /* add option */
211 bstrncat(opts, option, optlen);
212 Dmsg3(100, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
216 /* If option terminated by comma, eat it */
218 token = lex_get_token(lc, T_ALL); /* yes, eat comma */
224 * Store FileSet Include/Exclude info
226 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass)
229 int options = lc->options;
235 * Decide if we are doing a new Include or an old include. The
236 * new Include is followed immediately by {, whereas the
237 * old include has options following the Include.
239 token = lex_get_token(lc, T_ALL);
240 if (token == T_BOB) {
241 store_newinc(lc, item, index, pass);
244 if (token != T_EQUALS) {
245 scan_err1(lc, _("Expecting an equals sign, got: %s\n"), lc->str);
247 lc->options |= LOPT_NO_IDENT; /* make spaces significant */
248 memset(&res_incexe, 0, sizeof(INCEXE));
250 /* Get include options */
252 while ((token=lex_get_token(lc, T_ALL)) != T_BOB) {
254 keyword = INC_KW_NONE;
255 for (i=0; FS_option_kw[i].name; i++) {
256 if (strcasecmp(lc->str, FS_option_kw[i].name) == 0) {
257 keyword = FS_option_kw[i].token;
261 if (keyword == INC_KW_NONE) {
262 scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
264 /* Option keyword should be following by = <option> */
265 if ((token=lex_get_token(lc, T_ALL)) != T_EQUALS) {
266 scan_err1(lc, _("expected an = following keyword, got: %s"), lc->str);
268 /* Scan right hand side of option */
269 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
271 if (token == T_BOB) {
277 strcat(inc_opts, "0"); /* set no options */
279 inc_opts_len = strlen(inc_opts);
283 if (!res_all.res_fs.have_MD5) {
284 MD5Init(&res_all.res_fs.md5c);
285 res_all.res_fs.have_MD5 = TRUE;
287 setup_current_opts();
288 bstrncpy(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
289 Dmsg2(100, "old pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
291 /* Create incexe structure */
292 Dmsg0(200, "Create INCEXE structure\n");
293 incexe = (INCEXE *)malloc(sizeof(INCEXE));
294 memcpy(incexe, &res_incexe, sizeof(INCEXE));
295 memset(&res_incexe, 0, sizeof(INCEXE));
296 if (item->code == 0) { /* include */
297 if (res_all.res_fs.num_includes == 0) {
298 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
300 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
301 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
303 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
304 Dmsg1(200, "num_includes=%d\n", res_all.res_fs.num_includes);
305 } else { /* exclude */
306 if (res_all.res_fs.num_excludes == 0) {
307 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
309 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
310 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
312 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
313 Dmsg1(200, "num_excludes=%d\n", res_all.res_fs.num_excludes);
316 /* Pickup include/exclude names. They are stored in INCEXE
317 * structures which contains the options and the name.
319 while ((token = lex_get_token(lc, T_ALL)) != T_EOB) {
326 case T_UNQUOTED_STRING:
327 case T_QUOTED_STRING:
328 if (res_all.res_fs.have_MD5) {
329 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
331 if (incexe->name_list.size() == 0) {
332 incexe->name_list.init(10, true);
334 incexe->name_list.append(bstrdup(lc->str));
335 Dmsg1(200, "Add to name_list %s\n", lc->str);
338 scan_err1(lc, "Expected a filename, got: %s", lc->str);
341 /* Note, MD5Final is done in backup.c */
342 } else { /* pass 2 */
343 while (lex_get_token(lc, T_ALL) != T_EOB)
347 lc->options = options;
348 set_bit(index, res_all.hdr.item_present);
353 * Store FileSet FInclude/FExclude info
354 * Note, when this routine is called, we are inside a FileSet
355 * resource. We treat the Finclude/Fexeclude like a sort of
356 * mini-resource within the FileSet resource.
358 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
364 if (!res_all.res_fs.have_MD5) {
365 MD5Init(&res_all.res_fs.md5c);
366 res_all.res_fs.have_MD5 = TRUE;
368 memset(&res_incexe, 0, sizeof(INCEXE));
369 res_all.res_fs.new_include = TRUE;
370 while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
371 if (token == T_EOL) {
374 if (token == T_EOB) {
377 if (token != T_IDENTIFIER) {
378 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
380 for (i=0; newinc_items[i].name; i++) {
381 options = strcasecmp(lc->str, "options") == 0;
382 if (strcasecmp(newinc_items[i].name, lc->str) == 0) {
384 token = lex_get_token(lc, T_ALL);
385 if (token != T_EQUALS) {
386 scan_err1(lc, "expected an equals, got: %s", lc->str);
389 /* Call item handler */
390 newinc_items[i].handler(lc, &newinc_items[i], i, pass);
396 scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
400 incexe = (INCEXE *)malloc(sizeof(INCEXE));
401 memcpy(incexe, &res_incexe, sizeof(INCEXE));
402 memset(&res_incexe, 0, sizeof(INCEXE));
403 if (item->code == 0) { /* include */
404 if (res_all.res_fs.num_includes == 0) {
405 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
407 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
408 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
410 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
411 Dmsg1(200, "num_includes=%d\n", res_all.res_fs.num_includes);
412 } else { /* exclude */
413 if (res_all.res_fs.num_excludes == 0) {
414 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
416 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
417 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
419 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
420 Dmsg1(200, "num_excludes=%d\n", res_all.res_fs.num_excludes);
424 set_bit(index, res_all.hdr.item_present);
428 /* Store regex info */
429 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass)
434 /* Pickup regex string
436 token = lex_get_token(lc, T_ALL);
439 case T_UNQUOTED_STRING:
440 case T_QUOTED_STRING:
441 res_incexe.current_opts->regex.append(bstrdup(lc->str));
442 Dmsg3(200, "set regex %p size=%d %s\n",
443 res_incexe.current_opts, res_incexe.current_opts->regex.size(),lc->str);
446 scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
448 } else { /* pass 2 */
449 lex_get_token(lc, T_ALL);
454 /* Store Base info */
455 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
461 * Pickup Base Job Name
463 token = lex_get_token(lc, T_NAME);
464 res_incexe.current_opts->base.append(bstrdup(lc->str));
465 } else { /* pass 2 */
466 lex_get_token(lc, T_ALL);
472 /* Store Wild-card info */
473 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass)
479 * Pickup Wild-card string
481 token = lex_get_token(lc, T_ALL);
484 case T_UNQUOTED_STRING:
485 case T_QUOTED_STRING:
486 res_incexe.current_opts->wild.append(bstrdup(lc->str));
487 Dmsg3(200, "set wild %p size=%d %s\n",
488 res_incexe.current_opts, res_incexe.current_opts->wild.size(),lc->str);
491 scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
493 } else { /* pass 2 */
494 lex_get_token(lc, T_ALL);
501 * Store Filename info. Note, for minor efficiency reasons, we
502 * always increase the name buffer by 10 items because we expect
503 * to add more entries.
505 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass)
511 /* Pickup Filename string
513 token = lex_get_token(lc, T_ALL);
516 case T_UNQUOTED_STRING:
517 case T_QUOTED_STRING:
518 if (res_all.res_fs.have_MD5) {
519 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
521 incexe = &res_incexe;
522 if (incexe->name_list.size() == 0) {
523 incexe->name_list.init(10, true);
525 incexe->name_list.append(bstrdup(lc->str));
526 Dmsg1(200, "Add to name_list %s\n", lc->str);
529 scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
531 } else { /* pass 2 */
532 lex_get_token(lc, T_ALL);
538 * Come here when Options seen in Include/Exclude
540 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass)
544 token = lex_get_token(lc, T_ALL);
545 if (token != T_BOB) {
546 scan_err1(lc, "Expecting open brace. Got %s", lc->str);
550 setup_current_opts();
553 while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
554 if (token == T_EOL) {
557 if (token == T_EOB) {
560 if (token != T_IDENTIFIER) {
561 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
563 for (i=0; options_items[i].name; i++) {
564 if (strcasecmp(options_items[i].name, lc->str) == 0) {
565 token = lex_get_token(lc, T_ALL);
566 if (token != T_EQUALS) {
567 scan_err1(lc, "expected an equals, got: %s", lc->str);
569 /* Call item handler */
570 options_items[i].handler(lc, &options_items[i], i, pass);
576 scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
584 * New style options come here
586 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass)
593 keyword = INC_KW_NONE;
594 /* Look up the keyword */
595 for (i=0; FS_option_kw[i].name; i++) {
596 if (strcasecmp(item->name, FS_option_kw[i].name) == 0) {
597 keyword = FS_option_kw[i].token;
601 if (keyword == INC_KW_NONE) {
602 scan_err1(lc, "Expected a FileSet keyword, got: %s", lc->str);
604 /* Now scan for the value */
605 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
607 bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
608 Dmsg2(100, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
615 /* If current_opts not defined, create first entry */
616 static void setup_current_opts(void)
618 FOPTS *fo = (FOPTS *)malloc(sizeof(FOPTS));
619 memset(fo, 0, sizeof(FOPTS));
620 fo->regex.init(1, true);
621 fo->wild.init(1, true);
622 fo->base.init(1, true);
623 res_incexe.current_opts = fo;
624 if (res_incexe.num_opts == 0) {
625 res_incexe.opts_list = (FOPTS **)malloc(sizeof(FOPTS *));
627 res_incexe.opts_list = (FOPTS **)realloc(res_incexe.opts_list,
628 sizeof(FOPTS *) * (res_incexe.num_opts + 1));
630 res_incexe.opts_list[res_incexe.num_opts++] = fo;