2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2011 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
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
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 General Public License for more details.
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
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Configuration parser for Director Run Configuration
31 * directives, which are part of the Schedule Resource
33 * Kern Sibbald, May MM
41 extern "C" { // work around visual compiler mangling variables
47 extern struct s_jl joblevels[];
49 /* Forward referenced subroutines */
63 s_wom, /* 1st, 2nd, ...*/
64 s_woy /* week of year w00 - w53 */
68 const char *name; /* keyword */
69 enum e_state state; /* parser state */
70 int code; /* state value */
73 /* Keywords understood by parser */
74 static struct s_keyw keyw[] = {
75 {NT_("on"), s_none, 0},
78 {NT_("sun"), s_wday, 0},
79 {NT_("mon"), s_wday, 1},
80 {NT_("tue"), s_wday, 2},
81 {NT_("wed"), s_wday, 3},
82 {NT_("thu"), s_wday, 4},
83 {NT_("fri"), s_wday, 5},
84 {NT_("sat"), s_wday, 6},
85 {NT_("jan"), s_month, 0},
86 {NT_("feb"), s_month, 1},
87 {NT_("mar"), s_month, 2},
88 {NT_("apr"), s_month, 3},
89 {NT_("may"), s_month, 4},
90 {NT_("jun"), s_month, 5},
91 {NT_("jul"), s_month, 6},
92 {NT_("aug"), s_month, 7},
93 {NT_("sep"), s_month, 8},
94 {NT_("oct"), s_month, 9},
95 {NT_("nov"), s_month, 10},
96 {NT_("dec"), s_month, 11},
98 {NT_("sunday"), s_wday, 0},
99 {NT_("monday"), s_wday, 1},
100 {NT_("tuesday"), s_wday, 2},
101 {NT_("wednesday"), s_wday, 3},
102 {NT_("thursday"), s_wday, 4},
103 {NT_("friday"), s_wday, 5},
104 {NT_("saturday"), s_wday, 6},
105 {NT_("january"), s_month, 0},
106 {NT_("february"), s_month, 1},
107 {NT_("march"), s_month, 2},
108 {NT_("april"), s_month, 3},
109 {NT_("june"), s_month, 5},
110 {NT_("july"), s_month, 6},
111 {NT_("august"), s_month, 7},
112 {NT_("september"), s_month, 8},
113 {NT_("october"), s_month, 9},
114 {NT_("november"), s_month, 10},
115 {NT_("december"), s_month, 11},
117 {NT_("daily"), s_daily, 0},
118 {NT_("weekly"), s_weekly, 0},
119 {NT_("monthly"), s_monthly, 0},
120 {NT_("hourly"), s_hourly, 0},
122 {NT_("1st"), s_wom, 0},
123 {NT_("2nd"), s_wom, 1},
124 {NT_("3rd"), s_wom, 2},
125 {NT_("4th"), s_wom, 3},
126 {NT_("5th"), s_wom, 4},
128 {NT_("first"), s_wom, 0},
129 {NT_("second"), s_wom, 1},
130 {NT_("third"), s_wom, 2},
131 {NT_("fourth"), s_wom, 3},
132 {NT_("fifth"), s_wom, 4},
136 static bool have_hour, have_mday, have_wday, have_month, have_wom;
137 static bool have_at, have_woy;
140 static void set_defaults()
142 have_hour = have_mday = have_wday = have_month = have_wom = have_woy = false;
144 set_bits(0, 23, lrun.hour);
145 set_bits(0, 30, lrun.mday);
146 set_bits(0, 6, lrun.wday);
147 set_bits(0, 11, lrun.month);
148 set_bits(0, 4, lrun.wom);
149 set_bits(0, 53, lrun.woy);
153 /* Keywords (RHS) permitted in Run records */
154 static struct s_kw RunFields[] = {
157 {"incrementalpool", 'i'},
158 {"differentialpool", 'd'},
164 {"writepartafterjob", 'W'},
165 {"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; 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;
247 scan_err1(lc, _("Job level field: %s not found in run record"), lc->str);
251 case 'p': /* Priority */
252 token = lex_get_token(lc, T_PINT32);
254 lrun.Priority = lc->pint32_val;
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.full_pool = (POOL *)res;
277 lrun.inc_pool = (POOL *)res;
280 lrun.diff_pool = (POOL *)res;
285 case 'S': /* storage */
286 token = lex_get_token(lc, T_NAME);
288 res = GetResWithName(R_STORAGE, lc->str);
290 scan_err1(lc, _("Could not find specified Storage Resource: %s"),
294 lrun.storage = (STORE *)res;
297 case 'M': /* messages */
298 token = lex_get_token(lc, T_NAME);
300 res = GetResWithName(R_MSGS, lc->str);
302 scan_err1(lc, _("Could not find specified Messages Resource: %s"),
306 lrun.msgs = (MSGS *)res;
309 case 'm': /* max run sched time */
310 token = lex_get_token(lc, T_QUOTED_STRING);
311 if (!duration_to_utime(lc->str, &utime)) {
312 scan_err1(lc, _("expected a time period, got: %s"), lc->str);
315 lrun.MaxRunSchedTime = utime;
316 lrun.MaxRunSchedTime_set = true;
318 case 'a': /* accurate */
319 token = lex_get_token(lc, T_NAME);
320 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
321 lrun.accurate = true;
322 lrun.accurate_set = true;
323 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
324 lrun.accurate = false;
325 lrun.accurate_set = true;
327 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
331 scan_err1(lc, _("Expected a keyword name, got: %s"), lc->str);
335 } /* end if strcasecmp */
336 } /* end for RunFields */
338 /* At this point, it is not a keyword. Check for old syle
339 * Job Levels without keyword. This form is depreciated!!!
342 for (j=0; joblevels[j].level_name; j++) {
343 if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
344 lrun.level = joblevels[j].level;
345 lrun.job_type = joblevels[j].job_type;
351 } /* end for found */
355 * Scan schedule times.
356 * Default is: daily at 0:0
361 for ( ; token != T_EOL; (token = lex_get_token(lc, T_ALL))) {
368 code = atoi(lc->str) - 1;
369 if (code < 0 || code > 30) {
370 scan_err0(lc, _("Day number out of range (1-31)"));
373 case T_NAME: /* this handles drop through from keyword */
374 case T_UNQUOTED_STRING:
375 if (strchr(lc->str, (int)'-')) {
379 if (strchr(lc->str, (int)':')) {
383 if (lc->str_len == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') &&
384 is_an_integer(lc->str+1)) {
385 code = atoi(lc->str+1);
386 if (code < 0 || code > 53) {
387 scan_err0(lc, _("Week number out of range (0-53)"));
390 state = s_woy; /* week of year */
393 /* everything else must be a keyword */
394 for (i=0; keyw[i].name; i++) {
395 if (strcasecmp(lc->str, keyw[i].name) == 0) {
396 state = keyw[i].state;
403 scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
410 scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
417 case s_mday: /* day of month */
419 clear_bits(0, 30, lrun.mday);
422 set_bit(code, lrun.mday);
424 case s_month: /* month of year */
426 clear_bits(0, 11, lrun.month);
429 set_bit(code, lrun.month);
431 case s_wday: /* week day */
433 clear_bits(0, 6, lrun.wday);
436 set_bit(code, lrun.wday);
438 case s_wom: /* Week of month 1st, ... */
440 clear_bits(0, 4, lrun.wom);
443 set_bit(code, lrun.wom);
447 clear_bits(0, 53, lrun.woy);
450 set_bit(code, lrun.woy);
452 case s_time: /* time */
454 scan_err0(lc, _("Time must be preceded by keyword AT."));
458 clear_bits(0, 23, lrun.hour);
460 // Dmsg1(000, "s_time=%s\n", lc->str);
461 p = strchr(lc->str, ':');
463 scan_err0(lc, _("Time logic error.\n"));
466 *p++ = 0; /* separate two halves */
467 code = atoi(lc->str); /* pick up hour */
468 code2 = atoi(p); /* pick up minutes */
473 if (strcasecmp(p, "pm") == 0) {
475 } else if (strcasecmp(p, "am") == 0) {
477 } else if (len != 2) {
478 scan_err0(lc, _("Bad time specification."));
482 * Note, according to NIST, 12am and 12pm are ambiguous and
483 * can be defined to anything. However, 12:01am is the same
484 * as 00:01 and 12:01pm is the same as 12:01, so we define
485 * 12am as 00:00 and 12pm as 12:00.
488 /* Convert to 24 hour time */
493 } else if (am && code == 12) {
496 if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
497 scan_err0(lc, _("Bad time specification."));
500 // Dmsg2(000, "hour=%d min=%d\n", code, code2);
501 set_bit(code, lrun.hour);
509 p = strchr(lc->str, '-');
511 scan_err0(lc, _("Range logic error.\n"));
513 *p++ = 0; /* separate two halves */
515 /* Check for day range */
516 if (is_an_integer(lc->str) && is_an_integer(p)) {
517 code = atoi(lc->str) - 1;
519 if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
520 scan_err0(lc, _("Bad day range specification."));
523 clear_bits(0, 30, lrun.mday);
527 set_bits(code, code2, lrun.mday);
529 set_bits(code, 30, lrun.mday);
530 set_bits(0, code2, lrun.mday);
534 /* Check for week of year range */
535 if (strlen(lc->str) == 3 && strlen(p) == 3 &&
536 (lc->str[0] == 'w' || lc->str[0] == 'W') &&
537 (p[0] == 'w' || p[0] == 'W') &&
538 is_an_integer(lc->str+1) && is_an_integer(p+1)) {
539 code = atoi(lc->str+1);
541 if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
542 scan_err0(lc, _("Week number out of range (0-53)"));
545 clear_bits(0, 53, lrun.woy);
549 set_bits(code, code2, lrun.woy);
551 set_bits(code, 53, lrun.woy);
552 set_bits(0, code2, lrun.woy);
556 /* lookup first half of keyword range (week days or months) */
558 for (i=0; keyw[i].name; i++) {
559 if (strcmp(lc->str, keyw[i].name) == 0) {
560 state = keyw[i].state;
566 if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
567 scan_err0(lc, _("Invalid month, week or position day range"));
571 /* Lookup end of range */
573 for (i=0; keyw[i].name; i++) {
574 if (strcmp(p, keyw[i].name) == 0) {
575 state2 = keyw[i].state;
576 code2 = keyw[i].code;
581 if (i != 0 || state != state2 || code == code2) {
582 scan_err0(lc, _("Invalid month, weekday or position range"));
585 if (state == s_wday) {
587 clear_bits(0, 6, lrun.wday);
591 set_bits(code, code2, lrun.wday);
593 set_bits(code, 6, lrun.wday);
594 set_bits(0, code2, lrun.wday);
596 } else if (state == s_month) {
598 clear_bits(0, 11, lrun.month);
602 set_bits(code, code2, lrun.month);
604 /* this is a bit odd, but we accept it anyway */
605 set_bits(code, 11, lrun.month);
606 set_bits(0, code2, lrun.month);
609 /* Must be position */
611 clear_bits(0, 4, lrun.wom);
615 set_bits(code, code2, lrun.wom);
617 set_bits(code, 4, lrun.wom);
618 set_bits(0, code2, lrun.wom);
624 set_bits(0, 23, lrun.hour);
627 have_mday = have_wom = have_woy = true;
628 set_bits(0, 30, lrun.mday);
629 set_bits(0, 4, lrun.wom);
630 set_bits(0, 53, lrun.woy);
634 set_bits(0, 6, lrun.wday);
638 set_bits(0, 11, lrun.month);
641 scan_err0(lc, _("Unexpected run state\n"));
647 /* Allocate run record, copy new stuff into it,
648 * and append it to the list of run records
649 * in the schedule resource.
654 /* Create new run record */
655 RUN *nrun = (RUN *)malloc(sizeof(RUN));
656 memcpy(nrun, &lrun, sizeof(RUN));
659 if (!*run) { /* if empty list */
660 *run = nrun; /* add new record */
662 for (tail = *run; tail->next; tail=tail->next)
668 lc->options = options; /* restore scanner options */
669 set_bit(index, res_all.res_sch.hdr.item_present);