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