]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/run_conf.c
add jobq+serial.h+priorities+recycling
[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    s_wpos,                            /* 1st, 2nd, ...*/
51 };  
52
53 struct s_keyw {
54   char *name;                         /* keyword */
55   enum e_state state;                 /* parser state */
56   int code;                           /* state value */
57 };
58
59 /* Keywords understood by parser */
60 static struct s_keyw keyw[] = {
61   {N_("on"),         s_none,    0},
62   {N_("at"),         s_at,      0},
63
64   {N_("sun"),        s_wday,    0},
65   {N_("mon"),        s_wday,    1},
66   {N_("tue"),        s_wday,    2},
67   {N_("wed"),        s_wday,    3},
68   {N_("thu"),        s_wday,    4},
69   {N_("fri"),        s_wday,    5},
70   {N_("sat"),        s_wday,    6},
71   {N_("jan"),        s_month,   0},
72   {N_("feb"),        s_month,   1},
73   {N_("mar"),        s_month,   2},
74   {N_("apr"),        s_month,   3},
75   {N_("may"),        s_month,   4},
76   {N_("jun"),        s_month,   5},
77   {N_("jul"),        s_month,   6},
78   {N_("aug"),        s_month,   7},
79   {N_("sep"),        s_month,   8},
80   {N_("oct"),        s_month,   9},
81   {N_("nov"),        s_month,  10},
82   {N_("dec"),        s_month,  11},
83
84   {N_("sunday"),     s_wday,    0},
85   {N_("monday"),     s_wday,    1},
86   {N_("tuesday"),    s_wday,    2},
87   {N_("wednesday"),  s_wday,    3},
88   {N_("thursday"),   s_wday,    4},
89   {N_("friday"),     s_wday,    5},
90   {N_("saturday"),   s_wday,    6},
91   {N_("january"),    s_month,   0},
92   {N_("february"),   s_month,   1},
93   {N_("march"),      s_month,   2},
94   {N_("april"),      s_month,   3},
95   {N_("june"),       s_month,   5},
96   {N_("july"),       s_month,   6},
97   {N_("august"),     s_month,   7},
98   {N_("september"),  s_month,   8},
99   {N_("october"),    s_month,   9},
100   {N_("november"),   s_month,  10},
101   {N_("december"),   s_month,  11},
102
103   {N_("daily"),      s_daily,   0},
104   {N_("weekly"),     s_weekly,  0},
105   {N_("monthly"),    s_monthly, 0},
106   {N_("hourly"),     s_hourly,  0},
107
108   {N_("1st"),        s_wpos,    0},
109   {N_("2nd"),        s_wpos,    1},
110   {N_("3rd"),        s_wpos,    2},
111   {N_("4th"),        s_wpos,    3},
112   {N_("5th"),        s_wpos,    4},
113
114   {N_("first"),      s_wpos,    0},
115   {N_("second"),     s_wpos,    1},
116   {N_("third"),      s_wpos,    2},
117   {N_("fourth"),     s_wpos,    3},
118   {N_("fifth"),      s_wpos,    4},
119   {NULL,         s_none,    0}
120 };
121
122 static int have_hour, have_mday, have_wday, have_month, have_wpos;
123 static int have_at;
124 static RUN lrun;
125
126 static void clear_defaults()
127 {
128    have_hour = have_mday = have_wday = have_month = have_wpos = TRUE;
129    clear_bit(0,lrun.hour);
130    clear_bits(0, 30, lrun.mday);
131    clear_bits(0, 6, lrun.wday);
132    clear_bits(0, 11, lrun.month);
133    clear_bits(0, 4, lrun.wpos);
134 }
135
136 static void set_defaults()
137 {
138    have_hour = have_mday = have_wday = have_month = have_wpos = FALSE;
139    have_at = FALSE;
140    set_bit(0,lrun.hour);
141    set_bits(0, 30, lrun.mday);
142    set_bits(0, 6, lrun.wday);
143    set_bits(0, 11, lrun.month);
144    set_bits(0, 4, lrun.wpos);
145 }
146
147
148 /* Check if string is a number */
149 static int is_num(char *num)
150 {
151    char *p = num;
152    int ch;
153    while ((ch = *p++)) {
154       if (ch < '0' || ch > '9') {
155          return FALSE;
156       }
157    }
158    return TRUE;
159 }
160
161 /* Keywords (RHS) permitted in Run records */
162 static struct s_kw RunFields[] = {
163    {"pool",     'P'},
164    {"level",    'L'},
165    {"storage",  'S'},
166    {"messages", 'M'},
167    {"priority", 'p'},
168    {NULL,        0}
169 };
170
171 /* 
172  * Store Schedule Run information   
173  * 
174  * Parse Run statement:
175  *
176  *  Run <keyword=value ...> [on] 2 january at 23:45
177  *
178  *   Default Run time is daily at 0:0
179  *  
180  *   There can be multiple run statements, they are simply chained
181  *   together.
182  *
183  */
184 void store_run(LEX *lc, struct res_items *item, int index, int pass)
185 {
186    int i, j, found;
187    int token, state, state2 = 0, code = 0, code2 = 0;
188    int options = lc->options;
189    RUN **run = (RUN **)(item->value);   
190    RUN *trun;
191    char *p;
192    RES *res;
193
194
195    lc->options |= LOPT_NO_IDENT;      /* want only "strings" */
196
197    /* clear local copy of run record */
198    memset(&lrun, 0, sizeof(RUN));
199
200    /* scan for Job level "full", "incremental", ... */
201    for (found=TRUE; found; ) {
202       found = FALSE;
203       token = lex_get_token(lc, T_NAME);
204       for (i=0; RunFields[i].name; i++) {
205          if (strcasecmp(lc->str, RunFields[i].name) == 0) {
206             found = TRUE;
207             if (lex_get_token(lc, T_ALL) != T_EQUALS) {
208                scan_err1(lc, "Expected an equals, got: %s", lc->str);
209                /* NOT REACHED */ 
210             }
211             switch (RunFields[i].token) {
212             case 'L':                 /* level */
213                token = lex_get_token(lc, T_NAME);
214                for (j=0; joblevels[j].level_name; j++) {
215                   if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
216                      lrun.level = joblevels[j].level;
217                      lrun.job_type = joblevels[j].job_type;
218                      j = 0;
219                      break;
220                   }
221                }
222                if (j != 0) {
223                   scan_err1(lc, _("Job level field: %s not found in run record"), lc->str);
224                   /* NOT REACHED */
225                }
226                break;
227             case 'p':                 /* Priority */
228                token = lex_get_token(lc, T_PINT32);
229                if (pass == 2) {
230                   lrun.Priority = lc->pint32_val;
231                }
232                break;
233             case 'P':                 /* Pool */
234                token = lex_get_token(lc, T_NAME);
235                if (pass == 2) {
236                   res = GetResWithName(R_POOL, lc->str);
237                   if (res == NULL) {
238                      scan_err1(lc, "Could not find specified Pool Resource: %s",
239                                 lc->str);
240                      /* NOT REACHED */
241                   }
242                   lrun.pool = (POOL *)res;
243                }
244                break;
245             case 'S':                 /* storage */
246                token = lex_get_token(lc, T_NAME);
247                if (pass == 2) {
248                   res = GetResWithName(R_STORAGE, lc->str);
249                   if (res == NULL) {
250                      scan_err1(lc, "Could not find specified Storage Resource: %s",
251                                 lc->str);
252                      /* NOT REACHED */
253                   }
254                   lrun.storage = (STORE *)res;
255                }
256                break;
257             case 'M':                 /* messages */
258                token = lex_get_token(lc, T_NAME);
259                if (pass == 2) {
260                   res = GetResWithName(R_MSGS, lc->str);
261                   if (res == NULL) {
262                      scan_err1(lc, "Could not find specified Messages Resource: %s",
263                                 lc->str);
264                      /* NOT REACHED */
265                   }
266                   lrun.msgs = (MSGS *)res;
267                }
268                break;
269             default:
270                scan_err1(lc, "Expected a keyword name, got: %s", lc->str);
271                /* NOT REACHED */
272                break;
273             } /* end switch */     
274          } /* end if strcasecmp */
275       } /* end for RunFields */
276
277       /* At this point, it is not a keyword. Check for old syle
278        * Job Levels without keyword. This form is depreciated!!!
279        */
280       for (j=0; joblevels[j].level_name; j++) {
281          if (strcasecmp(lc->str, joblevels[j].level_name) == 0) {
282             lrun.level = joblevels[j].level;
283             lrun.job_type = joblevels[j].job_type;
284             found = TRUE;
285             break;
286          }
287       }
288    } /* end for found */
289
290
291    /*
292     * Scan schedule times.
293     * Default is: daily at 0:0
294     */
295    state = s_none;
296    set_defaults();
297
298    for ( ; token != T_EOL; (token = lex_get_token(lc, T_ALL))) {
299       int len, pm = 0;
300       switch (token) {
301          case T_NUMBER:
302             state = s_mday;
303             code = atoi(lc->str) - 1;
304             if (code < 0 || code > 30) {
305                scan_err0(lc, _("Day number out of range (1-31)"));
306             }
307             break;
308          case T_NAME:                 /* this handles drop through from keyword */
309          case T_UNQUOTED_STRING:
310             if (strchr(lc->str, (int)'-')) {
311                state = s_range;
312                break;
313             }
314             if (strchr(lc->str, (int)':')) {
315                state = s_time;
316                break;
317             }
318             /* everything else must be a keyword */
319             for (i=0; keyw[i].name; i++) {
320                if (strcasecmp(lc->str, keyw[i].name) == 0) {
321                   state = keyw[i].state;
322                   code   = keyw[i].code;
323                   i = 0;
324                   break;
325                }
326             }
327             if (i != 0) {
328                scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
329                /* NOT REACHED */
330             }
331             break;
332          case T_COMMA:
333             continue;
334          default:
335             scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
336             /* NOT REACHED */
337             break;
338       }
339       switch (state) {
340          case s_none:
341             continue;
342          case s_mday:                 /* day of month */
343             if (!have_mday) {
344                clear_bits(0, 30, lrun.mday);
345                clear_bits(0, 6, lrun.wday);
346                have_mday = TRUE;
347             }
348             set_bit(code, lrun.mday);
349             break;
350          case s_month:                /* month of year */
351             if (!have_month) {
352                clear_bits(0, 11, lrun.month);
353                have_month = TRUE;
354             }
355             set_bit(code, lrun.month);
356             break;
357          case s_wday:                 /* week day */
358             if (!have_wday) {
359                clear_bits(0, 6, lrun.wday);
360                clear_bits(0, 30, lrun.mday);
361                have_wday = TRUE;
362             }
363             set_bit(code, lrun.wday);
364             break;
365          case s_wpos:                 /* Week position 1st, ... */
366             if (!have_wpos) {
367                clear_bits(0, 4, lrun.wpos);
368                have_wpos = TRUE;
369             }
370             set_bit(code, lrun.wpos);
371             break;
372          case s_time:                 /* time */
373             if (!have_at) {
374                scan_err0(lc, _("Time must be preceded by keyword AT."));
375                /* NOT REACHED */
376             }
377             if (!have_hour) {
378                clear_bit(0, lrun.hour);
379             }
380             p = strchr(lc->str, ':');
381             if (!p)  {
382                scan_err0(lc, _("Time logic error.\n"));
383                /* NOT REACHED */
384             }
385             *p++ = 0;                 /* separate two halves */
386             code = atoi(lc->str);
387             len = strlen(p);
388             if (len > 2 && p[len-1] == 'm') {
389                if (p[len-2] == 'a') {
390                   pm = 0;
391                } else if (p[len-2] == 'p') {
392                   pm = 1;
393                } else {
394                   scan_err0(lc, _("Bad time specification."));
395                   /* NOT REACHED */
396                }
397             } else {
398                pm = 0;
399             }
400             code2 = atoi(p);
401             if (pm) {
402                code += 12;
403             }
404             if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
405                scan_err0(lc, _("Bad time specification."));
406                /* NOT REACHED */
407             }
408             set_bit(code, lrun.hour);
409             lrun.minute = code2;
410             have_hour = TRUE;
411             break;
412          case s_at:
413             have_at = TRUE;
414             break;
415          case s_range:
416             p = strchr(lc->str, '-');
417             if (!p) {
418                scan_err0(lc, _("Range logic error.\n"));
419             }
420             *p++ = 0;                 /* separate two halves */
421
422             /* Check for day range */
423             if (is_num(lc->str) && is_num(p)) {
424                code = atoi(lc->str) - 1;
425                code2 = atoi(p) - 1;
426                if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
427                   scan_err0(lc, _("Bad day range specification."));
428                }
429                if (!have_mday) {
430                   clear_bits(0, 30, lrun.mday);
431                   clear_bits(0, 6, lrun.wday);
432                   have_mday = TRUE;
433                }
434                if (code < code2) {
435                   set_bits(code, code2, lrun.mday);
436                } else {
437                   set_bits(code, 30, lrun.mday);
438                   set_bits(0, code2, lrun.mday);
439                }
440                break;
441             }
442
443             /* lookup first half of keyword range (week days or months) */
444             lcase(lc->str);
445             for (i=0; keyw[i].name; i++) {
446                if (strcmp(lc->str, keyw[i].name) == 0) {
447                   state = keyw[i].state;
448                   code   = keyw[i].code;
449                   i = 0;
450                   break;
451                }
452             }
453             if (i != 0 || (state != s_month && state != s_wday && state != s_wpos)) {
454                scan_err0(lc, _("Invalid month, week or position day range"));
455                /* NOT REACHED */
456             }
457
458             /* Lookup end of range */
459             lcase(p);
460             for (i=0; keyw[i].name; i++) {
461                if (strcmp(p, keyw[i].name) == 0) {
462                   state2  = keyw[i].state;
463                   code2   = keyw[i].code;
464                   i = 0;
465                   break;
466                }
467             }
468             if (i != 0 || state != state2 || code == code2) {
469                scan_err0(lc, _("Invalid month, weekday or position range"));
470                /* NOT REACHED */
471             }
472             if (state == s_wday) {
473                if (!have_wday) {
474                   clear_bits(0, 6, lrun.wday);
475                   clear_bits(0, 30, lrun.mday);
476                   have_wday = TRUE;
477                }
478                if (code < code2) {
479                   set_bits(code, code2, lrun.wday);
480                } else {
481                   set_bits(code, 6, lrun.wday);
482                   set_bits(0, code2, lrun.wday);
483                }
484             } else if (state == s_month) {
485                if (!have_month) {
486                   clear_bits(0, 30, lrun.month);
487                   have_month = TRUE;
488                }
489                if (code < code2) {
490                   set_bits(code, code2, lrun.month);
491                } else {
492                   /* this is a bit odd, but we accept it anyway */
493                   set_bits(code, 30, lrun.month);
494                   set_bits(0, code2, lrun.month);
495                }
496             } else {
497                /* Must be position */
498                if (!have_wpos) {
499                   clear_bits(0, 4, lrun.wpos);
500                   have_wpos = TRUE;
501                }
502                if (code < code2) {
503                   set_bits(code, code2, lrun.wpos);
504                } else {
505                   set_bits(code, 4, lrun.wpos);
506                   set_bits(0, code2, lrun.wpos);
507                }
508             }                      
509             break;
510          case s_hourly:
511             clear_defaults();
512             set_bits(0, 23, lrun.hour);
513             set_bits(0, 30, lrun.mday);
514             set_bits(0, 11, lrun.month);
515             set_bits(0, 4, lrun.wpos);
516             break;
517          case s_weekly:
518             clear_defaults();
519             set_bit(0, lrun.wday);
520             set_bits(0, 11, lrun.month);
521             set_bits(0, 4, lrun.wpos);
522             break;
523          case s_daily:
524             clear_defaults();
525             set_bits(0, 30, lrun.mday);
526             set_bits(0, 11, lrun.month);
527             set_bits(0, 4,  lrun.wpos);
528             break;
529          case s_monthly:
530             clear_defaults();
531             set_bits(0, 11, lrun.month);
532             set_bits(0, 4,  lrun.wpos);
533             break;
534          default:
535             scan_err0(lc, _("Unexpected run state\n"));
536             /* NOT REACHED */
537             break;
538       }
539    }
540
541    /* Allocate run record, copy new stuff into it,
542     * and link it into the list of run records 
543     * in the schedule resource.
544     */
545    if (pass == 2) {
546       trun = (RUN *)malloc(sizeof(RUN));
547       memcpy(trun, &lrun, sizeof(RUN));
548       if (*run) {
549          trun->next = *run;
550       }
551       *run = trun;
552    }
553
554    lc->options = options;             /* restore scanner options */
555    set_bit(index, res_all.res_sch.hdr.item_present);
556 }