2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 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 Bacula® is a registered trademark of Kern Sibbald.
18 * Configuration parser for Director Run Configuration
19 * directives, which are part of the Schedule Resource
21 * Written by Kern Sibbald, May MM
29 extern "C" { // work around visual compiler mangling variables
35 extern s_jl joblevels[];
37 /* Forward referenced subroutines */
51 s_wom, /* 1st, 2nd, ...*/
52 s_woy, /* week of year w00 - w53 */
53 s_ldom /* last day of month */
57 const char *name; /* keyword */
58 enum e_state state; /* parser state */
59 int code; /* state value */
62 /* Keywords understood by parser */
63 static struct s_keyw keyw[] = {
64 {NT_("on"), s_none, 0},
66 {NT_("lastday"), s_ldom, 0},
68 {NT_("sun"), s_wday, 0},
69 {NT_("mon"), s_wday, 1},
70 {NT_("tue"), s_wday, 2},
71 {NT_("wed"), s_wday, 3},
72 {NT_("thu"), s_wday, 4},
73 {NT_("fri"), s_wday, 5},
74 {NT_("sat"), s_wday, 6},
75 {NT_("jan"), s_month, 0},
76 {NT_("feb"), s_month, 1},
77 {NT_("mar"), s_month, 2},
78 {NT_("apr"), s_month, 3},
79 {NT_("may"), s_month, 4},
80 {NT_("jun"), s_month, 5},
81 {NT_("jul"), s_month, 6},
82 {NT_("aug"), s_month, 7},
83 {NT_("sep"), s_month, 8},
84 {NT_("oct"), s_month, 9},
85 {NT_("nov"), s_month, 10},
86 {NT_("dec"), s_month, 11},
88 {NT_("sunday"), s_wday, 0},
89 {NT_("monday"), s_wday, 1},
90 {NT_("tuesday"), s_wday, 2},
91 {NT_("wednesday"), s_wday, 3},
92 {NT_("thursday"), s_wday, 4},
93 {NT_("friday"), s_wday, 5},
94 {NT_("saturday"), s_wday, 6},
95 {NT_("january"), s_month, 0},
96 {NT_("february"), s_month, 1},
97 {NT_("march"), s_month, 2},
98 {NT_("april"), s_month, 3},
99 {NT_("june"), s_month, 5},
100 {NT_("july"), s_month, 6},
101 {NT_("august"), s_month, 7},
102 {NT_("september"), s_month, 8},
103 {NT_("october"), s_month, 9},
104 {NT_("november"), s_month, 10},
105 {NT_("december"), s_month, 11},
107 {NT_("daily"), s_daily, 0},
108 {NT_("weekly"), s_weekly, 0},
109 {NT_("monthly"), s_monthly, 0},
110 {NT_("hourly"), s_hourly, 0},
112 {NT_("1st"), s_wom, 0},
113 {NT_("2nd"), s_wom, 1},
114 {NT_("3rd"), s_wom, 2},
115 {NT_("4th"), s_wom, 3},
116 {NT_("5th"), s_wom, 4},
117 {NT_("6th"), s_wom, 5},
119 {NT_("first"), s_wom, 0},
120 {NT_("second"), s_wom, 1},
121 {NT_("third"), s_wom, 2},
122 {NT_("fourth"), s_wom, 3},
123 {NT_("fifth"), s_wom, 4},
124 {NT_("sixth"), s_wom, 5},
128 static bool have_hour, have_mday, have_wday, have_month, have_wom;
129 static bool have_at, have_woy;
132 static void set_defaults()
134 have_hour = have_mday = have_wday = have_month = have_wom = have_woy = false;
136 set_bits(0, 23, lrun.hour);
137 set_bits(0, 30, lrun.mday);
138 set_bits(0, 6, lrun.wday);
139 set_bits(0, 11, lrun.month);
140 set_bits(0, 5, lrun.wom);
141 set_bits(0, 53, lrun.woy);
146 * Keywords (RHS) permitted in Run records
153 {"incrementalpool", 'i'},
154 {"differentialpool", 'd'},
160 {"writepartafterjob", 'W'},
161 {"maxrunschedtime", 'm'},
168 * Store Schedule Run information
170 * Parse Run statement:
172 * Run <keyword=value ...> [on] 2 january at 23:45
174 * Default Run time is daily at 0:0
176 * There can be multiple run statements, they are simply chained
180 void store_run(LEX *lc, RES_ITEM *item, int index, int pass)
185 int token, state, state2 = 0, code = 0, code2 = 0;
186 int options = lc->options;
187 RUN **run = (RUN **)(item->value);
192 lc->options |= LOPT_NO_IDENT; /* want only "strings" */
194 /* clear local copy of run record */
195 memset(&lrun, 0, sizeof(RUN));
197 /* scan for Job level "full", "incremental", ... */
198 for (found=true; found; ) {
200 token = lex_get_token(lc, T_NAME);
201 for (i=0; !found && RunFields[i].name; i++) {
202 if (strcasecmp(lc->str, RunFields[i].name) == 0) {
204 if (lex_get_token(lc, T_ALL) != T_EQUALS) {
205 scan_err1(lc, _("Expected an equals, got: %s"), lc->str);
208 switch (RunFields[i].token) {
209 case 's': /* Data spooling */
210 token = lex_get_token(lc, T_NAME);
211 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
212 lrun.spool_data = true;
213 lrun.spool_data_set = true;
214 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
215 lrun.spool_data = false;
216 lrun.spool_data_set = true;
218 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
221 case 'W': /* Write part after job */
222 token = lex_get_token(lc, T_NAME);
223 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
224 lrun.write_part_after_job = true;
225 lrun.write_part_after_job_set = true;
226 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
227 lrun.write_part_after_job = false;
228 lrun.write_part_after_job_set = true;
230 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
233 case 'L': /* level */
234 token = lex_get_token(lc, T_NAME);
235 for (j=0; joblevels[j].level_name; j++) {
236 if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
237 lrun.level = joblevels[j].level;
238 lrun.job_type = joblevels[j].job_type;
239 lrun.level_set = true;
245 scan_err1(lc, _("Job level field: %s not found in run record"), lc->str);
249 case 'p': /* Priority */
250 token = lex_get_token(lc, T_PINT32);
252 lrun.Priority = lc->pint32_val;
253 lrun.priority_set = true;
257 case 'N': /* NextPool */
258 case 'f': /* FullPool */
259 case 'i': /* IncPool */
260 case 'd': /* DifPool */
261 token = lex_get_token(lc, T_NAME);
263 res = GetResWithName(R_POOL, lc->str);
265 scan_err1(lc, _("Could not find specified Pool Resource: %s"),
269 switch(RunFields[i].token) {
271 lrun.pool = (POOL *)res;
274 lrun.next_pool = (POOL *)res;
277 lrun.full_pool = (POOL *)res;
280 lrun.inc_pool = (POOL *)res;
283 lrun.diff_pool = (POOL *)res;
288 case 'S': /* storage */
289 token = lex_get_token(lc, T_NAME);
291 res = GetResWithName(R_STORAGE, lc->str);
293 scan_err1(lc, _("Could not find specified Storage Resource: %s"),
297 lrun.storage = (STORE *)res;
300 case 'M': /* messages */
301 token = lex_get_token(lc, T_NAME);
303 res = GetResWithName(R_MSGS, lc->str);
305 scan_err1(lc, _("Could not find specified Messages Resource: %s"),
309 lrun.msgs = (MSGS *)res;
312 case 'm': /* max run sched time */
313 token = lex_get_token(lc, T_QUOTED_STRING);
314 if (!duration_to_utime(lc->str, &utime)) {
315 scan_err1(lc, _("expected a time period, got: %s"), lc->str);
318 lrun.MaxRunSchedTime = utime;
319 lrun.MaxRunSchedTime_set = true;
321 case 'a': /* accurate */
322 token = lex_get_token(lc, T_NAME);
323 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
324 lrun.accurate = true;
325 lrun.accurate_set = true;
326 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
327 lrun.accurate = false;
328 lrun.accurate_set = true;
330 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
334 scan_err1(lc, _("Expected a keyword name, got: %s"), lc->str);
338 } /* end if strcasecmp */
339 } /* end for RunFields */
341 /* At this point, it is not a keyword. Check for old syle
342 * Job Levels without keyword. This form is depreciated!!!
345 for (j=0; joblevels[j].level_name; j++) {
346 if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
347 lrun.level = joblevels[j].level;
348 lrun.job_type = joblevels[j].job_type;
354 } /* end for found */
358 * Scan schedule times.
359 * Default is: daily at 0:0
364 for ( ; token != T_EOL; (token = lex_get_token(lc, T_ALL))) {
371 code = atoi(lc->str) - 1;
372 if (code < 0 || code > 30) {
373 scan_err0(lc, _("Day number out of range (1-31)"));
376 case T_NAME: /* this handles drop through from keyword */
377 case T_UNQUOTED_STRING:
378 if (strchr(lc->str, (int)'-')) {
382 if (strchr(lc->str, (int)':')) {
386 if (lc->str_len == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') &&
387 is_an_integer(lc->str+1)) {
388 code = atoi(lc->str+1);
389 if (code < 0 || code > 53) {
390 scan_err0(lc, _("Week number out of range (0-53)"));
393 state = s_woy; /* week of year */
396 /* everything else must be a keyword */
397 for (i=0; keyw[i].name; i++) {
398 if (strcasecmp(lc->str, keyw[i].name) == 0) {
399 state = keyw[i].state;
406 scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
413 scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
420 case s_mday: /* day of month */
422 clear_bits(0, 30, lrun.mday);
425 set_bit(code, lrun.mday);
427 case s_month: /* month of year */
429 clear_bits(0, 11, lrun.month);
432 set_bit(code, lrun.month);
434 case s_wday: /* week day */
436 clear_bits(0, 6, lrun.wday);
439 set_bit(code, lrun.wday);
441 case s_wom: /* Week of month 1st, ... */
443 clear_bits(0, 5, lrun.wom);
446 set_bit(code, lrun.wom);
450 clear_bits(0, 53, lrun.woy);
453 set_bit(code, lrun.woy);
455 case s_time: /* time */
457 scan_err0(lc, _("Time must be preceded by keyword AT."));
461 clear_bits(0, 23, lrun.hour);
463 // Dmsg1(000, "s_time=%s\n", lc->str);
464 p = strchr(lc->str, ':');
466 scan_err0(lc, _("Time logic error.\n"));
469 *p++ = 0; /* separate two halves */
470 code = atoi(lc->str); /* pick up hour */
471 code2 = atoi(p); /* pick up minutes */
476 if (strcasecmp(p, "pm") == 0) {
478 } else if (strcasecmp(p, "am") == 0) {
480 } else if (len != 2) {
481 scan_err0(lc, _("Bad time specification."));
485 * Note, according to NIST, 12am and 12pm are ambiguous and
486 * can be defined to anything. However, 12:01am is the same
487 * as 00:01 and 12:01pm is the same as 12:01, so we define
488 * 12am as 00:00 and 12pm as 12:00.
491 /* Convert to 24 hour time */
496 } else if (am && code == 12) {
499 if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
500 scan_err0(lc, _("Bad time specification."));
503 // Dmsg2(000, "hour=%d min=%d\n", code, code2);
504 set_bit(code, lrun.hour);
513 clear_bits(0, 30, lrun.mday);
516 set_bit(31, lrun.mday); /* day 32 => last day of month */
519 p = strchr(lc->str, '-');
521 scan_err0(lc, _("Range logic error.\n"));
523 *p++ = 0; /* separate two halves */
525 /* Check for day range */
526 if (is_an_integer(lc->str) && is_an_integer(p)) {
527 code = atoi(lc->str) - 1;
529 if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
530 scan_err0(lc, _("Bad day range specification."));
533 clear_bits(0, 30, lrun.mday);
537 set_bits(code, code2, lrun.mday);
539 set_bits(code, 30, lrun.mday);
540 set_bits(0, code2, lrun.mday);
544 /* Check for week of year range */
545 if (strlen(lc->str) == 3 && strlen(p) == 3 &&
546 (lc->str[0] == 'w' || lc->str[0] == 'W') &&
547 (p[0] == 'w' || p[0] == 'W') &&
548 is_an_integer(lc->str+1) && is_an_integer(p+1)) {
549 code = atoi(lc->str+1);
551 if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
552 scan_err0(lc, _("Week number out of range (0-53)"));
555 clear_bits(0, 53, lrun.woy);
559 set_bits(code, code2, lrun.woy);
561 set_bits(code, 53, lrun.woy);
562 set_bits(0, code2, lrun.woy);
566 /* lookup first half of keyword range (week days or months) */
568 for (i=0; keyw[i].name; i++) {
569 if (strcasecmp(lc->str, keyw[i].name) == 0) {
570 state = keyw[i].state;
576 if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
577 scan_err0(lc, _("Invalid month, week or position day range"));
581 /* Lookup end of range */
583 for (i=0; keyw[i].name; i++) {
584 if (strcasecmp(p, keyw[i].name) == 0) {
585 state2 = keyw[i].state;
586 code2 = keyw[i].code;
591 if (i != 0 || state != state2 || code == code2) {
592 scan_err0(lc, _("Invalid month, weekday or position range"));
595 if (state == s_wday) {
597 clear_bits(0, 6, lrun.wday);
601 set_bits(code, code2, lrun.wday);
603 set_bits(code, 6, lrun.wday);
604 set_bits(0, code2, lrun.wday);
606 } else if (state == s_month) {
608 clear_bits(0, 11, lrun.month);
612 set_bits(code, code2, lrun.month);
614 /* this is a bit odd, but we accept it anyway */
615 set_bits(code, 11, lrun.month);
616 set_bits(0, code2, lrun.month);
619 /* Must be position */
621 clear_bits(0, 5, lrun.wom);
625 set_bits(code, code2, lrun.wom);
627 set_bits(code, 5, lrun.wom);
628 set_bits(0, code2, lrun.wom);
634 set_bits(0, 23, lrun.hour);
637 have_mday = have_wom = have_woy = true;
638 set_bits(0, 30, lrun.mday);
639 set_bits(0, 5, lrun.wom);
640 set_bits(0, 53, lrun.woy);
644 set_bits(0, 6, lrun.wday);
648 set_bits(0, 11, lrun.month);
651 scan_err0(lc, _("Unexpected run state\n"));
657 /* Allocate run record, copy new stuff into it,
658 * and append it to the list of run records
659 * in the schedule resource.
664 /* Create new run record */
665 RUN *nrun = (RUN *)malloc(sizeof(RUN));
666 memcpy(nrun, &lrun, sizeof(RUN));
669 if (!*run) { /* if empty list */
670 *run = nrun; /* add new record */
672 for (tail = *run; tail->next; tail=tail->next)
678 lc->options = options; /* restore scanner options */
679 set_bit(index, res_all.res_sch.hdr.item_present);