]> granicus.if.org Git - fcron/blob - fileconf.c
Close pipe in job.c even if there is an error reading it, to avoid a fd leak (thanks...
[fcron] / fileconf.c
1 /*
2  * FCRON - periodic command scheduler 
3  *
4  *  Copyright 2000-2014 Thibault Godouet <fcron@free.fr>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  * 
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  * 
20  *  The GNU General Public License can also be found in the file
21  *  `LICENSE' that comes with the fcron source distribution.
22  */
23
24
25 #include "fcrontab.h"
26
27 #include "fileconf.h"
28
29 char *get_string(char *ptr);
30 int get_line(char *str, size_t size, FILE * file);
31 void init_default_line(cl_t * cl, cf_t * cf);
32 char *get_time(char *ptr, time_t * time, int zero_allowed);
33 char *get_num(char *ptr, int *num, int max, short int decimal,
34               const char **names);
35 char *get_nice(char *ptr, int *nice);
36 char *get_bool(char *ptr, int *i);
37 char *read_field(char *ptr, bitstr_t * ary, int max, const char **names);
38 void read_freq(char *ptr, cf_t * cf);
39 void read_arys(char *ptr, cf_t * cf);
40 void read_period(char *ptr, cf_t * cf);
41 int read_shortcut(char *ptr, cf_t * cf);
42 void read_env(char *ptr, cf_t * cf);
43 char *read_opt(char *ptr, cl_t * cl);
44 char *check_username(char *ptr, cf_t * cf, cl_t * cl);
45 size_t option_strlen(char *value);
46
47 char need_correction;
48 cl_t default_line;              /* default options for a line */
49 char *file_name;
50 int line;
51
52 /* warning : all names must have the same length */
53 const char *dows_ary[] = {
54     "sun", "mon", "tue", "wed", "thu", "fri", "sat",
55     NULL
56 };
57
58 /* warning : all names must have the same length */
59 const char *mons_ary[] = {
60     "jan", "feb", "mar", "apr", "may", "jun",
61     "jul", "aug", "sep", "oct", "nov", "dec",
62     NULL
63 };
64
65
66 #define GET_LINE_EOF 999
67
68 char *
69 get_string(char *ptr)
70     /* read string pointed by ptr, remove blanks and manage
71      * string placed in quotes */
72     /* return NULL on mismatched quotes */
73 {
74     char quote = 0;
75     int length = 0;
76     char *rtn_string = NULL;
77
78     if (*ptr == '\"' || *ptr == '\'') {
79         quote = *ptr;
80         ptr++;
81     }
82
83     length = remove_blanks(ptr);
84
85     if (quote != 0) {
86         if (*(ptr + length - 1) == quote)
87             *(ptr + length - 1) = '\0';
88         else {
89             /* mismatched quotes */
90             need_correction = 1;
91             return NULL;
92         }
93     }
94
95     Set(rtn_string, ptr);
96     return rtn_string;
97
98 }
99
100
101 int
102 get_line(char *str, size_t size, FILE * file)
103     /* similar to fgets, but increase line if necessary,
104      * and continue over an "\" followed by an "\n" char */
105 {
106     size_t size_max = size - 1;
107     int i = 0;
108     int c;
109
110     while (i < size_max) {
111
112         switch (c = getc(file)) {
113
114         case '\n':
115             /* check if the \n char is preceded by a "\" char :
116              *  in this case, suppress the "\", don't copy the \n,
117              *  and continue */
118             if (i > 0 && *(str + i - 1) == '\\') {
119                 i--;
120                 line++;
121                 continue;
122             }
123             else {
124                 *(str + i) = (char)'\0';
125                 return OK;
126             }
127             break;
128
129         case EOF:
130             *(str + i) = (char)'\0';
131             /* we couldn't return EOF ( equal to ERR by default )
132              * nor ERR, which is used for another error */
133             return GET_LINE_EOF;
134
135         default:
136             *(str + i) = (char)c;
137             i++;
138
139         }
140
141     }
142
143     /* line is too long : goto next line and return ERR */
144     while (((c = getc(file)) != EOF) && (c != '\n')) ;
145     line++;
146     need_correction = 1;
147     return ERR;
148
149 }
150
151 void
152 init_default_line(cl_t * cl, cf_t * cf)
153 /* clear all context/options from cl */
154 {
155     bzero(cl, sizeof(cl_t));
156     Set(cl->cl_runas, runas);
157     Set(cl->cl_mailto, runas);
158     Free_safe(cl->cl_tz);
159     set_default_opt(cl->cl_option);
160     cl->cl_file = cf;
161 }
162
163
164 int
165 read_file(char *filename, int fd)
166     /* read file "name" and append cf_t list */
167 {
168     cf_t *cf = NULL;
169     FILE *file = NULL;
170     char buf[LINE_LEN];
171     int max_lines;
172     int max_entries = MAXENTRIES;
173     int entries = 0;
174     char *ptr = NULL;
175     int ret;
176
177     bzero(buf, sizeof(buf));
178     need_correction = 0;
179     line = 1;
180     file_name = filename;
181
182     /* open file */
183
184     if ((file = fdopen(fd, "r")) == NULL) {
185         fprintf(stderr, "Could not open \"%s\": %s\n", file_name,
186                 strerror(errno));
187         return ERR;
188     }
189
190     /* Rewind, just in case */
191     rewind(file);
192
193     Alloc(cf, cf_t);
194     cf->cf_env_list = env_list_init();
195     Set(cf->cf_user, user);
196     init_default_line(&default_line, cf);
197
198     if (debug_opt)
199         fprintf(stderr, "FILE %s\n", file_name);
200
201     if (strcmp(runas, ROOTNAME) == 0)
202         max_entries = 65535;
203
204     /* max_lines acts here as a security counter to avoid endless loop. */
205     max_lines = (max_entries * 10) + 10;
206
207     while (entries < max_entries && line <= max_lines) {
208
209         ret = get_line(buf, sizeof(buf), file);
210
211         if (ret == ERR) {
212             fprintf(stderr,
213                     "%s:%d: Line is too long (more than %d): skipping line.\n",
214                     file_name, line, (int)sizeof(buf));
215             continue;
216         }
217
218         ptr = buf;
219         Skip_blanks(ptr);
220
221         if (debug_opt && *ptr != '#' && *ptr != '\0')
222             fprintf(stderr, "      %s\n", buf);
223
224         switch (*ptr) {
225         case '#':
226         case '\0':
227             /* comments or empty line: skipping */
228             break;
229         case '@':
230             /* if it is not a shortcut line then read_shortcut() won't do anything. */
231             if (!read_shortcut(ptr, cf))
232                 read_freq(ptr, cf);
233             entries++;
234             break;
235         case '&':
236             read_arys(ptr, cf);
237             entries++;
238             break;
239         case '%':
240             read_period(ptr, cf);
241             entries++;
242             break;
243         case '!':
244             ptr = read_opt(ptr, &default_line);
245             if (ptr != NULL && *ptr != '\0') {
246                 fprintf(stderr, "%s:%d: Syntax error: string \"%s\" ignored\n",
247                         file_name, line, ptr);
248                 need_correction = 1;
249             }
250             break;
251         default:
252             if (isdigit((int)*ptr) || *ptr == '*') {
253                 read_arys(ptr, cf);
254                 entries++;
255             }
256             else
257                 read_env(ptr, cf);
258         }
259
260         line++;
261
262         if (ret != OK)
263             /* in this case, ret == GET_LINE_EOF :
264              * no more lines, so we exit the loop */
265             break;
266
267     }
268
269     if (entries == max_entries) {
270         error("%s:%d: maximum number of entries (%d) has been reached by %s",
271               file_name, line, user);
272         fprintf(stderr, "Anything after this line will be ignored\n");
273     }
274     else if (line == max_lines)
275         error("%s:%d: maximum number of lines (%d) has been reached by %s",
276               file_name, line, user);
277
278     cf->cf_next = file_base;
279     file_base = cf;
280
281     /* don't close as underlying fd may still be used by calling function */
282     if (fflush(file) != 0)
283         error_e("could not fflush() file_name");
284
285     Free_safe(default_line.cl_runas);
286     Free_safe(default_line.cl_mailto);
287     Free_safe(default_line.cl_tz);
288
289     if (!need_correction)
290         return OK;
291     else
292         return 2;
293
294 }
295
296 void
297 read_env(char *ptr, cf_t * cf)
298     /* append env variable list.
299      * (remove blanks) */
300 {
301     char name[LINE_LEN];
302     int j = 0;
303     char *val = NULL;
304
305     bzero(name, sizeof(name));
306
307     /* copy env variable's name */
308     while ((isalnum((int)*ptr) || *ptr == '_') && *ptr != '='
309            && !isspace((int)*ptr) && j < sizeof(name)) {
310         name[j++] = *ptr;
311         ptr++;
312     }
313     name[j] = '\0';
314
315     if (name[0] == '\0')
316         goto error;
317
318     /* skip '=' and spaces around */
319     while (isspace((int)*ptr))
320         ptr++;
321
322     /* if j == 0 name is a zero length string */
323     if (*ptr++ != '=' || j == 0)
324         goto error;
325
326     while (isspace((int)*ptr))
327         ptr++;
328
329     /* get value */
330     if ((val = get_string(ptr)) == NULL) {
331         fprintf(stderr, "%s:%d: Mismatched  quotes: skipping line.\n",
332                 file_name, line);
333         need_correction = 1;
334         return;
335     }
336
337     if (debug_opt)
338         fprintf(stderr, "  Env : '%s=%s'\n", name, val);
339
340     /* we ignore USER/LOGNAME's assignment */
341     if (strcmp(name, "USER") == 0 || strcmp(name, "LOGNAME") == 0) {
342         fprintf(stderr,
343                 "%s:%d: USER or LOGNAME assignement is not allowed: ignored.\n",
344                 file_name, line);
345         return;
346     }
347
348     /* the MAILTO assignment is, in fact, an fcron option :
349      *  we don't store it in the same way. */
350     /* please note that we check if the mailto is valid in conf.c */
351     if (strcmp(name, "MAILTO") == 0) {
352         if (strcmp(val, "\0") == 0) {
353             clear_mail(default_line.cl_option);
354             clear_mailzerolength(default_line.cl_option);
355         }
356         else {
357             Set(default_line.cl_mailto, val);
358             set_mail(default_line.cl_option);
359         }
360
361     }
362     else {
363         env_list_setenv(cf->cf_env_list, name, val, 1);
364     }
365
366     Free_safe(val);
367
368     return;
369
370  error:
371     fprintf(stderr, "%s:%d: Syntax error: skipping line.\n", file_name, line);
372     need_correction = 1;
373     return;
374
375 }
376
377
378 char *
379 get_nice(char *ptr, int *nice)
380     /* read a nice value and put it in variable nice */
381 {
382     char negative = 0;
383
384     if (*ptr == '-') {
385         negative = 1;
386         ptr++;
387     }
388
389     if ((ptr = get_num(ptr, nice, 20, 0, NULL)) == NULL)
390         return NULL;
391
392     if (negative == 1) {
393         if (getuid() != rootuid) {
394             fprintf(stderr, "must be privileged to use a negative argument "
395                     "with nice: set to 0\n");
396             need_correction = 1;
397             *nice = 0;
398         }
399
400         *nice *= (-1);
401     }
402
403     return ptr;
404
405 }
406
407 size_t
408 option_strlen(char *value)
409 /* return the length of the string value of an option */
410 {
411     char *ptr = value;
412     size_t len = 0;
413
414     /* look for the end of the option value */
415     while (ptr != NULL && *ptr != ')' && *ptr != '\0') {
416         ptr++;
417         len++;
418     }
419
420     return len;
421 }
422
423 int
424 assign_option_string(char **var, char *value)
425 /* Read the value of an option: if non-empty, assign it to the var,
426  * otherwise set to NULL.
427  * Returns the length of the value (0 if empty), or -1 on error. */
428 {
429     char start = value[0];
430     char end = '\0';
431     size_t len = 0;
432
433     Free_safe(*var);
434
435     len = option_strlen(value);
436
437     if (len <= 0) {
438         return len;
439     }
440
441     end = value[len - 1];
442
443     /* spaces and quotes are not allowed before or after the value */
444     if (isspace((int)start) || isspace((int)end)
445         || start == '\'' || start == '"' || end == '\'' || end == '"') {
446         return -1;
447     }
448
449     *var = strndup2(value, len);
450
451     return len;
452 }
453
454
455 char *
456 get_bool(char *ptr, int *i)
457     /* get a bool value : either true (1) or false (0)
458      * return NULL on error */
459 {
460     if (*ptr == '1')
461         goto true;
462     else if (*ptr == '0')
463         goto false;
464     else if (strncmp(ptr, "true", 4) == 0) {
465         ptr += 3;
466         goto true;
467     }
468     else if (strncmp(ptr, "yes", 3) == 0) {
469         ptr += 2;
470         goto true;
471     }
472     else if (strncmp(ptr, "false", 5) == 0) {
473         ptr += 4;
474         goto false;
475     }
476     else if (strncmp(ptr, "no", 2) == 0) {
477         ptr += 1;
478         goto false;
479     }
480     else
481         return NULL;
482
483  true:
484     *i = 1;
485     ptr++;
486     return ptr;
487
488  false:
489     *i = 0;
490     ptr++;
491     return ptr;
492
493 }
494
495
496 char *
497 read_opt(char *ptr, cl_t * cl)
498     /* read one or several options and fill in the field "option" */
499 {
500     char opt_name[20];
501     int i;
502     char in_brackets;
503
504 #define Handle_err \
505     { \
506         fprintf(stderr, "%s:%d: Argument(s) for option \"%s\" not valid: " \
507                 "skipping end of line.\n", file_name, line, opt_name); \
508         need_correction = 1; \
509         return NULL; \
510     }
511
512     if (*ptr == '!')
513         ptr++;
514
515     do {
516         i = 0;
517         bzero(opt_name, sizeof(opt_name));
518
519         while (isalnum((int)*ptr) && i < sizeof(opt_name))
520             opt_name[i++] = *ptr++;
521
522         i = 1;
523         in_brackets = 0;
524
525         if (*ptr == '(') {
526             in_brackets = 1;
527             ptr++;
528
529             /* spaces are not allowed -- make sure there is no leading space. */
530             if (isspace((int)*ptr)) {
531                 Handle_err;
532             }
533         }
534
535         /* global options for a file */
536
537         if (strcmp(opt_name, "tzdiff") == 0) {
538             char negative = 0;
539
540             if (!in_brackets)
541                 Handle_err;
542             if (*ptr == '-') {
543                 negative = 1;
544                 ptr++;
545             }
546             if ((ptr = get_num(ptr, &i, 24, 0, NULL)) == NULL)
547                 Handle_err;
548             if (negative)
549                 cl->cl_file->cf_tzdiff = (-i);
550             else
551                 cl->cl_file->cf_tzdiff = i;
552
553             if (debug_opt)
554                 fprintf(stderr, "  Opt : \"%s\" (-)%d\n", opt_name, i);
555         }
556
557         /* options related to a line (or a set of lines) */
558
559         else if (strcmp(opt_name, "timezone") == 0) {
560             int len = -1;
561
562             if (!in_brackets) {
563                 Handle_err;
564             }
565
566             /* assign_option_string() will set cl_tz to NULL is the value is empty,
567              * which means "use the system timezone" */
568             len = assign_option_string(&(cl->cl_tz), ptr);
569             if (len < 0) {
570                 Handle_err;
571             }
572             else {
573                 ptr += len;
574             }
575
576             if (debug_opt) {
577                 fprintf(stderr, "  Opt : \"%s\" \"%s\"\n", opt_name, cl->cl_tz);
578             }
579         }
580
581
582         else if (strcmp(opt_name, "s") == 0 || strcmp(opt_name, "serial") == 0) {
583             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
584                 Handle_err;
585             if (i == 0)
586                 clear_serial(cl->cl_option);
587             else
588                 set_serial(cl->cl_option);
589             if (debug_opt)
590                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
591         }
592
593         else if (strcmp(opt_name, "serialonce") == 0) {
594             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
595                 Handle_err;
596             if (i == 0)
597                 set_serial_sev(cl->cl_option);
598             else
599                 clear_serial_sev(cl->cl_option);
600             if (debug_opt)
601                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
602         }
603
604         else if (strcmp(opt_name, "lavgonce") == 0) {
605             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
606                 Handle_err;
607             if (i == 0)
608                 set_lavg_sev(cl->cl_option);
609             else
610                 clear_lavg_sev(cl->cl_option);
611             if (debug_opt)
612                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
613         }
614
615         else if (strcmp(opt_name, "exesev") == 0) {
616             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
617                 Handle_err;
618             if (i == 0)
619                 clear_exe_sev(cl->cl_option);
620             else
621                 set_exe_sev(cl->cl_option);
622             if (debug_opt)
623                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
624         }
625
626         else if (strcmp(opt_name, "b") == 0 || strcmp(opt_name, "bootrun") == 0) {
627             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
628                 Handle_err;
629             if (i == 0)
630                 clear_bootrun(cl->cl_option);
631             else
632                 set_bootrun(cl->cl_option);
633             if (debug_opt)
634                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
635         }
636
637         else if (strcmp(opt_name, "rebootreset") == 0) {
638             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
639                 Handle_err;
640             if (i == 0)
641                 clear_rebootreset(cl->cl_option);
642             else
643                 set_rebootreset(cl->cl_option);
644             if (debug_opt)
645                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
646         }
647
648
649         else if (strcmp(opt_name, "runatreboot") == 0) {
650             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
651                 Handle_err;
652             if (i == 0)
653                 clear_runatreboot(cl->cl_option);
654             else
655                 set_runatreboot(cl->cl_option);
656             if (debug_opt)
657                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
658         }
659
660
661         else if (strcmp(opt_name, "runonce") == 0) {
662             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
663                 Handle_err;
664             if (i == 0)
665                 clear_runonce(cl->cl_option);
666             else
667                 set_runonce(cl->cl_option);
668             if (debug_opt)
669                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
670         }
671
672         else if (strcmp(opt_name, "reset") == 0) {
673             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
674                 Handle_err;
675             if (i == 1) {
676                 init_default_line(cl, cl->cl_file);
677             }
678             if (debug_opt)
679                 fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
680         }
681
682         else if (strcmp(opt_name, "f") == 0 || strcmp(opt_name, "first") == 0) {
683             if (!in_brackets
684                 || (ptr = get_time(ptr, &(cl->cl_first), 1)) == NULL)
685                 Handle_err;
686             if (debug_opt)
687                 fprintf(stderr, "  Opt : \"%s\" %ld\n", opt_name,
688                         (long int)cl->cl_first);
689         }
690
691         else if (strcmp(opt_name, "r") == 0 || strcmp(opt_name, "runfreq") == 0) {
692             if (cl->cl_runfreq == 1) {
693                 fprintf(stderr, "cannot change runfreq value in a %%-line");
694                 Handle_err;
695             }
696             if (!in_brackets
697                 || (ptr = get_num(ptr, &i, USHRT_MAX, 0, NULL)) == NULL)
698                 Handle_err;
699             if (i <= 1) {
700                 fprintf(stderr, "runfreq must be 2 or more.\n");
701                 Handle_err;
702             }
703             cl->cl_runfreq = i;
704             if (debug_opt)
705                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
706         }
707
708         else if (strcmp(opt_name, "strict") == 0) {
709             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
710                 Handle_err;
711             if (i == 0)
712                 clear_strict(cl->cl_option);
713             else
714                 set_strict(cl->cl_option);
715             if (debug_opt)
716                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
717         }
718
719         else if (strcmp(opt_name, "noticenotrun") == 0) {
720             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
721                 Handle_err;
722             if (i == 0)
723                 clear_notice_notrun(cl->cl_option);
724             else
725                 set_notice_notrun(cl->cl_option);
726             if (debug_opt)
727                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
728         }
729
730         else if (strcmp(opt_name, "lavg") == 0) {
731             if (!in_brackets
732                 || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL)
733                 Handle_err;
734             cl->cl_lavg[0] = i;
735             if (debug_opt)
736                 fprintf(stderr, "  Opt : 'lavg1' %d\n", i);
737             if (*ptr++ != ',')
738                 Handle_err;
739             if (!in_brackets
740                 || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL)
741                 Handle_err;
742             cl->cl_lavg[1] = i;
743             if (debug_opt)
744                 fprintf(stderr, "  Opt : 'lavg5' %d\n", i);
745             if (*ptr++ != ',')
746                 Handle_err;
747             if (!in_brackets
748                 || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL)
749                 Handle_err;
750             cl->cl_lavg[2] = i;
751             if (debug_opt)
752                 fprintf(stderr, "  Opt : 'lavg15' %d\n", i);
753             if (cl->cl_lavg[0] || cl->cl_lavg[1] || cl->cl_lavg[2])
754                 set_lavg(cl->cl_option);
755             else
756                 clear_lavg(cl->cl_option);
757 #ifdef NOLOADAVG
758             warn("As fcron has been compiled with no procfs support,\n"
759                  "you will not be able to use the lavg* options");
760 #endif                          /* NOLOADAVG */
761         }
762
763         else if (strcmp(opt_name, "lavg1") == 0) {
764             if (!in_brackets
765                 || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL)
766                 Handle_err;
767             cl->cl_lavg[0] = i;
768             if (cl->cl_lavg[0] || cl->cl_lavg[1] || cl->cl_lavg[2])
769                 set_lavg(cl->cl_option);
770             else
771                 clear_lavg(cl->cl_option);
772 #if NOLOADAVG
773             warn("As fcron has been compiled with no procfs support,\n"
774                  "you will not be able to use the lavg* options");
775 #endif                          /* NOLOADAVG */
776             if (debug_opt)
777                 fprintf(stderr, "  Opt : 'lavg1' %d\n", i);
778         }
779
780         else if (strcmp(opt_name, "lavg5") == 0) {
781             if (!in_brackets
782                 || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL)
783                 Handle_err;
784             cl->cl_lavg[1] = i;
785             if (cl->cl_lavg[0] || cl->cl_lavg[1] || cl->cl_lavg[2])
786                 set_lavg(cl->cl_option);
787             else
788                 clear_lavg(cl->cl_option);
789 #ifdef NOLOADAVG
790             warn("As fcron has been compiled with no procfs support,\n"
791                  "you will not be able to use the lavg* options");
792 #endif                          /* NOLOADAVG = 0 */
793             if (debug_opt)
794                 fprintf(stderr, "  Opt : 'lavg5' %d\n", i);
795         }
796
797         else if (strcmp(opt_name, "lavg15") == 0) {
798             if (!in_brackets
799                 || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL)
800                 Handle_err;
801             cl->cl_lavg[2] = i;
802             if (cl->cl_lavg[0] || cl->cl_lavg[1] || cl->cl_lavg[2])
803                 set_lavg(cl->cl_option);
804             else
805                 clear_lavg(cl->cl_option);
806 #ifdef NOLOADAVG
807             warn("As fcron has been compiled with no procfs support,\n"
808                  "you will not be able to use the lavg* options");
809 #endif                          /* NOLOADAVG = 0 */
810             if (debug_opt)
811                 fprintf(stderr, "  Opt : 'lavg15' %d\n", i);
812         }
813
814         else if (strcmp(opt_name, "lavgand") == 0) {
815             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
816                 Handle_err;
817             if (i == 0)
818                 set_lor(cl->cl_option);
819             else
820                 set_land(cl->cl_option);
821             if (debug_opt)
822                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
823         }
824
825         else if (strcmp(opt_name, "lavgor") == 0) {
826             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
827                 Handle_err;
828             if (i == 0)
829                 set_land(cl->cl_option);
830             else
831                 set_lor(cl->cl_option);
832             if (debug_opt)
833                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
834         }
835
836         else if (strcmp(opt_name, "u") == 0 || strcmp(opt_name, "until") == 0) {
837             if (!in_brackets
838                 || (ptr = get_time(ptr, &(cl->cl_until), 0)) == NULL)
839                 Handle_err;
840             if (debug_opt)
841                 fprintf(stderr, "  Opt : \"%s\" %ld\n", opt_name,
842                         (long int)cl->cl_until);
843         }
844
845         else if (strcmp(opt_name, "m") == 0 || strcmp(opt_name, "mail") == 0) {
846             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
847                 Handle_err;
848             if (i == 0) {
849                 clear_mail(cl->cl_option);
850                 clear_mailzerolength(cl->cl_option);
851             }
852             else
853                 set_mail(cl->cl_option);
854             if (debug_opt)
855                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
856         }
857
858         else if (strcmp(opt_name, "forcemail") == 0) {
859             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
860                 Handle_err;
861             if (i == 0)
862                 clear_mailzerolength(cl->cl_option);
863             else {
864                 set_mailzerolength(cl->cl_option);
865                 set_mail(cl->cl_option);
866             }
867             if (debug_opt)
868                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
869         }
870
871         else if (strcmp(opt_name, "mailto") == 0) {
872             int len = -1;
873
874             if (!in_brackets) {
875                 Handle_err;
876             }
877
878             /* assign_option_string() set the value to NULL if the length is zero.
879              * However cl_mailto must not be NULL (as expected in
880              * conf.c:add_line_to_file()), so we check if the length is >= 0
881              * before calling assign_option_string() */
882             /* Also please note that we check if the mailto is valid in conf.c */
883             len = option_strlen(ptr);
884             if (len <= 0) {
885                 clear_mail(cl->cl_option);
886                 clear_mailzerolength(cl->cl_option);
887                 if (debug_opt) {
888                     fprintf(stderr, "  Opt : \"mail\" 0\n");
889                     fprintf(stderr, "  Opt : \"forcemail\" 0\n");
890                 }
891             }
892             else {
893                 len = assign_option_string(&(cl->cl_mailto), ptr);
894                 if (len < 0) {
895                     Handle_err;
896                 }
897                 else {
898                     ptr += len;
899                     set_mail(cl->cl_option);
900                 }
901             }
902
903             if (debug_opt)
904                 fprintf(stderr, "  Opt : \"%s\" \"%s\"\n", opt_name,
905                         cl->cl_mailto);
906         }
907
908         else if (strcmp(opt_name, "erroronlymail") == 0) {
909             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
910                 Handle_err;
911             if (i == 0)
912                 clear_erroronlymail(cl->cl_option);
913             else
914                 set_erroronlymail(cl->cl_option);
915             if (debug_opt)
916                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
917         }
918
919         else if (strcmp(opt_name, "dayand") == 0) {
920             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
921                 Handle_err;
922             if (i == 0)
923                 set_dayor(cl->cl_option);
924             else
925                 set_dayand(cl->cl_option);
926             if (debug_opt)
927                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
928         }
929
930         else if (strcmp(opt_name, "dayor") == 0) {
931             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
932                 Handle_err;
933             if (i == 0)
934                 set_dayand(cl->cl_option);
935             else
936                 set_dayor(cl->cl_option);
937             if (debug_opt)
938                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
939         }
940
941         else if (strcmp(opt_name, "nolog") == 0) {
942             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
943                 Handle_err;
944             if (i == 0)
945                 clear_nolog(cl->cl_option);
946             else
947                 set_nolog(cl->cl_option);
948             if (debug_opt)
949                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
950         }
951
952         else if (strcmp(opt_name, "volatile") == 0) {
953             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
954                 Handle_err;
955             if (i == 0)
956                 clear_volatile(cl->cl_option);
957             else
958                 set_volatile(cl->cl_option);
959             if (debug_opt)
960                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
961         }
962
963         else if (strcmp(opt_name, "stdout") == 0) {
964             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
965                 Handle_err;
966             if (i == 0)
967                 clear_stdout(cl->cl_option);
968             else
969                 set_stdout(cl->cl_option);
970             if (debug_opt)
971                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
972         }
973
974         else if (strcmp(opt_name, "n") == 0 || strcmp(opt_name, "nice") == 0) {
975             if (!in_brackets || (ptr = get_nice(ptr, &i)) == NULL)
976                 Handle_err;
977             cl->cl_nice = (char)i;
978             if (debug_opt)
979                 fprintf(stderr, "  Opt : \"%s\" (-)%d\n", opt_name, i);
980         }
981
982         else if (strcmp(opt_name, "runas") == 0) {
983             int len = -1;
984             char *runas = NULL;
985             struct passwd *pas;
986
987             if (!in_brackets) {
988                 Handle_err;
989             }
990
991             len = assign_option_string(&runas, ptr);
992             if (len <= 0) {
993                 Handle_err;
994             }
995             else {
996                 ptr += len;
997             }
998
999             if (getuid() != rootuid) {
1000                 fprintf(stderr, "must be privileged to use option runas: "
1001                         "skipping option\n");
1002                 need_correction = 1;
1003             }
1004             else if (len > USER_NAME_LEN) {
1005                 fprintf(stderr, "runas: user name \"%s\" longer than %d"
1006                         "characters: skipping option\n", runas, USER_NAME_LEN);
1007                 need_correction = 1;
1008             }
1009             else if ((pas = getpwnam(runas)) == NULL) {
1010                 fprintf(stderr, "runas: \"%s\" is not in passwd file : "
1011                         "ignored", runas);
1012                 need_correction = 1;
1013             }
1014
1015             if (need_correction) {
1016                 Free_safe(runas);
1017             }
1018             else {
1019                 /* only set cl_runas if all is well, as cl_runas MUST always
1020                  * be set to a valid value */
1021                 Set(cl->cl_runas, runas);
1022             }
1023
1024             /* all good */
1025             if (debug_opt)
1026                 fprintf(stderr, "  Opt : \"%s\" %s ptr=%p\n", opt_name,
1027                         cl->cl_runas, cl->cl_runas);
1028
1029         }
1030
1031         else if (strcmp(opt_name, "random") == 0) {
1032             if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
1033                 Handle_err;
1034             if (i == 0)
1035                 clear_random(cl->cl_option);
1036             else
1037                 set_random(cl->cl_option);
1038             if (debug_opt)
1039                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
1040         }
1041
1042         else if (strcmp(opt_name, "jitter") == 0) {
1043             if (!in_brackets
1044                 || (ptr = get_num(ptr, &i, UCHAR_MAX, 0, NULL)) == NULL)
1045                 Handle_err;
1046             cl->cl_jitter = i;
1047             if (debug_opt)
1048                 fprintf(stderr, "  Opt : \"%s\" %d\n", opt_name, i);
1049         }
1050
1051         /* handle %-line : we check if we are really in a %-line (which we do not do
1052          * for other options), because writing "&hourly" in a fcrontab results in an
1053          * error (hourly ignored) hard to find, and, in any case, annoying. */
1054         else if (cl->cl_runfreq == 1) {
1055             /* options to run once per interval :
1056              * ignore every fields below the limit */
1057             if (strcmp(opt_name, "mins") == 0) {
1058                 /* nothing to do */
1059                 if (debug_opt)
1060                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1061             }
1062             else if (strcmp(opt_name, "hours") == 0) {
1063                 set_freq_mins(cl->cl_option);
1064                 if (debug_opt)
1065                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1066             }
1067             else if (strcmp(opt_name, "days") == 0) {
1068                 set_freq_mins(cl->cl_option);
1069                 set_freq_hrs(cl->cl_option);
1070                 if (debug_opt)
1071                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1072             }
1073             else if (strcmp(opt_name, "mons") == 0) {
1074                 set_freq_mins(cl->cl_option);
1075                 set_freq_hrs(cl->cl_option);
1076                 set_freq_days(cl->cl_option);
1077                 if (debug_opt)
1078                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1079             }
1080             else if (strcmp(opt_name, "dow") == 0) {
1081                 set_freq_mins(cl->cl_option);
1082                 set_freq_hrs(cl->cl_option);
1083                 set_freq_days(cl->cl_option);
1084                 set_freq_mons(cl->cl_option);
1085                 if (debug_opt)
1086                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1087             }
1088
1089             /* run once an element of the selected field
1090              * (once an hour, once a day, etc) */
1091             else if (strcmp(opt_name, "hourly") == 0) {
1092                 set_freq_hrs(cl->cl_option);
1093                 set_freq_periodically(cl->cl_option);
1094                 if (debug_opt)
1095                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1096             }
1097             else if (strcmp(opt_name, "daily") == 0) {
1098                 set_freq_days(cl->cl_option);
1099                 set_freq_periodically(cl->cl_option);
1100                 if (debug_opt)
1101                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1102             }
1103             else if (strcmp(opt_name, "monthly") == 0) {
1104                 set_freq_mons(cl->cl_option);
1105                 set_freq_periodically(cl->cl_option);
1106                 if (debug_opt)
1107                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1108             }
1109             else if (strcmp(opt_name, "weekly") == 0) {
1110                 set_freq_dow(cl->cl_option);
1111                 set_freq_periodically(cl->cl_option);
1112                 if (debug_opt)
1113                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1114             }
1115
1116             /* run once an element of the selected field
1117              * from middle to middle of that field
1118              * (ie once from 12h to 12h the following day) */
1119             else if (strcmp(opt_name, "midhourly") == 0) {
1120                 set_freq_hrs(cl->cl_option);
1121                 set_freq_periodically(cl->cl_option);
1122                 set_freq_mid(cl->cl_option);
1123                 if (debug_opt)
1124                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1125             }
1126             else if (strcmp(opt_name, "middaily") == 0
1127                      || strcmp(opt_name, "nightly") == 0) {
1128                 set_freq_days(cl->cl_option);
1129                 set_freq_periodically(cl->cl_option);
1130                 set_freq_mid(cl->cl_option);
1131                 if (debug_opt)
1132                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1133             }
1134             else if (strcmp(opt_name, "midmonthly") == 0) {
1135                 set_freq_mons(cl->cl_option);
1136                 set_freq_periodically(cl->cl_option);
1137                 set_freq_mid(cl->cl_option);
1138                 if (debug_opt)
1139                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1140             }
1141             else if (strcmp(opt_name, "midweekly") == 0) {
1142                 set_freq_dow(cl->cl_option);
1143                 set_freq_periodically(cl->cl_option);
1144                 set_freq_mid(cl->cl_option);
1145                 if (debug_opt)
1146                     fprintf(stderr, "  Opt : \"%s\"\n", opt_name);
1147             }
1148         }
1149
1150         else {
1151             fprintf(stderr, "%s:%d: Option \"%s\" unknown: "
1152                     "skipping option.\n", file_name, line, opt_name);
1153             need_correction = 1;
1154         }
1155
1156         if (in_brackets) {
1157             if (*ptr != ')') {
1158             Handle_err}
1159             else
1160                 ptr++;
1161         }
1162
1163     } while (*ptr == ',' && ptr++);
1164
1165     Skip_blanks(ptr);
1166     return ptr;
1167 }
1168
1169
1170 char *
1171 get_time(char *ptr, time_t * time, int zero_allowed)
1172     /* convert time read in string in time_t format */
1173 {
1174     time_t sum;
1175
1176     *time = 0;
1177
1178     while ((*ptr != ' ') && (*ptr != '\t') && (*ptr != '\0') && (*ptr != ')')) {
1179
1180         sum = 0;
1181
1182         while (isdigit((int)*ptr)) {
1183             sum *= 10;
1184             sum += *ptr - 48;
1185             ptr++;
1186         }
1187
1188         /* interpret multipliers */
1189         switch (*ptr) {
1190         case 'm':              /* months */
1191             sum *= 4;
1192         case 'w':              /* weeks */
1193             sum *= 7;
1194         case 'd':              /* days */
1195             sum *= 24;
1196         case 'h':              /* hours */
1197             sum *= 3600;
1198         case 's':              /* seconds */
1199             ptr++;
1200             break;
1201         case ' ':
1202         case '\t':
1203         case ')':
1204             sum *= 60;          /* minutes */
1205             break;
1206         default:
1207             need_correction = 1;
1208             return NULL;
1209         }
1210
1211         *time += sum;
1212
1213     }
1214
1215     Skip_blanks(ptr);
1216     if (*time == 0 && !zero_allowed) {
1217         need_correction = 1;
1218         return NULL;
1219     }
1220     else
1221         return ptr;
1222 }
1223
1224
1225 char *
1226 check_username(char *ptr, cf_t * cf, cl_t * cl)
1227     /* check ptr to see if the first word is a username, returns new ptr */
1228 {
1229     short int indx = 0;
1230     char username[USER_NAME_LEN];
1231     struct passwd *userpwent;
1232
1233     /* check to see if next word is a username */
1234     /* we don't allow quotes, to be able to distinguish a user name from
1235      * a command line (where quotes are allowed) */
1236     while (isalnum((int)ptr[indx]) || ptr[indx] == '-' || ptr[indx] == '_')
1237         indx++;
1238     if (indx >= USER_NAME_LEN)
1239         indx = USER_NAME_LEN - 1;
1240     strncpy(username, ptr, indx);
1241     username[indx] = '\0';
1242
1243     if ((userpwent = getpwnam(username)) != NULL) {
1244         /* found the user */
1245         ptr = ptr + indx;       /* move ptr to the next word */
1246         Skip_blanks(ptr);
1247
1248         if (getuid() != rootuid) {
1249             fprintf(stderr, "must be privileged to run as another user : "
1250                     "ignoring\n");
1251         }
1252         else {
1253             Set(cl->cl_runas, username);
1254             if (debug_opt)
1255                 fprintf(stderr, "  Opt : inline_runas %s\n", username);
1256         }
1257     }
1258
1259     return ptr;
1260 }
1261
1262
1263 char *
1264 read_word(char *ptr, char *buf, size_t buf_size)
1265 /* copy a word to buf with a max_size of buf_size and return a pointer
1266  * to the next char after the word */
1267 {
1268     int i = 0;
1269
1270     bzero(buf, buf_size);
1271
1272     while (isalnum((int)*ptr) && i < buf_size)
1273         buf[i++] = *ptr++;
1274
1275     return ptr;
1276 }
1277
1278 int
1279 read_shortcut(char *ptr, cf_t * cf)
1280 /* try to read a shortcut entry, and if it is one then append a line to cf
1281  * Return 1 if that was a shortcut entry. If it wasn't, return 0 and make sure
1282  * ptr is back to its orig value. */
1283 {
1284     cl_t *cl = NULL;
1285     char shortcut[20];
1286     char *ptr_orig = ptr;
1287
1288     cl = dups_cl(&default_line);
1289
1290     /* skip the @ */
1291     ptr++;
1292
1293     ptr = read_word(ptr, shortcut, sizeof(shortcut));
1294
1295     /* time&date by default -- we'll switch to freq if @reboot */
1296     set_td(cl->cl_option);
1297     cl->cl_remain = cl->cl_runfreq;     /* FIXME: necessary?? */
1298
1299     if (strcmp(shortcut, "reboot") == 0) {
1300         set_freq(cl->cl_option);
1301         set_runatreboot(cl->cl_option);
1302         set_runonce(cl->cl_option);
1303         clear_volatile(cl->cl_option);
1304         cl->cl_runfreq = 0;
1305         cl->cl_first = 0;
1306         /* the job will not be rescheduled after the first execution (flag is_hasrun),
1307          * we set timefreq to LONG_MAX just in case */
1308         cl->cl_timefreq = LONG_MAX;
1309
1310         if (debug_opt)
1311             fprintf(stderr, "  Shc : @reboot\n");
1312     }
1313     else if (strcmp(shortcut, "yearly") == 0
1314              || strcmp(shortcut, "annually") == 0) {
1315         bit_set(cl->cl_mins, 0);
1316         bit_set(cl->cl_hrs, 0);
1317         bit_set(cl->cl_days, 1);
1318         bit_set(cl->cl_mons, 0);
1319         bit_nset(cl->cl_dow, 0, 7);
1320
1321         if (debug_opt)
1322             fprintf(stderr, "  Shc : @yearly\n");
1323     }
1324     else if (strcmp(shortcut, "monthly") == 0) {
1325         bit_set(cl->cl_mins, 0);
1326         bit_set(cl->cl_hrs, 0);
1327         bit_set(cl->cl_days, 1);
1328         bit_nset(cl->cl_mons, 0, 11);
1329         bit_nset(cl->cl_dow, 0, 7);
1330
1331         if (debug_opt)
1332             fprintf(stderr, "  Shc : @monthly\n");
1333     }
1334     else if (strcmp(shortcut, "weekly") == 0) {
1335         bit_set(cl->cl_mins, 0);
1336         bit_set(cl->cl_hrs, 0);
1337         bit_nset(cl->cl_days, 0, 31);
1338         bit_nset(cl->cl_mons, 0, 11);
1339         bit_set(cl->cl_dow, 0);
1340         bit_set(cl->cl_dow, 7); /* 0 and 7 are both sunday */
1341
1342         if (debug_opt)
1343             fprintf(stderr, "  Shc : @weekly\n");
1344     }
1345     else if (strcmp(shortcut, "daily") == 0
1346              || strcmp(shortcut, "midnight") == 0) {
1347         bit_set(cl->cl_mins, 0);
1348         bit_set(cl->cl_hrs, 0);
1349         bit_nset(cl->cl_days, 0, 31);
1350         bit_nset(cl->cl_mons, 0, 11);
1351         bit_nset(cl->cl_dow, 0, 7);
1352
1353         if (debug_opt)
1354             fprintf(stderr, "  Shc : @daily\n");
1355     }
1356     else if (strcmp(shortcut, "hourly") == 0) {
1357         bit_set(cl->cl_mins, 0);
1358         bit_nset(cl->cl_hrs, 0, 23);
1359         bit_nset(cl->cl_days, 0, 31);
1360         bit_nset(cl->cl_mons, 0, 11);
1361         bit_nset(cl->cl_dow, 0, 7);
1362
1363         if (debug_opt)
1364             fprintf(stderr, "  Shc : @hourly\n");
1365     }
1366     else {
1367         /* this is not a shortcut line but a normal @-line:  */
1368         ptr = ptr_orig;
1369         return 0;
1370     }
1371
1372     /* The next char must be a space (no other option allowed when using
1373      * a shortcut: if the user wants to use options, they should use the 
1374      * native fcron lines */
1375     if (!isspace((int)*ptr)) {
1376         fprintf(stderr, "%s:%d: No space after shortcut: skipping line.\n",
1377                 file_name, line);
1378         goto exiterr;
1379     }
1380
1381     /* skip spaces before the username / shell command */
1382     while (isspace((int)*ptr))
1383         ptr++;
1384
1385     /* check for inline runas */
1386     ptr = check_username(ptr, cf, cl);
1387
1388     /* get cl_shell field ( remove trailing blanks ) */
1389     if ((cl->cl_shell = get_string(ptr)) == NULL) {
1390         fprintf(stderr, "%s:%d: Mismatched quotes: skipping line.\n",
1391                 file_name, line);
1392         goto exiterr;
1393     }
1394     if (strcmp(cl->cl_shell, "\0") == 0) {
1395         fprintf(stderr, "%s:%d: No shell command: skipping line.\n",
1396                 file_name, line);
1397         Free_safe(cl->cl_shell);
1398         goto exiterr;
1399     }
1400
1401 #ifndef USE_SENDMAIL
1402     clear_mail(cl->cl_option);
1403     clear_mailzerolength(cl->cl_option);
1404 #endif
1405
1406     cl->cl_next = cf->cf_line_base;
1407     cf->cf_line_base = cl;
1408
1409     if (debug_opt)
1410         fprintf(stderr, "  Cmd \"%s\" (shortcut)\n", cl->cl_shell);
1411     return 1;
1412
1413  exiterr:
1414     Free_safe(cl);
1415     need_correction = 1;
1416     return 1;
1417 }
1418
1419 void
1420 read_freq(char *ptr, cf_t * cf)
1421     /* read a freq entry, and append a line to cf */
1422 {
1423     cl_t *cl = NULL;
1424
1425     cl = dups_cl(&default_line);
1426
1427     cl->cl_first = -1;          /* 0 is a valid value, so we have to use -1 to detect unset */
1428
1429     /* skip the @ */
1430     ptr++;
1431
1432     /* get the time before first execution or the options */
1433     if (isdigit((int)*ptr)) {
1434         if ((ptr = get_time(ptr, &(cl->cl_first), 1)) == NULL) {
1435             fprintf(stderr, "%s:%d: Error while reading first delay:"
1436                     " skipping line.\n", file_name, line);
1437             goto exiterr;
1438         }
1439
1440         Skip_blanks(ptr);
1441     }
1442     else if (isalnum((int)*ptr)) {
1443         if ((ptr = read_opt(ptr, cl)) == NULL)
1444             goto exiterr;
1445     }
1446     else
1447         Skip_blanks(ptr);
1448
1449     /* we set this here, because it may be unset by read_opt (reset option) */
1450     cl->cl_runfreq = 0;
1451     set_freq(cl->cl_option);
1452
1453     /* then cl_timefreq */
1454     if ((ptr = get_time(ptr, (time_t *) & (cl->cl_timefreq), 0)) == NULL
1455         || cl->cl_timefreq < 1) {
1456         fprintf(stderr,
1457                 "%s:%d: Error while reading frequency %s: skipping line.\n",
1458                 file_name, line,
1459                 (cl->cl_timefreq < 10) ? "(lower than 10s) " : "");
1460         goto exiterr;
1461     }
1462
1463     if (cl->cl_timefreq == 0) {
1464         fprintf(stderr, "%s:%d: no freq specified: skipping line.\n",
1465                 file_name, line);
1466         goto exiterr;
1467     }
1468
1469     if (cl->cl_first == -1)
1470         /* time before first execution was not specified explicitely */
1471         cl->cl_first = cl->cl_timefreq;
1472
1473     /* check for inline runas */
1474     ptr = check_username(ptr, cf, cl);
1475
1476     /* get cl_shell field ( remove trailing blanks ) */
1477     if ((cl->cl_shell = get_string(ptr)) == NULL) {
1478         fprintf(stderr, "%s:%d: Mismatched quotes: skipping line.\n",
1479                 file_name, line);
1480         goto exiterr;
1481     }
1482     if (strcmp(cl->cl_shell, "\0") == 0) {
1483         fprintf(stderr, "%s:%d: No shell command: skipping line.\n",
1484                 file_name, line);
1485         Free_safe(cl->cl_shell);
1486         goto exiterr;
1487     }
1488
1489 #ifndef USE_SENDMAIL
1490     clear_mail(cl->cl_option);
1491     clear_mailzerolength(cl->cl_option);
1492 #endif
1493
1494     cl->cl_next = cf->cf_line_base;
1495     cf->cf_line_base = cl;
1496
1497     if (debug_opt)
1498         fprintf(stderr, "  Cmd \"%s\", timefreq %ld, first %ld\n",
1499                 cl->cl_shell, cl->cl_timefreq, (long int)cl->cl_first);
1500
1501     return;
1502
1503  exiterr:
1504     free_line(cl);
1505     need_correction = 1;
1506     return;
1507 }
1508
1509
1510
1511 #define R_field(PTR, ARY, MAX, ARYCONST, DESCRP) \
1512   if((PTR = read_field(PTR, ARY, MAX, ARYCONST)) == NULL) { \
1513       if (debug_opt) \
1514           fprintf(stderr, "\n"); \
1515       fprintf(stderr, "%s:%d: Error while reading " DESCRP " field: " \
1516              "skipping line.\n", file_name, line); \
1517       Free_safe(cl); \
1518       return; \
1519   }
1520
1521
1522 void
1523 read_arys(char *ptr, cf_t * cf)
1524     /* read a run freq number plus a normal fcron line */
1525 {
1526     cl_t *cl = NULL;
1527     int i = 0;
1528
1529     cl = dups_cl(&default_line);
1530
1531     /* set cl_remain if not specified */
1532     if (*ptr == '&') {
1533         ptr++;
1534         if (isdigit((int)*ptr)) {
1535             if ((ptr = get_num(ptr, &i, USHRT_MAX, 0, NULL)) == NULL) {
1536                 fprintf(stderr, "%s:%d: Error while reading runfreq:"
1537                         " skipping line.\n", file_name, line);
1538                 goto exiterr;
1539             }
1540             else {
1541                 if (i <= 1) {
1542                     fprintf(stderr, "%s:%d: runfreq must be 2 or more :"
1543                             " skipping line.\n", file_name, line);
1544                     goto exiterr;
1545                 }
1546                 cl->cl_runfreq = (unsigned short)i;
1547             }
1548         }
1549         else if (isalnum((int)*ptr))
1550             if ((ptr = read_opt(ptr, cl)) == NULL) {
1551                 goto exiterr;
1552             }
1553         Skip_blanks(ptr);
1554     }
1555
1556     cl->cl_remain = cl->cl_runfreq;
1557
1558     /* we set this here, because it may be unset by read_opt (reset option) */
1559     set_td(cl->cl_option);
1560
1561     if (debug_opt)
1562         fprintf(stderr, "     ");
1563
1564     /* get the fields (check for errors) */
1565     R_field(ptr, cl->cl_mins, 59, NULL, "minutes");
1566     R_field(ptr, cl->cl_hrs, 23, NULL, "hours");
1567     R_field(ptr, cl->cl_days, 31, NULL, "days");
1568     /* month are defined by user from 1 to 12 : max is 12 */
1569     R_field(ptr, cl->cl_mons, 12, mons_ary, "months");
1570     R_field(ptr, cl->cl_dow, 7, dows_ary, "days of week");
1571
1572     if (debug_opt)
1573         /* if debug_opt is set, we print informations in read_field function,
1574          *  but no end line : we print it here */
1575         fprintf(stderr, " remain %d\n", cl->cl_remain);
1576
1577     /* check for inline runas */
1578     ptr = check_username(ptr, cf, cl);
1579
1580     /* get the shell command (remove trailing blanks) */
1581     if ((cl->cl_shell = get_string(ptr)) == NULL) {
1582         fprintf(stderr, "%s:%d: Mismatched quotes: skipping line.\n",
1583                 file_name, line);
1584         goto exiterr;
1585     }
1586     if (strcmp(cl->cl_shell, "\0") == 0) {
1587         fprintf(stderr, "%s:%d: No shell command: skipping line.\n",
1588                 file_name, line);
1589         Free_safe(cl->cl_shell);
1590         goto exiterr;
1591     }
1592
1593 #ifndef USE_SENDMAIL
1594     clear_mail(cl->cl_option);
1595     clear_mailzerolength(cl->cl_option);
1596 #endif
1597
1598     cl->cl_next = cf->cf_line_base;
1599     cf->cf_line_base = cl;
1600
1601     if (debug_opt)
1602         fprintf(stderr, "  Cmd \"%s\"\n", cl->cl_shell);
1603     return;
1604
1605  exiterr:
1606     need_correction = 1;
1607     free_line(cl);
1608     return;
1609
1610 }
1611
1612 void
1613 read_period(char *ptr, cf_t * cf)
1614     /* read a line to run periodically (i.e. once a day, once a week, etc) */
1615 {
1616     cl_t *cl = NULL;
1617     short int remain = 8;
1618
1619     cl = dups_cl(&default_line);
1620
1621     /* skip the % */
1622     ptr++;
1623
1624     /* a runfreq set to 1 means : this is a periodical line 
1625      * (runfreq cannot be changed by read_opt() if already set to 1) */
1626     cl->cl_remain = cl->cl_runfreq = 1;
1627
1628     /* set cl_remain if not specified */
1629     if ((ptr = read_opt(ptr, cl)) == NULL) {
1630         goto exiterr;
1631     }
1632     Skip_blanks(ptr);
1633
1634     /* we set this here, because it may be unset by read_opt (reset option) */
1635     set_td(cl->cl_option);
1636
1637     if (debug_opt)
1638         fprintf(stderr, "     ");
1639
1640     if (is_freq_periodically(cl->cl_option)) {
1641         if (is_freq_mins(cl->cl_option))
1642             remain = 0;
1643         else if (is_freq_hrs(cl->cl_option))
1644             remain = 1;
1645         else if (is_freq_days(cl->cl_option))
1646             remain = 2;
1647         else if (is_freq_mons(cl->cl_option))
1648             remain = 3;
1649         else if (is_freq_dow(cl->cl_option))
1650             remain = 2;
1651     }
1652
1653     /* get the fields (check for errors) */
1654     if (remain-- > 0) {
1655     R_field(ptr, cl->cl_mins, 59, NULL, "minutes")}
1656     else
1657         bit_nset(cl->cl_mins, 0, 59);
1658     if (remain-- > 0) {
1659     R_field(ptr, cl->cl_hrs, 23, NULL, "hours")}
1660     else
1661         bit_nset(cl->cl_hrs, 0, 23);
1662     if (remain-- > 0) {
1663     R_field(ptr, cl->cl_days, 31, NULL, "days")}
1664     else
1665         bit_nset(cl->cl_days, 1, 32);
1666     /* month are defined by user from 1 to 12 : max is 12 */
1667     if (remain-- > 0) {
1668     R_field(ptr, cl->cl_mons, 12, mons_ary, "months")}
1669     else
1670         bit_nset(cl->cl_mons, 0, 11);
1671     if (remain-- > 0) {
1672     R_field(ptr, cl->cl_dow, 7, dows_ary, "days of week")}
1673     else
1674         bit_nset(cl->cl_dow, 0, 7);
1675
1676     if (debug_opt)
1677         /* if debug_opt is set, we print informations in read_field function,
1678          *  but no end line : we print it here */
1679         fprintf(stderr, " remain %d\n", cl->cl_remain);
1680
1681     /* check for inline runas */
1682     ptr = check_username(ptr, cf, cl);
1683
1684     /* get the shell command (remove trailing blanks) */
1685     if ((cl->cl_shell = get_string(ptr)) == NULL) {
1686         fprintf(stderr, "%s:%d: Mismatched quotes: skipping line.\n",
1687                 file_name, line);
1688         goto exiterr;
1689     }
1690     if (strcmp(cl->cl_shell, "\0") == 0) {
1691         fprintf(stderr, "%s:%d: No shell command: skipping line.\n",
1692                 file_name, line);
1693         Free_safe(cl->cl_shell);
1694         goto exiterr;
1695     }
1696     else if (cl->cl_shell[0] == '*' || isdigit((int)cl->cl_shell[0]))
1697         fprintf(stderr, "%s:%d: Warning : shell command beginning by '%c'.\n",
1698                 file_name, line, cl->cl_shell[0]);
1699
1700     /* check for non matching if option runfreq is set to 1 */
1701     if (!is_freq_periodically(cl->cl_option)) {
1702         const size_t s_mins = 60, s_hrs = 24;
1703         const size_t s_days = 32, s_mons = 12;
1704         const size_t s_dow = 8;
1705         int j = 0;
1706
1707         if (!is_freq_mins(cl->cl_option)) {
1708             bit_ffc(cl->cl_mins, s_mins, &j);
1709             if (j != -1 && j < s_mins)
1710                 goto ok;
1711         }
1712         if (!is_freq_hrs(cl->cl_option)) {
1713             bit_ffc(cl->cl_hrs, s_hrs, &j);
1714             if (j != -1 && j < s_hrs)
1715                 goto ok;
1716         }
1717         if (!is_freq_days(cl->cl_option)) {
1718             bit_ffc(cl->cl_days, s_days, &j);
1719             if (j != -1 && j < s_days) {
1720                 if (is_dayand(cl->cl_option))
1721                     goto ok;
1722                 else {
1723                     if (!is_freq_dow(cl->cl_option)) {
1724                         bit_ffc(cl->cl_dow, s_dow, &j);
1725                         if (j != -1 && j < s_dow)
1726                             goto ok;
1727                     }
1728                 }
1729             }
1730         }
1731         if (!is_freq_mons(cl->cl_option)) {
1732             bit_ffc(cl->cl_mons, s_mons, &j);
1733             if (j != -1 && j < s_mons)
1734                 goto ok;
1735         }
1736         if (!is_freq_dow(cl->cl_option)) {
1737             bit_ffc(cl->cl_dow, s_dow, &j);
1738             if (j != -1 && j < s_dow && is_dayand(cl->cl_option))
1739                 goto ok;
1740         }
1741
1742         fprintf(stderr, "%s:%d: periodical line with no intervals: "
1743                 "skipping line.\n", file_name, line);
1744         goto exiterr;
1745     }
1746
1747  ok:
1748 #ifndef USE_SENDMAIL
1749     clear_mail(cl->cl_option);
1750     clear_mailzerolength(cl->cl_option);
1751 #endif
1752
1753     cl->cl_next = cf->cf_line_base;
1754     cf->cf_line_base = cl;
1755
1756     if (debug_opt)
1757         fprintf(stderr, "  Cmd \"%s\"\n", cl->cl_shell);
1758     return;
1759
1760  exiterr:
1761     need_correction = 1;
1762     free_line(cl);
1763     return;
1764
1765 }
1766
1767 char *
1768 get_num(char *ptr, int *num, int max, short int decimal, const char **names)
1769     /* read a string's number and return it under int format.
1770      *  Also check if that number is less than max */
1771 {
1772     int i = 0;
1773     *num = 0;
1774
1775     if (isalpha((int)*ptr)) {
1776
1777         if (names == NULL) {
1778             need_correction = 1;
1779             return NULL;
1780         }
1781
1782         /* set string to lower case */
1783         for (i = 0; i < strlen(names[0]); i++)
1784             *(ptr + i) = tolower(*(ptr + i));
1785
1786         for (i = 0; names[i]; ++i)
1787             if (strncmp(ptr, names[i], strlen(names[i])) == 0) {
1788                 *num = i;
1789                 ptr += strlen(names[i]);
1790                 return ptr;
1791                 break;
1792             }
1793
1794         /* string is not in name list */
1795         need_correction = 1;
1796         return NULL;
1797
1798     }
1799     else {
1800
1801         while (isdigit((int)*ptr) || *ptr == '.') {
1802
1803             if (*ptr == '.' && ptr++ && i++ > 0)
1804                 return NULL;
1805             if (i > 0 && --decimal < 0) {
1806                 /* the decimal number is exceeded : we round off,
1807                  * skip the other decimals and return */
1808                 if (*ptr >= '5')
1809                     *num += 1;
1810                 while (isdigit((int)*(++ptr))) ;
1811                 ptr--;
1812             }
1813             else {
1814                 *num *= 10;
1815                 *num += *ptr - 48;
1816             }
1817
1818             if (*num > max) {
1819                 need_correction = 1;
1820                 return NULL;
1821             }
1822
1823             ptr++;
1824
1825         }
1826
1827         if (decimal > 0)
1828             *num *= 10 * decimal;
1829
1830     }
1831
1832     return ptr;
1833 }
1834
1835
1836 char *
1837 read_field(char *ptr, bitstr_t * ary, int max, const char **names)
1838     /* read a field like "2,5-8,10-20/2,21-30~25" and fill ary */
1839 {
1840     int start = 0;
1841     int stop = 0;
1842     int step = 0;
1843     int rm = 0;
1844     int i = 0;
1845
1846     while ((*ptr != ' ') && (*ptr != '\t') && (*ptr != '\0')) {
1847
1848         start = stop = step = 0;
1849
1850         /* there may be a "," */
1851         if (*ptr == ',')
1852             ptr++;
1853
1854         if (*ptr == '*') {
1855             /* we have to fill everything (may be modified by a step ) */
1856             start = 0;
1857             /* user set month from 1 to 12, but we manage it internally
1858              * as a number from 0 to 11 */
1859             stop = (max == 12) ? 11 : max;
1860             ptr++;
1861         }
1862         else {
1863
1864             ptr = get_num(ptr, &start, max, 0, names);
1865             if (ptr == NULL)
1866                 return NULL;
1867             if (max == 12)
1868                 /* this number is part of the month field.
1869                  * user set it from 1 to 12, but we manage it internally
1870                  * as a number from 0 to 11 : we remove 1 to start */
1871                 start -= 1;
1872
1873             if (*ptr == ',' || *ptr == ' ' || *ptr == '\t') {
1874                 /* this is a single number : set up array and continue */
1875                 if (debug_opt)
1876                     fprintf(stderr, " %d", start);
1877                 bit_set(ary, start);
1878                 continue;
1879             }
1880
1881             /* check for a dash */
1882             else if (*ptr == '-') {
1883                 ptr++;
1884                 ptr = get_num(ptr, &stop, max, 0, names);
1885                 if (ptr == NULL)
1886                     /* we reached the end of the string to parse */
1887                     return NULL;
1888                 if (max == 12)
1889                     /* this number is part of the month field.
1890                      * user set it from 1 to 12, but we manage it internally
1891                      * as a number from 0 to 11 : we remove 1 to stop */
1892                     stop -= 1;
1893             }
1894             else {
1895                 /* syntax error */
1896                 need_correction = 1;
1897                 return NULL;
1898             }
1899         }
1900
1901         /* check for step size */
1902         if (*ptr == '/') {
1903             ptr++;
1904             if ((ptr = get_num(ptr, &step, max, 0, names)) == NULL || step == 0)
1905                 return NULL;
1906         }
1907         else
1908             /* step undefined : default is 0 */
1909             step = 1;
1910
1911         /* fill array */
1912         if (debug_opt)
1913             fprintf(stderr, " %d-%d/%d", start, stop, step);
1914
1915         if (start < stop)
1916             for (i = start; i <= stop; i += step)
1917                 bit_set(ary, i);
1918         else {
1919             short int field_max = (max == 12) ? 11 : max;
1920             /* this is a field like (for hours field) "22-3" :
1921              * we should set from 22 to 3 (not from 3 to 22 or nothing :)) ) */
1922             for (i = start; i <= field_max; i += step)
1923                 bit_set(ary, i);
1924             for (i -= (field_max + 1); i <= stop; i += step)
1925                 bit_set(ary, i);
1926         }
1927
1928         /* finally, remove unwanted values */
1929         while (*ptr == '~') {
1930             ptr++;
1931             rm = 0;
1932             if ((ptr = get_num(ptr, &rm, max, 0, names)) == NULL)
1933                 return NULL;
1934             if (max == 12)
1935                 /* this number is part of the month field.
1936                  * user set it from 1 to 12, but we manage it internally
1937                  * as a number from 0 to 11 : we remove 1 to rm */
1938                 rm -= 1;
1939
1940             if (debug_opt)
1941                 fprintf(stderr, " ~%d", rm);
1942             bit_clear(ary, rm);
1943
1944             /* if we remove one value of Sunday, remove the other */
1945             if (max == 7 && rm == 0) {
1946                 bit_clear(ary, 7);
1947                 if (debug_opt)
1948                     fprintf(stderr, " ~%d", 7);
1949             }
1950             else if (max == 7 && rm == 7) {
1951                 bit_clear(ary, 0);
1952                 if (debug_opt)
1953                     fprintf(stderr, " ~%d", 0);
1954             }
1955
1956         }
1957
1958     }
1959
1960     /* Sunday is both 0 and 7 : if one is set, set the other */
1961     if (max == 7) {
1962         if (bit_test(ary, 0))
1963             bit_set(ary, 7);
1964         else if (bit_test(ary, 7))
1965             bit_set(ary, 0);
1966     }
1967
1968     Skip_blanks(ptr);
1969
1970     if (debug_opt)
1971         fprintf(stderr, " #");
1972
1973     return ptr;
1974 }
1975
1976 void
1977 delete_file(const char *user_name)
1978     /* free a file if user_name is not null 
1979      *   otherwise free all files */
1980 {
1981     cf_t *file = NULL;
1982     cf_t *prev_file = NULL;
1983     cl_t *line = NULL;
1984     cl_t *cur_line = NULL;
1985
1986     file = file_base;
1987     while (file != NULL) {
1988         if (strcmp(user_name, file->cf_user) == 0) {
1989
1990             /* free lines */
1991             cur_line = file->cf_line_base;
1992             while ((line = cur_line) != NULL) {
1993                 cur_line = line->cl_next;
1994                 free_line(line);
1995             }
1996             break;
1997
1998         }
1999         else {
2000             prev_file = file;
2001             file = file->cf_next;
2002         }
2003     }
2004
2005     if (file == NULL)
2006         /* file not in list */
2007         return;
2008
2009     /* remove file from list */
2010     if (prev_file == NULL)
2011         file_base = file->cf_next;
2012     else
2013         prev_file->cf_next = file->cf_next;
2014
2015     /* free env variables */
2016     env_list_destroy(file->cf_env_list);
2017
2018     /* finally free file itself */
2019     Free_safe(file->cf_user);
2020     Free_safe(file);
2021
2022 }
2023
2024 int
2025 save_file(char *path)
2026     /* Store the informations relatives to the executions
2027      * of tasks at a defined frequency of system's running time */
2028 {
2029     cf_t *file = NULL;
2030
2031     if (debug_opt)
2032         fprintf(stderr, "Saving ...\n");
2033
2034     for (file = file_base; file; file = file->cf_next) {
2035
2036         /* save_file() is run under user's rights.
2037          * If fcrontab is run by root for a normal user, we must change the file's
2038          * ownership to this user, in order to make fcron check the runas fields.
2039          * (a malicious user could put a runas(root) and wait for the fcrontab to be
2040          * installed by root) */
2041 #ifdef USE_SETE_ID
2042         if (save_file_safe(file, path, "fcrontab", asuid, fcrontab_gid, 0) ==
2043             ERR)
2044             return ERR;
2045 #else
2046         if (save_file_safe
2047             (file, path, "fcrontab", fcrontab_uid, fcrontab_gid, 0) == ERR)
2048             return ERR;
2049 #endif
2050
2051     }
2052
2053     return OK;
2054 }