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 struct s_jl joblevels[];
31 /* Forward referenced subroutines */
45 s_wom, /* 1st, 2nd, ...*/
46 s_woy /* week of year w00 - w53 */
50 const char *name; /* keyword */
51 enum e_state state; /* parser state */
52 int code; /* state value */
55 /* Keywords understood by parser */
56 static struct s_keyw keyw[] = {
57 {NT_("on"), s_none, 0},
60 {NT_("sun"), s_wday, 0},
61 {NT_("mon"), s_wday, 1},
62 {NT_("tue"), s_wday, 2},
63 {NT_("wed"), s_wday, 3},
64 {NT_("thu"), s_wday, 4},
65 {NT_("fri"), s_wday, 5},
66 {NT_("sat"), s_wday, 6},
67 {NT_("jan"), s_month, 0},
68 {NT_("feb"), s_month, 1},
69 {NT_("mar"), s_month, 2},
70 {NT_("apr"), s_month, 3},
71 {NT_("may"), s_month, 4},
72 {NT_("jun"), s_month, 5},
73 {NT_("jul"), s_month, 6},
74 {NT_("aug"), s_month, 7},
75 {NT_("sep"), s_month, 8},
76 {NT_("oct"), s_month, 9},
77 {NT_("nov"), s_month, 10},
78 {NT_("dec"), s_month, 11},
80 {NT_("sunday"), s_wday, 0},
81 {NT_("monday"), s_wday, 1},
82 {NT_("tuesday"), s_wday, 2},
83 {NT_("wednesday"), s_wday, 3},
84 {NT_("thursday"), s_wday, 4},
85 {NT_("friday"), s_wday, 5},
86 {NT_("saturday"), s_wday, 6},
87 {NT_("january"), s_month, 0},
88 {NT_("february"), s_month, 1},
89 {NT_("march"), s_month, 2},
90 {NT_("april"), s_month, 3},
91 {NT_("june"), s_month, 5},
92 {NT_("july"), s_month, 6},
93 {NT_("august"), s_month, 7},
94 {NT_("september"), s_month, 8},
95 {NT_("october"), s_month, 9},
96 {NT_("november"), s_month, 10},
97 {NT_("december"), s_month, 11},
99 {NT_("daily"), s_daily, 0},
100 {NT_("weekly"), s_weekly, 0},
101 {NT_("monthly"), s_monthly, 0},
102 {NT_("hourly"), s_hourly, 0},
104 {NT_("1st"), s_wom, 0},
105 {NT_("2nd"), s_wom, 1},
106 {NT_("3rd"), s_wom, 2},
107 {NT_("4th"), s_wom, 3},
108 {NT_("5th"), s_wom, 4},
110 {NT_("first"), s_wom, 0},
111 {NT_("second"), s_wom, 1},
112 {NT_("third"), s_wom, 2},
113 {NT_("fourth"), s_wom, 3},
114 {NT_("fifth"), s_wom, 4},
118 static bool have_hour, have_mday, have_wday, have_month, have_wom;
119 static bool have_at, have_woy;
122 static void set_defaults()
124 have_hour = have_mday = have_wday = have_month = have_wom = have_woy = false;
126 set_bits(0, 23, lrun.hour);
127 set_bits(0, 30, lrun.mday);
128 set_bits(0, 6, lrun.wday);
129 set_bits(0, 11, lrun.month);
130 set_bits(0, 4, lrun.wom);
131 set_bits(0, 53, lrun.woy);
135 /* Keywords (RHS) permitted in Run records */
136 static struct s_kw RunFields[] = {
139 {"incrementalpool", 'i'},
140 {"differentialpool", 'd'},
146 {"writepartafterjob", 'W'},
151 * Store Schedule Run information
153 * Parse Run statement:
155 * Run <keyword=value ...> [on] 2 january at 23:45
157 * Default Run time is daily at 0:0
159 * There can be multiple run statements, they are simply chained
163 void store_run(LEX *lc, RES_ITEM *item, int index, int pass)
167 int token, state, state2 = 0, code = 0, code2 = 0;
168 int options = lc->options;
169 RUN **run = (RUN **)(item->value);
175 lc->options |= LOPT_NO_IDENT; /* want only "strings" */
177 /* clear local copy of run record */
178 memset(&lrun, 0, sizeof(RUN));
180 /* scan for Job level "full", "incremental", ... */
181 for (found=true; found; ) {
183 token = lex_get_token(lc, T_NAME);
184 for (i=0; RunFields[i].name; i++) {
185 if (strcasecmp(lc->str, RunFields[i].name) == 0) {
187 if (lex_get_token(lc, T_ALL) != T_EQUALS) {
188 scan_err1(lc, _("Expected an equals, got: %s"), lc->str);
191 switch (RunFields[i].token) {
192 case 's': /* Data spooling */
193 token = lex_get_token(lc, T_NAME);
194 if (strcasecmp(lc->str, "yes") == 0) {
195 lrun.spool_data = true;
196 lrun.spool_data_set = true;
197 } else if (strcasecmp(lc->str, "no") == 0) {
198 lrun.spool_data = false;
199 lrun.spool_data_set = true;
201 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
204 case 'W': /* Write part after job */
205 token = lex_get_token(lc, T_NAME);
206 if (strcasecmp(lc->str, "yes") == 0) {
207 lrun.write_part_after_job = true;
208 lrun.write_part_after_job_set = true;
209 } else if (strcasecmp(lc->str, "no") == 0) {
210 lrun.write_part_after_job = false;
211 lrun.write_part_after_job_set = true;
213 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
216 case 'L': /* level */
217 token = lex_get_token(lc, T_NAME);
218 for (j=0; joblevels[j].level_name; j++) {
219 if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
220 lrun.level = joblevels[j].level;
221 lrun.job_type = joblevels[j].job_type;
227 scan_err1(lc, _("Job level field: %s not found in run record"), lc->str);
231 case 'p': /* Priority */
232 token = lex_get_token(lc, T_PINT32);
234 lrun.Priority = lc->pint32_val;
238 case 'f': /* FullPool */
239 case 'i': /* IncPool */
240 case 'd': /* DifPool */
241 token = lex_get_token(lc, T_NAME);
243 res = GetResWithName(R_POOL, lc->str);
245 scan_err1(lc, _("Could not find specified Pool Resource: %s"),
249 switch(RunFields[i].token) {
251 lrun.pool = (POOL *)res;
254 lrun.full_pool = (POOL *)res;
257 lrun.inc_pool = (POOL *)res;
260 lrun.diff_pool = (POOL *)res;
265 case 'S': /* storage */
266 token = lex_get_token(lc, T_NAME);
268 res = GetResWithName(R_STORAGE, lc->str);
270 scan_err1(lc, _("Could not find specified Storage Resource: %s"),
274 lrun.storage = (STORE *)res;
277 case 'M': /* messages */
278 token = lex_get_token(lc, T_NAME);
280 res = GetResWithName(R_MSGS, lc->str);
282 scan_err1(lc, _("Could not find specified Messages Resource: %s"),
286 lrun.msgs = (MSGS *)res;
290 scan_err1(lc, _("Expected a keyword name, got: %s"), lc->str);
294 } /* end if strcasecmp */
295 } /* end for RunFields */
297 /* At this point, it is not a keyword. Check for old syle
298 * Job Levels without keyword. This form is depreciated!!!
301 for (j=0; joblevels[j].level_name; j++) {
302 if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
303 lrun.level = joblevels[j].level;
304 lrun.job_type = joblevels[j].job_type;
310 } /* end for found */
314 * Scan schedule times.
315 * Default is: daily at 0:0
320 for ( ; token != T_EOL; (token = lex_get_token(lc, T_ALL))) {
325 code = atoi(lc->str) - 1;
326 if (code < 0 || code > 30) {
327 scan_err0(lc, _("Day number out of range (1-31)"));
330 case T_NAME: /* this handles drop through from keyword */
331 case T_UNQUOTED_STRING:
332 if (strchr(lc->str, (int)'-')) {
336 if (strchr(lc->str, (int)':')) {
340 if (lc->str_len == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') &&
341 is_an_integer(lc->str+1)) {
342 code = atoi(lc->str+1);
343 if (code < 0 || code > 53) {
344 scan_err0(lc, _("Week number out of range (0-53)"));
346 state = s_woy; /* week of year */
349 /* everything else must be a keyword */
350 for (i=0; keyw[i].name; i++) {
351 if (strcasecmp(lc->str, keyw[i].name) == 0) {
352 state = keyw[i].state;
359 scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
366 scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
373 case s_mday: /* day of month */
375 clear_bits(0, 30, lrun.mday);
378 set_bit(code, lrun.mday);
380 case s_month: /* month of year */
382 clear_bits(0, 11, lrun.month);
385 set_bit(code, lrun.month);
387 case s_wday: /* week day */
389 clear_bits(0, 6, lrun.wday);
392 set_bit(code, lrun.wday);
394 case s_wom: /* Week of month 1st, ... */
396 clear_bits(0, 4, lrun.wom);
399 set_bit(code, lrun.wom);
403 clear_bits(0, 53, lrun.woy);
406 set_bit(code, lrun.woy);
408 case s_time: /* time */
410 scan_err0(lc, _("Time must be preceded by keyword AT."));
414 clear_bits(0, 23, lrun.hour);
416 p = strchr(lc->str, ':');
418 scan_err0(lc, _("Time logic error.\n"));
421 *p++ = 0; /* separate two halves */
422 code = atoi(lc->str); /* pick up hour */
424 if (len > 2 && p[len-1] == 'm') {
425 if (p[len-2] == 'a') {
427 } else if (p[len-2] == 'p') {
430 scan_err0(lc, _("Bad time specification."));
436 code2 = atoi(p); /* pick up minutes */
438 /* Convert to 24 hour time */
445 if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
446 scan_err0(lc, _("Bad time specification."));
449 set_bit(code, lrun.hour);
457 p = strchr(lc->str, '-');
459 scan_err0(lc, _("Range logic error.\n"));
461 *p++ = 0; /* separate two halves */
463 /* Check for day range */
464 if (is_an_integer(lc->str) && is_an_integer(p)) {
465 code = atoi(lc->str) - 1;
467 if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
468 scan_err0(lc, _("Bad day range specification."));
471 clear_bits(0, 30, lrun.mday);
475 set_bits(code, code2, lrun.mday);
477 set_bits(code, 30, lrun.mday);
478 set_bits(0, code2, lrun.mday);
482 /* Check for week of year range */
483 if (strlen(lc->str) == 3 && strlen(p) == 3 &&
484 (lc->str[0] == 'w' || lc->str[0] == 'W') &&
485 (p[0] == 'w' || p[0] == 'W') &&
486 is_an_integer(lc->str+1) && is_an_integer(p+1)) {
487 code = atoi(lc->str+1);
489 if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
490 scan_err0(lc, _("Week number out of range (0-53)"));
493 clear_bits(0, 53, lrun.woy);
497 set_bits(code, code2, lrun.woy);
499 set_bits(code, 53, lrun.woy);
500 set_bits(0, code2, lrun.woy);
504 /* lookup first half of keyword range (week days or months) */
506 for (i=0; keyw[i].name; i++) {
507 if (strcmp(lc->str, keyw[i].name) == 0) {
508 state = keyw[i].state;
514 if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
515 scan_err0(lc, _("Invalid month, week or position day range"));
519 /* Lookup end of range */
521 for (i=0; keyw[i].name; i++) {
522 if (strcmp(p, keyw[i].name) == 0) {
523 state2 = keyw[i].state;
524 code2 = keyw[i].code;
529 if (i != 0 || state != state2 || code == code2) {
530 scan_err0(lc, _("Invalid month, weekday or position range"));
533 if (state == s_wday) {
535 clear_bits(0, 6, lrun.wday);
539 set_bits(code, code2, lrun.wday);
541 set_bits(code, 6, lrun.wday);
542 set_bits(0, code2, lrun.wday);
544 } else if (state == s_month) {
546 clear_bits(0, 11, lrun.month);
550 set_bits(code, code2, lrun.month);
552 /* this is a bit odd, but we accept it anyway */
553 set_bits(code, 11, lrun.month);
554 set_bits(0, code2, lrun.month);
557 /* Must be position */
559 clear_bits(0, 4, lrun.wom);
563 set_bits(code, code2, lrun.wom);
565 set_bits(code, 4, lrun.wom);
566 set_bits(0, code2, lrun.wom);
572 set_bits(0, 23, lrun.hour);
575 have_mday = have_wom = have_woy = true;
576 set_bits(0, 30, lrun.mday);
577 set_bits(0, 4, lrun.wom);
578 set_bits(0, 53, lrun.woy);
582 set_bits(0, 6, lrun.wday);
586 set_bits(0, 11, lrun.month);
589 scan_err0(lc, _("Unexpected run state\n"));
595 /* Allocate run record, copy new stuff into it,
596 * and link it into the list of run records
597 * in the schedule resource.
600 trun = (RUN *)malloc(sizeof(RUN));
601 memcpy(trun, &lrun, sizeof(RUN));
608 lc->options = options; /* restore scanner options */
609 set_bit(index, res_all.res_sch.hdr.item_present);