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 as
15 published by the Free Software Foundation; either version 2 of
16 the License, or (at your option) any later version.
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 GNU
21 General Public License for more details.
23 You should have received a copy of the GNU General Public
24 License along with this program; if not, write to the Free
25 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
34 extern struct s_jl joblevels[];
36 /* Forward referenced subroutines */
50 s_wom, /* 1st, 2nd, ...*/
51 s_woy /* week of year w00 - w53 */
55 const char *name; /* keyword */
56 enum e_state state; /* parser state */
57 int code; /* state value */
60 /* Keywords understood by parser */
61 static struct s_keyw keyw[] = {
62 {N_("on"), s_none, 0},
65 {N_("sun"), s_wday, 0},
66 {N_("mon"), s_wday, 1},
67 {N_("tue"), s_wday, 2},
68 {N_("wed"), s_wday, 3},
69 {N_("thu"), s_wday, 4},
70 {N_("fri"), s_wday, 5},
71 {N_("sat"), s_wday, 6},
72 {N_("jan"), s_month, 0},
73 {N_("feb"), s_month, 1},
74 {N_("mar"), s_month, 2},
75 {N_("apr"), s_month, 3},
76 {N_("may"), s_month, 4},
77 {N_("jun"), s_month, 5},
78 {N_("jul"), s_month, 6},
79 {N_("aug"), s_month, 7},
80 {N_("sep"), s_month, 8},
81 {N_("oct"), s_month, 9},
82 {N_("nov"), s_month, 10},
83 {N_("dec"), s_month, 11},
85 {N_("sunday"), s_wday, 0},
86 {N_("monday"), s_wday, 1},
87 {N_("tuesday"), s_wday, 2},
88 {N_("wednesday"), s_wday, 3},
89 {N_("thursday"), s_wday, 4},
90 {N_("friday"), s_wday, 5},
91 {N_("saturday"), s_wday, 6},
92 {N_("january"), s_month, 0},
93 {N_("february"), s_month, 1},
94 {N_("march"), s_month, 2},
95 {N_("april"), s_month, 3},
96 {N_("june"), s_month, 5},
97 {N_("july"), s_month, 6},
98 {N_("august"), s_month, 7},
99 {N_("september"), s_month, 8},
100 {N_("october"), s_month, 9},
101 {N_("november"), s_month, 10},
102 {N_("december"), s_month, 11},
104 {N_("daily"), s_daily, 0},
105 {N_("weekly"), s_weekly, 0},
106 {N_("monthly"), s_monthly, 0},
107 {N_("hourly"), s_hourly, 0},
109 {N_("1st"), s_wom, 0},
110 {N_("2nd"), s_wom, 1},
111 {N_("3rd"), s_wom, 2},
112 {N_("4th"), s_wom, 3},
113 {N_("5th"), s_wom, 4},
115 {N_("first"), s_wom, 0},
116 {N_("second"), s_wom, 1},
117 {N_("third"), s_wom, 2},
118 {N_("fourth"), s_wom, 3},
119 {N_("fifth"), s_wom, 4},
123 static bool have_hour, have_mday, have_wday, have_month, have_wom;
124 static bool have_at, have_woy;
127 static void set_defaults()
129 have_hour = have_mday = have_wday = have_month = have_wom = have_woy = false;
131 set_bits(0, 23, lrun.hour);
132 set_bits(0, 30, lrun.mday);
133 set_bits(0, 6, lrun.wday);
134 set_bits(0, 11, lrun.month);
135 set_bits(0, 4, lrun.wom);
136 set_bits(0, 53, lrun.woy);
140 /* Keywords (RHS) permitted in Run records */
141 static struct s_kw RunFields[] = {
144 {"incrementalpool", 'i'},
145 {"differentialpool", 'd'},
151 {"writepartafterjob", 'W'},
156 * Store Schedule Run information
158 * Parse Run statement:
160 * Run <keyword=value ...> [on] 2 january at 23:45
162 * Default Run time is daily at 0:0
164 * There can be multiple run statements, they are simply chained
168 void store_run(LEX *lc, RES_ITEM *item, int index, int pass)
172 int token, state, state2 = 0, code = 0, code2 = 0;
173 int options = lc->options;
174 RUN **run = (RUN **)(item->value);
180 lc->options |= LOPT_NO_IDENT; /* want only "strings" */
182 /* clear local copy of run record */
183 memset(&lrun, 0, sizeof(RUN));
185 /* scan for Job level "full", "incremental", ... */
186 for (found=true; found; ) {
188 token = lex_get_token(lc, T_NAME);
189 for (i=0; RunFields[i].name; i++) {
190 if (strcasecmp(lc->str, RunFields[i].name) == 0) {
192 if (lex_get_token(lc, T_ALL) != T_EQUALS) {
193 scan_err1(lc, "Expected an equals, got: %s", lc->str);
196 switch (RunFields[i].token) {
197 case 's': /* Data spooling */
198 token = lex_get_token(lc, T_NAME);
199 if (strcasecmp(lc->str, "yes") == 0) {
200 lrun.spool_data = true;
201 lrun.spool_data_set = true;
202 } else if (strcasecmp(lc->str, "no") == 0) {
203 lrun.spool_data = false;
204 lrun.spool_data_set = true;
206 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
209 case 'W': /* Write part after job */
210 token = lex_get_token(lc, T_NAME);
211 if (strcasecmp(lc->str, "yes") == 0) {
212 lrun.write_part_after_job = true;
213 lrun.write_part_after_job_set = true;
214 } else if (strcasecmp(lc->str, "no") == 0) {
215 lrun.write_part_after_job = false;
216 lrun.write_part_after_job_set = true;
218 scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
221 case 'L': /* level */
222 token = lex_get_token(lc, T_NAME);
223 for (j=0; joblevels[j].level_name; j++) {
224 if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
225 lrun.level = joblevels[j].level;
226 lrun.job_type = joblevels[j].job_type;
232 scan_err1(lc, _("Job level field: %s not found in run record"), lc->str);
236 case 'p': /* Priority */
237 token = lex_get_token(lc, T_PINT32);
239 lrun.Priority = lc->pint32_val;
243 case 'f': /* FullPool */
244 case 'i': /* IncPool */
245 case 'd': /* DifPool */
246 token = lex_get_token(lc, T_NAME);
248 res = GetResWithName(R_POOL, lc->str);
250 scan_err1(lc, "Could not find specified Pool Resource: %s",
254 switch(RunFields[i].token) {
256 lrun.pool = (POOL *)res;
259 lrun.full_pool = (POOL *)res;
262 lrun.inc_pool = (POOL *)res;
265 lrun.dif_pool = (POOL *)res;
270 case 'S': /* storage */
271 token = lex_get_token(lc, T_NAME);
273 res = GetResWithName(R_STORAGE, lc->str);
275 scan_err1(lc, "Could not find specified Storage Resource: %s",
279 lrun.storage = (STORE *)res;
282 case 'M': /* messages */
283 token = lex_get_token(lc, T_NAME);
285 res = GetResWithName(R_MSGS, lc->str);
287 scan_err1(lc, "Could not find specified Messages Resource: %s",
291 lrun.msgs = (MSGS *)res;
295 scan_err1(lc, "Expected a keyword name, got: %s", lc->str);
299 } /* end if strcasecmp */
300 } /* end for RunFields */
302 /* At this point, it is not a keyword. Check for old syle
303 * Job Levels without keyword. This form is depreciated!!!
305 for (j=0; joblevels[j].level_name; j++) {
306 if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
307 lrun.level = joblevels[j].level;
308 lrun.job_type = joblevels[j].job_type;
313 } /* end for found */
317 * Scan schedule times.
318 * Default is: daily at 0:0
323 for ( ; token != T_EOL; (token = lex_get_token(lc, T_ALL))) {
328 code = atoi(lc->str) - 1;
329 if (code < 0 || code > 30) {
330 scan_err0(lc, _("Day number out of range (1-31)"));
333 case T_NAME: /* this handles drop through from keyword */
334 case T_UNQUOTED_STRING:
335 if (strchr(lc->str, (int)'-')) {
339 if (strchr(lc->str, (int)':')) {
343 if (lc->str_len == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') &&
344 is_an_integer(lc->str+1)) {
345 code = atoi(lc->str+1);
346 if (code < 0 || code > 53) {
347 scan_err0(lc, _("Week number out of range (0-53)"));
349 state = s_woy; /* week of year */
352 /* everything else must be a keyword */
353 for (i=0; keyw[i].name; i++) {
354 if (strcasecmp(lc->str, keyw[i].name) == 0) {
355 state = keyw[i].state;
362 scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
369 scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
376 case s_mday: /* day of month */
378 clear_bits(0, 30, lrun.mday);
381 set_bit(code, lrun.mday);
383 case s_month: /* month of year */
385 clear_bits(0, 11, lrun.month);
388 set_bit(code, lrun.month);
390 case s_wday: /* week day */
392 clear_bits(0, 6, lrun.wday);
395 set_bit(code, lrun.wday);
397 case s_wom: /* Week of month 1st, ... */
399 clear_bits(0, 4, lrun.wom);
402 set_bit(code, lrun.wom);
406 clear_bits(0, 53, lrun.woy);
409 set_bit(code, lrun.woy);
411 case s_time: /* time */
413 scan_err0(lc, _("Time must be preceded by keyword AT."));
417 clear_bits(0, 23, lrun.hour);
419 p = strchr(lc->str, ':');
421 scan_err0(lc, _("Time logic error.\n"));
424 *p++ = 0; /* separate two halves */
425 code = atoi(lc->str);
427 if (len > 2 && p[len-1] == 'm') {
428 if (p[len-2] == 'a') {
430 } else if (p[len-2] == 'p') {
433 scan_err0(lc, _("Bad time specification."));
443 if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
444 scan_err0(lc, _("Bad time specification."));
447 /****FIXME**** convert to UTC */
448 set_bit(code, lrun.hour);
456 p = strchr(lc->str, '-');
458 scan_err0(lc, _("Range logic error.\n"));
460 *p++ = 0; /* separate two halves */
462 /* Check for day range */
463 if (is_an_integer(lc->str) && is_an_integer(p)) {
464 code = atoi(lc->str) - 1;
466 if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
467 scan_err0(lc, _("Bad day range specification."));
470 clear_bits(0, 30, lrun.mday);
474 set_bits(code, code2, lrun.mday);
476 set_bits(code, 30, lrun.mday);
477 set_bits(0, code2, lrun.mday);
481 /* Check for week of year range */
482 if (strlen(lc->str) == 3 && strlen(p) == 3 &&
483 (lc->str[0] == 'w' || lc->str[0] == 'W') &&
484 (p[0] == 'w' || p[0] == 'W') &&
485 is_an_integer(lc->str+1) && is_an_integer(p+1)) {
486 code = atoi(lc->str+1);
488 if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
489 scan_err0(lc, _("Week number out of range (0-53)"));
492 clear_bits(0, 53, lrun.woy);
496 set_bits(code, code2, lrun.woy);
498 set_bits(code, 53, lrun.woy);
499 set_bits(0, code2, lrun.woy);
503 /* lookup first half of keyword range (week days or months) */
505 for (i=0; keyw[i].name; i++) {
506 if (strcmp(lc->str, keyw[i].name) == 0) {
507 state = keyw[i].state;
513 if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
514 scan_err0(lc, _("Invalid month, week or position day range"));
518 /* Lookup end of range */
520 for (i=0; keyw[i].name; i++) {
521 if (strcmp(p, keyw[i].name) == 0) {
522 state2 = keyw[i].state;
523 code2 = keyw[i].code;
528 if (i != 0 || state != state2 || code == code2) {
529 scan_err0(lc, _("Invalid month, weekday or position range"));
532 if (state == s_wday) {
534 clear_bits(0, 6, lrun.wday);
538 set_bits(code, code2, lrun.wday);
540 set_bits(code, 6, lrun.wday);
541 set_bits(0, code2, lrun.wday);
543 } else if (state == s_month) {
545 clear_bits(0, 11, lrun.month);
549 set_bits(code, code2, lrun.month);
551 /* this is a bit odd, but we accept it anyway */
552 set_bits(code, 11, lrun.month);
553 set_bits(0, code2, lrun.month);
556 /* Must be position */
558 clear_bits(0, 4, lrun.wom);
562 set_bits(code, code2, lrun.wom);
564 set_bits(code, 4, lrun.wom);
565 set_bits(0, code2, lrun.wom);
571 set_bits(0, 23, lrun.hour);
574 have_mday = have_wom = have_woy = true;
575 set_bits(0, 30, lrun.mday);
576 set_bits(0, 4, lrun.wom);
577 set_bits(0, 53, lrun.woy);
581 set_bits(0, 6, lrun.wday);
585 set_bits(0, 11, lrun.month);
588 scan_err0(lc, _("Unexpected run state\n"));
594 /* Allocate run record, copy new stuff into it,
595 * and link it into the list of run records
596 * in the schedule resource.
599 trun = (RUN *)malloc(sizeof(RUN));
600 memcpy(trun, &lrun, sizeof(RUN));
607 lc->options = options; /* restore scanner options */
608 set_bit(index, res_all.res_sch.hdr.item_present);