]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/run_conf.c
b9e307bad332253d086d3884d6ec38c1db48ce67
[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 <keyword=value ...> [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, T_NAME);
188       for (i=0; RunFields[i].name; i++) {
189          if (strcasecmp(lc->str, RunFields[i].name) == 0) {
190             found = TRUE;
191             if (lex_get_token(lc, T_ALL) != T_EQUALS) {
192                scan_err1(lc, "Expected an equals, got: %s", lc->str);
193             }
194             token = lex_get_token(lc, T_NAME);
195             switch (RunFields[i].token) {
196             case 'L':                 /* level */
197                for (j=0; joblevels[j].level_name; j++) {
198                   if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
199                      lrun.level = joblevels[j].level;
200                      lrun.job_type = joblevels[j].job_type;
201                      j = 0;
202                      break;
203                   }
204                }
205                if (j != 0) {
206                   scan_err1(lc, _("Job level field: %s not found in run record"), lc->str);
207                }
208                break;
209             case 'P':                 /* Pool */
210                if (pass == 2) {
211                   res = GetResWithName(R_POOL, lc->str);
212                   if (res == NULL) {
213                      scan_err1(lc, "Could not find specified Pool Resource: %s",
214                                 lc->str);
215                   }
216                   lrun.pool = (POOL *)res;
217                }
218                break;
219             case 'S':                 /* storage */
220                if (pass == 2) {
221                   res = GetResWithName(R_STORAGE, lc->str);
222                   if (res == NULL) {
223                      scan_err1(lc, "Could not find specified Storage Resource: %s",
224                                 lc->str);
225                   }
226                   lrun.storage = (STORE *)res;
227                }
228                break;
229             case 'M':                 /* messages */
230                if (pass == 2) {
231                   res = GetResWithName(R_MSGS, lc->str);
232                   if (res == NULL) {
233                      scan_err1(lc, "Could not find specified Messages Resource: %s",
234                                 lc->str);
235                   }
236                   lrun.msgs = (MSGS *)res;
237                }
238                break;
239             default:
240                scan_err1(lc, "Expected a keyword name, got: %s", lc->str);
241                break;
242             } /* end switch */     
243          } /* end if strcasecmp */
244       } /* end for RunFields */
245
246       /* At this point, it is not a keyword. Check for old syle
247        * Job Levels without keyword. This form is depreciated!!!
248        */
249       for (j=0; joblevels[j].level_name; j++) {
250          if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
251             lrun.level = joblevels[j].level;
252             lrun.job_type = joblevels[j].job_type;
253             found = TRUE;
254             break;
255          }
256       }
257    } /* end for found */
258
259
260    /*
261     * Scan schedule times.
262     * Default is: daily at 0:0
263     */
264    state = s_none;
265    set_defaults();
266
267    for ( ; token != T_EOL; (token = lex_get_token(lc, T_ALL))) {
268       int len, pm;
269       switch (token) {
270          case T_NUMBER:
271             state = s_mday;
272             code = atoi(lc->str) - 1;
273             if (code < 0 || code > 30) {
274                scan_err0(lc, _("Day number out of range (1-31)"));
275             }
276             break;
277          case T_NAME:                 /* this handles drop through from keyword */
278          case T_UNQUOTED_STRING:
279             if (strchr(lc->str, (int)'-')) {
280                state = s_range;
281                break;
282             }
283             if (strchr(lc->str, (int)':')) {
284                state = s_time;
285                break;
286             }
287             /* everything else must be a keyword */
288             for (i=0; keyw[i].name; i++) {
289                if (strcasecmp(lc->str, keyw[i].name) == 0) {
290                   state = keyw[i].state;
291                   code   = keyw[i].code;
292                   i = 0;
293                   break;
294                }
295             }
296             if (i != 0) {
297                scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
298             }
299             break;
300          case T_COMMA:
301             continue;
302          default:
303             scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
304             break;
305       }
306       switch (state) {
307          case s_none:
308             continue;
309          case s_mday:                 /* day of month */
310             if (!have_mday) {
311                clear_bits(0, 30, lrun.mday);
312                clear_bits(0, 6, lrun.wday);
313                have_mday = TRUE;
314             }
315             set_bit(code, lrun.mday);
316             break;
317          case s_month:                /* month of year */
318             if (!have_month) {
319                clear_bits(0, 11, lrun.month);
320                have_month = TRUE;
321             }
322             set_bit(code, lrun.month);
323             break;
324          case s_wday:                 /* week day */
325             if (!have_wday) {
326                clear_bits(0, 6, lrun.wday);
327                clear_bits(0, 30, lrun.mday);
328                have_wday = TRUE;
329             }
330             set_bit(code, lrun.wday);
331             break;
332          case s_time:                 /* time */
333             if (!have_at) {
334                scan_err0(lc, _("Time must be preceded by keyword AT."));
335             }
336             if (!have_hour) {
337                clear_bit(0, lrun.hour);
338             }
339             p = strchr(lc->str, ':');
340             if (!p)  {
341                scan_err0(lc, _("Time logic error.\n"));
342             }
343             *p++ = 0;                 /* separate two halves */
344             code = atoi(lc->str);
345             len = strlen(p);
346             if (len > 2 && p[len-1] == 'm') {
347                if (p[len-2] == 'a') {
348                   pm = 0;
349                } else if (p[len-2] == 'p') {
350                   pm = 1;
351                } else {
352                   scan_err0(lc, _("Bad time specification."));
353                }
354             } else {
355                pm = 0;
356             }
357             code2 = atoi(p);
358             if (pm) {
359                code += 12;
360             }
361             if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
362                scan_err0(lc, _("Bad time specification."));
363             }
364             set_bit(code, lrun.hour);
365             lrun.minute = code2;
366             have_hour = TRUE;
367             break;
368          case s_at:
369             have_at = TRUE;
370             break;
371          case s_range:
372             p = strchr(lc->str, '-');
373             if (!p) {
374                scan_err0(lc, _("Range logic error.\n"));
375             }
376             *p++ = 0;                 /* separate two halves */
377
378             /* Check for day range */
379             if (is_num(lc->str) && is_num(p)) {
380                code = atoi(lc->str) - 1;
381                code2 = atoi(p) - 1;
382                if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
383                   scan_err0(lc, _("Bad day range specification."));
384                }
385                if (!have_mday) {
386                   clear_bits(0, 30, lrun.mday);
387                   clear_bits(0, 6, lrun.wday);
388                   have_mday = TRUE;
389                }
390                if (code < code2) {
391                   set_bits(code, code2, lrun.mday);
392                } else {
393                   set_bits(code, 30, lrun.mday);
394                   set_bits(0, code2, lrun.mday);
395                }
396                break;
397             }
398
399             /* lookup first half of keyword range (week days or months) */
400             lcase(lc->str);
401             for (i=0; keyw[i].name; i++) {
402                if (strcmp(lc->str, keyw[i].name) == 0) {
403                   state = keyw[i].state;
404                   code   = keyw[i].code;
405                   i = 0;
406                   break;
407                }
408             }
409             if (i != 0 || (state != s_month && state != s_wday)) {
410                scan_err0(lc, _("Invalid month or week day range"));
411             }
412
413             /* Lookup end of range */
414             lcase(p);
415             for (i=0; keyw[i].name; i++) {
416                if (strcmp(p, keyw[i].name) == 0) {
417                   state2  = keyw[i].state;
418                   code2   = keyw[i].code;
419                   i = 0;
420                   break;
421                }
422             }
423             if (i != 0 || state != state2 || 
424                (state2 != s_month && state2 != s_wday) || code == code2) {
425                scan_err0(lc, _("Invalid month or weekday range"));
426             }
427             if (state == s_wday) {
428                if (!have_wday) {
429                   clear_bits(0, 6, lrun.wday);
430                   clear_bits(0, 30, lrun.mday);
431                   have_wday = TRUE;
432                }
433                if (code < code2) {
434                   set_bits(code, code2, lrun.wday);
435                } else {
436                   set_bits(code, 6, lrun.wday);
437                   set_bits(0, code2, lrun.wday);
438                }
439             } else {
440                /* must be s_month */
441                if (!have_month) {
442                   clear_bits(0, 30, lrun.month);
443                   have_month = TRUE;
444                }
445                if (code < code2) {
446                   set_bits(code, code2, lrun.month);
447                } else {
448                   /* this is a bit odd, but we accept it anyway */
449                   set_bits(code, 30, lrun.month);
450                   set_bits(0, code2, lrun.month);
451                }
452             }
453             break;
454          case s_hourly:
455             clear_defaults();
456             set_bits(0, 23, lrun.hour);
457             set_bits(0, 30, lrun.mday);
458             set_bits(0, 11, lrun.month);
459             break;
460          case s_weekly:
461             clear_defaults();
462             set_bit(0, lrun.wday);
463             set_bits(0, 11, lrun.month);
464             break;
465          case s_daily:
466             clear_defaults();
467             set_bits(0, 30, lrun.mday);
468             set_bits(0, 11, lrun.month);
469             break;
470          case s_monthly:
471             clear_defaults();
472             set_bits(0, 11, lrun.month);
473             break;
474          default:
475             scan_err0(lc, _("Unexpected run state\n"));
476             break;
477       }
478    }
479
480    /* Allocate run record, copy new stuff into it,
481     * and link it into the list of run records 
482     * in the schedule resource.
483     */
484    if (pass == 2) {
485       trun = (RUN *)malloc(sizeof(RUN));
486       memcpy(trun, &lrun, sizeof(RUN));
487       if (*run) {
488          trun->next = *run;
489       }
490       *run = trun;
491    }
492
493    lc->options = options;             /* restore scanner options */
494    set_bit(index, res_all.res_sch.hdr.item_present);
495 }