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