3 * Configuration parser for Director Run Configuration
4 * directives, which are part of the Schedule Resource
11 Copyright (C) 2000-2005 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 ammended 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 {N_("on"), s_none, 0},
60 {N_("sun"), s_wday, 0},
61 {N_("mon"), s_wday, 1},
62 {N_("tue"), s_wday, 2},
63 {N_("wed"), s_wday, 3},
64 {N_("thu"), s_wday, 4},
65 {N_("fri"), s_wday, 5},
66 {N_("sat"), s_wday, 6},
67 {N_("jan"), s_month, 0},
68 {N_("feb"), s_month, 1},
69 {N_("mar"), s_month, 2},
70 {N_("apr"), s_month, 3},
71 {N_("may"), s_month, 4},
72 {N_("jun"), s_month, 5},
73 {N_("jul"), s_month, 6},
74 {N_("aug"), s_month, 7},
75 {N_("sep"), s_month, 8},
76 {N_("oct"), s_month, 9},
77 {N_("nov"), s_month, 10},
78 {N_("dec"), s_month, 11},
80 {N_("sunday"), s_wday, 0},
81 {N_("monday"), s_wday, 1},
82 {N_("tuesday"), s_wday, 2},
83 {N_("wednesday"), s_wday, 3},
84 {N_("thursday"), s_wday, 4},
85 {N_("friday"), s_wday, 5},
86 {N_("saturday"), s_wday, 6},
87 {N_("january"), s_month, 0},
88 {N_("february"), s_month, 1},
89 {N_("march"), s_month, 2},
90 {N_("april"), s_month, 3},
91 {N_("june"), s_month, 5},
92 {N_("july"), s_month, 6},
93 {N_("august"), s_month, 7},
94 {N_("september"), s_month, 8},
95 {N_("october"), s_month, 9},
96 {N_("november"), s_month, 10},
97 {N_("december"), s_month, 11},
99 {N_("daily"), s_daily, 0},
100 {N_("weekly"), s_weekly, 0},
101 {N_("monthly"), s_monthly, 0},
102 {N_("hourly"), s_hourly, 0},
104 {N_("1st"), s_wom, 0},
105 {N_("2nd"), s_wom, 1},
106 {N_("3rd"), s_wom, 2},
107 {N_("4th"), s_wom, 3},
108 {N_("5th"), s_wom, 4},
110 {N_("first"), s_wom, 0},
111 {N_("second"), s_wom, 1},
112 {N_("third"), s_wom, 2},
113 {N_("fourth"), s_wom, 3},
114 {N_("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.dif_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!!!
300 for (j=0; joblevels[j].level_name; j++) {
301 if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
302 lrun.level = joblevels[j].level;
303 lrun.job_type = joblevels[j].job_type;
308 } /* end for found */
312 * Scan schedule times.
313 * Default is: daily at 0:0
318 for ( ; token != T_EOL; (token = lex_get_token(lc, T_ALL))) {
323 code = atoi(lc->str) - 1;
324 if (code < 0 || code > 30) {
325 scan_err0(lc, _("Day number out of range (1-31)"));
328 case T_NAME: /* this handles drop through from keyword */
329 case T_UNQUOTED_STRING:
330 if (strchr(lc->str, (int)'-')) {
334 if (strchr(lc->str, (int)':')) {
338 if (lc->str_len == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') &&
339 is_an_integer(lc->str+1)) {
340 code = atoi(lc->str+1);
341 if (code < 0 || code > 53) {
342 scan_err0(lc, _("Week number out of range (0-53)"));
344 state = s_woy; /* week of year */
347 /* everything else must be a keyword */
348 for (i=0; keyw[i].name; i++) {
349 if (strcasecmp(lc->str, keyw[i].name) == 0) {
350 state = keyw[i].state;
357 scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
364 scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
371 case s_mday: /* day of month */
373 clear_bits(0, 30, lrun.mday);
376 set_bit(code, lrun.mday);
378 case s_month: /* month of year */
380 clear_bits(0, 11, lrun.month);
383 set_bit(code, lrun.month);
385 case s_wday: /* week day */
387 clear_bits(0, 6, lrun.wday);
390 set_bit(code, lrun.wday);
392 case s_wom: /* Week of month 1st, ... */
394 clear_bits(0, 4, lrun.wom);
397 set_bit(code, lrun.wom);
401 clear_bits(0, 53, lrun.woy);
404 set_bit(code, lrun.woy);
406 case s_time: /* time */
408 scan_err0(lc, _("Time must be preceded by keyword AT."));
412 clear_bits(0, 23, lrun.hour);
414 p = strchr(lc->str, ':');
416 scan_err0(lc, _("Time logic error.\n"));
419 *p++ = 0; /* separate two halves */
420 code = atoi(lc->str); /* pick up hour */
422 if (len > 2 && p[len-1] == 'm') {
423 if (p[len-2] == 'a') {
425 } else if (p[len-2] == 'p') {
428 scan_err0(lc, _("Bad time specification."));
434 code2 = atoi(p); /* pick up minutes */
436 /* Convert to 24 hour time */
443 if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
444 scan_err0(lc, _("Bad time specification."));
447 set_bit(code, lrun.hour);
455 p = strchr(lc->str, '-');
457 scan_err0(lc, _("Range logic error.\n"));
459 *p++ = 0; /* separate two halves */
461 /* Check for day range */
462 if (is_an_integer(lc->str) && is_an_integer(p)) {
463 code = atoi(lc->str) - 1;
465 if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
466 scan_err0(lc, _("Bad day range specification."));
469 clear_bits(0, 30, lrun.mday);
473 set_bits(code, code2, lrun.mday);
475 set_bits(code, 30, lrun.mday);
476 set_bits(0, code2, lrun.mday);
480 /* Check for week of year range */
481 if (strlen(lc->str) == 3 && strlen(p) == 3 &&
482 (lc->str[0] == 'w' || lc->str[0] == 'W') &&
483 (p[0] == 'w' || p[0] == 'W') &&
484 is_an_integer(lc->str+1) && is_an_integer(p+1)) {
485 code = atoi(lc->str+1);
487 if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
488 scan_err0(lc, _("Week number out of range (0-53)"));
491 clear_bits(0, 53, lrun.woy);
495 set_bits(code, code2, lrun.woy);
497 set_bits(code, 53, lrun.woy);
498 set_bits(0, code2, lrun.woy);
502 /* lookup first half of keyword range (week days or months) */
504 for (i=0; keyw[i].name; i++) {
505 if (strcmp(lc->str, keyw[i].name) == 0) {
506 state = keyw[i].state;
512 if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
513 scan_err0(lc, _("Invalid month, week or position day range"));
517 /* Lookup end of range */
519 for (i=0; keyw[i].name; i++) {
520 if (strcmp(p, keyw[i].name) == 0) {
521 state2 = keyw[i].state;
522 code2 = keyw[i].code;
527 if (i != 0 || state != state2 || code == code2) {
528 scan_err0(lc, _("Invalid month, weekday or position range"));
531 if (state == s_wday) {
533 clear_bits(0, 6, lrun.wday);
537 set_bits(code, code2, lrun.wday);
539 set_bits(code, 6, lrun.wday);
540 set_bits(0, code2, lrun.wday);
542 } else if (state == s_month) {
544 clear_bits(0, 11, lrun.month);
548 set_bits(code, code2, lrun.month);
550 /* this is a bit odd, but we accept it anyway */
551 set_bits(code, 11, lrun.month);
552 set_bits(0, code2, lrun.month);
555 /* Must be position */
557 clear_bits(0, 4, lrun.wom);
561 set_bits(code, code2, lrun.wom);
563 set_bits(code, 4, lrun.wom);
564 set_bits(0, code2, lrun.wom);
570 set_bits(0, 23, lrun.hour);
573 have_mday = have_wom = have_woy = true;
574 set_bits(0, 30, lrun.mday);
575 set_bits(0, 4, lrun.wom);
576 set_bits(0, 53, lrun.woy);
580 set_bits(0, 6, lrun.wday);
584 set_bits(0, 11, lrun.month);
587 scan_err0(lc, _("Unexpected run state\n"));
593 /* Allocate run record, copy new stuff into it,
594 * and link it into the list of run records
595 * in the schedule resource.
598 trun = (RUN *)malloc(sizeof(RUN));
599 memcpy(trun, &lrun, sizeof(RUN));
606 lc->options = options; /* restore scanner options */
607 set_bit(index, res_all.res_sch.hdr.item_present);