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