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