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