]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/run_conf.c
Add Accurate and MaxRunSchedTime in Schedule resource
[bacula/bacula] / bacula / src / dird / run_conf.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2011 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *  Configuration parser for Director Run Configuration
31  *   directives, which are part of the Schedule Resource
32  *
33  *     Kern Sibbald, May MM
34  *
35  */
36
37 #include "bacula.h"
38 #include "dird.h"
39
40 #if defined(_MSC_VER)
41 extern "C" { // work around visual compiler mangling variables
42    extern URES res_all;
43 }
44 #else
45 extern URES res_all;
46 #endif
47 extern struct s_jl joblevels[];
48
49 /* Forward referenced subroutines */
50
51 enum e_state {
52    s_none = 0,
53    s_range,
54    s_mday,
55    s_month,
56    s_time,
57    s_at,
58    s_wday,
59    s_daily,
60    s_weekly,
61    s_monthly,
62    s_hourly,
63    s_wom,                           /* 1st, 2nd, ...*/
64    s_woy                            /* week of year w00 - w53 */
65 };
66
67 struct s_keyw {
68   const char *name;                   /* keyword */
69   enum e_state state;                 /* parser state */
70   int code;                           /* state value */
71 };
72
73 /* Keywords understood by parser */
74 static struct s_keyw keyw[] = {
75   {NT_("on"),         s_none,    0},
76   {NT_("at"),         s_at,      0},
77
78   {NT_("sun"),        s_wday,    0},
79   {NT_("mon"),        s_wday,    1},
80   {NT_("tue"),        s_wday,    2},
81   {NT_("wed"),        s_wday,    3},
82   {NT_("thu"),        s_wday,    4},
83   {NT_("fri"),        s_wday,    5},
84   {NT_("sat"),        s_wday,    6},
85   {NT_("jan"),        s_month,   0},
86   {NT_("feb"),        s_month,   1},
87   {NT_("mar"),        s_month,   2},
88   {NT_("apr"),        s_month,   3},
89   {NT_("may"),        s_month,   4},
90   {NT_("jun"),        s_month,   5},
91   {NT_("jul"),        s_month,   6},
92   {NT_("aug"),        s_month,   7},
93   {NT_("sep"),        s_month,   8},
94   {NT_("oct"),        s_month,   9},
95   {NT_("nov"),        s_month,  10},
96   {NT_("dec"),        s_month,  11},
97
98   {NT_("sunday"),     s_wday,    0},
99   {NT_("monday"),     s_wday,    1},
100   {NT_("tuesday"),    s_wday,    2},
101   {NT_("wednesday"),  s_wday,    3},
102   {NT_("thursday"),   s_wday,    4},
103   {NT_("friday"),     s_wday,    5},
104   {NT_("saturday"),   s_wday,    6},
105   {NT_("january"),    s_month,   0},
106   {NT_("february"),   s_month,   1},
107   {NT_("march"),      s_month,   2},
108   {NT_("april"),      s_month,   3},
109   {NT_("june"),       s_month,   5},
110   {NT_("july"),       s_month,   6},
111   {NT_("august"),     s_month,   7},
112   {NT_("september"),  s_month,   8},
113   {NT_("october"),    s_month,   9},
114   {NT_("november"),   s_month,  10},
115   {NT_("december"),   s_month,  11},
116
117   {NT_("daily"),      s_daily,   0},
118   {NT_("weekly"),     s_weekly,  0},
119   {NT_("monthly"),    s_monthly, 0},
120   {NT_("hourly"),     s_hourly,  0},
121
122   {NT_("1st"),        s_wom,     0},
123   {NT_("2nd"),        s_wom,     1},
124   {NT_("3rd"),        s_wom,     2},
125   {NT_("4th"),        s_wom,     3},
126   {NT_("5th"),        s_wom,     4},
127
128   {NT_("first"),      s_wom,     0},
129   {NT_("second"),     s_wom,     1},
130   {NT_("third"),      s_wom,     2},
131   {NT_("fourth"),     s_wom,     3},
132   {NT_("fifth"),      s_wom,     4},
133   {NULL,         s_none,    0}
134 };
135
136 static bool have_hour, have_mday, have_wday, have_month, have_wom;
137 static bool have_at, have_woy;
138 static RUN lrun;
139
140 static void set_defaults()
141 {
142    have_hour = have_mday = have_wday = have_month = have_wom = have_woy = false;
143    have_at = false;
144    set_bits(0, 23, lrun.hour);
145    set_bits(0, 30, lrun.mday);
146    set_bits(0, 6,  lrun.wday);
147    set_bits(0, 11, lrun.month);
148    set_bits(0, 4,  lrun.wom);
149    set_bits(0, 53, lrun.woy);
150 }
151
152
153 /* Keywords (RHS) permitted in Run records */
154 static struct 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    {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; 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                      j = 0;
243                      break;
244                   }
245                }
246                if (j != 0) {
247                   scan_err1(lc, _("Job level field: %s not found in run record"), lc->str);
248                   /* NOT REACHED */
249                }
250                break;
251             case 'p':                 /* Priority */
252                token = lex_get_token(lc, T_PINT32);
253                if (pass == 2) {
254                   lrun.Priority = lc->pint32_val;
255                }
256                break;
257             case 'P':                 /* Pool */
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 'f':
274                      lrun.full_pool = (POOL *)res;
275                      break;
276                   case 'i':
277                      lrun.inc_pool = (POOL *)res;
278                      break;
279                   case 'd':
280                      lrun.diff_pool = (POOL *)res;
281                      break;
282                   }
283                }
284                break;
285             case 'S':                 /* storage */
286                token = lex_get_token(lc, T_NAME);
287                if (pass == 2) {
288                   res = GetResWithName(R_STORAGE, lc->str);
289                   if (res == NULL) {
290                      scan_err1(lc, _("Could not find specified Storage Resource: %s"),
291                                 lc->str);
292                      /* NOT REACHED */
293                   }
294                   lrun.storage = (STORE *)res;
295                }
296                break;
297             case 'M':                 /* messages */
298                token = lex_get_token(lc, T_NAME);
299                if (pass == 2) {
300                   res = GetResWithName(R_MSGS, lc->str);
301                   if (res == NULL) {
302                      scan_err1(lc, _("Could not find specified Messages Resource: %s"),
303                                 lc->str);
304                      /* NOT REACHED */
305                   }
306                   lrun.msgs = (MSGS *)res;
307                }
308                break;
309             case 'm':           /* max run sched time */
310                token = lex_get_token(lc, T_QUOTED_STRING); 
311                if (!duration_to_utime(lc->str, &utime)) {
312                   scan_err1(lc, _("expected a time period, got: %s"), lc->str);
313                   return;
314                }
315                lrun.MaxRunSchedTime = utime;
316                lrun.MaxRunSchedTime_set = true;
317                break;
318             case 'a':           /* accurate */
319                token = lex_get_token(lc, T_NAME);
320                if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
321                   lrun.accurate = true;
322                   lrun.accurate_set = true;
323                } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
324                   lrun.accurate = false;
325                   lrun.accurate_set = true;
326                } else {
327                   scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
328                }
329                break;
330             default:
331                scan_err1(lc, _("Expected a keyword name, got: %s"), lc->str);
332                /* NOT REACHED */
333                break;
334             } /* end switch */
335          } /* end if strcasecmp */
336       } /* end for RunFields */
337
338       /* At this point, it is not a keyword. Check for old syle
339        * Job Levels without keyword. This form is depreciated!!!
340        */
341       if (!found) {
342          for (j=0; joblevels[j].level_name; j++) {
343             if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
344                lrun.level = joblevels[j].level;
345                lrun.job_type = joblevels[j].job_type;
346                found = true;
347                break;
348             }
349          }
350       }
351    } /* end for found */
352
353
354    /*
355     * Scan schedule times.
356     * Default is: daily at 0:0
357     */
358    state = s_none;
359    set_defaults();
360
361    for ( ; token != T_EOL; (token = lex_get_token(lc, T_ALL))) {
362       int len; 
363       bool pm = false;
364       bool am = false;
365       switch (token) {
366       case T_NUMBER:
367          state = s_mday;
368          code = atoi(lc->str) - 1;
369          if (code < 0 || code > 30) {
370             scan_err0(lc, _("Day number out of range (1-31)"));
371          }
372          break;
373       case T_NAME:                 /* this handles drop through from keyword */
374       case T_UNQUOTED_STRING:
375          if (strchr(lc->str, (int)'-')) {
376             state = s_range;
377             break;
378          }
379          if (strchr(lc->str, (int)':')) {
380             state = s_time;
381             break;
382          }
383          if (lc->str_len == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') &&
384              is_an_integer(lc->str+1)) {
385             code = atoi(lc->str+1);
386             if (code < 0 || code > 53) {
387                scan_err0(lc, _("Week number out of range (0-53)"));
388               /* NOT REACHED */
389             }
390             state = s_woy;            /* week of year */
391             break;
392          }
393          /* everything else must be a keyword */
394          for (i=0; keyw[i].name; i++) {
395             if (strcasecmp(lc->str, keyw[i].name) == 0) {
396                state = keyw[i].state;
397                code   = keyw[i].code;
398                i = 0;
399                break;
400             }
401          }
402          if (i != 0) {
403             scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
404             /* NOT REACHED */
405          }
406          break;
407       case T_COMMA:
408          continue;
409       default:
410          scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
411          /* NOT REACHED */
412          break;
413       }
414       switch (state) {
415       case s_none:
416          continue;
417       case s_mday:                 /* day of month */
418          if (!have_mday) {
419             clear_bits(0, 30, lrun.mday);
420             have_mday = true;
421          }
422          set_bit(code, lrun.mday);
423          break;
424       case s_month:                /* month of year */
425          if (!have_month) {
426             clear_bits(0, 11, lrun.month);
427             have_month = true;
428          }
429          set_bit(code, lrun.month);
430          break;
431       case s_wday:                 /* week day */
432          if (!have_wday) {
433             clear_bits(0, 6, lrun.wday);
434             have_wday = true;
435          }
436          set_bit(code, lrun.wday);
437          break;
438       case s_wom:                  /* Week of month 1st, ... */
439          if (!have_wom) {
440             clear_bits(0, 4, lrun.wom);
441             have_wom = true;
442          }
443          set_bit(code, lrun.wom);
444          break;
445       case s_woy:
446          if (!have_woy) {
447             clear_bits(0, 53, lrun.woy);
448             have_woy = true;
449          }
450          set_bit(code, lrun.woy);
451          break;
452       case s_time:                 /* time */
453          if (!have_at) {
454             scan_err0(lc, _("Time must be preceded by keyword AT."));
455             /* NOT REACHED */
456          }
457          if (!have_hour) {
458             clear_bits(0, 23, lrun.hour);
459          }
460 //       Dmsg1(000, "s_time=%s\n", lc->str);
461          p = strchr(lc->str, ':');
462          if (!p)  {
463             scan_err0(lc, _("Time logic error.\n"));
464             /* NOT REACHED */
465          }
466          *p++ = 0;                 /* separate two halves */
467          code = atoi(lc->str);     /* pick up hour */
468          code2 = atoi(p);          /* pick up minutes */
469          len = strlen(p);
470          if (len >= 2) {
471             p += 2;
472          }
473          if (strcasecmp(p, "pm") == 0) {
474             pm = true;
475          } else if (strcasecmp(p, "am") == 0) {
476             am = true;
477          } else if (len != 2) {
478             scan_err0(lc, _("Bad time specification."));
479             /* NOT REACHED */
480          }   
481          /* 
482           * Note, according to NIST, 12am and 12pm are ambiguous and
483           *  can be defined to anything.  However, 12:01am is the same
484           *  as 00:01 and 12:01pm is the same as 12:01, so we define 
485           *  12am as 00:00 and 12pm as 12:00.
486           */
487          if (pm) {
488             /* Convert to 24 hour time */
489             if (code != 12) {
490                code += 12;
491             }
492          /* am */
493          } else if (am && code == 12) {
494             code -= 12;
495          }
496          if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
497             scan_err0(lc, _("Bad time specification."));
498             /* NOT REACHED */
499          }
500 //       Dmsg2(000, "hour=%d min=%d\n", code, code2);
501          set_bit(code, lrun.hour);
502          lrun.minute = code2;
503          have_hour = true;
504          break;
505       case s_at:
506          have_at = true;
507          break;
508       case s_range:
509          p = strchr(lc->str, '-');
510          if (!p) {
511             scan_err0(lc, _("Range logic error.\n"));
512          }
513          *p++ = 0;                 /* separate two halves */
514
515          /* Check for day range */
516          if (is_an_integer(lc->str) && is_an_integer(p)) {
517             code = atoi(lc->str) - 1;
518             code2 = atoi(p) - 1;
519             if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
520                scan_err0(lc, _("Bad day range specification."));
521             }
522             if (!have_mday) {
523                clear_bits(0, 30, lrun.mday);
524                have_mday = true;
525             }
526             if (code < code2) {
527                set_bits(code, code2, lrun.mday);
528             } else {
529                set_bits(code, 30, lrun.mday);
530                set_bits(0, code2, lrun.mday);
531             }
532             break;
533          }
534          /* Check for week of year range */
535          if (strlen(lc->str) == 3 && strlen(p) == 3 &&
536              (lc->str[0] == 'w' || lc->str[0] == 'W') &&
537              (p[0] == 'w' || p[0] == 'W') &&
538              is_an_integer(lc->str+1) && is_an_integer(p+1)) {
539             code = atoi(lc->str+1);
540             code2 = atoi(p+1);
541             if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
542                scan_err0(lc, _("Week number out of range (0-53)"));
543             }
544             if (!have_woy) {
545                clear_bits(0, 53, lrun.woy);
546                have_woy = true;
547             }
548             if (code < code2) {
549                set_bits(code, code2, lrun.woy);
550             } else {
551                set_bits(code, 53, lrun.woy);
552                set_bits(0, code2, lrun.woy);
553             }
554             break;
555          }
556          /* lookup first half of keyword range (week days or months) */
557          lcase(lc->str);
558          for (i=0; keyw[i].name; i++) {
559             if (strcmp(lc->str, keyw[i].name) == 0) {
560                state = keyw[i].state;
561                code   = keyw[i].code;
562                i = 0;
563                break;
564             }
565          }
566          if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
567             scan_err0(lc, _("Invalid month, week or position day range"));
568             /* NOT REACHED */
569          }
570
571          /* Lookup end of range */
572          lcase(p);
573          for (i=0; keyw[i].name; i++) {
574             if (strcmp(p, keyw[i].name) == 0) {
575                state2  = keyw[i].state;
576                code2   = keyw[i].code;
577                i = 0;
578                break;
579             }
580          }
581          if (i != 0 || state != state2 || code == code2) {
582             scan_err0(lc, _("Invalid month, weekday or position range"));
583             /* NOT REACHED */
584          }
585          if (state == s_wday) {
586             if (!have_wday) {
587                clear_bits(0, 6, lrun.wday);
588                have_wday = true;
589             }
590             if (code < code2) {
591                set_bits(code, code2, lrun.wday);
592             } else {
593                set_bits(code, 6, lrun.wday);
594                set_bits(0, code2, lrun.wday);
595             }
596          } else if (state == s_month) {
597             if (!have_month) {
598                clear_bits(0, 11, lrun.month);
599                have_month = true;
600             }
601             if (code < code2) {
602                set_bits(code, code2, lrun.month);
603             } else {
604                /* this is a bit odd, but we accept it anyway */
605                set_bits(code, 11, lrun.month);
606                set_bits(0, code2, lrun.month);
607             }
608          } else {
609             /* Must be position */
610             if (!have_wom) {
611                clear_bits(0, 4, lrun.wom);
612                have_wom = true;
613             }
614             if (code < code2) {
615                set_bits(code, code2, lrun.wom);
616             } else {
617                set_bits(code, 4, lrun.wom);
618                set_bits(0, code2, lrun.wom);
619             }
620          }
621          break;
622       case s_hourly:
623          have_hour = true;
624          set_bits(0, 23, lrun.hour);
625          break;
626       case s_weekly:
627          have_mday = have_wom = have_woy = true;
628          set_bits(0, 30, lrun.mday);
629          set_bits(0, 4,  lrun.wom);
630          set_bits(0, 53, lrun.woy);
631          break;
632       case s_daily:
633          have_mday = true;
634          set_bits(0, 6, lrun.wday);
635          break;
636       case s_monthly:
637          have_month = true;
638          set_bits(0, 11, lrun.month);
639          break;
640       default:
641          scan_err0(lc, _("Unexpected run state\n"));
642          /* NOT REACHED */
643          break;
644       }
645    }
646
647    /* Allocate run record, copy new stuff into it,
648     * and append it to the list of run records
649     * in the schedule resource.
650     */
651    if (pass == 2) {
652       RUN *tail;
653
654       /* Create new run record */
655       RUN *nrun = (RUN *)malloc(sizeof(RUN));
656       memcpy(nrun, &lrun, sizeof(RUN));
657       nrun ->next = NULL;
658
659       if (!*run) {                    /* if empty list */
660          *run = nrun;                 /* add new record */
661       } else {
662          for (tail = *run; tail->next; tail=tail->next)
663             {  }
664          tail->next = nrun;
665       }
666    }
667
668    lc->options = options;             /* restore scanner options */
669    set_bit(index, res_all.res_sch.hdr.item_present);
670 }