1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2012 Planets Communications B.V.
6 Copyright (C) 2013-2016 Bareos GmbH & Co. KG
7
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
11 in the file LICENSE.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Affero General Public License for more details.
17
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 02110-1301, USA.
22 */
23 /*
24 * Kern Sibbald, March MMIII
25 */
26 /*
27 * @file
28 * Configuration file parser for new and old Include and
29 * Exclude records
30 */
31
32 #include "include/bareos.h"
33 #include "dird.h"
34 #include "dird/dird_globals.h"
35 #include "findlib/match.h"
36
37 #ifndef HAVE_REGEX_H
38 #include "lib/bregex.h"
39 #else
40 #include <regex.h>
41 #endif
42 #include "findlib/find.h"
43
44 #include "inc_conf.h"
45 #include "lib/edit.h"
46
47 namespace directordaemon {
48
49 typedef struct {
50 bool configured;
51 std::string default_value;
52 } options_default_value_s;
53
54
55 /*
56 * Imported subroutines
57 */
58 extern void StoreInc(LEX *lc, ResourceItem *item, int index, int pass);
59
60 /* We build the current new Include and Exclude items here */
61 static IncludeExcludeItem res_incexe;
62
63 /*
64 * new Include/Exclude items
65 * name handler value code flags default_value
66 */
67 ResourceItem newinc_items[] = {
68 { "File", CFG_TYPE_FNAME, { 0 }, 0, 0, NULL, NULL, NULL },
69 { "Plugin", CFG_TYPE_PLUGINNAME, { 0 }, 0, 0, NULL, NULL, NULL },
70 { "ExcludeDirContaining", CFG_TYPE_EXCLUDEDIR, { 0 }, 0, 0, NULL, NULL, NULL },
71 { "Options", CFG_TYPE_OPTIONS, { 0 }, 0, 0, NULL, NULL, NULL },
72 { NULL, 0, { 0 }, 0, 0, NULL, NULL, NULL }
73 };
74
75 /*
76 * Items that are valid in an Options resource
77 * name handler value code flags default_value
78 */
79 ResourceItem options_items[] = {
80 { "Compression", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
81 { "Signature", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
82 { "BaseJob", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
83 { "Accurate", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
84 { "Verify", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
85 { "OneFs", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
86 { "Recurse", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
87 { "Sparse", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
88 { "HardLinks", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
89 { "ReadFifo", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
90 { "Replace", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
91 { "Portable", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
92 { "MtimeOnly", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
93 { "KeepAtime", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
94 { "Regex", CFG_TYPE_REGEX, { 0 }, 0, 0, NULL, NULL, NULL },
95 { "RegexDir", CFG_TYPE_REGEX, { 0 }, 1, 0, NULL, NULL, NULL },
96 { "RegexFile", CFG_TYPE_REGEX, { 0 }, 2, 0, NULL, NULL, NULL },
97 { "Base", CFG_TYPE_BASE, { 0 }, 0, 0, NULL, NULL, NULL },
98 { "Wild", CFG_TYPE_WILD, { 0 }, 0, 0, NULL, NULL, NULL },
99 { "WildDir", CFG_TYPE_WILD, { 0 }, 1, 0, NULL, NULL, NULL },
100 { "WildFile", CFG_TYPE_WILD, { 0 }, 2, 0, NULL, NULL, NULL },
101 { "Exclude", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
102 { "AclSupport", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
103 { "Plugin", CFG_TYPE_PLUGIN, { 0 }, 0, 0, NULL, NULL, NULL },
104 { "IgnoreCase", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
105 { "FsType", CFG_TYPE_FSTYPE, { 0 }, 0, 0, NULL, NULL, NULL },
106 { "HfsPlusSupport", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
107 { "NoAtime", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
108 { "EnhancedWild", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
109 { "DriveType", CFG_TYPE_DRIVETYPE, { 0 }, 0, 0, NULL, NULL, NULL },
110 { "CheckFileChanges", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
111 { "StripPath", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
112 { "HonornoDumpFlag", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
113 { "XAttrSupport", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
114 { "Size", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
115 { "Shadowing", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
116 { "AutoExclude", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
117 { "ForceEncryption", CFG_TYPE_OPTION, { 0 }, 0, 0, NULL, NULL, NULL },
118 { "Meta", CFG_TYPE_META, { 0 }, 0, 0, 0, NULL, NULL },
119 { NULL, 0, { 0 }, 0, 0, NULL, NULL, NULL }
120 };
121
122 /**
123 * determine used compression algorithms
124 */
FindUsedCompressalgos(PoolMem * compressalgos,JobControlRecord * jcr)125 void FindUsedCompressalgos(PoolMem *compressalgos, JobControlRecord *jcr)
126 {
127 int cnt = 0;
128 IncludeExcludeItem *inc;
129 FileOptions *fopts;
130 FilesetResource *fs;
131 struct s_fs_opt *fs_opt;
132
133 if (!jcr->res.job || !jcr->res.job->fileset) {
134 return;
135 }
136
137 fs = jcr->res.job->fileset;
138 for (int i = 0; i < fs->num_includes; i++) { /* Parse all Include {} */
139 inc = fs->include_items[i];
140
141 for (int j = 0; j < inc->num_opts; j++) { /* Parse all Options {} */
142 fopts = inc->opts_list[j];
143
144 for (char *k = fopts->opts; *k; k++) { /* Try to find one request */
145 switch (*k) {
146 case 'Z': /* Compression */
147 for (fs_opt = FS_options; fs_opt->name; fs_opt++) {
148 if (fs_opt->keyword != INC_KW_COMPRESSION) {
149 continue;
150 }
151
152 if (bstrncmp(k, fs_opt->option, strlen(fs_opt->option))) {
153 if (cnt > 0) {
154 compressalgos->strcat(",");
155 } else {
156 compressalgos->strcat(" (");
157 }
158 compressalgos->strcat(fs_opt->name);
159 k += strlen(fs_opt->option) - 1;
160 cnt++;
161 continue;
162 }
163 }
164 break;
165 default:
166 break;
167 }
168 }
169 }
170 }
171
172 if (cnt > 0) {
173 compressalgos->strcat(")");
174 }
175 }
176
177 /**
178 * Check if the configured options are valid.
179 */
IsInPermittedSet(LEX * lc,const char * SetType,const char * permitted_set)180 static inline void IsInPermittedSet(LEX *lc, const char *SetType, const char *permitted_set)
181 {
182 const char *p, *q;
183 bool found;
184
185 for (p = lc->str; *p; p++) {
186 found = false;
187 for (q = permitted_set; *q; q++) {
188 if (*p == *q) {
189 found = true;
190 break;
191 }
192 }
193
194 if (!found) {
195 scan_err3(lc, _("Illegal %s option %c, got option string: %s:"),
196 SetType, *p, lc->str);
197 }
198 }
199 }
200
201 /**
202 * Scan for right hand side of Include options (keyword=option) is
203 * converted into one or two characters. Verifyopts=xxxx is Vxxxx:
204 * Whatever is found is concatenated to the opts string.
205 *
206 * This code is also used inside an Options resource.
207 */
ScanIncludeOptions(LEX * lc,int keyword,char * opts,int optlen)208 static void ScanIncludeOptions(LEX *lc, int keyword, char *opts, int optlen)
209 {
210 int i;
211 char option[64];
212 int lcopts = lc->options;
213 struct s_sz_matching size_matching;
214
215 memset(option, 0, sizeof(option));
216 lc->options |= LOPT_STRING; /* force string */
217 LexGetToken(lc, BCT_STRING); /* expect at least one option */
218 if (keyword == INC_KW_VERIFY) { /* special case */
219 IsInPermittedSet(lc, _("verify"), PERMITTED_VERIFY_OPTIONS);
220 bstrncat(opts, "V", optlen); /* indicate Verify */
221 bstrncat(opts, lc->str, optlen);
222 bstrncat(opts, ":", optlen); /* Terminate it */
223 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
224 } else if (keyword == INC_KW_ACCURATE) { /* special case */
225 IsInPermittedSet(lc, _("accurate"), PERMITTED_ACCURATE_OPTIONS);
226 bstrncat(opts, "C", optlen); /* indicate Accurate */
227 bstrncat(opts, lc->str, optlen);
228 bstrncat(opts, ":", optlen); /* Terminate it */
229 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
230 } else if (keyword == INC_KW_BASEJOB) { /* special case */
231 IsInPermittedSet(lc, _("base job"), PERMITTED_BASEJOB_OPTIONS);
232 bstrncat(opts, "J", optlen); /* indicate BaseJob */
233 bstrncat(opts, lc->str, optlen);
234 bstrncat(opts, ":", optlen); /* Terminate it */
235 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
236 } else if (keyword == INC_KW_STRIPPATH) { /* special case */
237 if (!IsAnInteger(lc->str)) {
238 scan_err1(lc, _("Expected a strip path positive integer, got: %s:"), lc->str);
239 }
240 bstrncat(opts, "P", optlen); /* indicate strip path */
241 bstrncat(opts, lc->str, optlen);
242 bstrncat(opts, ":", optlen); /* Terminate it */
243 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
244 } else if (keyword == INC_KW_SIZE) { /* special case */
245 if (!ParseSizeMatch(lc->str, &size_matching)) {
246 scan_err1(lc, _("Expected a parseable size, got: %s:"), lc->str);
247 }
248 bstrncat(opts, "z", optlen); /* indicate size */
249 bstrncat(opts, lc->str, optlen);
250 bstrncat(opts, ":", optlen); /* Terminate it */
251 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
252 } else {
253 /*
254 * Standard keyword options for Include/Exclude
255 */
256 for (i = 0; FS_options[i].name; i++) {
257 if (FS_options[i].keyword == keyword && Bstrcasecmp(lc->str, FS_options[i].name)) {
258 bstrncpy(option, FS_options[i].option, sizeof(option));
259 i = 0;
260 break;
261 }
262 }
263 if (i != 0) {
264 scan_err1(lc, _("Expected a FileSet option keyword, got: %s:"), lc->str);
265 } else { /* add option */
266 bstrncat(opts, option, optlen);
267 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
268 }
269 }
270 lc->options = lcopts;
271
272 /*
273 * If option terminated by comma, eat it
274 */
275 if (lc->ch == ',') {
276 LexGetToken(lc, BCT_ALL); /* yes, eat comma */
277 }
278 }
279
280 /**
281 * Store regex info
282 */
StoreRegex(LEX * lc,ResourceItem * item,int index,int pass)283 static void StoreRegex(LEX *lc, ResourceItem *item, int index, int pass)
284 {
285 int token, rc;
286 regex_t preg;
287 char prbuf[500];
288 const char *type;
289 int newsize;
290
291 token = LexGetToken(lc, BCT_SKIP_EOL);
292 if (pass == 1) {
293 /* Pickup regex string
294 */
295 switch (token) {
296 case BCT_IDENTIFIER:
297 case BCT_UNQUOTED_STRING:
298 case BCT_QUOTED_STRING:
299 rc = regcomp(&preg, lc->str, REG_EXTENDED);
300 if (rc != 0) {
301 regerror(rc, &preg, prbuf, sizeof(prbuf));
302 regfree(&preg);
303 scan_err1(lc, _("Regex compile error. ERR=%s\n"), prbuf);
304 break;
305 }
306 regfree(&preg);
307 if (item->code == 1) {
308 type = "regexdir";
309 res_incexe.current_opts->regexdir.append(bstrdup(lc->str));
310 newsize = res_incexe.current_opts->regexdir.size();
311 } else if (item->code == 2) {
312 type = "regexfile";
313 res_incexe.current_opts->regexfile.append(bstrdup(lc->str));
314 newsize = res_incexe.current_opts->regexfile.size();
315 } else {
316 type = "regex";
317 res_incexe.current_opts->regex.append(bstrdup(lc->str));
318 newsize = res_incexe.current_opts->regex.size();
319 }
320 Dmsg4(900, "set %s %p size=%d %s\n",
321 type, res_incexe.current_opts, newsize, lc->str);
322 break;
323 default:
324 scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
325 }
326 }
327 ScanToEol(lc);
328 }
329
330 /**
331 * Store Base info
332 */
StoreBase(LEX * lc,ResourceItem * item,int index,int pass)333 static void StoreBase(LEX *lc, ResourceItem *item, int index, int pass)
334 {
335
336 LexGetToken(lc, BCT_NAME);
337 if (pass == 1) {
338 /*
339 * Pickup Base Job Name
340 */
341 res_incexe.current_opts->base.append(bstrdup(lc->str));
342 }
343 ScanToEol(lc);
344 }
345
346 /**
347 * Store reader info
348 */
StorePlugin(LEX * lc,ResourceItem * item,int index,int pass)349 static void StorePlugin(LEX *lc, ResourceItem *item, int index, int pass)
350 {
351
352 LexGetToken(lc, BCT_NAME);
353 if (pass == 1) {
354 /*
355 * Pickup plugin command
356 */
357 res_incexe.current_opts->plugin = bstrdup(lc->str);
358 }
359 ScanToEol(lc);
360 }
361
362 /**
363 * Store Wild-card info
364 */
StoreWild(LEX * lc,ResourceItem * item,int index,int pass)365 static void StoreWild(LEX *lc, ResourceItem *item, int index, int pass)
366 {
367 int token;
368 const char *type;
369 int newsize;
370
371 token = LexGetToken(lc, BCT_SKIP_EOL);
372 if (pass == 1) {
373 /*
374 * Pickup Wild-card string
375 */
376 switch (token) {
377 case BCT_IDENTIFIER:
378 case BCT_UNQUOTED_STRING:
379 case BCT_QUOTED_STRING:
380 if (item->code == 1) {
381 type = "wilddir";
382 res_incexe.current_opts->wilddir.append(bstrdup(lc->str));
383 newsize = res_incexe.current_opts->wilddir.size();
384 } else if (item->code == 2) {
385 if (strpbrk(lc->str, "/\\") != NULL) {
386 type = "wildfile";
387 res_incexe.current_opts->wildfile.append(bstrdup(lc->str));
388 newsize = res_incexe.current_opts->wildfile.size();
389 } else {
390 type = "wildbase";
391 res_incexe.current_opts->wildbase.append(bstrdup(lc->str));
392 newsize = res_incexe.current_opts->wildbase.size();
393 }
394 } else {
395 type = "wild";
396 res_incexe.current_opts->wild.append(bstrdup(lc->str));
397 newsize = res_incexe.current_opts->wild.size();
398 }
399 Dmsg4(9, "set %s %p size=%d %s\n",
400 type, res_incexe.current_opts, newsize, lc->str);
401 break;
402 default:
403 scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
404 }
405 }
406 ScanToEol(lc);
407 }
408
409 /**
410 * Store fstype info
411 */
StoreFstype(LEX * lc,ResourceItem * item,int index,int pass)412 static void StoreFstype(LEX *lc, ResourceItem *item, int index, int pass)
413 {
414 int token;
415
416 token = LexGetToken(lc, BCT_SKIP_EOL);
417 if (pass == 1) {
418 /* Pickup fstype string */
419 switch (token) {
420 case BCT_IDENTIFIER:
421 case BCT_UNQUOTED_STRING:
422 case BCT_QUOTED_STRING:
423 res_incexe.current_opts->fstype.append(bstrdup(lc->str));
424 Dmsg3(900, "set fstype %p size=%d %s\n",
425 res_incexe.current_opts, res_incexe.current_opts->fstype.size(), lc->str);
426 break;
427 default:
428 scan_err1(lc, _("Expected a fstype string, got: %s\n"), lc->str);
429 }
430 }
431 ScanToEol(lc);
432 }
433
434 /**
435 * Store Drivetype info
436 */
StoreDrivetype(LEX * lc,ResourceItem * item,int index,int pass)437 static void StoreDrivetype(LEX *lc, ResourceItem *item, int index, int pass)
438 {
439 int token;
440
441 token = LexGetToken(lc, BCT_SKIP_EOL);
442 if (pass == 1) {
443 /* Pickup Drivetype string */
444 switch (token) {
445 case BCT_IDENTIFIER:
446 case BCT_UNQUOTED_STRING:
447 case BCT_QUOTED_STRING:
448 res_incexe.current_opts->Drivetype.append(bstrdup(lc->str));
449 Dmsg3(900, "set Drivetype %p size=%d %s\n",
450 res_incexe.current_opts, res_incexe.current_opts->Drivetype.size(), lc->str);
451 break;
452 default:
453 scan_err1(lc, _("Expected a Drivetype string, got: %s\n"), lc->str);
454 }
455 }
456 ScanToEol(lc);
457 }
458
StoreMeta(LEX * lc,ResourceItem * item,int index,int pass)459 static void StoreMeta(LEX *lc, ResourceItem *item, int index, int pass)
460 {
461 int token;
462
463 token = LexGetToken(lc, BCT_SKIP_EOL);
464 if (pass == 1) {
465 /* Pickup fstype string */
466 switch (token) {
467 case BCT_IDENTIFIER:
468 case BCT_UNQUOTED_STRING:
469 case BCT_QUOTED_STRING:
470 res_incexe.current_opts->meta.append(bstrdup(lc->str));
471 Dmsg3(900, "set meta %p size=%d %s\n",
472 res_incexe.current_opts, res_incexe.current_opts->meta.size(), lc->str);
473 break;
474 default:
475 scan_err1(lc, _("Expected a meta string, got: %s\n"), lc->str);
476 }
477 }
478 ScanToEol(lc);
479 }
480
481 /**
482 * New style options come here
483 */
StoreOption(LEX * lc,ResourceItem * item,int index,int pass,std::map<int,options_default_value_s> & option_default_values)484 static void StoreOption(LEX *lc, ResourceItem *item, int index, int pass,
485 std::map<int,options_default_value_s> &option_default_values)
486 {
487 int i;
488 int keyword;
489 char inc_opts[100];
490
491 inc_opts[0] = 0;
492 keyword = INC_KW_NONE;
493
494 /*
495 * Look up the keyword
496 */
497 for (i = 0; FS_option_kw[i].name; i++) {
498 if (Bstrcasecmp(item->name, FS_option_kw[i].name)) {
499 keyword = FS_option_kw[i].token;
500 if (option_default_values.find(keyword) != option_default_values.end()) {
501 option_default_values[keyword].configured = true;
502 }
503 break;
504 }
505 }
506
507 if (keyword == INC_KW_NONE) {
508 scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
509 }
510
511 /*
512 * Now scan for the value
513 */
514 ScanIncludeOptions(lc, keyword, inc_opts, sizeof(inc_opts));
515 if (pass == 1) {
516 bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
517 Dmsg2(900, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
518 }
519
520 ScanToEol(lc);
521 }
522
523 /**
524 * If current_opts not defined, create first entry
525 */
SetupCurrentOpts(void)526 static void SetupCurrentOpts(void)
527 {
528 FileOptions *fo = (FileOptions *)malloc(sizeof(FileOptions));
529 memset(fo, 0, sizeof(FileOptions));
530 fo->regex.init(1, true);
531 fo->regexdir.init(1, true);
532 fo->regexfile.init(1, true);
533 fo->wild.init(1, true);
534 fo->wilddir.init(1, true);
535 fo->wildfile.init(1, true);
536 fo->wildbase.init(1, true);
537 fo->base.init(1, true);
538 fo->fstype.init(1, true);
539 fo->Drivetype.init(1, true);
540 fo->meta.init(1, true);
541 res_incexe.current_opts = fo;
542 if (res_incexe.num_opts == 0) {
543 res_incexe.opts_list = (FileOptions **)malloc(sizeof(FileOptions *));
544 } else {
545 res_incexe.opts_list = (FileOptions **)realloc(res_incexe.opts_list,
546 sizeof(FileOptions *) * (res_incexe.num_opts + 1));
547 }
548 res_incexe.opts_list[res_incexe.num_opts++] = fo;
549 }
550
551 /**
552 * Come here when Options seen in Include/Exclude
553 */
StoreOptionsRes(LEX * lc,ResourceItem * item,int index,int pass,bool exclude)554 static void StoreOptionsRes(LEX *lc, ResourceItem *item, int index, int pass, bool exclude)
555 {
556 int token, i;
557 std::map<int,options_default_value_s> option_default_values = {
558 { INC_KW_ACL, {false, "A"} },
559 { INC_KW_XATTR, {false, "X"} }
560 };
561
562 if (exclude) {
563 scan_err0(lc, _("Options section not permitted in Exclude\n"));
564 /* NOT REACHED */
565 }
566 token = LexGetToken(lc, BCT_SKIP_EOL);
567 if (token != BCT_BOB) {
568 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
569 }
570
571 if (pass == 1) {
572 SetupCurrentOpts();
573 }
574
575 while ((token = LexGetToken(lc, BCT_ALL)) != BCT_EOF) {
576 if (token == BCT_EOL) {
577 continue;
578 }
579 if (token == BCT_EOB) {
580 break;
581 }
582 if (token != BCT_IDENTIFIER) {
583 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
584 }
585 for (i=0; options_items[i].name; i++) {
586 if (Bstrcasecmp(options_items[i].name, lc->str)) {
587 token = LexGetToken(lc, BCT_SKIP_EOL);
588 if (token != BCT_EQUALS) {
589 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
590 }
591 /* Call item handler */
592 switch (options_items[i].type) {
593 case CFG_TYPE_OPTION:
594 StoreOption(lc, &options_items[i], i, pass, option_default_values);
595 break;
596 case CFG_TYPE_REGEX:
597 StoreRegex(lc, &options_items[i], i, pass);
598 break;
599 case CFG_TYPE_BASE:
600 StoreBase(lc, &options_items[i], i, pass);
601 break;
602 case CFG_TYPE_WILD:
603 StoreWild(lc, &options_items[i], i, pass);
604 break;
605 case CFG_TYPE_PLUGIN:
606 StorePlugin(lc, &options_items[i], i, pass);
607 break;
608 case CFG_TYPE_FSTYPE:
609 StoreFstype(lc, &options_items[i], i, pass);
610 break;
611 case CFG_TYPE_DRIVETYPE:
612 StoreDrivetype(lc, &options_items[i], i, pass);
613 break;
614 case CFG_TYPE_META:
615 StoreMeta(lc, &options_items[i], i, pass);
616 break;
617 default:
618 break;
619 }
620 i = -1;
621 break;
622 }
623 }
624 if (i >=0) {
625 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
626 }
627 }
628
629 /* apply default values for unset options */
630 if (pass == 1) {
631 for (auto const &o : option_default_values) {
632 int keyword_id = o.first;
633 bool was_set_in_config = o.second.configured;
634 std::string default_value = o.second.default_value;
635 if (!was_set_in_config) {
636 bstrncat(res_incexe.current_opts->opts, default_value.c_str(), MAX_FOPTS);
637 Dmsg2(900, "setting default value for keyword-id=%d, %s\n", keyword_id, default_value.c_str());
638 }
639 }
640 }
641 }
642
643 /**
644 * Store Filename info. Note, for minor efficiency reasons, we
645 * always increase the name buffer by 10 items because we expect
646 * to add more entries.
647 */
StoreFname(LEX * lc,ResourceItem * item,int index,int pass,bool exclude)648 static void StoreFname(LEX *lc, ResourceItem *item, int index, int pass, bool exclude)
649 {
650 int token;
651 IncludeExcludeItem *incexe;
652 UnionOfResources *res_all = (UnionOfResources *)my_config->res_all_;
653
654 token = LexGetToken(lc, BCT_SKIP_EOL);
655 if (pass == 1) {
656 /* Pickup Filename string
657 */
658 switch (token) {
659 case BCT_IDENTIFIER:
660 case BCT_UNQUOTED_STRING:
661 if (strchr(lc->str, '\\')) {
662 scan_err1(lc, _("Backslash found. Use forward slashes or quote the string.: %s\n"), lc->str);
663 /* NOT REACHED */
664 }
665 case BCT_QUOTED_STRING:
666 if (res_all->res_fs.have_MD5) {
667 MD5_Update(&res_all->res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
668 }
669 incexe = &res_incexe;
670 if (incexe->name_list.size() == 0) {
671 incexe->name_list.init(10, true);
672 }
673 incexe->name_list.append(bstrdup(lc->str));
674 Dmsg1(900, "Add to name_list %s\n", lc->str);
675 break;
676 default:
677 scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
678 }
679 }
680 ScanToEol(lc);
681 }
682
683 /**
684 * Store Filename info. Note, for minor efficiency reasons, we
685 * always increase the name buffer by 10 items because we expect
686 * to add more entries.
687 */
StorePluginName(LEX * lc,ResourceItem * item,int index,int pass,bool exclude)688 static void StorePluginName(LEX *lc, ResourceItem *item, int index, int pass, bool exclude)
689 {
690 int token;
691 IncludeExcludeItem *incexe;
692 UnionOfResources *res_all = (UnionOfResources *)my_config->res_all_;
693
694 if (exclude) {
695 scan_err0(lc, _("Plugin directive not permitted in Exclude\n"));
696 /* NOT REACHED */
697 }
698 token = LexGetToken(lc, BCT_SKIP_EOL);
699 if (pass == 1) {
700 /* Pickup Filename string
701 */
702 switch (token) {
703 case BCT_IDENTIFIER:
704 case BCT_UNQUOTED_STRING:
705 if (strchr(lc->str, '\\')) {
706 scan_err1(lc, _("Backslash found. Use forward slashes or quote the string.: %s\n"), lc->str);
707 /* NOT REACHED */
708 }
709 case BCT_QUOTED_STRING:
710 if (res_all->res_fs.have_MD5) {
711 MD5_Update(&res_all->res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
712 }
713 incexe = &res_incexe;
714 if (incexe->plugin_list.size() == 0) {
715 incexe->plugin_list.init(10, true);
716 }
717 incexe->plugin_list.append(bstrdup(lc->str));
718 Dmsg1(900, "Add to plugin_list %s\n", lc->str);
719 break;
720 default:
721 scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
722 /* NOT REACHED */
723 }
724 }
725 ScanToEol(lc);
726 }
727
728 /**
729 * Store exclude directory containing info
730 */
StoreExcludedir(LEX * lc,ResourceItem * item,int index,int pass,bool exclude)731 static void StoreExcludedir(LEX *lc, ResourceItem *item, int index, int pass, bool exclude)
732 {
733 IncludeExcludeItem *incexe;
734
735 if (exclude) {
736 scan_err0(lc, _("ExcludeDirContaining directive not permitted in Exclude.\n"));
737 /* NOT REACHED */
738 return;
739 }
740
741 LexGetToken(lc, BCT_NAME);
742 if (pass == 1) {
743 incexe = &res_incexe;
744 if (incexe->ignoredir.size() == 0) {
745 incexe->ignoredir.init(10, true);
746 }
747 incexe->ignoredir.append(bstrdup(lc->str));
748 Dmsg1(900, "Add to ignoredir_list %s\n", lc->str);
749 }
750 ScanToEol(lc);
751 }
752
753 /**
754 * Store new style FileSet Include/Exclude info
755 *
756 * Note, when this routine is called, we are inside a FileSet
757 * resource. We treat the Include/Exclude like a sort of
758 * mini-resource within the FileSet resource.
759 */
StoreNewinc(LEX * lc,ResourceItem * item,int index,int pass)760 static void StoreNewinc(LEX *lc, ResourceItem *item, int index, int pass)
761 {
762 bool options;
763 int token, i;
764 IncludeExcludeItem *incexe;
765 UnionOfResources *res_all = (UnionOfResources *)my_config->res_all_;
766
767 if (!res_all->res_fs.have_MD5) {
768 MD5_Init(&res_all->res_fs.md5c);
769 res_all->res_fs.have_MD5 = true;
770 }
771 memset(&res_incexe, 0, sizeof(res_incexe));
772 res_all->res_fs.new_include = true;
773 while ((token = LexGetToken(lc, BCT_SKIP_EOL)) != BCT_EOF) {
774 if (token == BCT_EOB) {
775 break;
776 }
777 if (token != BCT_IDENTIFIER) {
778 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
779 }
780 for (i=0; newinc_items[i].name; i++) {
781 options = Bstrcasecmp(lc->str, "options");
782 if (Bstrcasecmp(newinc_items[i].name, lc->str)) {
783 if (!options) {
784 token = LexGetToken(lc, BCT_SKIP_EOL);
785 if (token != BCT_EQUALS) {
786 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
787 }
788 }
789
790 /* Call item handler */
791 switch (newinc_items[i].type) {
792 case CFG_TYPE_FNAME:
793 StoreFname(lc, &newinc_items[i], i, pass, item->code);
794 break;
795 case CFG_TYPE_PLUGINNAME:
796 StorePluginName(lc, &newinc_items[i], i, pass, item->code);
797 break;
798 case CFG_TYPE_EXCLUDEDIR:
799 StoreExcludedir(lc, &newinc_items[i], i, pass, item->code);
800 break;
801 case CFG_TYPE_OPTIONS:
802 StoreOptionsRes(lc, &newinc_items[i], i, pass, item->code);
803 break;
804 default:
805 break;
806 }
807 i = -1;
808 break;
809 }
810 }
811 if (i >=0) {
812 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
813 }
814 }
815 if (pass == 1) {
816 incexe = (IncludeExcludeItem *)malloc(sizeof(IncludeExcludeItem));
817 memcpy(incexe, &res_incexe, sizeof(IncludeExcludeItem));
818 memset(&res_incexe, 0, sizeof(res_incexe));
819 if (item->code == 0) { /* include */
820 if (res_all->res_fs.num_includes == 0) {
821 res_all->res_fs.include_items = (IncludeExcludeItem **)malloc(sizeof(IncludeExcludeItem *));
822 } else {
823 res_all->res_fs.include_items = (IncludeExcludeItem **)realloc(res_all->res_fs.include_items,
824 sizeof(IncludeExcludeItem *) * (res_all->res_fs.num_includes + 1));
825 }
826 res_all->res_fs.include_items[res_all->res_fs.num_includes++] = incexe;
827 Dmsg1(900, "num_includes=%d\n", res_all->res_fs.num_includes);
828 } else { /* exclude */
829 if (res_all->res_fs.num_excludes == 0) {
830 res_all->res_fs.exclude_items = (IncludeExcludeItem **)malloc(sizeof(IncludeExcludeItem *));
831 } else {
832 res_all->res_fs.exclude_items = (IncludeExcludeItem **)realloc(res_all->res_fs.exclude_items,
833 sizeof(IncludeExcludeItem *) * (res_all->res_fs.num_excludes + 1));
834 }
835 res_all->res_fs.exclude_items[res_all->res_fs.num_excludes++] = incexe;
836 Dmsg1(900, "num_excludes=%d\n", res_all->res_fs.num_excludes);
837 }
838 }
839 ScanToEol(lc);
840 SetBit(index, res_all->hdr.item_present);
841 ClearBit(index, res_all->hdr.inherit_content);
842 }
843
844 /**
845 * Store FileSet Include/Exclude info
846 * new style includes are handled in StoreNewinc()
847 */
StoreInc(LEX * lc,ResourceItem * item,int index,int pass)848 void StoreInc(LEX *lc, ResourceItem *item, int index, int pass)
849 {
850 int token;
851
852 /*
853 * Decide if we are doing a new Include or an old include. The
854 * new Include is followed immediately by open brace, whereas the
855 * old include has options following the Include.
856 */
857 token = LexGetToken(lc, BCT_SKIP_EOL);
858 if (token == BCT_BOB) {
859 StoreNewinc(lc, item, index, pass);
860 return;
861 }
862 scan_err0(lc, _("Old style Include/Exclude not supported\n"));
863 }
864
865 #ifdef HAVE_JANSSON
json_incexc(const int type)866 json_t *json_incexc(const int type)
867 {
868 return json_datatype(type, newinc_items);
869 }
870
json_options(const int type)871 json_t *json_options(const int type)
872 {
873 return json_datatype(type, options_items);
874 }
875 #endif
876 } /* namespace directordaemon */
877