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