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))) {
346 code = atoi(lc->str) - 1;
347 if (code < 0 || code > 30) {
348 scan_err0(lc, _("Day number out of range (1-31)"));
351 case T_NAME: /* this handles drop through from keyword */
352 case T_UNQUOTED_STRING:
353 if (strchr(lc->str, (int)'-')) {
357 if (strchr(lc->str, (int)':')) {
361 if (lc->str_len == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') &&
362 is_an_integer(lc->str+1)) {
363 code = atoi(lc->str+1);
364 if (code < 0 || code > 53) {
365 scan_err0(lc, _("Week number out of range (0-53)"));
368 state = s_woy; /* week of year */
371 /* everything else must be a keyword */
372 for (i=0; keyw[i].name; i++) {
373 if (strcasecmp(lc->str, keyw[i].name) == 0) {
374 state = keyw[i].state;
381 scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
388 scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
395 case s_mday: /* day of month */
397 clear_bits(0, 30, lrun.mday);
400 set_bit(code, lrun.mday);
402 case s_month: /* month of year */
404 clear_bits(0, 11, lrun.month);
407 set_bit(code, lrun.month);
409 case s_wday: /* week day */
411 clear_bits(0, 6, lrun.wday);
414 set_bit(code, lrun.wday);
416 case s_wom: /* Week of month 1st, ... */
418 clear_bits(0, 4, lrun.wom);
421 set_bit(code, lrun.wom);
425 clear_bits(0, 53, lrun.woy);
428 set_bit(code, lrun.woy);
430 case s_time: /* time */
432 scan_err0(lc, _("Time must be preceded by keyword AT."));
436 clear_bits(0, 23, lrun.hour);
438 // Dmsg1(000, "s_time=%s\n", lc->str);
439 p = strchr(lc->str, ':');
441 scan_err0(lc, _("Time logic error.\n"));
444 *p++ = 0; /* separate two halves */
445 code = atoi(lc->str); /* pick up hour */
446 code2 = atoi(p); /* pick up minutes */
451 if (strcasecmp(p, "pm") == 0) {
453 } else if (strcasecmp(p, "am") == 0) {
455 } else if (len != 2) {
456 scan_err0(lc, _("Bad time specification."));
460 * Note, according to NIST, 12am and 12pm are ambiguous and
461 * can be defined to anything. However, 12:01am is the same
462 * as 00:01 and 12:01pm is the same as 12:01, so we define
463 * 12am as 00:00 and 12pm as 12:00.
466 /* Convert to 24 hour time */
471 } else if (am && code == 12) {
474 if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
475 scan_err0(lc, _("Bad time specification."));
478 // Dmsg2(000, "hour=%d min=%d\n", code, code2);
479 set_bit(code, lrun.hour);
487 p = strchr(lc->str, '-');
489 scan_err0(lc, _("Range logic error.\n"));
491 *p++ = 0; /* separate two halves */
493 /* Check for day range */
494 if (is_an_integer(lc->str) && is_an_integer(p)) {
495 code = atoi(lc->str) - 1;
497 if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
498 scan_err0(lc, _("Bad day range specification."));
501 clear_bits(0, 30, lrun.mday);
505 set_bits(code, code2, lrun.mday);
507 set_bits(code, 30, lrun.mday);
508 set_bits(0, code2, lrun.mday);
512 /* Check for week of year range */
513 if (strlen(lc->str) == 3 && strlen(p) == 3 &&
514 (lc->str[0] == 'w' || lc->str[0] == 'W') &&
515 (p[0] == 'w' || p[0] == 'W') &&
516 is_an_integer(lc->str+1) && is_an_integer(p+1)) {
517 code = atoi(lc->str+1);
519 if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
520 scan_err0(lc, _("Week number out of range (0-53)"));
523 clear_bits(0, 53, lrun.woy);
527 set_bits(code, code2, lrun.woy);
529 set_bits(code, 53, lrun.woy);
530 set_bits(0, code2, lrun.woy);
534 /* lookup first half of keyword range (week days or months) */
536 for (i=0; keyw[i].name; i++) {
537 if (strcmp(lc->str, keyw[i].name) == 0) {
538 state = keyw[i].state;
544 if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
545 scan_err0(lc, _("Invalid month, week or position day range"));
549 /* Lookup end of range */
551 for (i=0; keyw[i].name; i++) {
552 if (strcmp(p, keyw[i].name) == 0) {
553 state2 = keyw[i].state;
554 code2 = keyw[i].code;
559 if (i != 0 || state != state2 || code == code2) {
560 scan_err0(lc, _("Invalid month, weekday or position range"));
563 if (state == s_wday) {
565 clear_bits(0, 6, lrun.wday);
569 set_bits(code, code2, lrun.wday);
571 set_bits(code, 6, lrun.wday);
572 set_bits(0, code2, lrun.wday);
574 } else if (state == s_month) {
576 clear_bits(0, 11, lrun.month);
580 set_bits(code, code2, lrun.month);
582 /* this is a bit odd, but we accept it anyway */
583 set_bits(code, 11, lrun.month);
584 set_bits(0, code2, lrun.month);
587 /* Must be position */
589 clear_bits(0, 4, lrun.wom);
593 set_bits(code, code2, lrun.wom);
595 set_bits(code, 4, lrun.wom);
596 set_bits(0, code2, lrun.wom);
602 set_bits(0, 23, lrun.hour);
605 have_mday = have_wom = have_woy = true;
606 set_bits(0, 30, lrun.mday);
607 set_bits(0, 4, lrun.wom);
608 set_bits(0, 53, lrun.woy);
612 set_bits(0, 6, lrun.wday);
616 set_bits(0, 11, lrun.month);
619 scan_err0(lc, _("Unexpected run state\n"));
625 /* Allocate run record, copy new stuff into it,
626 * and link it into the list of run records
627 * in the schedule resource.
630 trun = (RUN *)malloc(sizeof(RUN));
631 memcpy(trun, &lrun, sizeof(RUN));
638 lc->options = options; /* restore scanner options */
639 set_bit(index, res_all.res_sch.hdr.item_present);