3 * Configuration parser for Director Run Configuration
4 * directives, which are part of the Schedule Resource
11 Bacula® - The Network Backup Solution
13 Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
15 The main author of Bacula is Kern Sibbald, with contributions from
16 many others, a complete list can be found in the file AUTHORS.
17 This program is Free Software; you can redistribute it and/or
18 modify it under the terms of version two of the GNU General Public
19 License as published by the Free Software Foundation plus additions
20 that are listed in the file LICENSE.
22 This program is distributed in the hope that it will be useful, but
23 WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 General Public License for more details.
27 You should have received a copy of the GNU General Public License
28 along with this program; if not, write to the Free Software
29 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
32 Bacula® is a registered trademark of John Walker.
33 The licensor of Bacula is the Free Software Foundation Europe
34 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
35 Switzerland, email:ftf@fsfeurope.org.
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))) {
344 code = atoi(lc->str) - 1;
345 if (code < 0 || code > 30) {
346 scan_err0(lc, _("Day number out of range (1-31)"));
349 case T_NAME: /* this handles drop through from keyword */
350 case T_UNQUOTED_STRING:
351 if (strchr(lc->str, (int)'-')) {
355 if (strchr(lc->str, (int)':')) {
359 if (lc->str_len == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') &&
360 is_an_integer(lc->str+1)) {
361 code = atoi(lc->str+1);
362 if (code < 0 || code > 53) {
363 scan_err0(lc, _("Week number out of range (0-53)"));
365 state = s_woy; /* week of year */
368 /* everything else must be a keyword */
369 for (i=0; keyw[i].name; i++) {
370 if (strcasecmp(lc->str, keyw[i].name) == 0) {
371 state = keyw[i].state;
378 scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
385 scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
392 case s_mday: /* day of month */
394 clear_bits(0, 30, lrun.mday);
397 set_bit(code, lrun.mday);
399 case s_month: /* month of year */
401 clear_bits(0, 11, lrun.month);
404 set_bit(code, lrun.month);
406 case s_wday: /* week day */
408 clear_bits(0, 6, lrun.wday);
411 set_bit(code, lrun.wday);
413 case s_wom: /* Week of month 1st, ... */
415 clear_bits(0, 4, lrun.wom);
418 set_bit(code, lrun.wom);
422 clear_bits(0, 53, lrun.woy);
425 set_bit(code, lrun.woy);
427 case s_time: /* time */
429 scan_err0(lc, _("Time must be preceded by keyword AT."));
433 clear_bits(0, 23, lrun.hour);
435 p = strchr(lc->str, ':');
437 scan_err0(lc, _("Time logic error.\n"));
440 *p++ = 0; /* separate two halves */
441 code = atoi(lc->str); /* pick up hour */
443 if (len > 2 && p[len-1] == 'm') {
444 if (p[len-2] == 'a') {
446 } else if (p[len-2] == 'p') {
449 scan_err0(lc, _("Bad time specification."));
455 code2 = atoi(p); /* pick up minutes */
457 /* Convert to 24 hour time */
464 if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
465 scan_err0(lc, _("Bad time specification."));
468 set_bit(code, lrun.hour);
476 p = strchr(lc->str, '-');
478 scan_err0(lc, _("Range logic error.\n"));
480 *p++ = 0; /* separate two halves */
482 /* Check for day range */
483 if (is_an_integer(lc->str) && is_an_integer(p)) {
484 code = atoi(lc->str) - 1;
486 if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
487 scan_err0(lc, _("Bad day range specification."));
490 clear_bits(0, 30, lrun.mday);
494 set_bits(code, code2, lrun.mday);
496 set_bits(code, 30, lrun.mday);
497 set_bits(0, code2, lrun.mday);
501 /* Check for week of year range */
502 if (strlen(lc->str) == 3 && strlen(p) == 3 &&
503 (lc->str[0] == 'w' || lc->str[0] == 'W') &&
504 (p[0] == 'w' || p[0] == 'W') &&
505 is_an_integer(lc->str+1) && is_an_integer(p+1)) {
506 code = atoi(lc->str+1);
508 if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
509 scan_err0(lc, _("Week number out of range (0-53)"));
512 clear_bits(0, 53, lrun.woy);
516 set_bits(code, code2, lrun.woy);
518 set_bits(code, 53, lrun.woy);
519 set_bits(0, code2, lrun.woy);
523 /* lookup first half of keyword range (week days or months) */
525 for (i=0; keyw[i].name; i++) {
526 if (strcmp(lc->str, keyw[i].name) == 0) {
527 state = keyw[i].state;
533 if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
534 scan_err0(lc, _("Invalid month, week or position day range"));
538 /* Lookup end of range */
540 for (i=0; keyw[i].name; i++) {
541 if (strcmp(p, keyw[i].name) == 0) {
542 state2 = keyw[i].state;
543 code2 = keyw[i].code;
548 if (i != 0 || state != state2 || code == code2) {
549 scan_err0(lc, _("Invalid month, weekday or position range"));
552 if (state == s_wday) {
554 clear_bits(0, 6, lrun.wday);
558 set_bits(code, code2, lrun.wday);
560 set_bits(code, 6, lrun.wday);
561 set_bits(0, code2, lrun.wday);
563 } else if (state == s_month) {
565 clear_bits(0, 11, lrun.month);
569 set_bits(code, code2, lrun.month);
571 /* this is a bit odd, but we accept it anyway */
572 set_bits(code, 11, lrun.month);
573 set_bits(0, code2, lrun.month);
576 /* Must be position */
578 clear_bits(0, 4, lrun.wom);
582 set_bits(code, code2, lrun.wom);
584 set_bits(code, 4, lrun.wom);
585 set_bits(0, code2, lrun.wom);
591 set_bits(0, 23, lrun.hour);
594 have_mday = have_wom = have_woy = true;
595 set_bits(0, 30, lrun.mday);
596 set_bits(0, 4, lrun.wom);
597 set_bits(0, 53, lrun.woy);
601 set_bits(0, 6, lrun.wday);
605 set_bits(0, 11, lrun.month);
608 scan_err0(lc, _("Unexpected run state\n"));
614 /* Allocate run record, copy new stuff into it,
615 * and link it into the list of run records
616 * in the schedule resource.
619 trun = (RUN *)malloc(sizeof(RUN));
620 memcpy(trun, &lrun, sizeof(RUN));
627 lc->options = options; /* restore scanner options */
628 set_bit(index, res_all.res_sch.hdr.item_present);