]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/run_conf.c
======================= Warning ==========================
[bacula/bacula] / bacula / src / dird / run_conf.c
1 /*
2  *
3  *  Configuration parser for Director Run Configuration
4  *   directives, which are part of the Schedule Resource
5  *
6  *     Kern Sibbald, May MM
7  *
8  *     Version $Id$
9  */
10 /*
11    Copyright (C) 2000-2006 Kern Sibbald
12
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.
17
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.
22
23  */
24
25 #include "bacula.h"
26 #include "dird.h"
27
28 #if defined(_MSC_VER)
29 extern "C" { // work around visual compiler mangling variables
30    extern URES res_all;
31 }
32 #else
33 extern URES res_all;
34 #endif
35 extern struct s_jl joblevels[];
36
37 /* Forward referenced subroutines */
38
39 enum e_state {
40    s_none = 0,
41    s_range,
42    s_mday,
43    s_month,
44    s_time,
45    s_at,
46    s_wday,
47    s_daily,
48    s_weekly,
49    s_monthly,
50    s_hourly,
51    s_wom,                           /* 1st, 2nd, ...*/
52    s_woy                            /* week of year w00 - w53 */
53 };
54
55 struct s_keyw {
56   const char *name;                           /* keyword */
57   enum e_state state;                 /* parser state */
58   int code;                           /* state value */
59 };
60
61 /* Keywords understood by parser */
62 static struct s_keyw keyw[] = {
63   {NT_("on"),         s_none,    0},
64   {NT_("at"),         s_at,      0},
65
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},
85
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},
104
105   {NT_("daily"),      s_daily,   0},
106   {NT_("weekly"),     s_weekly,  0},
107   {NT_("monthly"),    s_monthly, 0},
108   {NT_("hourly"),     s_hourly,  0},
109
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},
115
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},
121   {NULL,         s_none,    0}
122 };
123
124 static bool have_hour, have_mday, have_wday, have_month, have_wom;
125 static bool have_at, have_woy;
126 static RUN lrun;
127
128 static void set_defaults()
129 {
130    have_hour = have_mday = have_wday = have_month = have_wom = have_woy = false;
131    have_at = 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);
138 }
139
140
141 /* Keywords (RHS) permitted in Run records */
142 static struct s_kw RunFields[] = {
143    {"pool",              'P'},
144    {"fullpool",          'f'},
145    {"incrementalpool",   'i'},
146    {"differentialpool",  'd'},
147    {"level",             'L'},
148    {"storage",           'S'},
149    {"messages",          'M'},
150    {"priority",          'p'},
151    {"spooldata",         's'},
152    {"writepartafterjob", 'W'},
153    {NULL,                 0}
154 };
155
156 /*
157  * Store Schedule Run information
158  *
159  * Parse Run statement:
160  *
161  *  Run <keyword=value ...> [on] 2 january at 23:45
162  *
163  *   Default Run time is daily at 0:0
164  *
165  *   There can be multiple run statements, they are simply chained
166  *   together.
167  *
168  */
169 void store_run(LEX *lc, RES_ITEM *item, int index, int pass)
170 {
171    int i, j;
172    bool found;
173    int token, state, state2 = 0, code = 0, code2 = 0;
174    int options = lc->options;
175    RUN **run = (RUN **)(item->value);
176    RUN *trun;
177    char *p;
178    RES *res;
179
180
181    lc->options |= LOPT_NO_IDENT;      /* want only "strings" */
182
183    /* clear local copy of run record */
184    memset(&lrun, 0, sizeof(RUN));
185
186    /* scan for Job level "full", "incremental", ... */
187    for (found=true; found; ) {
188       found = false;
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) {
192             found = true;
193             if (lex_get_token(lc, T_ALL) != T_EQUALS) {
194                scan_err1(lc, _("Expected an equals, got: %s"), lc->str);
195                /* NOT REACHED */
196             }
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) {
201                   lrun.spool_data = true;
202                   lrun.spool_data_set = true;
203                } else if (strcasecmp(lc->str, "no") == 0) {
204                   lrun.spool_data = false;
205                   lrun.spool_data_set = true;
206                } else {
207                   scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
208                }
209                break;
210             case 'W':                 /* Write part after job */
211                token = lex_get_token(lc, T_NAME);
212                if (strcasecmp(lc->str, "yes") == 0) {
213                   lrun.write_part_after_job = true;
214                   lrun.write_part_after_job_set = true;
215                } else if (strcasecmp(lc->str, "no") == 0) {
216                   lrun.write_part_after_job = false;
217                   lrun.write_part_after_job_set = true;
218                } else {
219                   scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
220                }
221                break;
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;
228                      j = 0;
229                      break;
230                   }
231                }
232                if (j != 0) {
233                   scan_err1(lc, _("Job level field: %s not found in run record"), lc->str);
234                   /* NOT REACHED */
235                }
236                break;
237             case 'p':                 /* Priority */
238                token = lex_get_token(lc, T_PINT32);
239                if (pass == 2) {
240                   lrun.Priority = lc->pint32_val;
241                }
242                break;
243             case 'P':                 /* Pool */
244             case 'f':                 /* FullPool */
245             case 'i':                 /* IncPool */
246             case 'd':                 /* DifPool */
247                token = lex_get_token(lc, T_NAME);
248                if (pass == 2) {
249                   res = GetResWithName(R_POOL, lc->str);
250                   if (res == NULL) {
251                      scan_err1(lc, _("Could not find specified Pool Resource: %s"),
252                                 lc->str);
253                      /* NOT REACHED */
254                   }
255                   switch(RunFields[i].token) {
256                   case 'P':
257                      lrun.pool = (POOL *)res;
258                      break;
259                   case 'f':
260                      lrun.full_pool = (POOL *)res;
261                      break;
262                   case 'i':
263                      lrun.inc_pool = (POOL *)res;
264                      break;
265                   case 'd':
266                      lrun.diff_pool = (POOL *)res;
267                      break;
268                   }
269                }
270                break;
271             case 'S':                 /* storage */
272                token = lex_get_token(lc, T_NAME);
273                if (pass == 2) {
274                   res = GetResWithName(R_STORAGE, lc->str);
275                   if (res == NULL) {
276                      scan_err1(lc, _("Could not find specified Storage Resource: %s"),
277                                 lc->str);
278                      /* NOT REACHED */
279                   }
280                   lrun.storage = (STORE *)res;
281                }
282                break;
283             case 'M':                 /* messages */
284                token = lex_get_token(lc, T_NAME);
285                if (pass == 2) {
286                   res = GetResWithName(R_MSGS, lc->str);
287                   if (res == NULL) {
288                      scan_err1(lc, _("Could not find specified Messages Resource: %s"),
289                                 lc->str);
290                      /* NOT REACHED */
291                   }
292                   lrun.msgs = (MSGS *)res;
293                }
294                break;
295             default:
296                scan_err1(lc, _("Expected a keyword name, got: %s"), lc->str);
297                /* NOT REACHED */
298                break;
299             } /* end switch */
300          } /* end if strcasecmp */
301       } /* end for RunFields */
302
303       /* At this point, it is not a keyword. Check for old syle
304        * Job Levels without keyword. This form is depreciated!!!
305        */
306       if (!found) {
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;
311                found = true;
312                break;
313             }
314          }
315       }
316    } /* end for found */
317
318
319    /*
320     * Scan schedule times.
321     * Default is: daily at 0:0
322     */
323    state = s_none;
324    set_defaults();
325
326    for ( ; token != T_EOL; (token = lex_get_token(lc, T_ALL))) {
327       int len, pm = 0;
328       switch (token) {
329       case T_NUMBER:
330          state = s_mday;
331          code = atoi(lc->str) - 1;
332          if (code < 0 || code > 30) {
333             scan_err0(lc, _("Day number out of range (1-31)"));
334          }
335          break;
336       case T_NAME:                 /* this handles drop through from keyword */
337       case T_UNQUOTED_STRING:
338          if (strchr(lc->str, (int)'-')) {
339             state = s_range;
340             break;
341          }
342          if (strchr(lc->str, (int)':')) {
343             state = s_time;
344             break;
345          }
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)"));
351             }
352             state = s_woy;            /* week of year */
353             break;
354          }
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;
359                code   = keyw[i].code;
360                i = 0;
361                break;
362             }
363          }
364          if (i != 0) {
365             scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
366             /* NOT REACHED */
367          }
368          break;
369       case T_COMMA:
370          continue;
371       default:
372          scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
373          /* NOT REACHED */
374          break;
375       }
376       switch (state) {
377       case s_none:
378          continue;
379       case s_mday:                 /* day of month */
380          if (!have_mday) {
381             clear_bits(0, 30, lrun.mday);
382             have_mday = true;
383          }
384          set_bit(code, lrun.mday);
385          break;
386       case s_month:                /* month of year */
387          if (!have_month) {
388             clear_bits(0, 11, lrun.month);
389             have_month = true;
390          }
391          set_bit(code, lrun.month);
392          break;
393       case s_wday:                 /* week day */
394          if (!have_wday) {
395             clear_bits(0, 6, lrun.wday);
396             have_wday = true;
397          }
398          set_bit(code, lrun.wday);
399          break;
400       case s_wom:                  /* Week of month 1st, ... */
401          if (!have_wom) {
402             clear_bits(0, 4, lrun.wom);
403             have_wom = true;
404          }
405          set_bit(code, lrun.wom);
406          break;
407       case s_woy:
408          if (!have_woy) {
409             clear_bits(0, 53, lrun.woy);
410             have_woy = true;
411          }
412          set_bit(code, lrun.woy);
413          break;
414       case s_time:                 /* time */
415          if (!have_at) {
416             scan_err0(lc, _("Time must be preceded by keyword AT."));
417             /* NOT REACHED */
418          }
419          if (!have_hour) {
420             clear_bits(0, 23, lrun.hour);
421          }
422          p = strchr(lc->str, ':');
423          if (!p)  {
424             scan_err0(lc, _("Time logic error.\n"));
425             /* NOT REACHED */
426          }
427          *p++ = 0;                 /* separate two halves */
428          code = atoi(lc->str);     /* pick up hour */
429          len = strlen(p);
430          if (len > 2 && p[len-1] == 'm') {
431             if (p[len-2] == 'a') {
432                pm = 0;
433             } else if (p[len-2] == 'p') {
434                pm = 1;
435             } else {
436                scan_err0(lc, _("Bad time specification."));
437                /* NOT REACHED */
438             }
439          } else {
440             pm = 0;
441          }
442          code2 = atoi(p);             /* pick up minutes */
443          if (pm) {
444             /* Convert to 24 hour time */
445             if (code == 12) {
446                code -= 12;
447             } else {
448                code += 12;
449             }
450          }
451          if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
452             scan_err0(lc, _("Bad time specification."));
453             /* NOT REACHED */
454          }
455          set_bit(code, lrun.hour);
456          lrun.minute = code2;
457          have_hour = true;
458          break;
459       case s_at:
460          have_at = true;
461          break;
462       case s_range:
463          p = strchr(lc->str, '-');
464          if (!p) {
465             scan_err0(lc, _("Range logic error.\n"));
466          }
467          *p++ = 0;                 /* separate two halves */
468
469          /* Check for day range */
470          if (is_an_integer(lc->str) && is_an_integer(p)) {
471             code = atoi(lc->str) - 1;
472             code2 = atoi(p) - 1;
473             if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
474                scan_err0(lc, _("Bad day range specification."));
475             }
476             if (!have_mday) {
477                clear_bits(0, 30, lrun.mday);
478                have_mday = true;
479             }
480             if (code < code2) {
481                set_bits(code, code2, lrun.mday);
482             } else {
483                set_bits(code, 30, lrun.mday);
484                set_bits(0, code2, lrun.mday);
485             }
486             break;
487          }
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);
494             code2 = atoi(p+1);
495             if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
496                scan_err0(lc, _("Week number out of range (0-53)"));
497             }
498             if (!have_woy) {
499                clear_bits(0, 53, lrun.woy);
500                have_woy = true;
501             }
502             if (code < code2) {
503                set_bits(code, code2, lrun.woy);
504             } else {
505                set_bits(code, 53, lrun.woy);
506                set_bits(0, code2, lrun.woy);
507             }
508             break;
509          }
510          /* lookup first half of keyword range (week days or months) */
511          lcase(lc->str);
512          for (i=0; keyw[i].name; i++) {
513             if (strcmp(lc->str, keyw[i].name) == 0) {
514                state = keyw[i].state;
515                code   = keyw[i].code;
516                i = 0;
517                break;
518             }
519          }
520          if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
521             scan_err0(lc, _("Invalid month, week or position day range"));
522             /* NOT REACHED */
523          }
524
525          /* Lookup end of range */
526          lcase(p);
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;
531                i = 0;
532                break;
533             }
534          }
535          if (i != 0 || state != state2 || code == code2) {
536             scan_err0(lc, _("Invalid month, weekday or position range"));
537             /* NOT REACHED */
538          }
539          if (state == s_wday) {
540             if (!have_wday) {
541                clear_bits(0, 6, lrun.wday);
542                have_wday = true;
543             }
544             if (code < code2) {
545                set_bits(code, code2, lrun.wday);
546             } else {
547                set_bits(code, 6, lrun.wday);
548                set_bits(0, code2, lrun.wday);
549             }
550          } else if (state == s_month) {
551             if (!have_month) {
552                clear_bits(0, 11, lrun.month);
553                have_month = true;
554             }
555             if (code < code2) {
556                set_bits(code, code2, lrun.month);
557             } else {
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);
561             }
562          } else {
563             /* Must be position */
564             if (!have_wom) {
565                clear_bits(0, 4, lrun.wom);
566                have_wom = true;
567             }
568             if (code < code2) {
569                set_bits(code, code2, lrun.wom);
570             } else {
571                set_bits(code, 4, lrun.wom);
572                set_bits(0, code2, lrun.wom);
573             }
574          }
575          break;
576       case s_hourly:
577          have_hour = true;
578          set_bits(0, 23, lrun.hour);
579          break;
580       case s_weekly:
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);
585          break;
586       case s_daily:
587          have_mday = true;
588          set_bits(0, 6, lrun.wday);
589          break;
590       case s_monthly:
591          have_month = true;
592          set_bits(0, 11, lrun.month);
593          break;
594       default:
595          scan_err0(lc, _("Unexpected run state\n"));
596          /* NOT REACHED */
597          break;
598       }
599    }
600
601    /* Allocate run record, copy new stuff into it,
602     * and link it into the list of run records
603     * in the schedule resource.
604     */
605    if (pass == 2) {
606       trun = (RUN *)malloc(sizeof(RUN));
607       memcpy(trun, &lrun, sizeof(RUN));
608       if (*run) {
609          trun->next = *run;
610       }
611       *run = trun;
612    }
613
614    lc->options = options;             /* restore scanner options */
615    set_bit(index, res_all.res_sch.hdr.item_present);
616 }