]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/run_conf.c
Make out of freespace non-fatal for removable devices -- i.e. behaves like tape
[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 'v':                 /* VFullPool */
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 'v':
284                      lrun.vfull_pool = (POOL *)res;
285                      break;
286                   case 'i':
287                      lrun.inc_pool = (POOL *)res;
288                      break;
289                   case 'd':
290                      lrun.diff_pool = (POOL *)res;
291                      break;
292                   }
293                }
294                break;
295             case 'S':                 /* storage */
296                token = lex_get_token(lc, T_NAME);
297                if (pass == 2) {
298                   res = GetResWithName(R_STORAGE, lc->str);
299                   if (res == NULL) {
300                      scan_err1(lc, _("Could not find specified Storage Resource: %s"),
301                                 lc->str);
302                      /* NOT REACHED */
303                   }
304                   lrun.storage = (STORE *)res;
305                }
306                break;
307             case 'M':                 /* messages */
308                token = lex_get_token(lc, T_NAME);
309                if (pass == 2) {
310                   res = GetResWithName(R_MSGS, lc->str);
311                   if (res == NULL) {
312                      scan_err1(lc, _("Could not find specified Messages Resource: %s"),
313                                 lc->str);
314                      /* NOT REACHED */
315                   }
316                   lrun.msgs = (MSGS *)res;
317                }
318                break;
319             case 'm':           /* max run sched time */
320                token = lex_get_token(lc, T_QUOTED_STRING);
321                if (!duration_to_utime(lc->str, &utime)) {
322                   scan_err1(lc, _("expected a time period, got: %s"), lc->str);
323                   return;
324                }
325                lrun.MaxRunSchedTime = utime;
326                lrun.MaxRunSchedTime_set = true;
327                break;
328             case 'a':           /* accurate */
329                token = lex_get_token(lc, T_NAME);
330                if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
331                   lrun.accurate = true;
332                   lrun.accurate_set = true;
333                } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
334                   lrun.accurate = false;
335                   lrun.accurate_set = true;
336                } else {
337                   scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
338                }
339                break;
340             default:
341                scan_err1(lc, _("Expected a keyword name, got: %s"), lc->str);
342                /* NOT REACHED */
343                break;
344             } /* end switch */
345          } /* end if strcasecmp */
346       } /* end for RunFields */
347
348       /* At this point, it is not a keyword. Check for old syle
349        * Job Levels without keyword. This form is depreciated!!!
350        */
351       if (!found) {
352          for (j=0; joblevels[j].level_name; j++) {
353             if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
354                lrun.level = joblevels[j].level;
355                lrun.job_type = joblevels[j].job_type;
356                found = true;
357                break;
358             }
359          }
360       }
361    } /* end for found */
362
363
364    /*
365     * Scan schedule times.
366     * Default is: daily at 0:0
367     */
368    state = s_none;
369    set_defaults();
370
371    for ( ; token != T_EOL; (token = lex_get_token(lc, T_ALL))) {
372       int len;
373       bool pm = false;
374       bool am = false;
375       switch (token) {
376       case T_NUMBER:
377          state = s_mday;
378          code = atoi(lc->str) - 1;
379          if (code < 0 || code > 30) {
380             scan_err0(lc, _("Day number out of range (1-31)"));
381          }
382          break;
383       case T_NAME:                 /* this handles drop through from keyword */
384       case T_UNQUOTED_STRING:
385          if (strchr(lc->str, (int)'-')) {
386             state = s_range;
387             break;
388          }
389          if (strchr(lc->str, (int)':')) {
390             state = s_time;
391             break;
392          }
393          if (lc->str_len == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') &&
394              is_an_integer(lc->str+1)) {
395             code = atoi(lc->str+1);
396             if (code < 0 || code > 53) {
397                scan_err0(lc, _("Week number out of range (0-53)"));
398               /* NOT REACHED */
399             }
400             state = s_woy;            /* week of year */
401             break;
402          }
403          /* everything else must be a keyword */
404          for (i=0; keyw[i].name; i++) {
405             if (strcasecmp(lc->str, keyw[i].name) == 0) {
406                state = keyw[i].state;
407                code   = keyw[i].code;
408                i = 0;
409                break;
410             }
411          }
412          if (i != 0) {
413             scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
414             /* NOT REACHED */
415          }
416          break;
417       case T_COMMA:
418          continue;
419       default:
420          scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
421          /* NOT REACHED */
422          break;
423       }
424       switch (state) {
425       case s_none:
426          continue;
427       case s_mday:                 /* day of month */
428          if (!have_mday) {
429             clear_bits(0, 30, lrun.mday);
430             have_mday = true;
431          }
432          set_bit(code, lrun.mday);
433          break;
434       case s_month:                /* month of year */
435          if (!have_month) {
436             clear_bits(0, 11, lrun.month);
437             have_month = true;
438          }
439          set_bit(code, lrun.month);
440          break;
441       case s_wday:                 /* week day */
442          if (!have_wday) {
443             clear_bits(0, 6, lrun.wday);
444             have_wday = true;
445          }
446          set_bit(code, lrun.wday);
447          break;
448       case s_wom:                  /* Week of month 1st, ... */
449          if (!have_wom) {
450             clear_bits(0, 5, lrun.wom);
451             have_wom = true;
452          }
453          set_bit(code, lrun.wom);
454          break;
455       case s_woy:
456          if (!have_woy) {
457             clear_bits(0, 53, lrun.woy);
458             have_woy = true;
459          }
460          set_bit(code, lrun.woy);
461          break;
462       case s_time:                 /* time */
463          if (!have_at) {
464             scan_err0(lc, _("Time must be preceded by keyword AT."));
465             /* NOT REACHED */
466          }
467          if (!have_hour) {
468             clear_bits(0, 23, lrun.hour);
469          }
470 //       Dmsg1(000, "s_time=%s\n", lc->str);
471          p = strchr(lc->str, ':');
472          if (!p)  {
473             scan_err0(lc, _("Time logic error.\n"));
474             /* NOT REACHED */
475          }
476          *p++ = 0;                 /* separate two halves */
477          code = atoi(lc->str);     /* pick up hour */
478          code2 = atoi(p);          /* pick up minutes */
479          len = strlen(p);
480          if (len >= 2) {
481             p += 2;
482          }
483          if (strcasecmp(p, "pm") == 0) {
484             pm = true;
485          } else if (strcasecmp(p, "am") == 0) {
486             am = true;
487          } else if (len != 2) {
488             scan_err0(lc, _("Bad time specification."));
489             /* NOT REACHED */
490          }
491          /*
492           * Note, according to NIST, 12am and 12pm are ambiguous and
493           *  can be defined to anything.  However, 12:01am is the same
494           *  as 00:01 and 12:01pm is the same as 12:01, so we define
495           *  12am as 00:00 and 12pm as 12:00.
496           */
497          if (pm) {
498             /* Convert to 24 hour time */
499             if (code != 12) {
500                code += 12;
501             }
502          /* am */
503          } else if (am && code == 12) {
504             code -= 12;
505          }
506          if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
507             scan_err0(lc, _("Bad time specification."));
508             /* NOT REACHED */
509          }
510 //       Dmsg2(000, "hour=%d min=%d\n", code, code2);
511          set_bit(code, lrun.hour);
512          lrun.minute = code2;
513          have_hour = true;
514          break;
515       case s_at:
516          have_at = true;
517          break;
518       case s_ldom:
519          if (!have_mday) {
520             clear_bits(0, 30, lrun.mday);
521             have_mday = true;
522          }
523          lrun.last_day_set = true;
524          set_bit(31, lrun.mday);   /* day 32 => last day of month */
525          break;
526       case s_range:
527          p = strchr(lc->str, '-');
528          if (!p) {
529             scan_err0(lc, _("Range logic error.\n"));
530          }
531          *p++ = 0;                 /* separate two halves */
532
533          /* Check for day range */
534          if (is_an_integer(lc->str) && is_an_integer(p)) {
535             code = atoi(lc->str) - 1;
536             code2 = atoi(p) - 1;
537             if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
538                scan_err0(lc, _("Bad day range specification."));
539             }
540             if (!have_mday) {
541                clear_bits(0, 30, lrun.mday);
542                have_mday = true;
543             }
544             if (code < code2) {
545                set_bits(code, code2, lrun.mday);
546             } else {
547                set_bits(code, 30, lrun.mday);
548                set_bits(0, code2, lrun.mday);
549             }
550             break;
551          }
552          /* Check for week of year range */
553          if (strlen(lc->str) == 3 && strlen(p) == 3 &&
554              (lc->str[0] == 'w' || lc->str[0] == 'W') &&
555              (p[0] == 'w' || p[0] == 'W') &&
556              is_an_integer(lc->str+1) && is_an_integer(p+1)) {
557             code = atoi(lc->str+1);
558             code2 = atoi(p+1);
559             if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
560                scan_err0(lc, _("Week number out of range (0-53)"));
561             }
562             if (!have_woy) {
563                clear_bits(0, 53, lrun.woy);
564                have_woy = true;
565             }
566             if (code < code2) {
567                set_bits(code, code2, lrun.woy);
568             } else {
569                set_bits(code, 53, lrun.woy);
570                set_bits(0, code2, lrun.woy);
571             }
572             break;
573          }
574          /* lookup first half of keyword range (week days or months) */
575          lcase(lc->str);
576          for (i=0; keyw[i].name; i++) {
577             if (strcasecmp(lc->str, keyw[i].name) == 0) {
578                state = keyw[i].state;
579                code   = keyw[i].code;
580                i = 0;
581                break;
582             }
583          }
584          if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
585             scan_err0(lc, _("Invalid month, week or position day range"));
586             /* NOT REACHED */
587          }
588
589          /* Lookup end of range */
590          lcase(p);
591          for (i=0; keyw[i].name; i++) {
592             if (strcasecmp(p, keyw[i].name) == 0) {
593                state2  = keyw[i].state;
594                code2   = keyw[i].code;
595                i = 0;
596                break;
597             }
598          }
599          if (i != 0 || state != state2 || code == code2) {
600             scan_err0(lc, _("Invalid month, weekday or position range"));
601             /* NOT REACHED */
602          }
603          if (state == s_wday) {
604             if (!have_wday) {
605                clear_bits(0, 6, lrun.wday);
606                have_wday = true;
607             }
608             if (code < code2) {
609                set_bits(code, code2, lrun.wday);
610             } else {
611                set_bits(code, 6, lrun.wday);
612                set_bits(0, code2, lrun.wday);
613             }
614          } else if (state == s_month) {
615             if (!have_month) {
616                clear_bits(0, 11, lrun.month);
617                have_month = true;
618             }
619             if (code < code2) {
620                set_bits(code, code2, lrun.month);
621             } else {
622                /* this is a bit odd, but we accept it anyway */
623                set_bits(code, 11, lrun.month);
624                set_bits(0, code2, lrun.month);
625             }
626          } else {
627             /* Must be position */
628             if (!have_wom) {
629                clear_bits(0, 5, lrun.wom);
630                have_wom = true;
631             }
632             if (code < code2) {
633                set_bits(code, code2, lrun.wom);
634             } else {
635                set_bits(code, 5, lrun.wom);
636                set_bits(0, code2, lrun.wom);
637             }
638          }
639          break;
640       case s_hourly:
641          have_hour = true;
642          set_bits(0, 23, lrun.hour);
643          break;
644       case s_weekly:
645          have_mday = have_wom = have_woy = true;
646          set_bits(0, 30, lrun.mday);
647          set_bits(0, 5,  lrun.wom);
648          set_bits(0, 53, lrun.woy);
649          break;
650       case s_daily:
651          have_mday = true;
652          set_bits(0, 6, lrun.wday);
653          break;
654       case s_monthly:
655          have_month = true;
656          set_bits(0, 11, lrun.month);
657          break;
658       default:
659          scan_err0(lc, _("Unexpected run state\n"));
660          /* NOT REACHED */
661          break;
662       }
663    }
664
665    /* Allocate run record, copy new stuff into it,
666     * and append it to the list of run records
667     * in the schedule resource.
668     */
669    if (pass == 2) {
670       RUN *tail;
671
672       /* Create new run record */
673       RUN *nrun = (RUN *)malloc(sizeof(RUN));
674       memcpy(nrun, &lrun, sizeof(RUN));
675       nrun ->next = NULL;
676
677       if (!*run) {                    /* if empty list */
678          *run = nrun;                 /* add new record */
679       } else {
680          for (tail = *run; tail->next; tail=tail->next)
681             {  }
682          tail->next = nrun;
683       }
684    }
685
686    lc->options = options;             /* restore scanner options */
687    set_bit(index, res_all.res_sch.hdr.item_present);
688 }