2 * Configuration file parser for new and old Include and
5 * Kern Sibbald, March MMIII
10 Copyright (C) 2000-2003 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 store_base(LEX *lc, RES_ITEM *item, int index, int pass);
41 static void setup_current_opts(void);
44 /* We build the current resource here as we are
45 * scanning the resource configuration definition,
46 * then move it to allocated memory when the resource
50 extern int res_all_size;
52 /* We build the current new Include and Exclude items here */
53 static INCEXE res_incexe;
56 * new Include/Exclude items
57 * name handler value code flags default_value
59 static RES_ITEM newinc_items[] = {
60 {"compression", store_opts, NULL, 0, 0, 0},
61 {"signature", store_opts, NULL, 0, 0, 0},
62 {"verify", store_opts, NULL, 0, 0, 0},
63 {"onefs", store_opts, NULL, 0, 0, 0},
64 {"recurse", store_opts, NULL, 0, 0, 0},
65 {"sparse", store_opts, NULL, 0, 0, 0},
66 {"readfifo", store_opts, NULL, 0, 0, 0},
67 {"replace", store_opts, NULL, 0, 0, 0},
68 {"portable", store_opts, NULL, 0, 0, 0},
69 {"match", store_match, NULL, 0, 0, 0},
70 {"file", store_fname, NULL, 0, 0, 0},
71 {"base", store_base, NULL, 0, 0, 0},
72 {NULL, NULL, NULL, 0, 0, 0}
75 /* Define FileSet KeyWord values */
78 #define INC_KW_COMPRESSION 1
79 #define INC_KW_SIGNATURE 2
80 #define INC_KW_ENCRYPTION 3
81 #define INC_KW_VERIFY 4
82 #define INC_KW_ONEFS 5
83 #define INC_KW_RECURSE 6
84 #define INC_KW_SPARSE 7
85 #define INC_KW_REPLACE 8 /* restore options */
86 #define INC_KW_READFIFO 9 /* Causes fifo data to be read */
87 #define INC_KW_PORTABLE 10
88 #define INC_KW_MTIMEONLY 11
89 #define INC_KW_KEEPATIME 12
91 /* Include keywords -- these are keywords that can appear
92 * in the options lists of an old include ( Include = compression= ...)
94 static struct s_kw FS_option_kw[] = {
95 {"compression", INC_KW_COMPRESSION},
96 {"signature", INC_KW_SIGNATURE},
97 {"encryption", INC_KW_ENCRYPTION},
98 {"verify", INC_KW_VERIFY},
99 {"onefs", INC_KW_ONEFS},
100 {"recurse", INC_KW_RECURSE},
101 {"sparse", INC_KW_SPARSE},
102 {"replace", INC_KW_REPLACE},
103 {"readfifo", INC_KW_READFIFO},
104 {"portable", INC_KW_PORTABLE},
105 {"mtimeonly", INC_KW_MTIMEONLY},
106 {"keepatime", INC_KW_KEEPATIME},
110 /* Options for FileSet keywords */
119 * Options permitted for each keyword and resulting value.
120 * The output goes into opts, which are then transmitted to
121 * the FD for application as options to the following list of
124 static struct s_fs_opt FS_options[] = {
125 {"md5", INC_KW_SIGNATURE, "M"},
126 {"sha1", INC_KW_SIGNATURE, "S"},
127 {"gzip", INC_KW_COMPRESSION, "Z6"},
128 {"gzip1", INC_KW_COMPRESSION, "Z1"},
129 {"gzip2", INC_KW_COMPRESSION, "Z2"},
130 {"gzip3", INC_KW_COMPRESSION, "Z3"},
131 {"gzip4", INC_KW_COMPRESSION, "Z4"},
132 {"gzip5", INC_KW_COMPRESSION, "Z5"},
133 {"gzip6", INC_KW_COMPRESSION, "Z6"},
134 {"gzip7", INC_KW_COMPRESSION, "Z7"},
135 {"gzip8", INC_KW_COMPRESSION, "Z8"},
136 {"gzip9", INC_KW_COMPRESSION, "Z9"},
137 {"blowfish", INC_KW_ENCRYPTION, "B"}, /* ***FIXME*** not implemented */
138 {"3des", INC_KW_ENCRYPTION, "3"}, /* ***FIXME*** not implemented */
139 {"yes", INC_KW_ONEFS, "0"},
140 {"no", INC_KW_ONEFS, "f"},
141 {"yes", INC_KW_RECURSE, "0"},
142 {"no", INC_KW_RECURSE, "h"},
143 {"yes", INC_KW_SPARSE, "s"},
144 {"no", INC_KW_SPARSE, "0"},
145 {"always", INC_KW_REPLACE, "a"},
146 {"ifnewer", INC_KW_REPLACE, "w"},
147 {"never", INC_KW_REPLACE, "n"},
148 {"yes", INC_KW_READFIFO, "r"},
149 {"no", INC_KW_READFIFO, "0"},
150 {"yes", INC_KW_PORTABLE, "p"},
151 {"no", INC_KW_PORTABLE, "0"},
152 {"yes", INC_KW_MTIMEONLY, "m"},
153 {"no", INC_KW_MTIMEONLY, "0"},
154 {"yes", INC_KW_KEEPATIME, "k"},
155 {"no", INC_KW_KEEPATIME, "0"},
162 * Scan for right hand side of Include options (keyword=option) is
163 * converted into one or two characters. Verifyopts=xxxx is Vxxxx:
164 * Whatever is found is concatenated to the opts string.
166 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
171 option[0] = 0; /* default option = none */
172 option[2] = 0; /* terminate options */
173 token = lex_get_token(lc, T_NAME); /* expect at least one option */
174 if (keyword == INC_KW_VERIFY) { /* special case */
175 /* ***FIXME**** ensure these are in permitted set */
176 bstrncat(opts, "V", optlen); /* indicate Verify */
177 bstrncat(opts, lc->str, optlen);
178 bstrncat(opts, ":", optlen); /* terminate it */
179 Dmsg3(100, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
182 * Standard keyword options for Include/Exclude
185 for (i=0; FS_options[i].name; i++) {
186 if (strcasecmp(lc->str, FS_options[i].name) == 0 && FS_options[i].keyword == keyword) {
187 /* NOTE! maximum 2 letters here or increase option[3] */
188 option[0] = FS_options[i].option[0];
189 option[1] = FS_options[i].option[1];
195 scan_err1(lc, "Expected a FileSet option keyword, got:%s:", lc->str);
196 } else { /* add option */
197 bstrncat(opts, option, optlen);
198 Dmsg3(100, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
202 /* If option terminated by comma, eat it */
204 token = lex_get_token(lc, T_ALL); /* yes, eat comma */
208 /* Store FileSet Include/Exclude info */
209 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass)
212 int options = lc->options;
218 * Decide if we are doing a new Include or an old include. The
219 * new Include is followed immediately by {, whereas the
220 * old include has options following the Include.
222 token = lex_get_token(lc, T_ALL);
223 if (token == T_BOB) {
224 store_newinc(lc, item, index, pass);
227 if (token != T_EQUALS) {
228 scan_err1(lc, _("Expecting an equals sign, got: %s\n"), lc->str);
230 lc->options |= LOPT_NO_IDENT; /* make spaces significant */
231 memset(&res_incexe, 0, sizeof(INCEXE));
233 /* Get include options */
235 while ((token=lex_get_token(lc, T_ALL)) != T_BOB) {
237 keyword = INC_KW_NONE;
238 for (i=0; FS_option_kw[i].name; i++) {
239 if (strcasecmp(lc->str, FS_option_kw[i].name) == 0) {
240 keyword = FS_option_kw[i].token;
244 if (keyword == INC_KW_NONE) {
245 scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
247 /* Option keyword should be following by = <option> */
248 if ((token=lex_get_token(lc, T_ALL)) != T_EQUALS) {
249 scan_err1(lc, _("expected an = following keyword, got: %s"), lc->str);
251 /* Scan right hand side of option */
252 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
254 if (token == T_BOB) {
260 strcat(inc_opts, "0"); /* set no options */
262 inc_opts_len = strlen(inc_opts);
266 if (!res_all.res_fs.have_MD5) {
267 MD5Init(&res_all.res_fs.md5c);
268 res_all.res_fs.have_MD5 = TRUE;
270 setup_current_opts();
271 bstrncpy(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
272 Dmsg2(100, "old pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
274 /* Create incexe structure */
275 Dmsg0(200, "Create INCEXE structure\n");
276 incexe = (INCEXE *)malloc(sizeof(INCEXE));
277 memcpy(incexe, &res_incexe, sizeof(INCEXE));
278 memset(&res_incexe, 0, sizeof(INCEXE));
279 if (item->code == 0) { /* include */
280 if (res_all.res_fs.num_includes == 0) {
281 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
283 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
284 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
286 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
287 Dmsg1(200, "num_includes=%d\n", res_all.res_fs.num_includes);
288 } else { /* exclude */
289 if (res_all.res_fs.num_excludes == 0) {
290 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
292 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
293 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
295 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
296 Dmsg1(200, "num_excludes=%d\n", res_all.res_fs.num_excludes);
299 /* Pickup include/exclude names. They are stored in INCEXE
300 * structures which contains the options and the name.
302 while ((token = lex_get_token(lc, T_ALL)) != T_EOB) {
309 case T_UNQUOTED_STRING:
310 case T_QUOTED_STRING:
311 if (res_all.res_fs.have_MD5) {
312 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
314 if (incexe->name_list.size() == 0) {
315 incexe->name_list.init(10, true);
317 incexe->name_list.append(bstrdup(lc->str));
318 Dmsg1(200, "Add to name_list %s\n", lc->str);
321 scan_err1(lc, "Expected a filename, got: %s", lc->str);
324 /* Note, MD5Final is done in backup.c */
325 } else { /* pass 2 */
326 while (lex_get_token(lc, T_ALL) != T_EOB)
330 lc->options = options;
331 set_bit(index, res_all.hdr.item_present);
336 * Store FileSet FInclude/FExclude info
337 * Note, when this routine is called, we are inside a FileSet
338 * resource. We treat the Finclude/Fexeclude like a sort of
339 * mini-resource within the FileSet resource.
341 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
346 if (!res_all.res_fs.have_MD5) {
347 MD5Init(&res_all.res_fs.md5c);
348 res_all.res_fs.have_MD5 = TRUE;
350 memset(&res_incexe, 0, sizeof(INCEXE));
351 res_all.res_fs.new_include = TRUE;
352 while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
353 if (token == T_EOL) {
356 if (token == T_EOB) {
359 if (token != T_IDENTIFIER) {
360 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
362 for (i=0; newinc_items[i].name; i++) {
363 if (strcasecmp(newinc_items[i].name, lc->str) == 0) {
364 token = lex_get_token(lc, T_ALL);
365 if (token != T_EQUALS) {
366 scan_err1(lc, "expected an equals, got: %s", lc->str);
368 /* Call item handler */
369 newinc_items[i].handler(lc, &newinc_items[i], i, pass);
375 scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
379 incexe = (INCEXE *)malloc(sizeof(INCEXE));
380 memcpy(incexe, &res_incexe, sizeof(INCEXE));
381 memset(&res_incexe, 0, sizeof(INCEXE));
382 if (item->code == 0) { /* include */
383 if (res_all.res_fs.num_includes == 0) {
384 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
386 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
387 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
389 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
390 Dmsg1(200, "num_includes=%d\n", res_all.res_fs.num_includes);
391 } else { /* exclude */
392 if (res_all.res_fs.num_excludes == 0) {
393 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
395 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
396 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
398 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
399 Dmsg1(200, "num_excludes=%d\n", res_all.res_fs.num_excludes);
403 set_bit(index, res_all.hdr.item_present);
407 /* Store Match info */
408 static void store_match(LEX *lc, RES_ITEM *item, int index, int pass)
413 /* Pickup Match string
415 token = lex_get_token(lc, T_ALL);
418 case T_UNQUOTED_STRING:
419 case T_QUOTED_STRING:
420 setup_current_opts();
421 res_incexe.current_opts->match.append(bstrdup(lc->str));
424 scan_err1(lc, _("Expected a filename, got: %s\n"), lc->str);
426 } else { /* pass 2 */
427 lex_get_token(lc, T_ALL);
432 /* Store Base info */
433 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
438 setup_current_opts();
440 * Pickup Base Job Name
442 token = lex_get_token(lc, T_NAME);
443 res_incexe.current_opts->base_list.append(bstrdup(lc->str));
444 } else { /* pass 2 */
445 lex_get_token(lc, T_ALL);
450 * Store Filename info. Note, for minor efficiency reasons, we
451 * always increase the name buffer by 10 items because we expect
452 * to add more entries.
454 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass)
460 /* Pickup Filename string
462 token = lex_get_token(lc, T_ALL);
465 case T_UNQUOTED_STRING:
466 case T_QUOTED_STRING:
467 if (res_all.res_fs.have_MD5) {
468 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
470 incexe = &res_incexe;
471 if (incexe->name_list.size() == 0) {
472 incexe->name_list.init(10, true);
474 incexe->name_list.append(bstrdup(lc->str));
475 Dmsg1(200, "Add to name_list %s\n", lc->str);
478 scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
480 } else { /* pass 2 */
481 lex_get_token(lc, T_ALL);
487 * New style options come here
489 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass)
496 keyword = INC_KW_NONE;
497 /* Look up the keyword */
498 for (i=0; FS_option_kw[i].name; i++) {
499 if (strcasecmp(item->name, FS_option_kw[i].name) == 0) {
500 keyword = FS_option_kw[i].token;
504 if (keyword == INC_KW_NONE) {
505 scan_err1(lc, "Expected a FileSet keyword, got: %s", lc->str);
507 /* Now scan for the value */
508 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
510 setup_current_opts();
511 bstrncpy(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
512 Dmsg2(100, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
519 /* If current_opts not defined, create first entry */
520 static void setup_current_opts(void)
522 if (res_incexe.current_opts == NULL) {
523 res_incexe.current_opts = (FOPTS *)malloc(sizeof(FOPTS));
524 memset(res_incexe.current_opts, 0, sizeof(FOPTS));
525 res_incexe.current_opts->match.init(1, true);
526 res_incexe.current_opts->base_list.init(1, true);
527 res_incexe.num_opts = 1;
528 res_incexe.opts_list = (FOPTS **)malloc(sizeof(FOPTS *));
529 res_incexe.opts_list[0] = res_incexe.current_opts;