2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
21 * Configuration parser for Director Run Configuration
22 * directives, which are part of the Schedule Resource
24 * Kern Sibbald, May MM
32 extern "C" { // work around visual compiler mangling variables
38 extern s_jl joblevels[];
40 /* Forward referenced subroutines */
54 s_wom, /* 1st, 2nd, ...*/
55 s_woy, /* week of year w00 - w53 */
56 s_ldom /* last day of month */
60 const char *name; /* keyword */
61 enum e_state state; /* parser state */
62 int code; /* state value */
65 /* Keywords understood by parser */
66 static struct s_keyw keyw[] = {
67 {NT_("on"), s_none, 0},
69 {NT_("lastday"), s_ldom, 0},
71 {NT_("sun"), s_wday, 0},
72 {NT_("mon"), s_wday, 1},
73 {NT_("tue"), s_wday, 2},
74 {NT_("wed"), s_wday, 3},
75 {NT_("thu"), s_wday, 4},
76 {NT_("fri"), s_wday, 5},
77 {NT_("sat"), s_wday, 6},
78 {NT_("jan"), s_month, 0},
79 {NT_("feb"), s_month, 1},
80 {NT_("mar"), s_month, 2},
81 {NT_("apr"), s_month, 3},
82 {NT_("may"), s_month, 4},
83 {NT_("jun"), s_month, 5},
84 {NT_("jul"), s_month, 6},
85 {NT_("aug"), s_month, 7},
86 {NT_("sep"), s_month, 8},
87 {NT_("oct"), s_month, 9},
88 {NT_("nov"), s_month, 10},
89 {NT_("dec"), s_month, 11},
91 {NT_("sunday"), s_wday, 0},
92 {NT_("monday"), s_wday, 1},
93 {NT_("tuesday"), s_wday, 2},
94 {NT_("wednesday"), s_wday, 3},
95 {NT_("thursday"), s_wday, 4},
96 {NT_("friday"), s_wday, 5},
97 {NT_("saturday"), s_wday, 6},
98 {NT_("january"), s_month, 0},
99 {NT_("february"), s_month, 1},
100 {NT_("march"), s_month, 2},
101 {NT_("april"), s_month, 3},
102 {NT_("june"), s_month, 5},
103 {NT_("july"), s_month, 6},
104 {NT_("august"), s_month, 7},
105 {NT_("september"), s_month, 8},
106 {NT_("october"), s_month, 9},
107 {NT_("november"), s_month, 10},
108 {NT_("december"), s_month, 11},
110 {NT_("daily"), s_daily, 0},
111 {NT_("weekly"), s_weekly, 0},
112 {NT_("monthly"), s_monthly, 0},
113 {NT_("hourly"), s_hourly, 0},
115 {NT_("1st"), s_wom, 0},
116 {NT_("2nd"), s_wom, 1},
117 {NT_("3rd"), s_wom, 2},
118 {NT_("4th"), s_wom, 3},
119 {NT_("5th"), s_wom, 4},
120 {NT_("6th"), s_wom, 5},
122 {NT_("first"), s_wom, 0},
123 {NT_("second"), s_wom, 1},
124 {NT_("third"), s_wom, 2},
125 {NT_("fourth"), s_wom, 3},
126 {NT_("fifth"), s_wom, 4},
127 {NT_("sixth"), s_wom, 5},
131 static bool have_hour, have_mday, have_wday, have_month, have_wom;
132 static bool have_at, have_woy;
135 static void set_defaults()
137 have_hour = have_mday = have_wday = have_month = have_wom = have_woy = false;
139 set_bits(0, 23, lrun.hour);
140 set_bits(0, 30, lrun.mday);
141 set_bits(0, 6, lrun.wday);
142 set_bits(0, 11, lrun.month);
143 set_bits(0, 5, lrun.wom);
144 set_bits(0, 53, lrun.woy);
149 * Keywords (RHS) permitted in Run records
156 {"IncrementalPool", 'i'},
157 {"DifferentialPool", 'd'},
163 {"writepartafterjob", 'W'},
164 {"MaxRunSchedTime", 'm'},
171 * Store Schedule Run information
173 * Parse Run statement:
175 * Run <keyword=value ...> [on] 2 january at 23:45
177 * Default Run time is daily at 0:0
179 * There can be multiple run statements, they are simply chained
183 void store_run(LEX *lc, RES_ITEM *item, int index, int pass)
188 int token, state, state2 = 0, code = 0, code2 = 0;
189 int options = lc->options;
190 RUN **run = (RUN **)(item->value);
195 lc->options |= LOPT_NO_IDENT; /* want only "strings" */
197 /* clear local copy of run record */
198 memset(&lrun, 0, sizeof(RUN));
200 /* scan for Job level "full", "incremental", ... */
201 for (found=true; found; ) {
203 token = lex_get_token(lc, T_NAME);
204 for (i=0; !found && RunFields[i].name; i++) {
205 if (strcasecmp(lc->str, RunFields[i].name) == 0) {
207 if (lex_get_token(lc, T_ALL) != T_EQUALS) {
208 scan_err1(lc, _("Expected an equals, got: %s"), lc->str);
211 switch (RunFields[i].token) {
212 case 's': /* Data spooling */
213 token = lex_get_token(lc, T_NAME);
214 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
215 lrun.spool_data = true;
216 lrun.spool_data_set = true;
217 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
218 lrun.spool_data = false;
219 lrun.spool_data_set = true;
221 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
224 case 'W': /* Write part after job */
225 token = lex_get_token(lc, T_NAME);
226 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
227 lrun.write_part_after_job = true;
228 lrun.write_part_after_job_set = true;
229 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
230 lrun.write_part_after_job = false;
231 lrun.write_part_after_job_set = true;
233 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
236 case 'L': /* level */
237 token = lex_get_token(lc, T_NAME);
238 for (j=0; joblevels[j].level_name; j++) {
239 if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
240 lrun.level = joblevels[j].level;
241 lrun.job_type = joblevels[j].job_type;
242 lrun.level_set = true;
248 scan_err1(lc, _("Job level field: %s not found in run record"), lc->str);
252 case 'p': /* Priority */
253 token = lex_get_token(lc, T_PINT32);
255 lrun.Priority = lc->pint32_val;
256 lrun.priority_set = true;
260 case 'N': /* NextPool */
261 case 'f': /* FullPool */
262 case 'i': /* IncPool */
263 case 'd': /* DifPool */
264 token = lex_get_token(lc, T_NAME);
266 res = GetResWithName(R_POOL, lc->str);
268 scan_err1(lc, _("Could not find specified Pool Resource: %s"),
272 switch(RunFields[i].token) {
274 lrun.pool = (POOL *)res;
277 lrun.next_pool = (POOL *)res;
280 lrun.full_pool = (POOL *)res;
283 lrun.inc_pool = (POOL *)res;
286 lrun.diff_pool = (POOL *)res;
291 case 'S': /* storage */
292 token = lex_get_token(lc, T_NAME);
294 res = GetResWithName(R_STORAGE, lc->str);
296 scan_err1(lc, _("Could not find specified Storage Resource: %s"),
300 lrun.storage = (STORE *)res;
303 case 'M': /* messages */
304 token = lex_get_token(lc, T_NAME);
306 res = GetResWithName(R_MSGS, lc->str);
308 scan_err1(lc, _("Could not find specified Messages Resource: %s"),
312 lrun.msgs = (MSGS *)res;
315 case 'm': /* max run sched time */
316 token = lex_get_token(lc, T_QUOTED_STRING);
317 if (!duration_to_utime(lc->str, &utime)) {
318 scan_err1(lc, _("expected a time period, got: %s"), lc->str);
321 lrun.MaxRunSchedTime = utime;
322 lrun.MaxRunSchedTime_set = true;
324 case 'a': /* accurate */
325 token = lex_get_token(lc, T_NAME);
326 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
327 lrun.accurate = true;
328 lrun.accurate_set = true;
329 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
330 lrun.accurate = false;
331 lrun.accurate_set = true;
333 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
337 scan_err1(lc, _("Expected a keyword name, got: %s"), lc->str);
341 } /* end if strcasecmp */
342 } /* end for RunFields */
344 /* At this point, it is not a keyword. Check for old syle
345 * Job Levels without keyword. This form is depreciated!!!
348 for (j=0; joblevels[j].level_name; j++) {
349 if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
350 lrun.level = joblevels[j].level;
351 lrun.job_type = joblevels[j].job_type;
357 } /* end for found */
361 * Scan schedule times.
362 * Default is: daily at 0:0
367 for ( ; token != T_EOL; (token = lex_get_token(lc, T_ALL))) {
374 code = atoi(lc->str) - 1;
375 if (code < 0 || code > 30) {
376 scan_err0(lc, _("Day number out of range (1-31)"));
379 case T_NAME: /* this handles drop through from keyword */
380 case T_UNQUOTED_STRING:
381 if (strchr(lc->str, (int)'-')) {
385 if (strchr(lc->str, (int)':')) {
389 if (lc->str_len == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') &&
390 is_an_integer(lc->str+1)) {
391 code = atoi(lc->str+1);
392 if (code < 0 || code > 53) {
393 scan_err0(lc, _("Week number out of range (0-53)"));
396 state = s_woy; /* week of year */
399 /* everything else must be a keyword */
400 for (i=0; keyw[i].name; i++) {
401 if (strcasecmp(lc->str, keyw[i].name) == 0) {
402 state = keyw[i].state;
409 scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
416 scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
423 case s_mday: /* day of month */
425 clear_bits(0, 30, lrun.mday);
428 set_bit(code, lrun.mday);
430 case s_month: /* month of year */
432 clear_bits(0, 11, lrun.month);
435 set_bit(code, lrun.month);
437 case s_wday: /* week day */
439 clear_bits(0, 6, lrun.wday);
442 set_bit(code, lrun.wday);
444 case s_wom: /* Week of month 1st, ... */
446 clear_bits(0, 5, lrun.wom);
449 set_bit(code, lrun.wom);
453 clear_bits(0, 53, lrun.woy);
456 set_bit(code, lrun.woy);
458 case s_time: /* time */
460 scan_err0(lc, _("Time must be preceded by keyword AT."));
464 clear_bits(0, 23, lrun.hour);
466 // Dmsg1(000, "s_time=%s\n", lc->str);
467 p = strchr(lc->str, ':');
469 scan_err0(lc, _("Time logic error.\n"));
472 *p++ = 0; /* separate two halves */
473 code = atoi(lc->str); /* pick up hour */
474 code2 = atoi(p); /* pick up minutes */
479 if (strcasecmp(p, "pm") == 0) {
481 } else if (strcasecmp(p, "am") == 0) {
483 } else if (len != 2) {
484 scan_err0(lc, _("Bad time specification."));
488 * Note, according to NIST, 12am and 12pm are ambiguous and
489 * can be defined to anything. However, 12:01am is the same
490 * as 00:01 and 12:01pm is the same as 12:01, so we define
491 * 12am as 00:00 and 12pm as 12:00.
494 /* Convert to 24 hour time */
499 } else if (am && code == 12) {
502 if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
503 scan_err0(lc, _("Bad time specification."));
506 // Dmsg2(000, "hour=%d min=%d\n", code, code2);
507 set_bit(code, lrun.hour);
516 clear_bits(0, 30, lrun.mday);
519 lrun.last_day_set = true;
520 set_bit(31, lrun.mday); /* day 32 => last day of month */
523 p = strchr(lc->str, '-');
525 scan_err0(lc, _("Range logic error.\n"));
527 *p++ = 0; /* separate two halves */
529 /* Check for day range */
530 if (is_an_integer(lc->str) && is_an_integer(p)) {
531 code = atoi(lc->str) - 1;
533 if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
534 scan_err0(lc, _("Bad day range specification."));
537 clear_bits(0, 30, lrun.mday);
541 set_bits(code, code2, lrun.mday);
543 set_bits(code, 30, lrun.mday);
544 set_bits(0, code2, lrun.mday);
548 /* Check for week of year range */
549 if (strlen(lc->str) == 3 && strlen(p) == 3 &&
550 (lc->str[0] == 'w' || lc->str[0] == 'W') &&
551 (p[0] == 'w' || p[0] == 'W') &&
552 is_an_integer(lc->str+1) && is_an_integer(p+1)) {
553 code = atoi(lc->str+1);
555 if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
556 scan_err0(lc, _("Week number out of range (0-53)"));
559 clear_bits(0, 53, lrun.woy);
563 set_bits(code, code2, lrun.woy);
565 set_bits(code, 53, lrun.woy);
566 set_bits(0, code2, lrun.woy);
570 /* lookup first half of keyword range (week days or months) */
572 for (i=0; keyw[i].name; i++) {
573 if (strcasecmp(lc->str, keyw[i].name) == 0) {
574 state = keyw[i].state;
580 if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
581 scan_err0(lc, _("Invalid month, week or position day range"));
585 /* Lookup end of range */
587 for (i=0; keyw[i].name; i++) {
588 if (strcasecmp(p, keyw[i].name) == 0) {
589 state2 = keyw[i].state;
590 code2 = keyw[i].code;
595 if (i != 0 || state != state2 || code == code2) {
596 scan_err0(lc, _("Invalid month, weekday or position range"));
599 if (state == s_wday) {
601 clear_bits(0, 6, lrun.wday);
605 set_bits(code, code2, lrun.wday);
607 set_bits(code, 6, lrun.wday);
608 set_bits(0, code2, lrun.wday);
610 } else if (state == s_month) {
612 clear_bits(0, 11, lrun.month);
616 set_bits(code, code2, lrun.month);
618 /* this is a bit odd, but we accept it anyway */
619 set_bits(code, 11, lrun.month);
620 set_bits(0, code2, lrun.month);
623 /* Must be position */
625 clear_bits(0, 5, lrun.wom);
629 set_bits(code, code2, lrun.wom);
631 set_bits(code, 5, lrun.wom);
632 set_bits(0, code2, lrun.wom);
638 set_bits(0, 23, lrun.hour);
641 have_mday = have_wom = have_woy = true;
642 set_bits(0, 30, lrun.mday);
643 set_bits(0, 5, lrun.wom);
644 set_bits(0, 53, lrun.woy);
648 set_bits(0, 6, lrun.wday);
652 set_bits(0, 11, lrun.month);
655 scan_err0(lc, _("Unexpected run state\n"));
661 /* Allocate run record, copy new stuff into it,
662 * and append it to the list of run records
663 * in the schedule resource.
668 /* Create new run record */
669 RUN *nrun = (RUN *)malloc(sizeof(RUN));
670 memcpy(nrun, &lrun, sizeof(RUN));
673 if (!*run) { /* if empty list */
674 *run = nrun; /* add new record */
676 for (tail = *run; tail->next; tail=tail->next)
682 lc->options = options; /* restore scanner options */
683 set_bit(index, res_all.res_sch.hdr.item_present);