]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/run_conf.c
Backport from Bacula Enterprise
[bacula/bacula] / bacula / src / dird / run_conf.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5    Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
6
7    The original author of Bacula is Kern Sibbald, with contributions
8    from many others, a complete list can be found in the file AUTHORS.
9
10    You may use this file and others of this release according to the
11    license defined in the LICENSE file, which includes the Affero General
12    Public License, v3.0 ("AGPLv3") and some additional permissions and
13    terms pursuant to its AGPLv3 Section 7.
14
15    This notice must be preserved when any source code is 
16    conveyed and/or propagated.
17
18    Bacula(R) is a registered trademark of Kern Sibbald.
19 */
20 /*
21  *
22  *  Configuration parser for Director Run Configuration
23  *   directives, which are part of the Schedule Resource
24  *
25  *     Kern Sibbald, May MM
26  *
27  */
28
29 #include "bacula.h"
30 #include "dird.h"
31
32 #if defined(_MSC_VER)
33 extern "C" { // work around visual compiler mangling variables
34    extern URES res_all;
35 }
36 #else
37 extern URES res_all;
38 #endif
39 extern s_jl joblevels[];
40
41 /* Forward referenced subroutines */
42
43 enum e_state {
44    s_none = 0,
45    s_range,
46    s_mday,
47    s_month,
48    s_time,
49    s_at,
50    s_wday,
51    s_daily,
52    s_weekly,
53    s_monthly,
54    s_hourly,
55    s_wom,                           /* 1st, 2nd, ...*/
56    s_woy,                           /* week of year w00 - w53 */
57    s_ldom                           /* last day of month */
58 };
59
60 struct s_keyw {
61   const char *name;                   /* keyword */
62   enum e_state state;                 /* parser state */
63   int code;                           /* state value */
64 };
65
66 /* Keywords understood by parser */
67 static struct s_keyw keyw[] = {
68   {NT_("on"),         s_none,    0},
69   {NT_("at"),         s_at,      0},
70   {NT_("lastday"),    s_ldom,    0},
71
72   {NT_("sun"),        s_wday,    0},
73   {NT_("mon"),        s_wday,    1},
74   {NT_("tue"),        s_wday,    2},
75   {NT_("wed"),        s_wday,    3},
76   {NT_("thu"),        s_wday,    4},
77   {NT_("fri"),        s_wday,    5},
78   {NT_("sat"),        s_wday,    6},
79   {NT_("jan"),        s_month,   0},
80   {NT_("feb"),        s_month,   1},
81   {NT_("mar"),        s_month,   2},
82   {NT_("apr"),        s_month,   3},
83   {NT_("may"),        s_month,   4},
84   {NT_("jun"),        s_month,   5},
85   {NT_("jul"),        s_month,   6},
86   {NT_("aug"),        s_month,   7},
87   {NT_("sep"),        s_month,   8},
88   {NT_("oct"),        s_month,   9},
89   {NT_("nov"),        s_month,  10},
90   {NT_("dec"),        s_month,  11},
91
92   {NT_("sunday"),     s_wday,    0},
93   {NT_("monday"),     s_wday,    1},
94   {NT_("tuesday"),    s_wday,    2},
95   {NT_("wednesday"),  s_wday,    3},
96   {NT_("thursday"),   s_wday,    4},
97   {NT_("friday"),     s_wday,    5},
98   {NT_("saturday"),   s_wday,    6},
99   {NT_("january"),    s_month,   0},
100   {NT_("february"),   s_month,   1},
101   {NT_("march"),      s_month,   2},
102   {NT_("april"),      s_month,   3},
103   {NT_("june"),       s_month,   5},
104   {NT_("july"),       s_month,   6},
105   {NT_("august"),     s_month,   7},
106   {NT_("september"),  s_month,   8},
107   {NT_("october"),    s_month,   9},
108   {NT_("november"),   s_month,  10},
109   {NT_("december"),   s_month,  11},
110
111   {NT_("daily"),      s_daily,   0},
112   {NT_("weekly"),     s_weekly,  0},
113   {NT_("monthly"),    s_monthly, 0},
114   {NT_("hourly"),     s_hourly,  0},
115
116   {NT_("1st"),        s_wom,     0},
117   {NT_("2nd"),        s_wom,     1},
118   {NT_("3rd"),        s_wom,     2},
119   {NT_("4th"),        s_wom,     3},
120   {NT_("5th"),        s_wom,     4},
121   {NT_("6th"),        s_wom,     5},
122
123   {NT_("first"),      s_wom,     0},
124   {NT_("second"),     s_wom,     1},
125   {NT_("third"),      s_wom,     2},
126   {NT_("fourth"),     s_wom,     3},
127   {NT_("fifth"),      s_wom,     4},
128   {NT_("sixth"),      s_wom,     5},
129   {NULL,         s_none,    0}
130 };
131
132 static bool have_hour, have_mday, have_wday, have_month, have_wom;
133 static bool have_at, have_woy;
134 static RUN lrun;
135
136 static void set_defaults()
137 {
138    have_hour = have_mday = have_wday = have_month = have_wom = have_woy = false;
139    have_at = false;
140    set_bits(0, 23, lrun.hour);
141    set_bits(0, 30, lrun.mday);
142    set_bits(0, 6,  lrun.wday);
143    set_bits(0, 11, lrun.month);
144    set_bits(0, 5,  lrun.wom);
145    set_bits(0, 53, lrun.woy);
146 }
147
148
149 /*
150  * Keywords (RHS) permitted in Run records
151  *
152  *    name              token
153  */
154 s_kw RunFields[] = {
155    {"Pool",              'P'},
156    {"FullPool",          'f'},
157    {"IncrementalPool",   'i'},
158    {"DifferentialPool",  'd'},
159    {"Level",             'L'},
160    {"Storage",           'S'},
161    {"Messages",          'M'},
162    {"Priority",          'p'},
163    {"SpoolData",         's'},
164    {"writepartafterjob", 'W'},
165    {"MaxRunSchedTime",   'm'},
166    {"Accurate",          'a'},
167    {"NextPool",          'N'},
168    {NULL,                 0}
169 };
170
171 /*
172  * Store Schedule Run information
173  *
174  * Parse Run statement:
175  *
176  *  Run <keyword=value ...> [on] 2 january at 23:45
177  *
178  *   Default Run time is daily at 0:0
179  *
180  *   There can be multiple run statements, they are simply chained
181  *   together.
182  *
183  */
184 void store_run(LEX *lc, RES_ITEM *item, int index, int pass)
185 {
186    int i, j;
187    bool found;
188    utime_t utime;
189    int token, state, state2 = 0, code = 0, code2 = 0;
190    int options = lc->options;
191    RUN **run = (RUN **)(item->value);
192    char *p;
193    RES *res;
194
195
196    lc->options |= LOPT_NO_IDENT;      /* want only "strings" */
197
198    /* clear local copy of run record */
199    memset(&lrun, 0, sizeof(RUN));
200
201    /* scan for Job level "full", "incremental", ... */
202    for (found=true; found; ) {
203       found = false;
204       token = lex_get_token(lc, T_NAME);
205       for (i=0; !found && RunFields[i].name; i++) {
206          if (strcasecmp(lc->str, RunFields[i].name) == 0) {
207             found = true;
208             if (lex_get_token(lc, T_ALL) != T_EQUALS) {
209                scan_err1(lc, _("Expected an equals, got: %s"), lc->str);
210                /* NOT REACHED */
211             }
212             switch (RunFields[i].token) {
213             case 's':                 /* Data spooling */
214                token = lex_get_token(lc, T_NAME);
215                if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
216                   lrun.spool_data = true;
217                   lrun.spool_data_set = true;
218                } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
219                   lrun.spool_data = false;
220                   lrun.spool_data_set = true;
221                } else {
222                   scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
223                }
224                break;
225             case 'W':                 /* Write part after job */
226                token = lex_get_token(lc, T_NAME);
227                if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
228                   lrun.write_part_after_job = true;
229                   lrun.write_part_after_job_set = true;
230                } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
231                   lrun.write_part_after_job = false;
232                   lrun.write_part_after_job_set = true;
233                } else {
234                   scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
235                }
236                break;
237             case 'L':                 /* level */
238                token = lex_get_token(lc, T_NAME);
239                for (j=0; joblevels[j].level_name; j++) {
240                   if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
241                      lrun.level = joblevels[j].level;
242                      lrun.job_type = joblevels[j].job_type;
243                      lrun.level_set = true;
244                      j = 0;
245                      break;
246                   }
247                }
248                if (j != 0) {
249                   scan_err1(lc, _("Job level field: %s not found in run record"), lc->str);
250                   /* NOT REACHED */
251                }
252                break;
253             case 'p':                 /* Priority */
254                token = lex_get_token(lc, T_PINT32);
255                if (pass == 2) {
256                   lrun.Priority = lc->pint32_val;
257                   lrun.priority_set = true;
258                }
259                break;
260             case 'P':                 /* Pool */
261             case 'N':                 /* NextPool */
262             case 'f':                 /* FullPool */
263             case 'i':                 /* IncPool */
264             case 'd':                 /* DifPool */
265                token = lex_get_token(lc, T_NAME);
266                if (pass == 2) {
267                   res = GetResWithName(R_POOL, lc->str);
268                   if (res == NULL) {
269                      scan_err1(lc, _("Could not find specified Pool Resource: %s"),
270                                 lc->str);
271                      /* NOT REACHED */
272                   }
273                   switch(RunFields[i].token) {
274                   case 'P':
275                      lrun.pool = (POOL *)res;
276                      break;
277                   case 'N':
278                      lrun.next_pool = (POOL *)res;
279                      break;
280                   case 'f':
281                      lrun.full_pool = (POOL *)res;
282                      break;
283                   case 'i':
284                      lrun.inc_pool = (POOL *)res;
285                      break;
286                   case 'd':
287                      lrun.diff_pool = (POOL *)res;
288                      break;
289                   }
290                }
291                break;
292             case 'S':                 /* storage */
293                token = lex_get_token(lc, T_NAME);
294                if (pass == 2) {
295                   res = GetResWithName(R_STORAGE, lc->str);
296                   if (res == NULL) {
297                      scan_err1(lc, _("Could not find specified Storage Resource: %s"),
298                                 lc->str);
299                      /* NOT REACHED */
300                   }
301                   lrun.storage = (STORE *)res;
302                }
303                break;
304             case 'M':                 /* messages */
305                token = lex_get_token(lc, T_NAME);
306                if (pass == 2) {
307                   res = GetResWithName(R_MSGS, lc->str);
308                   if (res == NULL) {
309                      scan_err1(lc, _("Could not find specified Messages Resource: %s"),
310                                 lc->str);
311                      /* NOT REACHED */
312                   }
313                   lrun.msgs = (MSGS *)res;
314                }
315                break;
316             case 'm':           /* max run sched time */
317                token = lex_get_token(lc, T_QUOTED_STRING);
318                if (!duration_to_utime(lc->str, &utime)) {
319                   scan_err1(lc, _("expected a time period, got: %s"), lc->str);
320                   return;
321                }
322                lrun.MaxRunSchedTime = utime;
323                lrun.MaxRunSchedTime_set = true;
324                break;
325             case 'a':           /* accurate */
326                token = lex_get_token(lc, T_NAME);
327                if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
328                   lrun.accurate = true;
329                   lrun.accurate_set = true;
330                } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
331                   lrun.accurate = false;
332                   lrun.accurate_set = true;
333                } else {
334                   scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
335                }
336                break;
337             default:
338                scan_err1(lc, _("Expected a keyword name, got: %s"), lc->str);
339                /* NOT REACHED */
340                break;
341             } /* end switch */
342          } /* end if strcasecmp */
343       } /* end for RunFields */
344
345       /* At this point, it is not a keyword. Check for old syle
346        * Job Levels without keyword. This form is depreciated!!!
347        */
348       if (!found) {
349          for (j=0; joblevels[j].level_name; j++) {
350             if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
351                lrun.level = joblevels[j].level;
352                lrun.job_type = joblevels[j].job_type;
353                found = true;
354                break;
355             }
356          }
357       }
358    } /* end for found */
359
360
361    /*
362     * Scan schedule times.
363     * Default is: daily at 0:0
364     */
365    state = s_none;
366    set_defaults();
367
368    for ( ; token != T_EOL; (token = lex_get_token(lc, T_ALL))) {
369       int len;
370       bool pm = false;
371       bool am = false;
372       switch (token) {
373       case T_NUMBER:
374          state = s_mday;
375          code = atoi(lc->str) - 1;
376          if (code < 0 || code > 30) {
377             scan_err0(lc, _("Day number out of range (1-31)"));
378          }
379          break;
380       case T_NAME:                 /* this handles drop through from keyword */
381       case T_UNQUOTED_STRING:
382          if (strchr(lc->str, (int)'-')) {
383             state = s_range;
384             break;
385          }
386          if (strchr(lc->str, (int)':')) {
387             state = s_time;
388             break;
389          }
390          if (lc->str_len == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') &&
391              is_an_integer(lc->str+1)) {
392             code = atoi(lc->str+1);
393             if (code < 0 || code > 53) {
394                scan_err0(lc, _("Week number out of range (0-53)"));
395               /* NOT REACHED */
396             }
397             state = s_woy;            /* week of year */
398             break;
399          }
400          /* everything else must be a keyword */
401          for (i=0; keyw[i].name; i++) {
402             if (strcasecmp(lc->str, keyw[i].name) == 0) {
403                state = keyw[i].state;
404                code   = keyw[i].code;
405                i = 0;
406                break;
407             }
408          }
409          if (i != 0) {
410             scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
411             /* NOT REACHED */
412          }
413          break;
414       case T_COMMA:
415          continue;
416       default:
417          scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
418          /* NOT REACHED */
419          break;
420       }
421       switch (state) {
422       case s_none:
423          continue;
424       case s_mday:                 /* day of month */
425          if (!have_mday) {
426             clear_bits(0, 30, lrun.mday);
427             have_mday = true;
428          }
429          set_bit(code, lrun.mday);
430          break;
431       case s_month:                /* month of year */
432          if (!have_month) {
433             clear_bits(0, 11, lrun.month);
434             have_month = true;
435          }
436          set_bit(code, lrun.month);
437          break;
438       case s_wday:                 /* week day */
439          if (!have_wday) {
440             clear_bits(0, 6, lrun.wday);
441             have_wday = true;
442          }
443          set_bit(code, lrun.wday);
444          break;
445       case s_wom:                  /* Week of month 1st, ... */
446          if (!have_wom) {
447             clear_bits(0, 5, lrun.wom);
448             have_wom = true;
449          }
450          set_bit(code, lrun.wom);
451          break;
452       case s_woy:
453          if (!have_woy) {
454             clear_bits(0, 53, lrun.woy);
455             have_woy = true;
456          }
457          set_bit(code, lrun.woy);
458          break;
459       case s_time:                 /* time */
460          if (!have_at) {
461             scan_err0(lc, _("Time must be preceded by keyword AT."));
462             /* NOT REACHED */
463          }
464          if (!have_hour) {
465             clear_bits(0, 23, lrun.hour);
466          }
467 //       Dmsg1(000, "s_time=%s\n", lc->str);
468          p = strchr(lc->str, ':');
469          if (!p)  {
470             scan_err0(lc, _("Time logic error.\n"));
471             /* NOT REACHED */
472          }
473          *p++ = 0;                 /* separate two halves */
474          code = atoi(lc->str);     /* pick up hour */
475          code2 = atoi(p);          /* pick up minutes */
476          len = strlen(p);
477          if (len >= 2) {
478             p += 2;
479          }
480          if (strcasecmp(p, "pm") == 0) {
481             pm = true;
482          } else if (strcasecmp(p, "am") == 0) {
483             am = true;
484          } else if (len != 2) {
485             scan_err0(lc, _("Bad time specification."));
486             /* NOT REACHED */
487          }
488          /*
489           * Note, according to NIST, 12am and 12pm are ambiguous and
490           *  can be defined to anything.  However, 12:01am is the same
491           *  as 00:01 and 12:01pm is the same as 12:01, so we define
492           *  12am as 00:00 and 12pm as 12:00.
493           */
494          if (pm) {
495             /* Convert to 24 hour time */
496             if (code != 12) {
497                code += 12;
498             }
499          /* am */
500          } else if (am && code == 12) {
501             code -= 12;
502          }
503          if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
504             scan_err0(lc, _("Bad time specification."));
505             /* NOT REACHED */
506          }
507 //       Dmsg2(000, "hour=%d min=%d\n", code, code2);
508          set_bit(code, lrun.hour);
509          lrun.minute = code2;
510          have_hour = true;
511          break;
512       case s_at:
513          have_at = true;
514          break;
515       case s_ldom:
516          if (!have_mday) {
517             clear_bits(0, 30, lrun.mday);
518             have_mday = true;
519          }
520          lrun.last_day_set = true;
521          set_bit(31, lrun.mday);   /* day 32 => last day of month */
522          break;
523       case s_range:
524          p = strchr(lc->str, '-');
525          if (!p) {
526             scan_err0(lc, _("Range logic error.\n"));
527          }
528          *p++ = 0;                 /* separate two halves */
529
530          /* Check for day range */
531          if (is_an_integer(lc->str) && is_an_integer(p)) {
532             code = atoi(lc->str) - 1;
533             code2 = atoi(p) - 1;
534             if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
535                scan_err0(lc, _("Bad day range specification."));
536             }
537             if (!have_mday) {
538                clear_bits(0, 30, lrun.mday);
539                have_mday = true;
540             }
541             if (code < code2) {
542                set_bits(code, code2, lrun.mday);
543             } else {
544                set_bits(code, 30, lrun.mday);
545                set_bits(0, code2, lrun.mday);
546             }
547             break;
548          }
549          /* Check for week of year range */
550          if (strlen(lc->str) == 3 && strlen(p) == 3 &&
551              (lc->str[0] == 'w' || lc->str[0] == 'W') &&
552              (p[0] == 'w' || p[0] == 'W') &&
553              is_an_integer(lc->str+1) && is_an_integer(p+1)) {
554             code = atoi(lc->str+1);
555             code2 = atoi(p+1);
556             if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
557                scan_err0(lc, _("Week number out of range (0-53)"));
558             }
559             if (!have_woy) {
560                clear_bits(0, 53, lrun.woy);
561                have_woy = true;
562             }
563             if (code < code2) {
564                set_bits(code, code2, lrun.woy);
565             } else {
566                set_bits(code, 53, lrun.woy);
567                set_bits(0, code2, lrun.woy);
568             }
569             break;
570          }
571          /* lookup first half of keyword range (week days or months) */
572          lcase(lc->str);
573          for (i=0; keyw[i].name; i++) {
574             if (strcasecmp(lc->str, keyw[i].name) == 0) {
575                state = keyw[i].state;
576                code   = keyw[i].code;
577                i = 0;
578                break;
579             }
580          }
581          if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
582             scan_err0(lc, _("Invalid month, week or position day range"));
583             /* NOT REACHED */
584          }
585
586          /* Lookup end of range */
587          lcase(p);
588          for (i=0; keyw[i].name; i++) {
589             if (strcasecmp(p, keyw[i].name) == 0) {
590                state2  = keyw[i].state;
591                code2   = keyw[i].code;
592                i = 0;
593                break;
594             }
595          }
596          if (i != 0 || state != state2 || code == code2) {
597             scan_err0(lc, _("Invalid month, weekday or position range"));
598             /* NOT REACHED */
599          }
600          if (state == s_wday) {
601             if (!have_wday) {
602                clear_bits(0, 6, lrun.wday);
603                have_wday = true;
604             }
605             if (code < code2) {
606                set_bits(code, code2, lrun.wday);
607             } else {
608                set_bits(code, 6, lrun.wday);
609                set_bits(0, code2, lrun.wday);
610             }
611          } else if (state == s_month) {
612             if (!have_month) {
613                clear_bits(0, 11, lrun.month);
614                have_month = true;
615             }
616             if (code < code2) {
617                set_bits(code, code2, lrun.month);
618             } else {
619                /* this is a bit odd, but we accept it anyway */
620                set_bits(code, 11, lrun.month);
621                set_bits(0, code2, lrun.month);
622             }
623          } else {
624             /* Must be position */
625             if (!have_wom) {
626                clear_bits(0, 5, lrun.wom);
627                have_wom = true;
628             }
629             if (code < code2) {
630                set_bits(code, code2, lrun.wom);
631             } else {
632                set_bits(code, 5, lrun.wom);
633                set_bits(0, code2, lrun.wom);
634             }
635          }
636          break;
637       case s_hourly:
638          have_hour = true;
639          set_bits(0, 23, lrun.hour);
640          break;
641       case s_weekly:
642          have_mday = have_wom = have_woy = true;
643          set_bits(0, 30, lrun.mday);
644          set_bits(0, 5,  lrun.wom);
645          set_bits(0, 53, lrun.woy);
646          break;
647       case s_daily:
648          have_mday = true;
649          set_bits(0, 6, lrun.wday);
650          break;
651       case s_monthly:
652          have_month = true;
653          set_bits(0, 11, lrun.month);
654          break;
655       default:
656          scan_err0(lc, _("Unexpected run state\n"));
657          /* NOT REACHED */
658          break;
659       }
660    }
661
662    /* Allocate run record, copy new stuff into it,
663     * and append it to the list of run records
664     * in the schedule resource.
665     */
666    if (pass == 2) {
667       RUN *tail;
668
669       /* Create new run record */
670       RUN *nrun = (RUN *)malloc(sizeof(RUN));
671       memcpy(nrun, &lrun, sizeof(RUN));
672       nrun ->next = NULL;
673
674       if (!*run) {                    /* if empty list */
675          *run = nrun;                 /* add new record */
676       } else {
677          for (tail = *run; tail->next; tail=tail->next)
678             {  }
679          tail->next = nrun;
680       }
681    }
682
683    lc->options = options;             /* restore scanner options */
684    set_bit(index, res_all.res_sch.hdr.item_present);
685 }