]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/run_conf.c
e173356a549e2c1033de4e636dd788fb0151847e
[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, 2001, 2002 Kern Sibbald and John Walker
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 as
15    published by the Free Software Foundation; either version 2 of
16    the License, or (at your option) any later version.
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 GNU
21    General Public License for more details.
22
23    You should have received a copy of the GNU General Public
24    License along with this program; if not, write to the Free
25    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
26    MA 02111-1307, USA.
27
28  */
29
30 #include "bacula.h"
31 #include "dird.h"
32
33 extern URES res_all;
34 extern struct s_jl joblevels[];
35
36 /* Forward referenced subroutines */
37
38 enum e_state {
39    s_none = 0,
40    s_range,
41    s_mday,
42    s_month,
43    s_time,
44    s_at,
45    s_wday,
46    s_daily,
47    s_weekly,
48    s_monthly,
49    s_hourly,
50 };  
51
52 struct s_keyw {
53   char *name;                         /* keyword */
54   enum e_state state;                 /* parser state */
55   int code;                           /* state value */
56 };
57
58 /* Keywords understood by parser */
59 static struct s_keyw keyw[] = {
60   {N_("on"),         s_none,    0},
61   {N_("at"),         s_at,      0},
62
63   {N_("sun"),        s_wday,    0},
64   {N_("mon"),        s_wday,    1},
65   {N_("tue"),        s_wday,    2},
66   {N_("wed"),        s_wday,    3},
67   {N_("thu"),        s_wday,    4},
68   {N_("fri"),        s_wday,    5},
69   {N_("sat"),        s_wday,    6},
70   {N_("jan"),        s_month,   0},
71   {N_("feb"),        s_month,   1},
72   {N_("mar"),        s_month,   2},
73   {N_("apr"),        s_month,   3},
74   {N_("may"),        s_month,   4},
75   {N_("jun"),        s_month,   5},
76   {N_("jul"),        s_month,   6},
77   {N_("aug"),        s_month,   7},
78   {N_("sep"),        s_month,   8},
79   {N_("oct"),        s_month,   9},
80   {N_("nov"),        s_month,  10},
81   {N_("dec"),        s_month,  11},
82
83   {N_("sunday"),     s_wday,    0},
84   {N_("monday"),     s_wday,    1},
85   {N_("tuesday"),    s_wday,    2},
86   {N_("wednesday"),  s_wday,    3},
87   {N_("thursday"),   s_wday,    4},
88   {N_("friday"),     s_wday,    5},
89   {N_("saturday"),   s_wday,    6},
90   {N_("january"),    s_month,   0},
91   {N_("february"),   s_month,   1},
92   {N_("march"),      s_month,   2},
93   {N_("april"),      s_month,   3},
94   {N_("june"),       s_month,   5},
95   {N_("july"),       s_month,   6},
96   {N_("august"),     s_month,   7},
97   {N_("september"),  s_month,   8},
98   {N_("october"),    s_month,   9},
99   {N_("november"),   s_month,  10},
100   {N_("december"),   s_month,  11},
101
102   {N_("daily"),      s_daily,   0},
103   {N_("weekly"),     s_weekly,  0},
104   {N_("monthly"),    s_monthly, 0},
105   {N_("hourly"),     s_hourly,  0},
106   {NULL,         s_none,    0}
107 };
108
109 static int have_hour, have_mday, have_wday, have_month;
110 static int have_at;
111 static RUN lrun;
112
113 static void clear_defaults()
114 {
115    have_hour = have_mday = have_wday = have_month = TRUE;
116    clear_bit(0,lrun.hour);
117    clear_bits(0, 30, lrun.mday);
118    clear_bits(0, 6, lrun.wday);
119    clear_bits(0, 11, lrun.month);
120 }
121
122 static void set_defaults()
123 {
124    have_hour = have_mday = have_wday = have_month = FALSE;
125    have_at = FALSE;
126    set_bit(0,lrun.hour);
127    set_bits(0, 30, lrun.mday);
128    set_bits(0, 6, lrun.wday);
129    set_bits(0, 11, lrun.month);
130 }
131
132
133 /* Check if string is a number */
134 static int is_num(char *num)
135 {
136    char *p = num;
137    int ch;
138    while ((ch = *p++)) {
139       if (ch < '0' || ch > '9') {
140          return FALSE;
141       }
142    }
143    return TRUE;
144 }
145
146 /* Keywords (RHS) permitted in Run records */
147 static struct s_kw RunFields[] = {
148    {"pool",     'P'},
149    {"level",    'L'},
150    {"storage",  'S'},
151    {"messages", 'M'},
152    {NULL,        0}
153 };
154
155 /* 
156  * Store Schedule Run information   
157  * 
158  * Parse Run statement:
159  *
160  *  Run <full|incremental|...> [on] 2 january at 23:45
161  *
162  *   Default Run time is daily at 0:0
163  *  
164  *   There can be multiple run statements, they are simply chained
165  *   together.
166  *
167  */
168 void store_run(LEX *lc, struct res_items *item, int index, int pass)
169 {
170    int i, j, found;
171    int token, state, state2, code, code2;
172    int options = lc->options;
173    RUN **run = (RUN **)(item->value);   
174    RUN *trun;
175    char *p;
176    RES *res;
177
178
179    lc->options |= LOPT_NO_IDENT;      /* want only "strings" */
180
181    /* clear local copy of run record */
182    memset(&lrun, 0, sizeof(RUN));
183
184    /* scan for Job level "full", "incremental", ... */
185    for (found=TRUE; found; ) {
186       found = FALSE;
187       token = lex_get_token(lc);
188       if (token != T_IDENTIFIER && token != T_STRING && token != T_QUOTED_STRING) {
189          scan_err1(lc, "Expected a keyword name, got: %s", lc->str);
190       }
191       for (i=0; RunFields[i].name; i++) {
192          if (strcasecmp(lc->str, RunFields[i].name) == 0) {
193             found = TRUE;
194             if (lex_get_token(lc) != T_EQUALS) {
195                scan_err1(lc, "Expected an equals, got: %s", lc->str);
196             }
197             token = lex_get_token(lc);
198             if (token != T_IDENTIFIER && token != T_STRING && token != T_QUOTED_STRING) {
199                scan_err1(lc, "Expected a keyword name, got: %s", lc->str);
200             }
201             switch (RunFields[i].token) {
202             case 'L':                 /* level */
203                for (j=0; joblevels[j].level_name; j++) {
204                   if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
205                      lrun.level = joblevels[j].level;
206                      lrun.job_type = joblevels[j].job_type;
207                      j = 0;
208                      break;
209                   }
210                }
211                if (j != 0) {
212                   scan_err1(lc, _("Job level field: %s not found in run record"), lc->str);
213                }
214                break;
215             case 'P':                 /* Pool */
216                if (pass == 2) {
217                   res = GetResWithName(R_POOL, lc->str);
218                   if (res == NULL) {
219                      scan_err1(lc, "Could not find specified Pool Resource: %s",
220                                 lc->str);
221                   }
222                   lrun.pool = (POOL *)res;
223                }
224                break;
225             case 'S':                 /* storage */
226                if (pass == 2) {
227                   res = GetResWithName(R_STORAGE, lc->str);
228                   if (res == NULL) {
229                      scan_err1(lc, "Could not find specified Storage Resource: %s",
230                                 lc->str);
231                   }
232                   lrun.storage = (STORE *)res;
233                }
234                break;
235             case 'M':                 /* messages */
236                if (pass == 2) {
237                   res = GetResWithName(R_MSGS, lc->str);
238                   if (res == NULL) {
239                      scan_err1(lc, "Could not find specified Messages Resource: %s",
240                                 lc->str);
241                   }
242                   lrun.msgs = (MSGS *)res;
243                }
244                break;
245             default:
246                scan_err1(lc, "Expected a keyword name, got: %s", lc->str);
247                break;
248             } /* end switch */     
249          } /* end if strcasecmp */
250       } /* end for RunFields */
251    } /* end for found */
252
253
254    /*
255     * Scan schedule times.
256     * Default is: daily at 0:0
257     */
258    state = s_none;
259    set_defaults();
260
261    while ((token = lex_get_token(lc)) != T_EOL) {
262       int len, pm;
263       switch (token) {
264          case T_NUMBER:
265             state = s_mday;
266             code = atoi(lc->str) - 1;
267             if (code < 0 || code > 30) {
268                scan_err0(lc, _("Day number out of range (1-31)"));
269             }
270             break;
271          case T_STRING:
272             if (strchr(lc->str, (int)'-')) {
273                state = s_range;
274                break;
275             }
276             if (strchr(lc->str, (int)':')) {
277                state = s_time;
278                break;
279             }
280             /* everything else must be a keyword */
281             for (i=0; keyw[i].name; i++) {
282                if (strcasecmp(lc->str, keyw[i].name) == 0) {
283                   state = keyw[i].state;
284                   code   = keyw[i].code;
285                   i = 0;
286                   break;
287                }
288             }
289             if (i != 0) {
290                scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
291             }
292             break;
293          case T_COMMA:
294             continue;
295          default:
296             scan_err1(lc, _("Unexpected token: %s"), lc->str);
297             break;
298       }
299       switch (state) {
300          case s_none:
301             continue;
302          case s_mday:                 /* day of month */
303             if (!have_mday) {
304                clear_bits(0, 30, lrun.mday);
305                clear_bits(0, 6, lrun.wday);
306                have_mday = TRUE;
307             }
308             set_bit(code, lrun.mday);
309             break;
310          case s_month:                /* month of year */
311             if (!have_month) {
312                clear_bits(0, 11, lrun.month);
313                have_month = TRUE;
314             }
315             set_bit(code, lrun.month);
316             break;
317          case s_wday:                 /* week day */
318             if (!have_wday) {
319                clear_bits(0, 6, lrun.wday);
320                clear_bits(0, 30, lrun.mday);
321                have_wday = TRUE;
322             }
323             set_bit(code, lrun.wday);
324             break;
325          case s_time:                 /* time */
326             if (!have_at) {
327                scan_err0(lc, _("Time must be preceded by keyword AT."));
328             }
329             if (!have_hour) {
330                clear_bit(0, lrun.hour);
331                have_hour = TRUE;
332             }
333             p = strchr(lc->str, ':');
334             if (!p)  {
335                scan_err0(lc, _("Time logic error.\n"));
336             }
337             *p++ = 0;                 /* separate two halves */
338             code = atoi(lc->str);
339             len = strlen(p);
340             if (len > 2 && p[len-1] == 'm') {
341                if (p[len-2] == 'a') {
342                   pm = 0;
343                } else if (p[len-2] == 'p') {
344                   pm = 1;
345                } else {
346                   scan_err0(lc, _("Bad time specification."));
347                }
348             } else {
349                pm = 0;
350             }
351             code2 = atoi(p);
352             if (pm) {
353                code += 12;
354             }
355             if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
356                scan_err0(lc, _("Bad time specification."));
357             }
358             set_bit(code, lrun.hour);
359             lrun.minute = code2;
360             break;
361          case s_at:
362             have_at = TRUE;
363             break;
364          case s_range:
365             p = strchr(lc->str, '-');
366             if (!p) {
367                scan_err0(lc, _("Range logic error.\n"));
368             }
369             *p++ = 0;                 /* separate two halves */
370
371             /* Check for day range */
372             if (is_num(lc->str) && is_num(p)) {
373                code = atoi(lc->str) - 1;
374                code2 = atoi(p) - 1;
375                if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
376                   scan_err0(lc, _("Bad day range specification."));
377                }
378                if (!have_mday) {
379                   clear_bits(0, 30, lrun.mday);
380                   clear_bits(0, 6, lrun.wday);
381                   have_mday = TRUE;
382                }
383                if (code < code2) {
384                   set_bits(code, code2, lrun.mday);
385                } else {
386                   set_bits(code, 30, lrun.mday);
387                   set_bits(0, code2, lrun.mday);
388                }
389                break;
390             }
391
392             /* lookup first half of keyword range (week days or months) */
393             lcase(lc->str);
394             for (i=0; keyw[i].name; i++) {
395                if (strcmp(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 || (state != s_month && state != s_wday)) {
403                scan_err0(lc, _("Invalid month or week day range"));
404             }
405
406             /* Lookup end of range */
407             lcase(p);
408             for (i=0; keyw[i].name; i++) {
409                if (strcmp(p, keyw[i].name) == 0) {
410                   state2  = keyw[i].state;
411                   code2   = keyw[i].code;
412                   i = 0;
413                   break;
414                }
415             }
416             if (i != 0 || state != state2 || 
417                (state2 != s_month && state2 != s_wday) || code == code2) {
418                scan_err0(lc, _("Invalid month or weekday range"));
419             }
420             if (state == s_wday) {
421                if (!have_wday) {
422                   clear_bits(0, 6, lrun.wday);
423                   clear_bits(0, 30, lrun.mday);
424                   have_wday = TRUE;
425                }
426                if (code < code2) {
427                   set_bits(code, code2, lrun.wday);
428                } else {
429                   set_bits(code, 6, lrun.wday);
430                   set_bits(0, code2, lrun.wday);
431                }
432             } else {
433                /* must be s_month */
434                if (!have_month) {
435                   clear_bits(0, 30, lrun.month);
436                   have_month = TRUE;
437                }
438                if (code < code2) {
439                   set_bits(code, code2, lrun.month);
440                } else {
441                   /* this is a bit odd, but we accept it anyway */
442                   set_bits(code, 30, lrun.month);
443                   set_bits(0, code2, lrun.month);
444                }
445             }
446             break;
447          case s_hourly:
448             clear_defaults();
449             set_bits(0, 23, lrun.hour);
450             set_bits(0, 30, lrun.mday);
451             set_bits(0, 11, lrun.month);
452             break;
453          case s_weekly:
454             clear_defaults();
455             set_bit(0, lrun.wday);
456             set_bits(0, 11, lrun.month);
457             break;
458          case s_daily:
459             clear_defaults();
460             set_bits(0, 30, lrun.mday);
461             set_bits(0, 11, lrun.month);
462             break;
463          case s_monthly:
464             clear_defaults();
465             set_bit(0, lrun.mday);
466             set_bits(0, 11, lrun.month);
467             break;
468          default:
469             scan_err0(lc, _("Unexpected run state\n"));
470             break;
471       }
472    }
473
474    /* Allocate run record, copy new stuff into it,
475     * and link it into the list of run records 
476     * in the schedule resource.
477     */
478    if (pass == 1) {
479       trun = (RUN *) malloc(sizeof(RUN));
480       memcpy(trun, &lrun, sizeof(RUN));
481       if (*run) {
482          trun->next = *run;
483       }
484       *run = trun;
485    }
486
487    lc->options = options;             /* restore scanner options */
488    set_bit(index, res_all.res_sch.hdr.item_present);
489 }