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