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