2 * Configuration file parser for Include, Exclude, Finclude
3 * and FExclude records.
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, struct res_items *item, int index, int pass);
35 void store_finc(LEX *lc, struct res_items *item, int index, int pass);
37 static void store_match(LEX *lc, struct res_items *item, int index, int pass);
38 static void store_opts(LEX *lc, struct res_items *item, int index, int pass);
39 static void store_fname(LEX *lc, struct res_items *item, int index, int pass);
40 static void store_base(LEX *lc, struct res_items *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 Finclude Fexclude item here */
53 static INCEXE res_incexe;
56 * FInclude/FExclude items
57 * name handler value code flags default_value
59 static struct res_items finc_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 {"match", store_match, NULL, 0, 0, 0},
69 {"file", store_fname, NULL, 0, 0, 0},
70 {"base", store_base, NULL, 0, 0, 0},
71 {NULL, NULL, NULL, 0, 0, 0}
74 /* Define FileSet KeyWord values */
77 #define INC_KW_COMPRESSION 1
78 #define INC_KW_SIGNATURE 2
79 #define INC_KW_ENCRYPTION 3
80 #define INC_KW_VERIFY 4
81 #define INC_KW_ONEFS 5
82 #define INC_KW_RECURSE 6
83 #define INC_KW_SPARSE 7
84 #define INC_KW_REPLACE 8 /* restore options */
85 #define INC_KW_READFIFO 9 /* Causes fifo data to be read */
87 /* Include keywords -- these are keywords that can appear
88 * in the options lists of an include ( Include = compression= ...)
90 static struct s_kw FS_option_kw[] = {
91 {"compression", INC_KW_COMPRESSION},
92 {"signature", INC_KW_SIGNATURE},
93 {"encryption", INC_KW_ENCRYPTION},
94 {"verify", INC_KW_VERIFY},
95 {"onefs", INC_KW_ONEFS},
96 {"recurse", INC_KW_RECURSE},
97 {"sparse", INC_KW_SPARSE},
98 {"replace", INC_KW_REPLACE},
99 {"readfifo", INC_KW_READFIFO},
103 /* Options for FileSet keywords */
112 * Options permitted for each keyword and resulting value.
113 * The output goes into opts, which are then transmitted to
114 * the FD for application as options to the following list of
117 static struct s_fs_opt FS_options[] = {
118 {"md5", INC_KW_SIGNATURE, "M"},
119 {"sha1", INC_KW_SIGNATURE, "S"},
120 {"gzip", INC_KW_COMPRESSION, "Z6"},
121 {"gzip1", INC_KW_COMPRESSION, "Z1"},
122 {"gzip2", INC_KW_COMPRESSION, "Z2"},
123 {"gzip3", INC_KW_COMPRESSION, "Z3"},
124 {"gzip4", INC_KW_COMPRESSION, "Z4"},
125 {"gzip5", INC_KW_COMPRESSION, "Z5"},
126 {"gzip6", INC_KW_COMPRESSION, "Z6"},
127 {"gzip7", INC_KW_COMPRESSION, "Z7"},
128 {"gzip8", INC_KW_COMPRESSION, "Z8"},
129 {"gzip9", INC_KW_COMPRESSION, "Z9"},
130 {"blowfish", INC_KW_ENCRYPTION, "B"}, /* ***FIXME*** not implemented */
131 {"3des", INC_KW_ENCRYPTION, "3"}, /* ***FIXME*** not implemented */
132 {"yes", INC_KW_ONEFS, "0"},
133 {"no", INC_KW_ONEFS, "f"},
134 {"yes", INC_KW_RECURSE, "0"},
135 {"no", INC_KW_RECURSE, "h"},
136 {"yes", INC_KW_SPARSE, "s"},
137 {"no", INC_KW_SPARSE, "0"},
138 {"always", INC_KW_REPLACE, "a"},
139 {"ifnewer", INC_KW_REPLACE, "w"},
140 {"never", INC_KW_REPLACE, "n"},
141 {"yes", INC_KW_READFIFO, "r"},
142 {"no", INC_KW_READFIFO, "0"},
149 * Scan for Include options (keyword=option) is converted into one or
150 * two characters. Verifyopts=xxxx is Vxxxx:
152 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
157 option[0] = 0; /* default option = none */
158 option[2] = 0; /* terminate options */
159 token = lex_get_token(lc, T_NAME); /* expect at least one option */
160 if (keyword == INC_KW_VERIFY) { /* special case */
161 /* ***FIXME**** ensure these are in permitted set */
162 bstrncat(opts, "V", optlen); /* indicate Verify */
163 bstrncat(opts, lc->str, optlen);
164 bstrncat(opts, ":", optlen); /* terminate it */
167 * Standard keyword options for Include/Exclude
170 for (i=0; FS_options[i].name; i++) {
171 if (strcasecmp(lc->str, FS_options[i].name) == 0 && FS_options[i].keyword == keyword) {
172 /* NOTE! maximum 2 letters here or increase option[3] */
173 option[0] = FS_options[i].option[0];
174 option[1] = FS_options[i].option[1];
180 scan_err1(lc, "Expected a FileSet option keyword, got:%s:", lc->str);
181 } else { /* add option */
182 bstrncat(opts, option, optlen);
183 Dmsg3(200, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
187 /* If option terminated by comma, eat it */
189 token = lex_get_token(lc, T_ALL); /* yes, eat comma */
193 /* Store FileSet Include/Exclude info */
194 void store_inc(LEX *lc, struct res_items *item, int index, int pass)
197 int options = lc->options;
202 lc->options |= LOPT_NO_IDENT; /* make spaces significant */
203 memset(&res_incexe, 0, sizeof(INCEXE));
205 /* Get include options */
207 while ((token=lex_get_token(lc, T_ALL)) != T_BOB) {
208 keyword = INC_KW_NONE;
209 for (i=0; FS_option_kw[i].name; i++) {
210 if (strcasecmp(lc->str, FS_option_kw[i].name) == 0) {
211 keyword = FS_option_kw[i].token;
215 if (keyword == INC_KW_NONE) {
216 scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
218 /* Option keyword should be following by = <option> */
219 if ((token=lex_get_token(lc, T_ALL)) != T_EQUALS) {
220 scan_err1(lc, _("expected an = following keyword, got: %s"), lc->str);
222 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
224 if (token == T_BOB) {
229 strcat(inc_opts, "0"); /* set no options */
231 inc_opts_len = strlen(inc_opts);
235 if (!res_all.res_fs.have_MD5) {
236 MD5Init(&res_all.res_fs.md5c);
237 res_all.res_fs.have_MD5 = TRUE;
239 setup_current_opts();
240 bstrncpy(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
241 Dmsg1(200, "incexe opts=%s\n", res_incexe.current_opts->opts);
243 /* Create incexe structure */
244 Dmsg0(200, "Create INCEXE structure\n");
245 incexe = (INCEXE *)malloc(sizeof(INCEXE));
246 memcpy(incexe, &res_incexe, sizeof(INCEXE));
247 memset(&res_incexe, 0, sizeof(INCEXE));
248 if (item->code == 0) { /* include */
249 if (res_all.res_fs.num_includes == 0) {
250 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
252 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
253 sizeof(INCEXE *) * res_all.res_fs.num_includes + 1);
255 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
256 Dmsg1(200, "num_includes=%d\n", res_all.res_fs.num_includes);
257 } else { /* exclude */
258 if (res_all.res_fs.num_excludes == 0) {
259 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
261 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
262 sizeof(INCEXE *) * res_all.res_fs.num_excludes + 1);
264 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
265 Dmsg1(200, "num_excludes=%d\n", res_all.res_fs.num_excludes);
268 /* Pickup include/exclude names. They are stored in INCEXE
269 * structures which contains the options and the name.
271 while ((token = lex_get_token(lc, T_ALL)) != T_EOB) {
278 case T_UNQUOTED_STRING:
279 case T_QUOTED_STRING:
280 if (res_all.res_fs.have_MD5) {
281 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
283 if (incexe->num_names == incexe->max_names) {
284 incexe->max_names += 10;
285 if (incexe->name_list == NULL) {
286 incexe->name_list = (char **)malloc(sizeof(char *) * incexe->max_names);
288 incexe->name_list = (char **)realloc(incexe->name_list,
289 sizeof(char *) * incexe->max_names);
292 incexe->name_list[incexe->num_names++] = bstrdup(lc->str);
293 Dmsg1(200, "Add to name_list %s\n", incexe->name_list[incexe->num_names -1]);
296 scan_err1(lc, "Expected a filename, got: %s", lc->str);
299 /* Note, MD5Final is done in backup.c */
300 } else { /* pass 2 */
301 while (lex_get_token(lc, T_ALL) != T_EOB)
305 lc->options = options;
306 set_bit(index, res_all.hdr.item_present);
311 * Store FileSet FInclude/FExclude info
312 * Note, when this routine is called, we are inside a FileSet
313 * resource. We treat the Finclude/Fexeclude like a sort of
314 * mini-resource within the FileSet resource.
316 void store_finc(LEX *lc, struct res_items *item, int index, int pass)
321 if (!res_all.res_fs.have_MD5) {
322 MD5Init(&res_all.res_fs.md5c);
323 res_all.res_fs.have_MD5 = TRUE;
325 res_all.res_fs.finclude = TRUE;
326 token = lex_get_token(lc, T_ALL);
327 if (token != T_BOB) {
328 scan_err1(lc, _("Expecting a beginning brace, got: %s\n"), lc->str);
330 memset(&res_incexe, 0, sizeof(INCEXE));
331 while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
332 if (token == T_EOL) {
335 if (token == T_EOB) {
338 if (token != T_IDENTIFIER) {
339 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
341 for (i=0; finc_items[i].name; i++) {
342 if (strcasecmp(finc_items[i].name, lc->str) == 0) {
343 token = lex_get_token(lc, T_ALL);
344 if (token != T_EQUALS) {
345 scan_err1(lc, "expected an equals, got: %s", lc->str);
347 /* Call item handler */
348 finc_items[i].handler(lc, &finc_items[i], i, pass);
354 scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
358 incexe = (INCEXE *)malloc(sizeof(INCEXE));
359 memcpy(incexe, &res_incexe, sizeof(INCEXE));
360 memset(&res_incexe, 0, sizeof(INCEXE));
361 if (item->code == 0) { /* include */
362 if (res_all.res_fs.num_includes == 0) {
363 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
365 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
366 sizeof(INCEXE *) * res_all.res_fs.num_includes + 1);
368 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
369 Dmsg1(200, "num_includes=%d\n", res_all.res_fs.num_includes);
370 } else { /* exclude */
371 if (res_all.res_fs.num_excludes == 0) {
372 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
374 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
375 sizeof(INCEXE *) * res_all.res_fs.num_excludes + 1);
377 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
378 Dmsg1(200, "num_excludes=%d\n", res_all.res_fs.num_excludes);
382 set_bit(index, res_all.hdr.item_present);
386 /* Store Match info */
387 static void store_match(LEX *lc, struct res_items *item, int index, int pass)
392 /* Pickup Match string
394 token = lex_get_token(lc, T_ALL);
397 case T_UNQUOTED_STRING:
398 case T_QUOTED_STRING:
399 setup_current_opts();
400 if (res_incexe.current_opts->match) {
401 scan_err0(lc, _("More than one match specified.\n"));
403 res_incexe.current_opts->match = bstrdup(lc->str);
406 scan_err1(lc, _("Expected a filename, got: %s\n"), lc->str);
408 } else { /* pass 2 */
409 lex_get_token(lc, T_ALL);
414 /* Store Base info */
415 static void store_base(LEX *lc, struct res_items *item, int index, int pass)
421 setup_current_opts();
423 * Pickup Base Job Name
425 token = lex_get_token(lc, T_NAME);
426 copt = res_incexe.current_opts;
427 if (copt->base_list == NULL) {
428 copt->base_list = (char **)malloc(sizeof(char *));
430 copt->base_list = (char **)realloc(copt->base_list,
431 sizeof(char *) * (copt->num_base+1));
433 copt->base_list[copt->num_base++] = bstrdup(lc->str);
434 } else { /* pass 2 */
435 lex_get_token(lc, T_ALL);
440 * Store Filename info. Note, for minor efficiency reasons, we
441 * always increase the name buffer by 10 items because we expect
442 * to add more entries.
444 static void store_fname(LEX *lc, struct res_items *item, int index, int pass)
450 /* Pickup Filename string
452 token = lex_get_token(lc, T_ALL);
455 case T_UNQUOTED_STRING:
456 case T_QUOTED_STRING:
457 if (res_all.res_fs.have_MD5) {
458 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
460 incexe = &res_incexe;
461 if (incexe->num_names == incexe->max_names) {
462 incexe->max_names += 10;
463 if (incexe->name_list == NULL) {
464 incexe->name_list = (char **)malloc(sizeof(char *) * incexe->max_names);
466 incexe->name_list = (char **)realloc(incexe->name_list,
467 sizeof(char *) * incexe->max_names);
470 incexe->name_list[incexe->num_names++] = bstrdup(lc->str);
471 Dmsg1(200, "Add to name_list %s\n", incexe->name_list[incexe->num_names -1]);
474 scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
476 } else { /* pass 2 */
477 lex_get_token(lc, T_ALL);
483 static void store_opts(LEX *lc, struct res_items *item, int index, int pass)
490 keyword = INC_KW_NONE;
491 for (i=0; FS_option_kw[i].name; i++) {
492 if (strcasecmp(item->name, FS_option_kw[i].name) == 0) {
493 keyword = FS_option_kw[i].token;
497 if (keyword == INC_KW_NONE) {
498 scan_err1(lc, "Expected a FileSet keyword, got: %s", lc->str);
500 Dmsg2(200, "keyword=%d %s\n", keyword, FS_option_kw[keyword].name);
501 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
504 setup_current_opts();
506 bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
514 /* If current_opts not defined, create first entry */
515 static void setup_current_opts(void)
517 if (res_incexe.current_opts == NULL) {
518 res_incexe.current_opts = (FOPTS *)malloc(sizeof(FOPTS));
519 memset(res_incexe.current_opts, 0, sizeof(FOPTS));
520 res_incexe.num_opts = 1;
521 res_incexe.opts_list = (FOPTS **)malloc(sizeof(FOPTS *));
522 res_incexe.opts_list[0] = res_incexe.current_opts;