3 * Configuration parser for Director Run Configuration
4 * directives, which are part of the Schedule Resource
11 Copyright (C) 2000-2006 Kern Sibbald
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License
15 version 2 as amended with additional clauses defined in the
16 file LICENSE in the main source directory.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 the file LICENSE for additional details.
29 extern "C" { // work around visual compiler mangling variables
35 extern struct s_jl joblevels[];
37 /* Forward referenced subroutines */
51 s_wom, /* 1st, 2nd, ...*/
52 s_woy /* week of year w00 - w53 */
56 const char *name; /* keyword */
57 enum e_state state; /* parser state */
58 int code; /* state value */
61 /* Keywords understood by parser */
62 static struct s_keyw keyw[] = {
63 {NT_("on"), s_none, 0},
66 {NT_("sun"), s_wday, 0},
67 {NT_("mon"), s_wday, 1},
68 {NT_("tue"), s_wday, 2},
69 {NT_("wed"), s_wday, 3},
70 {NT_("thu"), s_wday, 4},
71 {NT_("fri"), s_wday, 5},
72 {NT_("sat"), s_wday, 6},
73 {NT_("jan"), s_month, 0},
74 {NT_("feb"), s_month, 1},
75 {NT_("mar"), s_month, 2},
76 {NT_("apr"), s_month, 3},
77 {NT_("may"), s_month, 4},
78 {NT_("jun"), s_month, 5},
79 {NT_("jul"), s_month, 6},
80 {NT_("aug"), s_month, 7},
81 {NT_("sep"), s_month, 8},
82 {NT_("oct"), s_month, 9},
83 {NT_("nov"), s_month, 10},
84 {NT_("dec"), s_month, 11},
86 {NT_("sunday"), s_wday, 0},
87 {NT_("monday"), s_wday, 1},
88 {NT_("tuesday"), s_wday, 2},
89 {NT_("wednesday"), s_wday, 3},
90 {NT_("thursday"), s_wday, 4},
91 {NT_("friday"), s_wday, 5},
92 {NT_("saturday"), s_wday, 6},
93 {NT_("january"), s_month, 0},
94 {NT_("february"), s_month, 1},
95 {NT_("march"), s_month, 2},
96 {NT_("april"), s_month, 3},
97 {NT_("june"), s_month, 5},
98 {NT_("july"), s_month, 6},
99 {NT_("august"), s_month, 7},
100 {NT_("september"), s_month, 8},
101 {NT_("october"), s_month, 9},
102 {NT_("november"), s_month, 10},
103 {NT_("december"), s_month, 11},
105 {NT_("daily"), s_daily, 0},
106 {NT_("weekly"), s_weekly, 0},
107 {NT_("monthly"), s_monthly, 0},
108 {NT_("hourly"), s_hourly, 0},
110 {NT_("1st"), s_wom, 0},
111 {NT_("2nd"), s_wom, 1},
112 {NT_("3rd"), s_wom, 2},
113 {NT_("4th"), s_wom, 3},
114 {NT_("5th"), s_wom, 4},
116 {NT_("first"), s_wom, 0},
117 {NT_("second"), s_wom, 1},
118 {NT_("third"), s_wom, 2},
119 {NT_("fourth"), s_wom, 3},
120 {NT_("fifth"), s_wom, 4},
124 static bool have_hour, have_mday, have_wday, have_month, have_wom;
125 static bool have_at, have_woy;
128 static void set_defaults()
130 have_hour = have_mday = have_wday = have_month = have_wom = have_woy = false;
132 set_bits(0, 23, lrun.hour);
133 set_bits(0, 30, lrun.mday);
134 set_bits(0, 6, lrun.wday);
135 set_bits(0, 11, lrun.month);
136 set_bits(0, 4, lrun.wom);
137 set_bits(0, 53, lrun.woy);
141 /* Keywords (RHS) permitted in Run records */
142 static struct s_kw RunFields[] = {
145 {"incrementalpool", 'i'},
146 {"differentialpool", 'd'},
152 {"writepartafterjob", 'W'},
157 * Store Schedule Run information
159 * Parse Run statement:
161 * Run <keyword=value ...> [on] 2 january at 23:45
163 * Default Run time is daily at 0:0
165 * There can be multiple run statements, they are simply chained
169 void store_run(LEX *lc, RES_ITEM *item, int index, int pass)
173 int token, state, state2 = 0, code = 0, code2 = 0;
174 int options = lc->options;
175 RUN **run = (RUN **)(item->value);
181 lc->options |= LOPT_NO_IDENT; /* want only "strings" */
183 /* clear local copy of run record */
184 memset(&lrun, 0, sizeof(RUN));
186 /* scan for Job level "full", "incremental", ... */
187 for (found=true; found; ) {
189 token = lex_get_token(lc, T_NAME);
190 for (i=0; RunFields[i].name; i++) {
191 if (strcasecmp(lc->str, RunFields[i].name) == 0) {
193 if (lex_get_token(lc, T_ALL) != T_EQUALS) {
194 scan_err1(lc, _("Expected an equals, got: %s"), lc->str);
197 switch (RunFields[i].token) {
198 case 's': /* Data spooling */
199 token = lex_get_token(lc, T_NAME);
200 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
201 lrun.spool_data = true;
202 lrun.spool_data_set = true;
203 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
204 lrun.spool_data = false;
205 lrun.spool_data_set = true;
207 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
210 case 'W': /* Write part after job */
211 token = lex_get_token(lc, T_NAME);
212 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
213 lrun.write_part_after_job = true;
214 lrun.write_part_after_job_set = true;
215 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
216 lrun.write_part_after_job = false;
217 lrun.write_part_after_job_set = true;
219 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
222 case 'L': /* level */
223 token = lex_get_token(lc, T_NAME);
224 for (j=0; joblevels[j].level_name; j++) {
225 if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
226 lrun.level = joblevels[j].level;
227 lrun.job_type = joblevels[j].job_type;
233 scan_err1(lc, _("Job level field: %s not found in run record"), lc->str);
237 case 'p': /* Priority */
238 token = lex_get_token(lc, T_PINT32);
240 lrun.Priority = lc->pint32_val;
244 case 'f': /* FullPool */
245 case 'i': /* IncPool */
246 case 'd': /* DifPool */
247 token = lex_get_token(lc, T_NAME);
249 res = GetResWithName(R_POOL, lc->str);
251 scan_err1(lc, _("Could not find specified Pool Resource: %s"),
255 switch(RunFields[i].token) {
257 lrun.pool = (POOL *)res;
260 lrun.full_pool = (POOL *)res;
263 lrun.inc_pool = (POOL *)res;
266 lrun.diff_pool = (POOL *)res;
271 case 'S': /* storage */
272 token = lex_get_token(lc, T_NAME);
274 res = GetResWithName(R_STORAGE, lc->str);
276 scan_err1(lc, _("Could not find specified Storage Resource: %s"),
280 lrun.storage = (STORE *)res;
283 case 'M': /* messages */
284 token = lex_get_token(lc, T_NAME);
286 res = GetResWithName(R_MSGS, lc->str);
288 scan_err1(lc, _("Could not find specified Messages Resource: %s"),
292 lrun.msgs = (MSGS *)res;
296 scan_err1(lc, _("Expected a keyword name, got: %s"), lc->str);
300 } /* end if strcasecmp */
301 } /* end for RunFields */
303 /* At this point, it is not a keyword. Check for old syle
304 * Job Levels without keyword. This form is depreciated!!!
307 for (j=0; joblevels[j].level_name; j++) {
308 if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
309 lrun.level = joblevels[j].level;
310 lrun.job_type = joblevels[j].job_type;
316 } /* end for found */
320 * Scan schedule times.
321 * Default is: daily at 0:0
326 for ( ; token != T_EOL; (token = lex_get_token(lc, T_ALL))) {
331 code = atoi(lc->str) - 1;
332 if (code < 0 || code > 30) {
333 scan_err0(lc, _("Day number out of range (1-31)"));
336 case T_NAME: /* this handles drop through from keyword */
337 case T_UNQUOTED_STRING:
338 if (strchr(lc->str, (int)'-')) {
342 if (strchr(lc->str, (int)':')) {
346 if (lc->str_len == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') &&
347 is_an_integer(lc->str+1)) {
348 code = atoi(lc->str+1);
349 if (code < 0 || code > 53) {
350 scan_err0(lc, _("Week number out of range (0-53)"));
352 state = s_woy; /* week of year */
355 /* everything else must be a keyword */
356 for (i=0; keyw[i].name; i++) {
357 if (strcasecmp(lc->str, keyw[i].name) == 0) {
358 state = keyw[i].state;
365 scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
372 scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
379 case s_mday: /* day of month */
381 clear_bits(0, 30, lrun.mday);
384 set_bit(code, lrun.mday);
386 case s_month: /* month of year */
388 clear_bits(0, 11, lrun.month);
391 set_bit(code, lrun.month);
393 case s_wday: /* week day */
395 clear_bits(0, 6, lrun.wday);
398 set_bit(code, lrun.wday);
400 case s_wom: /* Week of month 1st, ... */
402 clear_bits(0, 4, lrun.wom);
405 set_bit(code, lrun.wom);
409 clear_bits(0, 53, lrun.woy);
412 set_bit(code, lrun.woy);
414 case s_time: /* time */
416 scan_err0(lc, _("Time must be preceded by keyword AT."));
420 clear_bits(0, 23, lrun.hour);
422 p = strchr(lc->str, ':');
424 scan_err0(lc, _("Time logic error.\n"));
427 *p++ = 0; /* separate two halves */
428 code = atoi(lc->str); /* pick up hour */
430 if (len > 2 && p[len-1] == 'm') {
431 if (p[len-2] == 'a') {
433 } else if (p[len-2] == 'p') {
436 scan_err0(lc, _("Bad time specification."));
442 code2 = atoi(p); /* pick up minutes */
444 /* Convert to 24 hour time */
451 if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
452 scan_err0(lc, _("Bad time specification."));
455 set_bit(code, lrun.hour);
463 p = strchr(lc->str, '-');
465 scan_err0(lc, _("Range logic error.\n"));
467 *p++ = 0; /* separate two halves */
469 /* Check for day range */
470 if (is_an_integer(lc->str) && is_an_integer(p)) {
471 code = atoi(lc->str) - 1;
473 if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
474 scan_err0(lc, _("Bad day range specification."));
477 clear_bits(0, 30, lrun.mday);
481 set_bits(code, code2, lrun.mday);
483 set_bits(code, 30, lrun.mday);
484 set_bits(0, code2, lrun.mday);
488 /* Check for week of year range */
489 if (strlen(lc->str) == 3 && strlen(p) == 3 &&
490 (lc->str[0] == 'w' || lc->str[0] == 'W') &&
491 (p[0] == 'w' || p[0] == 'W') &&
492 is_an_integer(lc->str+1) && is_an_integer(p+1)) {
493 code = atoi(lc->str+1);
495 if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
496 scan_err0(lc, _("Week number out of range (0-53)"));
499 clear_bits(0, 53, lrun.woy);
503 set_bits(code, code2, lrun.woy);
505 set_bits(code, 53, lrun.woy);
506 set_bits(0, code2, lrun.woy);
510 /* lookup first half of keyword range (week days or months) */
512 for (i=0; keyw[i].name; i++) {
513 if (strcmp(lc->str, keyw[i].name) == 0) {
514 state = keyw[i].state;
520 if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
521 scan_err0(lc, _("Invalid month, week or position day range"));
525 /* Lookup end of range */
527 for (i=0; keyw[i].name; i++) {
528 if (strcmp(p, keyw[i].name) == 0) {
529 state2 = keyw[i].state;
530 code2 = keyw[i].code;
535 if (i != 0 || state != state2 || code == code2) {
536 scan_err0(lc, _("Invalid month, weekday or position range"));
539 if (state == s_wday) {
541 clear_bits(0, 6, lrun.wday);
545 set_bits(code, code2, lrun.wday);
547 set_bits(code, 6, lrun.wday);
548 set_bits(0, code2, lrun.wday);
550 } else if (state == s_month) {
552 clear_bits(0, 11, lrun.month);
556 set_bits(code, code2, lrun.month);
558 /* this is a bit odd, but we accept it anyway */
559 set_bits(code, 11, lrun.month);
560 set_bits(0, code2, lrun.month);
563 /* Must be position */
565 clear_bits(0, 4, lrun.wom);
569 set_bits(code, code2, lrun.wom);
571 set_bits(code, 4, lrun.wom);
572 set_bits(0, code2, lrun.wom);
578 set_bits(0, 23, lrun.hour);
581 have_mday = have_wom = have_woy = true;
582 set_bits(0, 30, lrun.mday);
583 set_bits(0, 4, lrun.wom);
584 set_bits(0, 53, lrun.woy);
588 set_bits(0, 6, lrun.wday);
592 set_bits(0, 11, lrun.month);
595 scan_err0(lc, _("Unexpected run state\n"));
601 /* Allocate run record, copy new stuff into it,
602 * and link it into the list of run records
603 * in the schedule resource.
606 trun = (RUN *)malloc(sizeof(RUN));
607 memcpy(trun, &lrun, sizeof(RUN));
614 lc->options = options; /* restore scanner options */
615 set_bit(index, res_all.res_sch.hdr.item_present);