2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2007 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 two of the GNU General Public
10 License as published by the Free Software Foundation plus additions
11 that are listed in the file LICENSE.
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 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 John Walker.
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);
194 lc->options |= LOPT_NO_IDENT; /* want only "strings" */
196 /* clear local copy of run record */
197 memset(&lrun, 0, sizeof(RUN));
199 /* scan for Job level "full", "incremental", ... */
200 for (found=true; found; ) {
202 token = lex_get_token(lc, T_NAME);
203 for (i=0; RunFields[i].name; i++) {
204 if (strcasecmp(lc->str, RunFields[i].name) == 0) {
206 if (lex_get_token(lc, T_ALL) != T_EQUALS) {
207 scan_err1(lc, _("Expected an equals, got: %s"), lc->str);
210 switch (RunFields[i].token) {
211 case 's': /* Data spooling */
212 token = lex_get_token(lc, T_NAME);
213 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
214 lrun.spool_data = true;
215 lrun.spool_data_set = true;
216 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
217 lrun.spool_data = false;
218 lrun.spool_data_set = true;
220 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
223 case 'W': /* Write part after job */
224 token = lex_get_token(lc, T_NAME);
225 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
226 lrun.write_part_after_job = true;
227 lrun.write_part_after_job_set = true;
228 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
229 lrun.write_part_after_job = false;
230 lrun.write_part_after_job_set = true;
232 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
235 case 'L': /* level */
236 token = lex_get_token(lc, T_NAME);
237 for (j=0; joblevels[j].level_name; j++) {
238 if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
239 lrun.level = joblevels[j].level;
240 lrun.job_type = joblevels[j].job_type;
246 scan_err1(lc, _("Job level field: %s not found in run record"), lc->str);
250 case 'p': /* Priority */
251 token = lex_get_token(lc, T_PINT32);
253 lrun.Priority = lc->pint32_val;
257 case 'f': /* FullPool */
258 case 'i': /* IncPool */
259 case 'd': /* DifPool */
260 token = lex_get_token(lc, T_NAME);
262 res = GetResWithName(R_POOL, lc->str);
264 scan_err1(lc, _("Could not find specified Pool Resource: %s"),
268 switch(RunFields[i].token) {
270 lrun.pool = (POOL *)res;
273 lrun.full_pool = (POOL *)res;
276 lrun.inc_pool = (POOL *)res;
279 lrun.diff_pool = (POOL *)res;
284 case 'S': /* storage */
285 token = lex_get_token(lc, T_NAME);
287 res = GetResWithName(R_STORAGE, lc->str);
289 scan_err1(lc, _("Could not find specified Storage Resource: %s"),
293 lrun.storage = (STORE *)res;
296 case 'M': /* messages */
297 token = lex_get_token(lc, T_NAME);
299 res = GetResWithName(R_MSGS, lc->str);
301 scan_err1(lc, _("Could not find specified Messages Resource: %s"),
305 lrun.msgs = (MSGS *)res;
309 scan_err1(lc, _("Expected a keyword name, got: %s"), lc->str);
313 } /* end if strcasecmp */
314 } /* end for RunFields */
316 /* At this point, it is not a keyword. Check for old syle
317 * Job Levels without keyword. This form is depreciated!!!
320 for (j=0; joblevels[j].level_name; j++) {
321 if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
322 lrun.level = joblevels[j].level;
323 lrun.job_type = joblevels[j].job_type;
329 } /* end for found */
333 * Scan schedule times.
334 * Default is: daily at 0:0
339 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 p = strchr(lc->str, ':');
439 scan_err0(lc, _("Time logic error.\n"));
442 *p++ = 0; /* separate two halves */
443 code = atoi(lc->str); /* pick up hour */
445 if (len > 2 && p[len-1] == 'm') {
446 if (p[len-2] == 'a') {
448 } else if (p[len-2] == 'p') {
451 scan_err0(lc, _("Bad time specification."));
457 code2 = atoi(p); /* pick up minutes */
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 (code == 12) {
473 if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
474 scan_err0(lc, _("Bad time specification."));
477 set_bit(code, lrun.hour);
485 p = strchr(lc->str, '-');
487 scan_err0(lc, _("Range logic error.\n"));
489 *p++ = 0; /* separate two halves */
491 /* Check for day range */
492 if (is_an_integer(lc->str) && is_an_integer(p)) {
493 code = atoi(lc->str) - 1;
495 if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
496 scan_err0(lc, _("Bad day range specification."));
499 clear_bits(0, 30, lrun.mday);
503 set_bits(code, code2, lrun.mday);
505 set_bits(code, 30, lrun.mday);
506 set_bits(0, code2, lrun.mday);
510 /* Check for week of year range */
511 if (strlen(lc->str) == 3 && strlen(p) == 3 &&
512 (lc->str[0] == 'w' || lc->str[0] == 'W') &&
513 (p[0] == 'w' || p[0] == 'W') &&
514 is_an_integer(lc->str+1) && is_an_integer(p+1)) {
515 code = atoi(lc->str+1);
517 if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
518 scan_err0(lc, _("Week number out of range (0-53)"));
521 clear_bits(0, 53, lrun.woy);
525 set_bits(code, code2, lrun.woy);
527 set_bits(code, 53, lrun.woy);
528 set_bits(0, code2, lrun.woy);
532 /* lookup first half of keyword range (week days or months) */
534 for (i=0; keyw[i].name; i++) {
535 if (strcmp(lc->str, keyw[i].name) == 0) {
536 state = keyw[i].state;
542 if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
543 scan_err0(lc, _("Invalid month, week or position day range"));
547 /* Lookup end of range */
549 for (i=0; keyw[i].name; i++) {
550 if (strcmp(p, keyw[i].name) == 0) {
551 state2 = keyw[i].state;
552 code2 = keyw[i].code;
557 if (i != 0 || state != state2 || code == code2) {
558 scan_err0(lc, _("Invalid month, weekday or position range"));
561 if (state == s_wday) {
563 clear_bits(0, 6, lrun.wday);
567 set_bits(code, code2, lrun.wday);
569 set_bits(code, 6, lrun.wday);
570 set_bits(0, code2, lrun.wday);
572 } else if (state == s_month) {
574 clear_bits(0, 11, lrun.month);
578 set_bits(code, code2, lrun.month);
580 /* this is a bit odd, but we accept it anyway */
581 set_bits(code, 11, lrun.month);
582 set_bits(0, code2, lrun.month);
585 /* Must be position */
587 clear_bits(0, 4, lrun.wom);
591 set_bits(code, code2, lrun.wom);
593 set_bits(code, 4, lrun.wom);
594 set_bits(0, code2, lrun.wom);
600 set_bits(0, 23, lrun.hour);
603 have_mday = have_wom = have_woy = true;
604 set_bits(0, 30, lrun.mday);
605 set_bits(0, 4, lrun.wom);
606 set_bits(0, 53, lrun.woy);
610 set_bits(0, 6, lrun.wday);
614 set_bits(0, 11, lrun.month);
617 scan_err0(lc, _("Unexpected run state\n"));
623 /* Allocate run record, copy new stuff into it,
624 * and link it into the list of run records
625 * in the schedule resource.
628 trun = (RUN *)malloc(sizeof(RUN));
629 memcpy(trun, &lrun, sizeof(RUN));
636 lc->options = options; /* restore scanner options */
637 set_bit(index, res_all.res_sch.hdr.item_present);