2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
22 * Configuration parser for Director Run Configuration
23 * directives, which are part of the Schedule Resource
25 * Kern Sibbald, May MM
33 extern "C" { // work around visual compiler mangling variables
39 extern s_jl joblevels[];
41 /* Forward referenced subroutines */
55 s_wom, /* 1st, 2nd, ...*/
56 s_woy, /* week of year w00 - w53 */
57 s_ldom /* last day of month */
61 const char *name; /* keyword */
62 enum e_state state; /* parser state */
63 int code; /* state value */
66 /* Keywords understood by parser */
67 static struct s_keyw keyw[] = {
68 {NT_("on"), s_none, 0},
70 {NT_("lastday"), s_ldom, 0},
72 {NT_("sun"), s_wday, 0},
73 {NT_("mon"), s_wday, 1},
74 {NT_("tue"), s_wday, 2},
75 {NT_("wed"), s_wday, 3},
76 {NT_("thu"), s_wday, 4},
77 {NT_("fri"), s_wday, 5},
78 {NT_("sat"), s_wday, 6},
79 {NT_("jan"), s_month, 0},
80 {NT_("feb"), s_month, 1},
81 {NT_("mar"), s_month, 2},
82 {NT_("apr"), s_month, 3},
83 {NT_("may"), s_month, 4},
84 {NT_("jun"), s_month, 5},
85 {NT_("jul"), s_month, 6},
86 {NT_("aug"), s_month, 7},
87 {NT_("sep"), s_month, 8},
88 {NT_("oct"), s_month, 9},
89 {NT_("nov"), s_month, 10},
90 {NT_("dec"), s_month, 11},
92 {NT_("sunday"), s_wday, 0},
93 {NT_("monday"), s_wday, 1},
94 {NT_("tuesday"), s_wday, 2},
95 {NT_("wednesday"), s_wday, 3},
96 {NT_("thursday"), s_wday, 4},
97 {NT_("friday"), s_wday, 5},
98 {NT_("saturday"), s_wday, 6},
99 {NT_("january"), s_month, 0},
100 {NT_("february"), s_month, 1},
101 {NT_("march"), s_month, 2},
102 {NT_("april"), s_month, 3},
103 {NT_("june"), s_month, 5},
104 {NT_("july"), s_month, 6},
105 {NT_("august"), s_month, 7},
106 {NT_("september"), s_month, 8},
107 {NT_("october"), s_month, 9},
108 {NT_("november"), s_month, 10},
109 {NT_("december"), s_month, 11},
111 {NT_("daily"), s_daily, 0},
112 {NT_("weekly"), s_weekly, 0},
113 {NT_("monthly"), s_monthly, 0},
114 {NT_("hourly"), s_hourly, 0},
116 {NT_("1st"), s_wom, 0},
117 {NT_("2nd"), s_wom, 1},
118 {NT_("3rd"), s_wom, 2},
119 {NT_("4th"), s_wom, 3},
120 {NT_("5th"), s_wom, 4},
121 {NT_("6th"), s_wom, 5},
123 {NT_("first"), s_wom, 0},
124 {NT_("second"), s_wom, 1},
125 {NT_("third"), s_wom, 2},
126 {NT_("fourth"), s_wom, 3},
127 {NT_("fifth"), s_wom, 4},
128 {NT_("sixth"), s_wom, 5},
132 static bool have_hour, have_mday, have_wday, have_month, have_wom;
133 static bool have_at, have_woy;
136 static void set_defaults()
138 have_hour = have_mday = have_wday = have_month = have_wom = have_woy = false;
140 set_bits(0, 23, lrun.hour);
141 set_bits(0, 30, lrun.mday);
142 set_bits(0, 6, lrun.wday);
143 set_bits(0, 11, lrun.month);
144 set_bits(0, 5, lrun.wom);
145 set_bits(0, 53, lrun.woy);
150 * Keywords (RHS) permitted in Run records
157 {"IncrementalPool", 'i'},
158 {"DifferentialPool", 'd'},
164 {"writepartafterjob", 'W'},
165 {"MaxRunSchedTime", 'm'},
172 * Store Schedule Run information
174 * Parse Run statement:
176 * Run <keyword=value ...> [on] 2 january at 23:45
178 * Default Run time is daily at 0:0
180 * There can be multiple run statements, they are simply chained
184 void store_run(LEX *lc, RES_ITEM *item, int index, int pass)
189 int token, state, state2 = 0, code = 0, code2 = 0;
190 int options = lc->options;
191 RUN **run = (RUN **)(item->value);
196 lc->options |= LOPT_NO_IDENT; /* want only "strings" */
198 /* clear local copy of run record */
199 memset(&lrun, 0, sizeof(RUN));
201 /* scan for Job level "full", "incremental", ... */
202 for (found=true; found; ) {
204 token = lex_get_token(lc, T_NAME);
205 for (i=0; !found && RunFields[i].name; i++) {
206 if (strcasecmp(lc->str, RunFields[i].name) == 0) {
208 if (lex_get_token(lc, T_ALL) != T_EQUALS) {
209 scan_err1(lc, _("Expected an equals, got: %s"), lc->str);
212 switch (RunFields[i].token) {
213 case 's': /* Data spooling */
214 token = lex_get_token(lc, T_NAME);
215 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
216 lrun.spool_data = true;
217 lrun.spool_data_set = true;
218 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
219 lrun.spool_data = false;
220 lrun.spool_data_set = true;
222 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
225 case 'W': /* Write part after job */
226 token = lex_get_token(lc, T_NAME);
227 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
228 lrun.write_part_after_job = true;
229 lrun.write_part_after_job_set = true;
230 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
231 lrun.write_part_after_job = false;
232 lrun.write_part_after_job_set = true;
234 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
237 case 'L': /* level */
238 token = lex_get_token(lc, T_NAME);
239 for (j=0; joblevels[j].level_name; j++) {
240 if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
241 lrun.level = joblevels[j].level;
242 lrun.job_type = joblevels[j].job_type;
243 lrun.level_set = true;
249 scan_err1(lc, _("Job level field: %s not found in run record"), lc->str);
253 case 'p': /* Priority */
254 token = lex_get_token(lc, T_PINT32);
256 lrun.Priority = lc->pint32_val;
257 lrun.priority_set = true;
261 case 'N': /* NextPool */
262 case 'f': /* FullPool */
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.inc_pool = (POOL *)res;
287 lrun.diff_pool = (POOL *)res;
292 case 'S': /* storage */
293 token = lex_get_token(lc, T_NAME);
295 res = GetResWithName(R_STORAGE, lc->str);
297 scan_err1(lc, _("Could not find specified Storage Resource: %s"),
301 lrun.storage = (STORE *)res;
304 case 'M': /* messages */
305 token = lex_get_token(lc, T_NAME);
307 res = GetResWithName(R_MSGS, lc->str);
309 scan_err1(lc, _("Could not find specified Messages Resource: %s"),
313 lrun.msgs = (MSGS *)res;
316 case 'm': /* max run sched time */
317 token = lex_get_token(lc, T_QUOTED_STRING);
318 if (!duration_to_utime(lc->str, &utime)) {
319 scan_err1(lc, _("expected a time period, got: %s"), lc->str);
322 lrun.MaxRunSchedTime = utime;
323 lrun.MaxRunSchedTime_set = true;
325 case 'a': /* accurate */
326 token = lex_get_token(lc, T_NAME);
327 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
328 lrun.accurate = true;
329 lrun.accurate_set = true;
330 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
331 lrun.accurate = false;
332 lrun.accurate_set = true;
334 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
338 scan_err1(lc, _("Expected a keyword name, got: %s"), lc->str);
342 } /* end if strcasecmp */
343 } /* end for RunFields */
345 /* At this point, it is not a keyword. Check for old syle
346 * Job Levels without keyword. This form is depreciated!!!
349 for (j=0; joblevels[j].level_name; j++) {
350 if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
351 lrun.level = joblevels[j].level;
352 lrun.job_type = joblevels[j].job_type;
358 } /* end for found */
362 * Scan schedule times.
363 * Default is: daily at 0:0
368 for ( ; token != T_EOL; (token = lex_get_token(lc, T_ALL))) {
375 code = atoi(lc->str) - 1;
376 if (code < 0 || code > 30) {
377 scan_err0(lc, _("Day number out of range (1-31)"));
380 case T_NAME: /* this handles drop through from keyword */
381 case T_UNQUOTED_STRING:
382 if (strchr(lc->str, (int)'-')) {
386 if (strchr(lc->str, (int)':')) {
390 if (lc->str_len == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') &&
391 is_an_integer(lc->str+1)) {
392 code = atoi(lc->str+1);
393 if (code < 0 || code > 53) {
394 scan_err0(lc, _("Week number out of range (0-53)"));
397 state = s_woy; /* week of year */
400 /* everything else must be a keyword */
401 for (i=0; keyw[i].name; i++) {
402 if (strcasecmp(lc->str, keyw[i].name) == 0) {
403 state = keyw[i].state;
410 scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
417 scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
424 case s_mday: /* day of month */
426 clear_bits(0, 30, lrun.mday);
429 set_bit(code, lrun.mday);
431 case s_month: /* month of year */
433 clear_bits(0, 11, lrun.month);
436 set_bit(code, lrun.month);
438 case s_wday: /* week day */
440 clear_bits(0, 6, lrun.wday);
443 set_bit(code, lrun.wday);
445 case s_wom: /* Week of month 1st, ... */
447 clear_bits(0, 5, lrun.wom);
450 set_bit(code, lrun.wom);
454 clear_bits(0, 53, lrun.woy);
457 set_bit(code, lrun.woy);
459 case s_time: /* time */
461 scan_err0(lc, _("Time must be preceded by keyword AT."));
465 clear_bits(0, 23, lrun.hour);
467 // Dmsg1(000, "s_time=%s\n", lc->str);
468 p = strchr(lc->str, ':');
470 scan_err0(lc, _("Time logic error.\n"));
473 *p++ = 0; /* separate two halves */
474 code = atoi(lc->str); /* pick up hour */
475 code2 = atoi(p); /* pick up minutes */
480 if (strcasecmp(p, "pm") == 0) {
482 } else if (strcasecmp(p, "am") == 0) {
484 } else if (len != 2) {
485 scan_err0(lc, _("Bad time specification."));
489 * Note, according to NIST, 12am and 12pm are ambiguous and
490 * can be defined to anything. However, 12:01am is the same
491 * as 00:01 and 12:01pm is the same as 12:01, so we define
492 * 12am as 00:00 and 12pm as 12:00.
495 /* Convert to 24 hour time */
500 } else if (am && code == 12) {
503 if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
504 scan_err0(lc, _("Bad time specification."));
507 // Dmsg2(000, "hour=%d min=%d\n", code, code2);
508 set_bit(code, lrun.hour);
517 clear_bits(0, 30, lrun.mday);
520 lrun.last_day_set = true;
521 set_bit(31, lrun.mday); /* day 32 => last day of month */
524 p = strchr(lc->str, '-');
526 scan_err0(lc, _("Range logic error.\n"));
528 *p++ = 0; /* separate two halves */
530 /* Check for day range */
531 if (is_an_integer(lc->str) && is_an_integer(p)) {
532 code = atoi(lc->str) - 1;
534 if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
535 scan_err0(lc, _("Bad day range specification."));
538 clear_bits(0, 30, lrun.mday);
542 set_bits(code, code2, lrun.mday);
544 set_bits(code, 30, lrun.mday);
545 set_bits(0, code2, lrun.mday);
549 /* Check for week of year range */
550 if (strlen(lc->str) == 3 && strlen(p) == 3 &&
551 (lc->str[0] == 'w' || lc->str[0] == 'W') &&
552 (p[0] == 'w' || p[0] == 'W') &&
553 is_an_integer(lc->str+1) && is_an_integer(p+1)) {
554 code = atoi(lc->str+1);
556 if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
557 scan_err0(lc, _("Week number out of range (0-53)"));
560 clear_bits(0, 53, lrun.woy);
564 set_bits(code, code2, lrun.woy);
566 set_bits(code, 53, lrun.woy);
567 set_bits(0, code2, lrun.woy);
571 /* lookup first half of keyword range (week days or months) */
573 for (i=0; keyw[i].name; i++) {
574 if (strcasecmp(lc->str, keyw[i].name) == 0) {
575 state = keyw[i].state;
581 if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
582 scan_err0(lc, _("Invalid month, week or position day range"));
586 /* Lookup end of range */
588 for (i=0; keyw[i].name; i++) {
589 if (strcasecmp(p, keyw[i].name) == 0) {
590 state2 = keyw[i].state;
591 code2 = keyw[i].code;
596 if (i != 0 || state != state2 || code == code2) {
597 scan_err0(lc, _("Invalid month, weekday or position range"));
600 if (state == s_wday) {
602 clear_bits(0, 6, lrun.wday);
606 set_bits(code, code2, lrun.wday);
608 set_bits(code, 6, lrun.wday);
609 set_bits(0, code2, lrun.wday);
611 } else if (state == s_month) {
613 clear_bits(0, 11, lrun.month);
617 set_bits(code, code2, lrun.month);
619 /* this is a bit odd, but we accept it anyway */
620 set_bits(code, 11, lrun.month);
621 set_bits(0, code2, lrun.month);
624 /* Must be position */
626 clear_bits(0, 5, lrun.wom);
630 set_bits(code, code2, lrun.wom);
632 set_bits(code, 5, lrun.wom);
633 set_bits(0, code2, lrun.wom);
639 set_bits(0, 23, lrun.hour);
642 have_mday = have_wom = have_woy = true;
643 set_bits(0, 30, lrun.mday);
644 set_bits(0, 5, lrun.wom);
645 set_bits(0, 53, lrun.woy);
649 set_bits(0, 6, lrun.wday);
653 set_bits(0, 11, lrun.month);
656 scan_err0(lc, _("Unexpected run state\n"));
662 /* Allocate run record, copy new stuff into it,
663 * and append it to the list of run records
664 * in the schedule resource.
669 /* Create new run record */
670 RUN *nrun = (RUN *)malloc(sizeof(RUN));
671 memcpy(nrun, &lrun, sizeof(RUN));
674 if (!*run) { /* if empty list */
675 *run = nrun; /* add new record */
677 for (tail = *run; tail->next; tail=tail->next)
683 lc->options = options; /* restore scanner options */
684 set_bit(index, res_all.res_sch.hdr.item_present);