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 'v': /* VFullPool */
263 case 'i': /* IncPool */
264 case 'd': /* DifPool */
265 token = lex_get_token(lc, T_NAME);
267 res = GetResWithName(R_POOL, lc->str);
269 scan_err1(lc, _("Could not find specified Pool Resource: %s"),
273 switch(RunFields[i].token) {
275 lrun.pool = (POOL *)res;
278 lrun.next_pool = (POOL *)res;
281 lrun.full_pool = (POOL *)res;
284 lrun.vfull_pool = (POOL *)res;
287 lrun.inc_pool = (POOL *)res;
290 lrun.diff_pool = (POOL *)res;
295 case 'S': /* storage */
296 token = lex_get_token(lc, T_NAME);
298 res = GetResWithName(R_STORAGE, lc->str);
300 scan_err1(lc, _("Could not find specified Storage Resource: %s"),
304 lrun.storage = (STORE *)res;
307 case 'M': /* messages */
308 token = lex_get_token(lc, T_NAME);
310 res = GetResWithName(R_MSGS, lc->str);
312 scan_err1(lc, _("Could not find specified Messages Resource: %s"),
316 lrun.msgs = (MSGS *)res;
319 case 'm': /* max run sched time */
320 token = lex_get_token(lc, T_QUOTED_STRING);
321 if (!duration_to_utime(lc->str, &utime)) {
322 scan_err1(lc, _("expected a time period, got: %s"), lc->str);
325 lrun.MaxRunSchedTime = utime;
326 lrun.MaxRunSchedTime_set = true;
328 case 'a': /* accurate */
329 token = lex_get_token(lc, T_NAME);
330 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
331 lrun.accurate = true;
332 lrun.accurate_set = true;
333 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
334 lrun.accurate = false;
335 lrun.accurate_set = true;
337 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
341 scan_err1(lc, _("Expected a keyword name, got: %s"), lc->str);
345 } /* end if strcasecmp */
346 } /* end for RunFields */
348 /* At this point, it is not a keyword. Check for old syle
349 * Job Levels without keyword. This form is depreciated!!!
352 for (j=0; joblevels[j].level_name; j++) {
353 if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
354 lrun.level = joblevels[j].level;
355 lrun.job_type = joblevels[j].job_type;
361 } /* end for found */
365 * Scan schedule times.
366 * Default is: daily at 0:0
371 for ( ; token != T_EOL; (token = lex_get_token(lc, T_ALL))) {
378 code = atoi(lc->str) - 1;
379 if (code < 0 || code > 30) {
380 scan_err0(lc, _("Day number out of range (1-31)"));
383 case T_NAME: /* this handles drop through from keyword */
384 case T_UNQUOTED_STRING:
385 if (strchr(lc->str, (int)'-')) {
389 if (strchr(lc->str, (int)':')) {
393 if (lc->str_len == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') &&
394 is_an_integer(lc->str+1)) {
395 code = atoi(lc->str+1);
396 if (code < 0 || code > 53) {
397 scan_err0(lc, _("Week number out of range (0-53)"));
400 state = s_woy; /* week of year */
403 /* everything else must be a keyword */
404 for (i=0; keyw[i].name; i++) {
405 if (strcasecmp(lc->str, keyw[i].name) == 0) {
406 state = keyw[i].state;
413 scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
420 scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
427 case s_mday: /* day of month */
429 clear_bits(0, 30, lrun.mday);
432 set_bit(code, lrun.mday);
434 case s_month: /* month of year */
436 clear_bits(0, 11, lrun.month);
439 set_bit(code, lrun.month);
441 case s_wday: /* week day */
443 clear_bits(0, 6, lrun.wday);
446 set_bit(code, lrun.wday);
448 case s_wom: /* Week of month 1st, ... */
450 clear_bits(0, 5, lrun.wom);
453 set_bit(code, lrun.wom);
457 clear_bits(0, 53, lrun.woy);
460 set_bit(code, lrun.woy);
462 case s_time: /* time */
464 scan_err0(lc, _("Time must be preceded by keyword AT."));
468 clear_bits(0, 23, lrun.hour);
470 // Dmsg1(000, "s_time=%s\n", lc->str);
471 p = strchr(lc->str, ':');
473 scan_err0(lc, _("Time logic error.\n"));
476 *p++ = 0; /* separate two halves */
477 code = atoi(lc->str); /* pick up hour */
478 code2 = atoi(p); /* pick up minutes */
483 if (strcasecmp(p, "pm") == 0) {
485 } else if (strcasecmp(p, "am") == 0) {
487 } else if (len != 2) {
488 scan_err0(lc, _("Bad time specification."));
492 * Note, according to NIST, 12am and 12pm are ambiguous and
493 * can be defined to anything. However, 12:01am is the same
494 * as 00:01 and 12:01pm is the same as 12:01, so we define
495 * 12am as 00:00 and 12pm as 12:00.
498 /* Convert to 24 hour time */
503 } else if (am && code == 12) {
506 if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
507 scan_err0(lc, _("Bad time specification."));
510 // Dmsg2(000, "hour=%d min=%d\n", code, code2);
511 set_bit(code, lrun.hour);
520 clear_bits(0, 30, lrun.mday);
523 lrun.last_day_set = true;
524 set_bit(31, lrun.mday); /* day 32 => last day of month */
527 p = strchr(lc->str, '-');
529 scan_err0(lc, _("Range logic error.\n"));
531 *p++ = 0; /* separate two halves */
533 /* Check for day range */
534 if (is_an_integer(lc->str) && is_an_integer(p)) {
535 code = atoi(lc->str) - 1;
537 if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
538 scan_err0(lc, _("Bad day range specification."));
541 clear_bits(0, 30, lrun.mday);
545 set_bits(code, code2, lrun.mday);
547 set_bits(code, 30, lrun.mday);
548 set_bits(0, code2, lrun.mday);
552 /* Check for week of year range */
553 if (strlen(lc->str) == 3 && strlen(p) == 3 &&
554 (lc->str[0] == 'w' || lc->str[0] == 'W') &&
555 (p[0] == 'w' || p[0] == 'W') &&
556 is_an_integer(lc->str+1) && is_an_integer(p+1)) {
557 code = atoi(lc->str+1);
559 if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
560 scan_err0(lc, _("Week number out of range (0-53)"));
563 clear_bits(0, 53, lrun.woy);
567 set_bits(code, code2, lrun.woy);
569 set_bits(code, 53, lrun.woy);
570 set_bits(0, code2, lrun.woy);
574 /* lookup first half of keyword range (week days or months) */
576 for (i=0; keyw[i].name; i++) {
577 if (strcasecmp(lc->str, keyw[i].name) == 0) {
578 state = keyw[i].state;
584 if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
585 scan_err0(lc, _("Invalid month, week or position day range"));
589 /* Lookup end of range */
591 for (i=0; keyw[i].name; i++) {
592 if (strcasecmp(p, keyw[i].name) == 0) {
593 state2 = keyw[i].state;
594 code2 = keyw[i].code;
599 if (i != 0 || state != state2 || code == code2) {
600 scan_err0(lc, _("Invalid month, weekday or position range"));
603 if (state == s_wday) {
605 clear_bits(0, 6, lrun.wday);
609 set_bits(code, code2, lrun.wday);
611 set_bits(code, 6, lrun.wday);
612 set_bits(0, code2, lrun.wday);
614 } else if (state == s_month) {
616 clear_bits(0, 11, lrun.month);
620 set_bits(code, code2, lrun.month);
622 /* this is a bit odd, but we accept it anyway */
623 set_bits(code, 11, lrun.month);
624 set_bits(0, code2, lrun.month);
627 /* Must be position */
629 clear_bits(0, 5, lrun.wom);
633 set_bits(code, code2, lrun.wom);
635 set_bits(code, 5, lrun.wom);
636 set_bits(0, code2, lrun.wom);
642 set_bits(0, 23, lrun.hour);
645 have_mday = have_wom = have_woy = true;
646 set_bits(0, 30, lrun.mday);
647 set_bits(0, 5, lrun.wom);
648 set_bits(0, 53, lrun.woy);
652 set_bits(0, 6, lrun.wday);
656 set_bits(0, 11, lrun.month);
659 scan_err0(lc, _("Unexpected run state\n"));
665 /* Allocate run record, copy new stuff into it,
666 * and append it to the list of run records
667 * in the schedule resource.
672 /* Create new run record */
673 RUN *nrun = (RUN *)malloc(sizeof(RUN));
674 memcpy(nrun, &lrun, sizeof(RUN));
677 if (!*run) { /* if empty list */
678 *run = nrun; /* add new record */
680 for (tail = *run; tail->next; tail=tail->next)
686 lc->options = options; /* restore scanner options */
687 set_bit(index, res_all.res_sch.hdr.item_present);