]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/run_conf.c
kes Change Bacula trademark owner from John Walker to Kern Sibbald
[bacula/bacula] / bacula / src / dird / run_conf.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2008 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 two of the GNU 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 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  *     Version $Id$
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    char *p;
190    RES *res;
191
192
193    lc->options |= LOPT_NO_IDENT;      /* want only "strings" */
194
195    /* clear local copy of run record */
196    memset(&lrun, 0, sizeof(RUN));
197
198    /* scan for Job level "full", "incremental", ... */
199    for (found=true; found; ) {
200       found = false;
201       token = lex_get_token(lc, T_NAME);
202       for (i=0; RunFields[i].name; i++) {
203          if (strcasecmp(lc->str, RunFields[i].name) == 0) {
204             found = true;
205             if (lex_get_token(lc, T_ALL) != T_EQUALS) {
206                scan_err1(lc, _("Expected an equals, got: %s"), lc->str);
207                /* NOT REACHED */
208             }
209             switch (RunFields[i].token) {
210             case 's':                 /* Data spooling */
211                token = lex_get_token(lc, T_NAME);
212                if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
213                   lrun.spool_data = true;
214                   lrun.spool_data_set = true;
215                } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
216                   lrun.spool_data = false;
217                   lrun.spool_data_set = true;
218                } else {
219                   scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
220                }
221                break;
222             case 'W':                 /* Write part after job */
223                token = lex_get_token(lc, T_NAME);
224                if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
225                   lrun.write_part_after_job = true;
226                   lrun.write_part_after_job_set = true;
227                } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
228                   lrun.write_part_after_job = false;
229                   lrun.write_part_after_job_set = true;
230                } else {
231                   scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str);
232                }
233                break;
234             case 'L':                 /* level */
235                token = lex_get_token(lc, T_NAME);
236                for (j=0; joblevels[j].level_name; j++) {
237                   if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
238                      lrun.level = joblevels[j].level;
239                      lrun.job_type = joblevels[j].job_type;
240                      j = 0;
241                      break;
242                   }
243                }
244                if (j != 0) {
245                   scan_err1(lc, _("Job level field: %s not found in run record"), lc->str);
246                   /* NOT REACHED */
247                }
248                break;
249             case 'p':                 /* Priority */
250                token = lex_get_token(lc, T_PINT32);
251                if (pass == 2) {
252                   lrun.Priority = lc->pint32_val;
253                }
254                break;
255             case 'P':                 /* Pool */
256             case 'f':                 /* FullPool */
257             case 'i':                 /* IncPool */
258             case 'd':                 /* DifPool */
259                token = lex_get_token(lc, T_NAME);
260                if (pass == 2) {
261                   res = GetResWithName(R_POOL, lc->str);
262                   if (res == NULL) {
263                      scan_err1(lc, _("Could not find specified Pool Resource: %s"),
264                                 lc->str);
265                      /* NOT REACHED */
266                   }
267                   switch(RunFields[i].token) {
268                   case 'P':
269                      lrun.pool = (POOL *)res;
270                      break;
271                   case 'f':
272                      lrun.full_pool = (POOL *)res;
273                      break;
274                   case 'i':
275                      lrun.inc_pool = (POOL *)res;
276                      break;
277                   case 'd':
278                      lrun.diff_pool = (POOL *)res;
279                      break;
280                   }
281                }
282                break;
283             case 'S':                 /* storage */
284                token = lex_get_token(lc, T_NAME);
285                if (pass == 2) {
286                   res = GetResWithName(R_STORAGE, lc->str);
287                   if (res == NULL) {
288                      scan_err1(lc, _("Could not find specified Storage Resource: %s"),
289                                 lc->str);
290                      /* NOT REACHED */
291                   }
292                   lrun.storage = (STORE *)res;
293                }
294                break;
295             case 'M':                 /* messages */
296                token = lex_get_token(lc, T_NAME);
297                if (pass == 2) {
298                   res = GetResWithName(R_MSGS, lc->str);
299                   if (res == NULL) {
300                      scan_err1(lc, _("Could not find specified Messages Resource: %s"),
301                                 lc->str);
302                      /* NOT REACHED */
303                   }
304                   lrun.msgs = (MSGS *)res;
305                }
306                break;
307             default:
308                scan_err1(lc, _("Expected a keyword name, got: %s"), lc->str);
309                /* NOT REACHED */
310                break;
311             } /* end switch */
312          } /* end if strcasecmp */
313       } /* end for RunFields */
314
315       /* At this point, it is not a keyword. Check for old syle
316        * Job Levels without keyword. This form is depreciated!!!
317        */
318       if (!found) {
319          for (j=0; joblevels[j].level_name; j++) {
320             if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
321                lrun.level = joblevels[j].level;
322                lrun.job_type = joblevels[j].job_type;
323                found = true;
324                break;
325             }
326          }
327       }
328    } /* end for found */
329
330
331    /*
332     * Scan schedule times.
333     * Default is: daily at 0:0
334     */
335    state = s_none;
336    set_defaults();
337
338    for ( ; token != T_EOL; (token = lex_get_token(lc, T_ALL))) {
339       int len; 
340       bool pm = false;
341       bool am = false;
342       switch (token) {
343       case T_NUMBER:
344          state = s_mday;
345          code = atoi(lc->str) - 1;
346          if (code < 0 || code > 30) {
347             scan_err0(lc, _("Day number out of range (1-31)"));
348          }
349          break;
350       case T_NAME:                 /* this handles drop through from keyword */
351       case T_UNQUOTED_STRING:
352          if (strchr(lc->str, (int)'-')) {
353             state = s_range;
354             break;
355          }
356          if (strchr(lc->str, (int)':')) {
357             state = s_time;
358             break;
359          }
360          if (lc->str_len == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') &&
361              is_an_integer(lc->str+1)) {
362             code = atoi(lc->str+1);
363             if (code < 0 || code > 53) {
364                scan_err0(lc, _("Week number out of range (0-53)"));
365               /* NOT REACHED */
366             }
367             state = s_woy;            /* week of year */
368             break;
369          }
370          /* everything else must be a keyword */
371          for (i=0; keyw[i].name; i++) {
372             if (strcasecmp(lc->str, keyw[i].name) == 0) {
373                state = keyw[i].state;
374                code   = keyw[i].code;
375                i = 0;
376                break;
377             }
378          }
379          if (i != 0) {
380             scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
381             /* NOT REACHED */
382          }
383          break;
384       case T_COMMA:
385          continue;
386       default:
387          scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
388          /* NOT REACHED */
389          break;
390       }
391       switch (state) {
392       case s_none:
393          continue;
394       case s_mday:                 /* day of month */
395          if (!have_mday) {
396             clear_bits(0, 30, lrun.mday);
397             have_mday = true;
398          }
399          set_bit(code, lrun.mday);
400          break;
401       case s_month:                /* month of year */
402          if (!have_month) {
403             clear_bits(0, 11, lrun.month);
404             have_month = true;
405          }
406          set_bit(code, lrun.month);
407          break;
408       case s_wday:                 /* week day */
409          if (!have_wday) {
410             clear_bits(0, 6, lrun.wday);
411             have_wday = true;
412          }
413          set_bit(code, lrun.wday);
414          break;
415       case s_wom:                  /* Week of month 1st, ... */
416          if (!have_wom) {
417             clear_bits(0, 4, lrun.wom);
418             have_wom = true;
419          }
420          set_bit(code, lrun.wom);
421          break;
422       case s_woy:
423          if (!have_woy) {
424             clear_bits(0, 53, lrun.woy);
425             have_woy = true;
426          }
427          set_bit(code, lrun.woy);
428          break;
429       case s_time:                 /* time */
430          if (!have_at) {
431             scan_err0(lc, _("Time must be preceded by keyword AT."));
432             /* NOT REACHED */
433          }
434          if (!have_hour) {
435             clear_bits(0, 23, lrun.hour);
436          }
437 //       Dmsg1(000, "s_time=%s\n", lc->str);
438          p = strchr(lc->str, ':');
439          if (!p)  {
440             scan_err0(lc, _("Time logic error.\n"));
441             /* NOT REACHED */
442          }
443          *p++ = 0;                 /* separate two halves */
444          code = atoi(lc->str);     /* pick up hour */
445          code2 = atoi(p);          /* pick up minutes */
446          len = strlen(p);
447          if (len >= 2) {
448             p += 2;
449          }
450          if (strcasecmp(p, "pm") == 0) {
451             pm = true;
452          } else if (strcasecmp(p, "am") == 0) {
453             am = true;
454          } else if (len != 2) {
455             scan_err0(lc, _("Bad time specification."));
456             /* NOT REACHED */
457          }   
458          /* 
459           * Note, according to NIST, 12am and 12pm are ambiguous and
460           *  can be defined to anything.  However, 12:01am is the same
461           *  as 00:01 and 12:01pm is the same as 12:01, so we define 
462           *  12am as 00:00 and 12pm as 12:00.
463           */
464          if (pm) {
465             /* Convert to 24 hour time */
466             if (code != 12) {
467                code += 12;
468             }
469          /* am */
470          } else if (am && code == 12) {
471             code -= 12;
472          }
473          if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
474             scan_err0(lc, _("Bad time specification."));
475             /* NOT REACHED */
476          }
477 //       Dmsg2(000, "hour=%d min=%d\n", code, code2);
478          set_bit(code, lrun.hour);
479          lrun.minute = code2;
480          have_hour = true;
481          break;
482       case s_at:
483          have_at = true;
484          break;
485       case s_range:
486          p = strchr(lc->str, '-');
487          if (!p) {
488             scan_err0(lc, _("Range logic error.\n"));
489          }
490          *p++ = 0;                 /* separate two halves */
491
492          /* Check for day range */
493          if (is_an_integer(lc->str) && is_an_integer(p)) {
494             code = atoi(lc->str) - 1;
495             code2 = atoi(p) - 1;
496             if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
497                scan_err0(lc, _("Bad day range specification."));
498             }
499             if (!have_mday) {
500                clear_bits(0, 30, lrun.mday);
501                have_mday = true;
502             }
503             if (code < code2) {
504                set_bits(code, code2, lrun.mday);
505             } else {
506                set_bits(code, 30, lrun.mday);
507                set_bits(0, code2, lrun.mday);
508             }
509             break;
510          }
511          /* Check for week of year range */
512          if (strlen(lc->str) == 3 && strlen(p) == 3 &&
513              (lc->str[0] == 'w' || lc->str[0] == 'W') &&
514              (p[0] == 'w' || p[0] == 'W') &&
515              is_an_integer(lc->str+1) && is_an_integer(p+1)) {
516             code = atoi(lc->str+1);
517             code2 = atoi(p+1);
518             if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
519                scan_err0(lc, _("Week number out of range (0-53)"));
520             }
521             if (!have_woy) {
522                clear_bits(0, 53, lrun.woy);
523                have_woy = true;
524             }
525             if (code < code2) {
526                set_bits(code, code2, lrun.woy);
527             } else {
528                set_bits(code, 53, lrun.woy);
529                set_bits(0, code2, lrun.woy);
530             }
531             break;
532          }
533          /* lookup first half of keyword range (week days or months) */
534          lcase(lc->str);
535          for (i=0; keyw[i].name; i++) {
536             if (strcmp(lc->str, keyw[i].name) == 0) {
537                state = keyw[i].state;
538                code   = keyw[i].code;
539                i = 0;
540                break;
541             }
542          }
543          if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
544             scan_err0(lc, _("Invalid month, week or position day range"));
545             /* NOT REACHED */
546          }
547
548          /* Lookup end of range */
549          lcase(p);
550          for (i=0; keyw[i].name; i++) {
551             if (strcmp(p, keyw[i].name) == 0) {
552                state2  = keyw[i].state;
553                code2   = keyw[i].code;
554                i = 0;
555                break;
556             }
557          }
558          if (i != 0 || state != state2 || code == code2) {
559             scan_err0(lc, _("Invalid month, weekday or position range"));
560             /* NOT REACHED */
561          }
562          if (state == s_wday) {
563             if (!have_wday) {
564                clear_bits(0, 6, lrun.wday);
565                have_wday = true;
566             }
567             if (code < code2) {
568                set_bits(code, code2, lrun.wday);
569             } else {
570                set_bits(code, 6, lrun.wday);
571                set_bits(0, code2, lrun.wday);
572             }
573          } else if (state == s_month) {
574             if (!have_month) {
575                clear_bits(0, 11, lrun.month);
576                have_month = true;
577             }
578             if (code < code2) {
579                set_bits(code, code2, lrun.month);
580             } else {
581                /* this is a bit odd, but we accept it anyway */
582                set_bits(code, 11, lrun.month);
583                set_bits(0, code2, lrun.month);
584             }
585          } else {
586             /* Must be position */
587             if (!have_wom) {
588                clear_bits(0, 4, lrun.wom);
589                have_wom = true;
590             }
591             if (code < code2) {
592                set_bits(code, code2, lrun.wom);
593             } else {
594                set_bits(code, 4, lrun.wom);
595                set_bits(0, code2, lrun.wom);
596             }
597          }
598          break;
599       case s_hourly:
600          have_hour = true;
601          set_bits(0, 23, lrun.hour);
602          break;
603       case s_weekly:
604          have_mday = have_wom = have_woy = true;
605          set_bits(0, 30, lrun.mday);
606          set_bits(0, 4,  lrun.wom);
607          set_bits(0, 53, lrun.woy);
608          break;
609       case s_daily:
610          have_mday = true;
611          set_bits(0, 6, lrun.wday);
612          break;
613       case s_monthly:
614          have_month = true;
615          set_bits(0, 11, lrun.month);
616          break;
617       default:
618          scan_err0(lc, _("Unexpected run state\n"));
619          /* NOT REACHED */
620          break;
621       }
622    }
623
624    /* Allocate run record, copy new stuff into it,
625     * and append it to the list of run records
626     * in the schedule resource.
627     */
628    if (pass == 2) {
629       RUN *tail;
630
631       /* Create new run record */
632       RUN *nrun = (RUN *)malloc(sizeof(RUN));
633       memcpy(nrun, &lrun, sizeof(RUN));
634       nrun ->next = NULL;
635
636       if (!*run) {                    /* if empty list */
637          *run = nrun;                 /* add new record */
638       } else {
639          for (tail = *run; tail->next; tail=tail->next)
640             {  }
641          tail->next = nrun;
642       }
643    }
644
645    lc->options = options;             /* restore scanner options */
646    set_bit(index, res_all.res_sch.hdr.item_present);
647 }