2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2008 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
42 extern "C" { // work around visual compiler mangling variables
48 extern struct s_jl joblevels[];
50 /* Forward referenced subroutines */
64 s_wom, /* 1st, 2nd, ...*/
65 s_woy /* week of year w00 - w53 */
69 const char *name; /* keyword */
70 enum e_state state; /* parser state */
71 int code; /* state value */
74 /* Keywords understood by parser */
75 static struct s_keyw keyw[] = {
76 {NT_("on"), s_none, 0},
79 {NT_("sun"), s_wday, 0},
80 {NT_("mon"), s_wday, 1},
81 {NT_("tue"), s_wday, 2},
82 {NT_("wed"), s_wday, 3},
83 {NT_("thu"), s_wday, 4},
84 {NT_("fri"), s_wday, 5},
85 {NT_("sat"), s_wday, 6},
86 {NT_("jan"), s_month, 0},
87 {NT_("feb"), s_month, 1},
88 {NT_("mar"), s_month, 2},
89 {NT_("apr"), s_month, 3},
90 {NT_("may"), s_month, 4},
91 {NT_("jun"), s_month, 5},
92 {NT_("jul"), s_month, 6},
93 {NT_("aug"), s_month, 7},
94 {NT_("sep"), s_month, 8},
95 {NT_("oct"), s_month, 9},
96 {NT_("nov"), s_month, 10},
97 {NT_("dec"), s_month, 11},
99 {NT_("sunday"), s_wday, 0},
100 {NT_("monday"), s_wday, 1},
101 {NT_("tuesday"), s_wday, 2},
102 {NT_("wednesday"), s_wday, 3},
103 {NT_("thursday"), s_wday, 4},
104 {NT_("friday"), s_wday, 5},
105 {NT_("saturday"), s_wday, 6},
106 {NT_("january"), s_month, 0},
107 {NT_("february"), s_month, 1},
108 {NT_("march"), s_month, 2},
109 {NT_("april"), s_month, 3},
110 {NT_("june"), s_month, 5},
111 {NT_("july"), s_month, 6},
112 {NT_("august"), s_month, 7},
113 {NT_("september"), s_month, 8},
114 {NT_("october"), s_month, 9},
115 {NT_("november"), s_month, 10},
116 {NT_("december"), s_month, 11},
118 {NT_("daily"), s_daily, 0},
119 {NT_("weekly"), s_weekly, 0},
120 {NT_("monthly"), s_monthly, 0},
121 {NT_("hourly"), s_hourly, 0},
123 {NT_("1st"), s_wom, 0},
124 {NT_("2nd"), s_wom, 1},
125 {NT_("3rd"), s_wom, 2},
126 {NT_("4th"), s_wom, 3},
127 {NT_("5th"), s_wom, 4},
129 {NT_("first"), s_wom, 0},
130 {NT_("second"), s_wom, 1},
131 {NT_("third"), s_wom, 2},
132 {NT_("fourth"), s_wom, 3},
133 {NT_("fifth"), s_wom, 4},
137 static bool have_hour, have_mday, have_wday, have_month, have_wom;
138 static bool have_at, have_woy;
141 static void set_defaults()
143 have_hour = have_mday = have_wday = have_month = have_wom = have_woy = false;
145 set_bits(0, 23, lrun.hour);
146 set_bits(0, 30, lrun.mday);
147 set_bits(0, 6, lrun.wday);
148 set_bits(0, 11, lrun.month);
149 set_bits(0, 4, lrun.wom);
150 set_bits(0, 53, lrun.woy);
154 /* Keywords (RHS) permitted in Run records */
155 static struct s_kw RunFields[] = {
158 {"incrementalpool", 'i'},
159 {"differentialpool", 'd'},
165 {"writepartafterjob", 'W'},
170 * Store Schedule Run information
172 * Parse Run statement:
174 * Run <keyword=value ...> [on] 2 january at 23:45
176 * Default Run time is daily at 0:0
178 * There can be multiple run statements, they are simply chained
182 void store_run(LEX *lc, RES_ITEM *item, int index, int pass)
186 int token, state, state2 = 0, code = 0, code2 = 0;
187 int options = lc->options;
188 RUN **run = (RUN **)(item->value);
193 lc->options |= LOPT_NO_IDENT; /* want only "strings" */
195 /* clear local copy of run record */
196 memset(&lrun, 0, sizeof(RUN));
198 /* scan for Job level "full", "incremental", ... */
199 for (found=true; found; ) {
201 token = lex_get_token(lc, T_NAME);
202 for (i=0; RunFields[i].name; i++) {
203 if (strcasecmp(lc->str, RunFields[i].name) == 0) {
205 if (lex_get_token(lc, T_ALL) != T_EQUALS) {
206 scan_err1(lc, _("Expected an equals, got: %s"), lc->str);
209 switch (RunFields[i].token) {
210 case 's': /* Data spooling */
211 token = lex_get_token(lc, T_NAME);
212 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
213 lrun.spool_data = true;
214 lrun.spool_data_set = true;
215 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
216 lrun.spool_data = false;
217 lrun.spool_data_set = true;
219 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
222 case 'W': /* Write part after job */
223 token = lex_get_token(lc, T_NAME);
224 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
225 lrun.write_part_after_job = true;
226 lrun.write_part_after_job_set = true;
227 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
228 lrun.write_part_after_job = false;
229 lrun.write_part_after_job_set = true;
231 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
234 case 'L': /* level */
235 token = lex_get_token(lc, T_NAME);
236 for (j=0; joblevels[j].level_name; j++) {
237 if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
238 lrun.level = joblevels[j].level;
239 lrun.job_type = joblevels[j].job_type;
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;
256 case 'f': /* FullPool */
257 case 'i': /* IncPool */
258 case 'd': /* DifPool */
259 token = lex_get_token(lc, T_NAME);
261 res = GetResWithName(R_POOL, lc->str);
263 scan_err1(lc, _("Could not find specified Pool Resource: %s"),
267 switch(RunFields[i].token) {
269 lrun.pool = (POOL *)res;
272 lrun.full_pool = (POOL *)res;
275 lrun.inc_pool = (POOL *)res;
278 lrun.diff_pool = (POOL *)res;
283 case 'S': /* storage */
284 token = lex_get_token(lc, T_NAME);
286 res = GetResWithName(R_STORAGE, lc->str);
288 scan_err1(lc, _("Could not find specified Storage Resource: %s"),
292 lrun.storage = (STORE *)res;
295 case 'M': /* messages */
296 token = lex_get_token(lc, T_NAME);
298 res = GetResWithName(R_MSGS, lc->str);
300 scan_err1(lc, _("Could not find specified Messages Resource: %s"),
304 lrun.msgs = (MSGS *)res;
308 scan_err1(lc, _("Expected a keyword name, got: %s"), lc->str);
312 } /* end if strcasecmp */
313 } /* end for RunFields */
315 /* At this point, it is not a keyword. Check for old syle
316 * Job Levels without keyword. This form is depreciated!!!
319 for (j=0; joblevels[j].level_name; j++) {
320 if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
321 lrun.level = joblevels[j].level;
322 lrun.job_type = joblevels[j].job_type;
328 } /* end for found */
332 * Scan schedule times.
333 * Default is: daily at 0:0
338 for ( ; token != T_EOL; (token = lex_get_token(lc, T_ALL))) {
345 code = atoi(lc->str) - 1;
346 if (code < 0 || code > 30) {
347 scan_err0(lc, _("Day number out of range (1-31)"));
350 case T_NAME: /* this handles drop through from keyword */
351 case T_UNQUOTED_STRING:
352 if (strchr(lc->str, (int)'-')) {
356 if (strchr(lc->str, (int)':')) {
360 if (lc->str_len == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') &&
361 is_an_integer(lc->str+1)) {
362 code = atoi(lc->str+1);
363 if (code < 0 || code > 53) {
364 scan_err0(lc, _("Week number out of range (0-53)"));
367 state = s_woy; /* week of year */
370 /* everything else must be a keyword */
371 for (i=0; keyw[i].name; i++) {
372 if (strcasecmp(lc->str, keyw[i].name) == 0) {
373 state = keyw[i].state;
380 scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
387 scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
394 case s_mday: /* day of month */
396 clear_bits(0, 30, lrun.mday);
399 set_bit(code, lrun.mday);
401 case s_month: /* month of year */
403 clear_bits(0, 11, lrun.month);
406 set_bit(code, lrun.month);
408 case s_wday: /* week day */
410 clear_bits(0, 6, lrun.wday);
413 set_bit(code, lrun.wday);
415 case s_wom: /* Week of month 1st, ... */
417 clear_bits(0, 4, lrun.wom);
420 set_bit(code, lrun.wom);
424 clear_bits(0, 53, lrun.woy);
427 set_bit(code, lrun.woy);
429 case s_time: /* time */
431 scan_err0(lc, _("Time must be preceded by keyword AT."));
435 clear_bits(0, 23, lrun.hour);
437 // Dmsg1(000, "s_time=%s\n", lc->str);
438 p = strchr(lc->str, ':');
440 scan_err0(lc, _("Time logic error.\n"));
443 *p++ = 0; /* separate two halves */
444 code = atoi(lc->str); /* pick up hour */
445 code2 = atoi(p); /* pick up minutes */
450 if (strcasecmp(p, "pm") == 0) {
452 } else if (strcasecmp(p, "am") == 0) {
454 } else if (len != 2) {
455 scan_err0(lc, _("Bad time specification."));
459 * Note, according to NIST, 12am and 12pm are ambiguous and
460 * can be defined to anything. However, 12:01am is the same
461 * as 00:01 and 12:01pm is the same as 12:01, so we define
462 * 12am as 00:00 and 12pm as 12:00.
465 /* Convert to 24 hour time */
470 } else if (am && code == 12) {
473 if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
474 scan_err0(lc, _("Bad time specification."));
477 // Dmsg2(000, "hour=%d min=%d\n", code, code2);
478 set_bit(code, lrun.hour);
486 p = strchr(lc->str, '-');
488 scan_err0(lc, _("Range logic error.\n"));
490 *p++ = 0; /* separate two halves */
492 /* Check for day range */
493 if (is_an_integer(lc->str) && is_an_integer(p)) {
494 code = atoi(lc->str) - 1;
496 if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
497 scan_err0(lc, _("Bad day range specification."));
500 clear_bits(0, 30, lrun.mday);
504 set_bits(code, code2, lrun.mday);
506 set_bits(code, 30, lrun.mday);
507 set_bits(0, code2, lrun.mday);
511 /* Check for week of year range */
512 if (strlen(lc->str) == 3 && strlen(p) == 3 &&
513 (lc->str[0] == 'w' || lc->str[0] == 'W') &&
514 (p[0] == 'w' || p[0] == 'W') &&
515 is_an_integer(lc->str+1) && is_an_integer(p+1)) {
516 code = atoi(lc->str+1);
518 if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
519 scan_err0(lc, _("Week number out of range (0-53)"));
522 clear_bits(0, 53, lrun.woy);
526 set_bits(code, code2, lrun.woy);
528 set_bits(code, 53, lrun.woy);
529 set_bits(0, code2, lrun.woy);
533 /* lookup first half of keyword range (week days or months) */
535 for (i=0; keyw[i].name; i++) {
536 if (strcmp(lc->str, keyw[i].name) == 0) {
537 state = keyw[i].state;
543 if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
544 scan_err0(lc, _("Invalid month, week or position day range"));
548 /* Lookup end of range */
550 for (i=0; keyw[i].name; i++) {
551 if (strcmp(p, keyw[i].name) == 0) {
552 state2 = keyw[i].state;
553 code2 = keyw[i].code;
558 if (i != 0 || state != state2 || code == code2) {
559 scan_err0(lc, _("Invalid month, weekday or position range"));
562 if (state == s_wday) {
564 clear_bits(0, 6, lrun.wday);
568 set_bits(code, code2, lrun.wday);
570 set_bits(code, 6, lrun.wday);
571 set_bits(0, code2, lrun.wday);
573 } else if (state == s_month) {
575 clear_bits(0, 11, lrun.month);
579 set_bits(code, code2, lrun.month);
581 /* this is a bit odd, but we accept it anyway */
582 set_bits(code, 11, lrun.month);
583 set_bits(0, code2, lrun.month);
586 /* Must be position */
588 clear_bits(0, 4, lrun.wom);
592 set_bits(code, code2, lrun.wom);
594 set_bits(code, 4, lrun.wom);
595 set_bits(0, code2, lrun.wom);
601 set_bits(0, 23, lrun.hour);
604 have_mday = have_wom = have_woy = true;
605 set_bits(0, 30, lrun.mday);
606 set_bits(0, 4, lrun.wom);
607 set_bits(0, 53, lrun.woy);
611 set_bits(0, 6, lrun.wday);
615 set_bits(0, 11, lrun.month);
618 scan_err0(lc, _("Unexpected run state\n"));
624 /* Allocate run record, copy new stuff into it,
625 * and append it to the list of run records
626 * in the schedule resource.
631 /* Create new run record */
632 RUN *nrun = (RUN *)malloc(sizeof(RUN));
633 memcpy(nrun, &lrun, sizeof(RUN));
636 if (!*run) { /* if empty list */
637 *run = nrun; /* add new record */
639 for (tail = *run; tail->next; tail=tail->next)
645 lc->options = options; /* restore scanner options */
646 set_bit(index, res_all.res_sch.hdr.item_present);