]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/run_conf.c
Add core fd plugin code
[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 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 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       bool am = false;
343       switch (token) {
344       case T_NUMBER:
345          state = s_mday;
346          code = atoi(lc->str) - 1;
347          if (code < 0 || code > 30) {
348             scan_err0(lc, _("Day number out of range (1-31)"));
349          }
350          break;
351       case T_NAME:                 /* this handles drop through from keyword */
352       case T_UNQUOTED_STRING:
353          if (strchr(lc->str, (int)'-')) {
354             state = s_range;
355             break;
356          }
357          if (strchr(lc->str, (int)':')) {
358             state = s_time;
359             break;
360          }
361          if (lc->str_len == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') &&
362              is_an_integer(lc->str+1)) {
363             code = atoi(lc->str+1);
364             if (code < 0 || code > 53) {
365                scan_err0(lc, _("Week number out of range (0-53)"));
366               /* NOT REACHED */
367             }
368             state = s_woy;            /* week of year */
369             break;
370          }
371          /* everything else must be a keyword */
372          for (i=0; keyw[i].name; i++) {
373             if (strcasecmp(lc->str, keyw[i].name) == 0) {
374                state = keyw[i].state;
375                code   = keyw[i].code;
376                i = 0;
377                break;
378             }
379          }
380          if (i != 0) {
381             scan_err1(lc, _("Job type field: %s in run record not found"), lc->str);
382             /* NOT REACHED */
383          }
384          break;
385       case T_COMMA:
386          continue;
387       default:
388          scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str);
389          /* NOT REACHED */
390          break;
391       }
392       switch (state) {
393       case s_none:
394          continue;
395       case s_mday:                 /* day of month */
396          if (!have_mday) {
397             clear_bits(0, 30, lrun.mday);
398             have_mday = true;
399          }
400          set_bit(code, lrun.mday);
401          break;
402       case s_month:                /* month of year */
403          if (!have_month) {
404             clear_bits(0, 11, lrun.month);
405             have_month = true;
406          }
407          set_bit(code, lrun.month);
408          break;
409       case s_wday:                 /* week day */
410          if (!have_wday) {
411             clear_bits(0, 6, lrun.wday);
412             have_wday = true;
413          }
414          set_bit(code, lrun.wday);
415          break;
416       case s_wom:                  /* Week of month 1st, ... */
417          if (!have_wom) {
418             clear_bits(0, 4, lrun.wom);
419             have_wom = true;
420          }
421          set_bit(code, lrun.wom);
422          break;
423       case s_woy:
424          if (!have_woy) {
425             clear_bits(0, 53, lrun.woy);
426             have_woy = true;
427          }
428          set_bit(code, lrun.woy);
429          break;
430       case s_time:                 /* time */
431          if (!have_at) {
432             scan_err0(lc, _("Time must be preceded by keyword AT."));
433             /* NOT REACHED */
434          }
435          if (!have_hour) {
436             clear_bits(0, 23, lrun.hour);
437          }
438 //       Dmsg1(000, "s_time=%s\n", lc->str);
439          p = strchr(lc->str, ':');
440          if (!p)  {
441             scan_err0(lc, _("Time logic error.\n"));
442             /* NOT REACHED */
443          }
444          *p++ = 0;                 /* separate two halves */
445          code = atoi(lc->str);     /* pick up hour */
446          code2 = atoi(p);          /* pick up minutes */
447          len = strlen(p);
448          if (len >= 2) {
449             p += 2;
450          }
451          if (strcasecmp(p, "pm") == 0) {
452             pm = true;
453          } else if (strcasecmp(p, "am") == 0) {
454             am = true;
455          } else if (len != 2) {
456             scan_err0(lc, _("Bad time specification."));
457             /* NOT REACHED */
458          }   
459          /* 
460           * Note, according to NIST, 12am and 12pm are ambiguous and
461           *  can be defined to anything.  However, 12:01am is the same
462           *  as 00:01 and 12:01pm is the same as 12:01, so we define 
463           *  12am as 00:00 and 12pm as 12:00.
464           */
465          if (pm) {
466             /* Convert to 24 hour time */
467             if (code != 12) {
468                code += 12;
469             }
470          /* am */
471          } else if (am && code == 12) {
472             code -= 12;
473          }
474          if (code < 0 || code > 23 || code2 < 0 || code2 > 59) {
475             scan_err0(lc, _("Bad time specification."));
476             /* NOT REACHED */
477          }
478 //       Dmsg2(000, "hour=%d min=%d\n", code, code2);
479          set_bit(code, lrun.hour);
480          lrun.minute = code2;
481          have_hour = true;
482          break;
483       case s_at:
484          have_at = true;
485          break;
486       case s_range:
487          p = strchr(lc->str, '-');
488          if (!p) {
489             scan_err0(lc, _("Range logic error.\n"));
490          }
491          *p++ = 0;                 /* separate two halves */
492
493          /* Check for day range */
494          if (is_an_integer(lc->str) && is_an_integer(p)) {
495             code = atoi(lc->str) - 1;
496             code2 = atoi(p) - 1;
497             if (code < 0 || code > 30 || code2 < 0 || code2 > 30) {
498                scan_err0(lc, _("Bad day range specification."));
499             }
500             if (!have_mday) {
501                clear_bits(0, 30, lrun.mday);
502                have_mday = true;
503             }
504             if (code < code2) {
505                set_bits(code, code2, lrun.mday);
506             } else {
507                set_bits(code, 30, lrun.mday);
508                set_bits(0, code2, lrun.mday);
509             }
510             break;
511          }
512          /* Check for week of year range */
513          if (strlen(lc->str) == 3 && strlen(p) == 3 &&
514              (lc->str[0] == 'w' || lc->str[0] == 'W') &&
515              (p[0] == 'w' || p[0] == 'W') &&
516              is_an_integer(lc->str+1) && is_an_integer(p+1)) {
517             code = atoi(lc->str+1);
518             code2 = atoi(p+1);
519             if (code < 0 || code > 53 || code2 < 0 || code2 > 53) {
520                scan_err0(lc, _("Week number out of range (0-53)"));
521             }
522             if (!have_woy) {
523                clear_bits(0, 53, lrun.woy);
524                have_woy = true;
525             }
526             if (code < code2) {
527                set_bits(code, code2, lrun.woy);
528             } else {
529                set_bits(code, 53, lrun.woy);
530                set_bits(0, code2, lrun.woy);
531             }
532             break;
533          }
534          /* lookup first half of keyword range (week days or months) */
535          lcase(lc->str);
536          for (i=0; keyw[i].name; i++) {
537             if (strcmp(lc->str, keyw[i].name) == 0) {
538                state = keyw[i].state;
539                code   = keyw[i].code;
540                i = 0;
541                break;
542             }
543          }
544          if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) {
545             scan_err0(lc, _("Invalid month, week or position day range"));
546             /* NOT REACHED */
547          }
548
549          /* Lookup end of range */
550          lcase(p);
551          for (i=0; keyw[i].name; i++) {
552             if (strcmp(p, keyw[i].name) == 0) {
553                state2  = keyw[i].state;
554                code2   = keyw[i].code;
555                i = 0;
556                break;
557             }
558          }
559          if (i != 0 || state != state2 || code == code2) {
560             scan_err0(lc, _("Invalid month, weekday or position range"));
561             /* NOT REACHED */
562          }
563          if (state == s_wday) {
564             if (!have_wday) {
565                clear_bits(0, 6, lrun.wday);
566                have_wday = true;
567             }
568             if (code < code2) {
569                set_bits(code, code2, lrun.wday);
570             } else {
571                set_bits(code, 6, lrun.wday);
572                set_bits(0, code2, lrun.wday);
573             }
574          } else if (state == s_month) {
575             if (!have_month) {
576                clear_bits(0, 11, lrun.month);
577                have_month = true;
578             }
579             if (code < code2) {
580                set_bits(code, code2, lrun.month);
581             } else {
582                /* this is a bit odd, but we accept it anyway */
583                set_bits(code, 11, lrun.month);
584                set_bits(0, code2, lrun.month);
585             }
586          } else {
587             /* Must be position */
588             if (!have_wom) {
589                clear_bits(0, 4, lrun.wom);
590                have_wom = true;
591             }
592             if (code < code2) {
593                set_bits(code, code2, lrun.wom);
594             } else {
595                set_bits(code, 4, lrun.wom);
596                set_bits(0, code2, lrun.wom);
597             }
598          }
599          break;
600       case s_hourly:
601          have_hour = true;
602          set_bits(0, 23, lrun.hour);
603          break;
604       case s_weekly:
605          have_mday = have_wom = have_woy = true;
606          set_bits(0, 30, lrun.mday);
607          set_bits(0, 4,  lrun.wom);
608          set_bits(0, 53, lrun.woy);
609          break;
610       case s_daily:
611          have_mday = true;
612          set_bits(0, 6, lrun.wday);
613          break;
614       case s_monthly:
615          have_month = true;
616          set_bits(0, 11, lrun.month);
617          break;
618       default:
619          scan_err0(lc, _("Unexpected run state\n"));
620          /* NOT REACHED */
621          break;
622       }
623    }
624
625    /* Allocate run record, copy new stuff into it,
626     * and link it into the list of run records
627     * in the schedule resource.
628     */
629    if (pass == 2) {
630       trun = (RUN *)malloc(sizeof(RUN));
631       memcpy(trun, &lrun, sizeof(RUN));
632       if (*run) {
633          trun->next = *run;
634       }
635       *run = trun;
636    }
637
638    lc->options = options;             /* restore scanner options */
639    set_bit(index, res_all.res_sch.hdr.item_present);
640 }