]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/run_conf.c
kes Extend new GUI api code to tree commands.
[bacula/bacula] / bacula / src / dird / run_conf.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2007 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 plus additions
11    that are listed 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 John Walker.
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    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; 
341       bool pm = 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          p = strchr(lc->str, ':');
438          if (!p)  {
439             scan_err0(lc, _("Time logic error.\n"));
440             /* NOT REACHED */
441          }
442          *p++ = 0;                 /* separate two halves */
443          code = atoi(lc->str);     /* pick up hour */
444          len = strlen(p);
445          if (len > 2 && p[len-1] == 'm') {
446             if (p[len-2] == 'a') {
447                pm = false;
448             } else if (p[len-2] == 'p') {
449                pm = true;
450             } else {
451                scan_err0(lc, _("Bad time specification."));
452                /* NOT REACHED */
453             }
454          } else {
455             pm = false;
456          }
457          code2 = atoi(p);             /* pick up minutes */
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 (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          set_bit(code, lrun.hour);
478          lrun.minute = code2;
479          have_hour = true;
480          break;
481       case s_at:
482          have_at = true;
483          break;
484       case s_range:
485          p = strchr(lc->str, '-');
486          if (!p) {
487             scan_err0(lc, _("Range logic error.\n"));
488          }
489          *p++ = 0;                 /* separate two halves */
490
491          /* Check for day range */
492          if (is_an_integer(lc->str) && is_an_integer(p)) {
493             code = atoi(lc->str) - 1;
494             code2 = atoi(p) - 1;
495             if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
496                scan_err0(lc, _("Bad day range specification."));
497             }
498             if (!have_mday) {
499                clear_bits(0, 30, lrun.mday);
500                have_mday = true;
501             }
502             if (code < code2) {
503                set_bits(code, code2, lrun.mday);
504             } else {
505                set_bits(code, 30, lrun.mday);
506                set_bits(0, code2, lrun.mday);
507             }
508             break;
509          }
510          /* Check for week of year range */
511          if (strlen(lc->str) == 3 && strlen(p) == 3 &&
512              (lc->str[0] == 'w' || lc->str[0] == 'W') &&
513              (p[0] == 'w' || p[0] == 'W') &&
514              is_an_integer(lc->str+1) && is_an_integer(p+1)) {
515             code = atoi(lc->str+1);
516             code2 = atoi(p+1);
517             if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
518                scan_err0(lc, _("Week number out of range (0-53)"));
519             }
520             if (!have_woy) {
521                clear_bits(0, 53, lrun.woy);
522                have_woy = true;
523             }
524             if (code < code2) {
525                set_bits(code, code2, lrun.woy);
526             } else {
527                set_bits(code, 53, lrun.woy);
528                set_bits(0, code2, lrun.woy);
529             }
530             break;
531          }
532          /* lookup first half of keyword range (week days or months) */
533          lcase(lc->str);
534          for (i=0; keyw[i].name; i++) {
535             if (strcmp(lc->str, keyw[i].name) == 0) {
536                state = keyw[i].state;
537                code   = keyw[i].code;
538                i = 0;
539                break;
540             }
541          }
542          if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
543             scan_err0(lc, _("Invalid month, week or position day range"));
544             /* NOT REACHED */
545          }
546
547          /* Lookup end of range */
548          lcase(p);
549          for (i=0; keyw[i].name; i++) {
550             if (strcmp(p, keyw[i].name) == 0) {
551                state2  = keyw[i].state;
552                code2   = keyw[i].code;
553                i = 0;
554                break;
555             }
556          }
557          if (i != 0 || state != state2 || code == code2) {
558             scan_err0(lc, _("Invalid month, weekday or position range"));
559             /* NOT REACHED */
560          }
561          if (state == s_wday) {
562             if (!have_wday) {
563                clear_bits(0, 6, lrun.wday);
564                have_wday = true;
565             }
566             if (code < code2) {
567                set_bits(code, code2, lrun.wday);
568             } else {
569                set_bits(code, 6, lrun.wday);
570                set_bits(0, code2, lrun.wday);
571             }
572          } else if (state == s_month) {
573             if (!have_month) {
574                clear_bits(0, 11, lrun.month);
575                have_month = true;
576             }
577             if (code < code2) {
578                set_bits(code, code2, lrun.month);
579             } else {
580                /* this is a bit odd, but we accept it anyway */
581                set_bits(code, 11, lrun.month);
582                set_bits(0, code2, lrun.month);
583             }
584          } else {
585             /* Must be position */
586             if (!have_wom) {
587                clear_bits(0, 4, lrun.wom);
588                have_wom = true;
589             }
590             if (code < code2) {
591                set_bits(code, code2, lrun.wom);
592             } else {
593                set_bits(code, 4, lrun.wom);
594                set_bits(0, code2, lrun.wom);
595             }
596          }
597          break;
598       case s_hourly:
599          have_hour = true;
600          set_bits(0, 23, lrun.hour);
601          break;
602       case s_weekly:
603          have_mday = have_wom = have_woy = true;
604          set_bits(0, 30, lrun.mday);
605          set_bits(0, 4,  lrun.wom);
606          set_bits(0, 53, lrun.woy);
607          break;
608       case s_daily:
609          have_mday = true;
610          set_bits(0, 6, lrun.wday);
611          break;
612       case s_monthly:
613          have_month = true;
614          set_bits(0, 11, lrun.month);
615          break;
616       default:
617          scan_err0(lc, _("Unexpected run state\n"));
618          /* NOT REACHED */
619          break;
620       }
621    }
622
623    /* Allocate run record, copy new stuff into it,
624     * and link it into the list of run records
625     * in the schedule resource.
626     */
627    if (pass == 2) {
628       trun = (RUN *)malloc(sizeof(RUN));
629       memcpy(trun, &lrun, sizeof(RUN));
630       if (*run) {
631          trun->next = *run;
632       }
633       *run = trun;
634    }
635
636    lc->options = options;             /* restore scanner options */
637    set_bit(index, res_all.res_sch.hdr.item_present);
638 }