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