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_match(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 setup_current_opts(void);
45 /* We build the current resource here as we are
46 * scanning the resource configuration definition,
47 * then move it to allocated memory when the resource
51 extern int res_all_size;
53 /* We build the current new Include and Exclude items here */
54 static INCEXE res_incexe;
57 * new Include/Exclude items
58 * name handler value code flags default_value
60 static RES_ITEM newinc_items[] = {
61 {"file", store_fname, NULL, 0, 0, 0},
62 {"options", options_res, NULL, 0, 0, 0},
63 {NULL, NULL, NULL, 0, 0, 0}
67 * Items that are valid in an Options resource
69 static RES_ITEM options_items[] = {
70 {"compression", store_opts, NULL, 0, 0, 0},
71 {"signature", store_opts, NULL, 0, 0, 0},
72 {"verify", store_opts, NULL, 0, 0, 0},
73 {"onefs", store_opts, NULL, 0, 0, 0},
74 {"recurse", store_opts, NULL, 0, 0, 0},
75 {"sparse", store_opts, NULL, 0, 0, 0},
76 {"readfifo", store_opts, NULL, 0, 0, 0},
77 {"replace", store_opts, NULL, 0, 0, 0},
78 {"portable", store_opts, NULL, 0, 0, 0},
79 {"match", store_match, NULL, 0, 0, 0},
80 {"base", store_base, NULL, 0, 0, 0},
81 {NULL, NULL, NULL, 0, 0, 0}
85 /* Define FileSet KeyWord values */
88 #define INC_KW_COMPRESSION 1
89 #define INC_KW_SIGNATURE 2
90 #define INC_KW_ENCRYPTION 3
91 #define INC_KW_VERIFY 4
92 #define INC_KW_ONEFS 5
93 #define INC_KW_RECURSE 6
94 #define INC_KW_SPARSE 7
95 #define INC_KW_REPLACE 8 /* restore options */
96 #define INC_KW_READFIFO 9 /* Causes fifo data to be read */
97 #define INC_KW_PORTABLE 10
98 #define INC_KW_MTIMEONLY 11
99 #define INC_KW_KEEPATIME 12
101 /* Include keywords -- these are keywords that can appear
102 * in the options lists of an old include ( Include = compression= ...)
104 static struct s_kw FS_option_kw[] = {
105 {"compression", INC_KW_COMPRESSION},
106 {"signature", INC_KW_SIGNATURE},
107 {"encryption", INC_KW_ENCRYPTION},
108 {"verify", INC_KW_VERIFY},
109 {"onefs", INC_KW_ONEFS},
110 {"recurse", INC_KW_RECURSE},
111 {"sparse", INC_KW_SPARSE},
112 {"replace", INC_KW_REPLACE},
113 {"readfifo", INC_KW_READFIFO},
114 {"portable", INC_KW_PORTABLE},
115 {"mtimeonly", INC_KW_MTIMEONLY},
116 {"keepatime", INC_KW_KEEPATIME},
120 /* Options for FileSet keywords */
129 * Options permitted for each keyword and resulting value.
130 * The output goes into opts, which are then transmitted to
131 * the FD for application as options to the following list of
134 static struct s_fs_opt FS_options[] = {
135 {"md5", INC_KW_SIGNATURE, "M"},
136 {"sha1", INC_KW_SIGNATURE, "S"},
137 {"gzip", INC_KW_COMPRESSION, "Z6"},
138 {"gzip1", INC_KW_COMPRESSION, "Z1"},
139 {"gzip2", INC_KW_COMPRESSION, "Z2"},
140 {"gzip3", INC_KW_COMPRESSION, "Z3"},
141 {"gzip4", INC_KW_COMPRESSION, "Z4"},
142 {"gzip5", INC_KW_COMPRESSION, "Z5"},
143 {"gzip6", INC_KW_COMPRESSION, "Z6"},
144 {"gzip7", INC_KW_COMPRESSION, "Z7"},
145 {"gzip8", INC_KW_COMPRESSION, "Z8"},
146 {"gzip9", INC_KW_COMPRESSION, "Z9"},
147 {"blowfish", INC_KW_ENCRYPTION, "B"}, /* ***FIXME*** not implemented */
148 {"3des", INC_KW_ENCRYPTION, "3"}, /* ***FIXME*** not implemented */
149 {"yes", INC_KW_ONEFS, "0"},
150 {"no", INC_KW_ONEFS, "f"},
151 {"yes", INC_KW_RECURSE, "0"},
152 {"no", INC_KW_RECURSE, "h"},
153 {"yes", INC_KW_SPARSE, "s"},
154 {"no", INC_KW_SPARSE, "0"},
155 {"always", INC_KW_REPLACE, "a"},
156 {"ifnewer", INC_KW_REPLACE, "w"},
157 {"never", INC_KW_REPLACE, "n"},
158 {"yes", INC_KW_READFIFO, "r"},
159 {"no", INC_KW_READFIFO, "0"},
160 {"yes", INC_KW_PORTABLE, "p"},
161 {"no", INC_KW_PORTABLE, "0"},
162 {"yes", INC_KW_MTIMEONLY, "m"},
163 {"no", INC_KW_MTIMEONLY, "0"},
164 {"yes", INC_KW_KEEPATIME, "k"},
165 {"no", INC_KW_KEEPATIME, "0"},
172 * Scan for right hand side of Include options (keyword=option) is
173 * converted into one or two characters. Verifyopts=xxxx is Vxxxx:
174 * Whatever is found is concatenated to the opts string.
175 * This code is also used inside an Options resource.
177 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
182 option[0] = 0; /* default option = none */
183 option[2] = 0; /* terminate options */
184 token = lex_get_token(lc, T_NAME); /* expect at least one option */
185 if (keyword == INC_KW_VERIFY) { /* special case */
186 /* ***FIXME**** ensure these are in permitted set */
187 bstrncat(opts, "V", optlen); /* indicate Verify */
188 bstrncat(opts, lc->str, optlen);
189 bstrncat(opts, ":", optlen); /* terminate it */
190 Dmsg3(100, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
193 * Standard keyword options for Include/Exclude
196 for (i=0; FS_options[i].name; i++) {
197 if (strcasecmp(lc->str, FS_options[i].name) == 0 && FS_options[i].keyword == keyword) {
198 /* NOTE! maximum 2 letters here or increase option[3] */
199 option[0] = FS_options[i].option[0];
200 option[1] = FS_options[i].option[1];
206 scan_err1(lc, "Expected a FileSet option keyword, got:%s:", lc->str);
207 } else { /* add option */
208 bstrncat(opts, option, optlen);
209 Dmsg3(100, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
213 /* If option terminated by comma, eat it */
215 token = lex_get_token(lc, T_ALL); /* yes, eat comma */
221 * Store FileSet Include/Exclude info
223 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass)
226 int options = lc->options;
232 * Decide if we are doing a new Include or an old include. The
233 * new Include is followed immediately by {, whereas the
234 * old include has options following the Include.
236 token = lex_get_token(lc, T_ALL);
237 if (token == T_BOB) {
238 store_newinc(lc, item, index, pass);
241 if (token != T_EQUALS) {
242 scan_err1(lc, _("Expecting an equals sign, got: %s\n"), lc->str);
244 lc->options |= LOPT_NO_IDENT; /* make spaces significant */
245 memset(&res_incexe, 0, sizeof(INCEXE));
247 /* Get include options */
249 while ((token=lex_get_token(lc, T_ALL)) != T_BOB) {
251 keyword = INC_KW_NONE;
252 for (i=0; FS_option_kw[i].name; i++) {
253 if (strcasecmp(lc->str, FS_option_kw[i].name) == 0) {
254 keyword = FS_option_kw[i].token;
258 if (keyword == INC_KW_NONE) {
259 scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
261 /* Option keyword should be following by = <option> */
262 if ((token=lex_get_token(lc, T_ALL)) != T_EQUALS) {
263 scan_err1(lc, _("expected an = following keyword, got: %s"), lc->str);
265 /* Scan right hand side of option */
266 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
268 if (token == T_BOB) {
274 strcat(inc_opts, "0"); /* set no options */
276 inc_opts_len = strlen(inc_opts);
280 if (!res_all.res_fs.have_MD5) {
281 MD5Init(&res_all.res_fs.md5c);
282 res_all.res_fs.have_MD5 = TRUE;
284 setup_current_opts();
285 bstrncpy(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
286 Dmsg2(100, "old pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
288 /* Create incexe structure */
289 Dmsg0(200, "Create INCEXE structure\n");
290 incexe = (INCEXE *)malloc(sizeof(INCEXE));
291 memcpy(incexe, &res_incexe, sizeof(INCEXE));
292 memset(&res_incexe, 0, sizeof(INCEXE));
293 if (item->code == 0) { /* include */
294 if (res_all.res_fs.num_includes == 0) {
295 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
297 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
298 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
300 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
301 Dmsg1(200, "num_includes=%d\n", res_all.res_fs.num_includes);
302 } else { /* exclude */
303 if (res_all.res_fs.num_excludes == 0) {
304 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
306 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
307 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
309 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
310 Dmsg1(200, "num_excludes=%d\n", res_all.res_fs.num_excludes);
313 /* Pickup include/exclude names. They are stored in INCEXE
314 * structures which contains the options and the name.
316 while ((token = lex_get_token(lc, T_ALL)) != T_EOB) {
323 case T_UNQUOTED_STRING:
324 case T_QUOTED_STRING:
325 if (res_all.res_fs.have_MD5) {
326 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
328 if (incexe->name_list.size() == 0) {
329 incexe->name_list.init(10, true);
331 incexe->name_list.append(bstrdup(lc->str));
332 Dmsg1(200, "Add to name_list %s\n", lc->str);
335 scan_err1(lc, "Expected a filename, got: %s", lc->str);
338 /* Note, MD5Final is done in backup.c */
339 } else { /* pass 2 */
340 while (lex_get_token(lc, T_ALL) != T_EOB)
344 lc->options = options;
345 set_bit(index, res_all.hdr.item_present);
350 * Store FileSet FInclude/FExclude info
351 * Note, when this routine is called, we are inside a FileSet
352 * resource. We treat the Finclude/Fexeclude like a sort of
353 * mini-resource within the FileSet resource.
355 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
361 if (!res_all.res_fs.have_MD5) {
362 MD5Init(&res_all.res_fs.md5c);
363 res_all.res_fs.have_MD5 = TRUE;
365 memset(&res_incexe, 0, sizeof(INCEXE));
366 res_all.res_fs.new_include = TRUE;
367 while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
368 if (token == T_EOL) {
371 if (token == T_EOB) {
374 if (token != T_IDENTIFIER) {
375 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
377 for (i=0; newinc_items[i].name; i++) {
378 options = strcasecmp(lc->str, "options") == 0;
379 if (strcasecmp(newinc_items[i].name, lc->str) == 0) {
381 token = lex_get_token(lc, T_ALL);
382 if (token != T_EQUALS) {
383 scan_err1(lc, "expected an equals, got: %s", lc->str);
386 /* Call item handler */
387 newinc_items[i].handler(lc, &newinc_items[i], i, pass);
393 scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
397 incexe = (INCEXE *)malloc(sizeof(INCEXE));
398 memcpy(incexe, &res_incexe, sizeof(INCEXE));
399 memset(&res_incexe, 0, sizeof(INCEXE));
400 if (item->code == 0) { /* include */
401 if (res_all.res_fs.num_includes == 0) {
402 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
404 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
405 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
407 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
408 Dmsg1(200, "num_includes=%d\n", res_all.res_fs.num_includes);
409 } else { /* exclude */
410 if (res_all.res_fs.num_excludes == 0) {
411 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
413 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
414 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
416 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
417 Dmsg1(200, "num_excludes=%d\n", res_all.res_fs.num_excludes);
421 set_bit(index, res_all.hdr.item_present);
425 /* Store Match info */
426 static void store_match(LEX *lc, RES_ITEM *item, int index, int pass)
431 /* Pickup Match string
433 token = lex_get_token(lc, T_ALL);
436 case T_UNQUOTED_STRING:
437 case T_QUOTED_STRING:
438 setup_current_opts();
439 res_incexe.current_opts->match.append(bstrdup(lc->str));
442 scan_err1(lc, _("Expected a filename, got: %s\n"), lc->str);
444 } else { /* pass 2 */
445 lex_get_token(lc, T_ALL);
450 /* Store Base info */
451 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
456 setup_current_opts();
458 * Pickup Base Job Name
460 token = lex_get_token(lc, T_NAME);
461 res_incexe.current_opts->base_list.append(bstrdup(lc->str));
462 } else { /* pass 2 */
463 lex_get_token(lc, T_ALL);
468 * Store Filename info. Note, for minor efficiency reasons, we
469 * always increase the name buffer by 10 items because we expect
470 * to add more entries.
472 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass)
478 /* Pickup Filename string
480 token = lex_get_token(lc, T_ALL);
483 case T_UNQUOTED_STRING:
484 case T_QUOTED_STRING:
485 if (res_all.res_fs.have_MD5) {
486 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
488 incexe = &res_incexe;
489 if (incexe->name_list.size() == 0) {
490 incexe->name_list.init(10, true);
492 incexe->name_list.append(bstrdup(lc->str));
493 Dmsg1(200, "Add to name_list %s\n", lc->str);
496 scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
498 } else { /* pass 2 */
499 lex_get_token(lc, T_ALL);
505 * Come here when Options seen in Include/Exclude
507 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass)
511 token = lex_get_token(lc, T_ALL);
512 if (token != T_BOB) {
513 scan_err1(lc, "Expecting open brace. Got %s", lc->str);
516 while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
517 if (token == T_EOL) {
520 if (token == T_EOB) {
523 if (token != T_IDENTIFIER) {
524 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
526 for (i=0; options_items[i].name; i++) {
527 if (strcasecmp(options_items[i].name, lc->str) == 0) {
528 token = lex_get_token(lc, T_ALL);
529 if (token != T_EQUALS) {
530 scan_err1(lc, "expected an equals, got: %s", lc->str);
532 /* Call item handler */
533 options_items[i].handler(lc, &options_items[i], i, pass);
539 scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
547 * New style options come here
549 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass)
556 keyword = INC_KW_NONE;
557 /* Look up the keyword */
558 for (i=0; FS_option_kw[i].name; i++) {
559 if (strcasecmp(item->name, FS_option_kw[i].name) == 0) {
560 keyword = FS_option_kw[i].token;
564 if (keyword == INC_KW_NONE) {
565 scan_err1(lc, "Expected a FileSet keyword, got: %s", lc->str);
567 /* Now scan for the value */
568 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
570 setup_current_opts();
571 bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
572 Dmsg2(100, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
579 /* If current_opts not defined, create first entry */
580 static void setup_current_opts(void)
582 if (res_incexe.current_opts == NULL) {
583 res_incexe.current_opts = (FOPTS *)malloc(sizeof(FOPTS));
584 memset(res_incexe.current_opts, 0, sizeof(FOPTS));
585 res_incexe.current_opts->match.init(1, true);
586 res_incexe.current_opts->base_list.init(1, true);
587 res_incexe.num_opts = 1;
588 res_incexe.opts_list = (FOPTS **)malloc(sizeof(FOPTS *));
589 res_incexe.opts_list[0] = res_incexe.current_opts;