2 * FCRON - periodic command scheduler
4 * Copyright 2000-2012 Thibault Godouet <fcron@free.fr>
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.
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.
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
20 * The GNU General Public License can also be found in the file
21 * `LICENSE' that comes with the fcron source distribution.
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,
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);
47 cl_t default_line; /* default options for a line */
51 /* warning : all names must have the same length */
52 const char *dows_ary[] = {
53 "sun", "mon", "tue", "wed", "thu", "fri", "sat",
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",
65 #define GET_LINE_EOF 999
69 /* read string pointed by ptr, remove blanks and manage
70 * string placed in quotes */
71 /* return NULL on mismatched quotes */
75 char *rtn_string = NULL;
77 if ( *ptr == '\"' || *ptr == '\'' ) {
82 length = remove_blanks(ptr);
85 if ( *(ptr + length - 1) == quote )
86 *(ptr + length - 1) = '\0';
88 /* mismatched quotes */
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 */
105 size_t size_max = size - 1 ;
109 while (i < size_max ) {
111 switch ( c = getc(file) ) {
114 /* check if the \n char is preceded by a "\" char :
115 * in this case, suppress the "\", don't copy the \n,
117 if ( i > 0 && *(str + i - 1) == '\\') {
123 *(str + i) = (char) '\0';
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 */
135 *(str + i) = (char) c;
142 /* line is too long : goto next line and return ERR */
143 while ( ( (c = getc(file)) != EOF ) && ( c != '\n') )
152 init_default_line(cl_t *cl, cf_t *cf)
153 /* clear all context/options from cl */
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);
165 read_file(char *filename, int fd)
166 /* read file "name" and append cf_t list */
172 int max_entries = MAXENTRIES;
177 bzero(buf, sizeof(buf));
180 file_name = filename;
184 if ( (file = fdopen(fd, "r")) == NULL ) {
185 fprintf(stderr, "Could not open \"%s\": %s\n", file_name,
190 /* Rewind, just in case */
194 cf->cf_env_list = env_list_init();
195 Set(cf->cf_user, user);
196 init_default_line(&default_line, cf);
199 fprintf(stderr, "FILE %s\n", file_name);
201 if (strcmp(runas, ROOTNAME) == 0)
204 /* max_lines acts here as a security counter to avoid endless loop. */
205 max_lines = (max_entries * 10) + 10;
207 while ( entries < max_entries && line <= max_lines ) {
209 ret = get_line(buf, sizeof(buf), file);
212 fprintf(stderr, "%s:%d: Line is too long (more than %d): skipping line.\n",
213 file_name, line, (int)sizeof(buf));
220 if (debug_opt && *ptr != '#' && *ptr != '\0')
221 fprintf(stderr, " %s\n", buf);
226 /* comments or empty line: skipping */
229 /* if it is not a shortcut line then read_shortcut() won't do anything. */
230 if ( ! read_shortcut(ptr, cf))
239 read_period(ptr, cf);
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);
251 if ( isdigit( (int) *ptr) || *ptr == '*' ) {
261 /* in this case, ret == GET_LINE_EOF :
262 * no more lines, so we exit the loop */
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");
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);
276 cf->cf_next = file_base;
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");
283 Free_safe(default_line.cl_runas);
284 Free_safe(default_line.cl_mailto);
285 Free_safe(default_line.cl_tz);
287 if ( ! need_correction )
295 read_env(char *ptr, cf_t *cf)
296 /* append env variable list.
303 bzero(name, sizeof(name));
305 /* copy env variable's name */
306 while ( (isalnum( (int) *ptr) || *ptr == '_') && *ptr != '='
307 && ! isspace( (int) *ptr) && j < sizeof(name) ) {
313 if ( name[0] == '\0' )
316 /* skip '=' and spaces around */
317 while ( isspace( (int) *ptr) )
320 /* if j == 0 name is a zero length string */
321 if ( *ptr++ != '=' || j == 0 )
324 while ( isspace( (int) *ptr) )
328 if ( ( val = get_string(ptr)) == NULL ) {
329 fprintf(stderr, "%s:%d: Mismatched quotes: skipping line.\n",
336 fprintf(stderr, " Env : '%s=%s'\n", name, val);
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",
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);
354 Set(default_line.cl_mailto, val);
355 set_mail(default_line.cl_option);
360 env_list_setenv(cf->cf_env_list, name, val, 1);
368 fprintf(stderr, "%s:%d: Syntax error: skipping line.\n",
377 get_nice(char *ptr, int *nice)
378 /* read a nice value and put it in variable nice */
387 if ( (ptr = get_num(ptr, nice, 20, 0, NULL)) == NULL )
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");
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. */
417 /* look for the end of the option value */
418 while ( ptr != NULL && *ptr != ')' ) {
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 == '\"' ) {
436 *var = strndup2(value, len);
443 get_bool(char *ptr, int *i)
444 /* get a bool value : either true (1) or false (0)
445 * return NULL on error */
449 else if ( *ptr == '0' )
451 else if ( strncmp(ptr, "true", 4) == 0 ) {
455 else if ( strncmp(ptr, "yes", 3) == 0 ) {
459 else if ( strncmp(ptr, "false", 5) == 0 ) {
463 else if ( strncmp(ptr, "no", 2) == 0 ) {
484 read_opt(char *ptr, cl_t *cl)
485 /* read one or several options and fill in the field "option" */
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; \
504 bzero(opt_name, sizeof(opt_name));
506 while ( isalnum( (int) *ptr) && i < sizeof(opt_name))
507 opt_name[i++] = *ptr++;
517 /* spaces are not allowed -- make sure there is no leading space. */
518 if ( isspace( (int) *ptr) ) {
522 /* global options for a file */
524 if ( strcmp(opt_name, "tzdiff") == 0 ) {
533 if ( (ptr = get_num(ptr, &i, 24, 0, NULL)) == NULL )
536 cl->cl_file->cf_tzdiff = (- i);
538 cl->cl_file->cf_tzdiff = i;
541 fprintf(stderr, " Opt : \"%s\" (-)%d\n", opt_name, i);
544 /* options related to a line (or a set of lines) */
546 else if ( strcmp(opt_name, "timezone") == 0 ) {
549 if ( ! in_brackets ) {
553 len = assign_option_string(&(cl->cl_tz), ptr);
562 fprintf(stderr, " Opt : \"%s\" \"%s\"\n", opt_name, cl->cl_tz);
567 else if(strcmp(opt_name, "s") == 0 || strcmp(opt_name, "serial") == 0){
568 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
571 clear_serial(cl->cl_option);
573 set_serial(cl->cl_option);
575 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
578 else if (strcmp(opt_name, "serialonce") == 0 ) {
579 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
582 set_serial_sev(cl->cl_option);
584 clear_serial_sev(cl->cl_option);
586 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
589 else if (strcmp(opt_name, "lavgonce") == 0 ) {
590 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
593 set_lavg_sev(cl->cl_option);
595 clear_lavg_sev(cl->cl_option);
597 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
600 else if (strcmp(opt_name, "exesev") == 0 ) {
601 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
604 clear_exe_sev(cl->cl_option);
606 set_exe_sev(cl->cl_option);
608 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
611 else if(strcmp(opt_name, "b")==0 || strcmp(opt_name, "bootrun")==0){
612 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
615 clear_bootrun(cl->cl_option);
617 set_bootrun(cl->cl_option);
619 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
622 else if(strcmp(opt_name, "rebootreset")==0) {
623 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
626 clear_rebootreset(cl->cl_option);
628 set_rebootreset(cl->cl_option);
630 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
634 else if(strcmp(opt_name, "runatreboot")==0){
635 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
638 clear_runatreboot(cl->cl_option);
640 set_runatreboot(cl->cl_option);
642 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
646 else if(strcmp(opt_name, "runonce")==0){
647 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
650 clear_runonce(cl->cl_option);
652 set_runonce(cl->cl_option);
654 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
657 else if( strcmp(opt_name, "reset")==0 ) {
658 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
661 init_default_line(cl, cl->cl_file);
664 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
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 )
671 fprintf(stderr, " Opt : \"%s\" %ld\n", opt_name,
672 (long int)cl->cl_first);
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");
680 if ( !in_brackets || (ptr=get_num(ptr,&i,USHRT_MAX,0,NULL)) == NULL)
683 fprintf(stderr, "runfreq must be 2 or more.\n");
688 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
691 else if ( strcmp(opt_name, "strict") == 0 ) {
692 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
695 clear_strict(cl->cl_option);
697 set_strict(cl->cl_option);
699 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
702 else if ( strcmp(opt_name, "noticenotrun") == 0 ) {
703 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
706 clear_notice_notrun(cl->cl_option);
708 set_notice_notrun(cl->cl_option);
710 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
713 else if ( strcmp(opt_name, "lavg") == 0 ) {
714 if (!in_brackets || (ptr=get_num(ptr,&i,UCHAR_MAX,1,NULL)) == NULL)
718 fprintf(stderr, " Opt : 'lavg1' %d\n", i);
721 if (!in_brackets || (ptr=get_num(ptr,&i,UCHAR_MAX,1,NULL)) == NULL)
725 fprintf(stderr, " Opt : 'lavg5' %d\n", i);
728 if(!in_brackets || (ptr=get_num(ptr,&i,UCHAR_MAX,1,NULL)) == NULL )
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);
736 clear_lavg(cl->cl_option);
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 */
743 else if( strcmp(opt_name, "lavg1") == 0 ) {
744 if(!in_brackets ||(ptr=get_num(ptr, &i, UCHAR_MAX, 1, NULL))==NULL)
747 if ( cl->cl_lavg[0] || cl->cl_lavg[1] || cl->cl_lavg[2] )
748 set_lavg(cl->cl_option);
750 clear_lavg(cl->cl_option);
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 */
756 fprintf(stderr, " Opt : 'lavg1' %d\n", i);
759 else if( strcmp(opt_name, "lavg5") == 0 ) {
760 if(!in_brackets ||(ptr=get_num(ptr, &i, UCHAR_MAX, 1, NULL))==NULL)
763 if ( cl->cl_lavg[0] || cl->cl_lavg[1] || cl->cl_lavg[2] )
764 set_lavg(cl->cl_option);
766 clear_lavg(cl->cl_option);
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 */
772 fprintf(stderr, " Opt : 'lavg5' %d\n", i);
775 else if( strcmp(opt_name, "lavg15") == 0 ) {
776 if(!in_brackets ||(ptr=get_num(ptr, &i, UCHAR_MAX, 1, NULL))==NULL)
779 if ( cl->cl_lavg[0] || cl->cl_lavg[1] || cl->cl_lavg[2] )
780 set_lavg(cl->cl_option);
782 clear_lavg(cl->cl_option);
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 */
788 fprintf(stderr, " Opt : 'lavg15' %d\n", i);
791 else if( strcmp(opt_name, "lavgand") == 0 ) {
792 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
795 set_lor(cl->cl_option);
797 set_land(cl->cl_option);
799 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
802 else if( strcmp(opt_name, "lavgor") == 0 ) {
803 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
806 set_land(cl->cl_option);
808 set_lor(cl->cl_option);
810 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
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 )
817 fprintf(stderr, " Opt : \"%s\" %ld\n", opt_name,
818 (long int)cl->cl_until);
821 else if(strcmp(opt_name, "m")==0 || strcmp(opt_name, "mail")==0){
822 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
825 clear_mail(cl->cl_option);
826 clear_mailzerolength(cl->cl_option);
829 set_mail(cl->cl_option);
831 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
834 else if( strcmp(opt_name, "forcemail") == 0 ) {
835 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
838 clear_mailzerolength(cl->cl_option);
840 set_mailzerolength(cl->cl_option);
841 set_mail(cl->cl_option);
844 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
847 else if( strcmp(opt_name, "mailto") == 0) {
850 if ( ! in_brackets ) {
854 /* please note that we check if the mailto is valid in conf.c */
855 len = assign_option_string(&(cl->cl_mailto), ptr);
859 else if ( len == 0 ) {
860 clear_mail(cl->cl_option);
861 clear_mailzerolength(cl->cl_option);
865 set_mail(cl->cl_option);
869 fprintf(stderr, " Opt : \"%s\" \"%s\"\n", opt_name, cl->cl_mailto);
872 else if( strcmp(opt_name, "erroronlymail") == 0 ) {
873 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
876 clear_erroronlymail(cl->cl_option);
878 set_erroronlymail(cl->cl_option);
880 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
883 else if( strcmp(opt_name, "dayand") == 0 ) {
884 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
887 set_dayor(cl->cl_option);
889 set_dayand(cl->cl_option);
891 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
894 else if( strcmp(opt_name, "dayor") == 0 ) {
895 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
898 set_dayand(cl->cl_option);
900 set_dayor(cl->cl_option);
902 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
905 else if ( strcmp(opt_name, "nolog") == 0 ) {
906 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
909 clear_nolog(cl->cl_option);
911 set_nolog(cl->cl_option);
913 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
916 else if ( strcmp(opt_name, "volatile") == 0 ) {
917 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
920 clear_volatile(cl->cl_option);
922 set_volatile(cl->cl_option);
924 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
927 else if ( strcmp(opt_name, "stdout") == 0 ) {
928 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
931 clear_stdout(cl->cl_option);
933 set_stdout(cl->cl_option);
935 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
938 else if(strcmp(opt_name, "n") == 0 || strcmp(opt_name, "nice") == 0) {
939 if( ! in_brackets || (ptr = get_nice(ptr, &i)) == NULL )
941 cl->cl_nice = (char)i;
943 fprintf(stderr, " Opt : \"%s\" (-)%d\n", opt_name, i);
946 else if(strcmp(opt_name, "runas") == 0) {
951 if ( ! in_brackets ) {
955 len = assign_option_string(&runas, ptr);
963 if (getuid() != rootuid) {
964 fprintf(stderr, "must be privileged to use option runas: "
965 "skipping option\n");
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);
974 else if ((pas = getpwnam(runas)) == NULL) {
975 fprintf(stderr, "runas: \"%s\" is not in passwd file : "
980 if (need_correction) {
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);
991 fprintf(stderr, " Opt : \"%s\" %s ptr=%p\n", opt_name, cl->cl_runas, cl->cl_runas);
995 else if( strcmp(opt_name, "random") == 0 ) {
996 if ( in_brackets && (ptr = get_bool(ptr, &i)) == NULL )
999 clear_random(cl->cl_option);
1001 set_random(cl->cl_option);
1003 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
1006 else if( strcmp(opt_name, "jitter") == 0 ) {
1007 if(!in_brackets ||(ptr=get_num(ptr, &i, UCHAR_MAX, 0, NULL))==NULL)
1011 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
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) {
1023 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
1025 else if (strcmp(opt_name, "hours") == 0) {
1026 set_freq_mins(cl->cl_option);
1028 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
1030 else if (strcmp(opt_name, "days") == 0) {
1031 set_freq_mins(cl->cl_option);
1032 set_freq_hrs(cl->cl_option);
1034 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
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);
1041 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
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);
1049 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
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);
1058 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
1060 else if (strcmp(opt_name, "daily") == 0) {
1061 set_freq_days(cl->cl_option);
1062 set_freq_periodically(cl->cl_option);
1064 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
1066 else if (strcmp(opt_name, "monthly") == 0) {
1067 set_freq_mons(cl->cl_option);
1068 set_freq_periodically(cl->cl_option);
1070 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
1072 else if (strcmp(opt_name, "weekly") == 0) {
1073 set_freq_dow(cl->cl_option);
1074 set_freq_periodically(cl->cl_option);
1076 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
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);
1087 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
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);
1095 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
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);
1102 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
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);
1109 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
1114 fprintf(stderr, "%s:%d: Option \"%s\" unknown: "
1115 "skipping option.\n", file_name, line, opt_name);
1116 need_correction = 1;
1119 if ( in_brackets ) {
1126 } while ( *ptr == ',' && ptr++);
1134 get_time(char *ptr, time_t *time, int zero_allowed)
1135 /* convert time read in string in time_t format */
1141 while( (*ptr != ' ') && (*ptr != '\t') && (*ptr != '\0') &&
1146 while ( isdigit( (int) *ptr) ) {
1152 /* interpret multipliers */
1154 case 'm': /* months */
1156 case 'w': /* weeks */
1158 case 'd': /* days */
1160 case 'h': /* hours */
1162 case 's': /* seconds */
1168 sum *= 60; /* minutes */
1171 need_correction = 1;
1180 if (*time == 0 && ! zero_allowed) {
1181 need_correction = 1;
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 */
1194 char username[USER_NAME_LEN];
1195 struct passwd *userpwent;
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';
1205 if ((userpwent = getpwnam(username)) != NULL) {
1206 /* found the user */
1207 ptr = ptr + indx; /* move ptr to the next word */
1210 if (getuid() != rootuid) {
1211 fprintf(stderr, "must be privileged to run as another user : "
1214 Set(cl->cl_runas, username);
1216 fprintf(stderr, " Opt : inline_runas %s\n", username);
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 */
1231 bzero(buf, buf_size);
1233 while ( isalnum( (int) *ptr) && i < buf_size )
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. */
1247 char *ptr_orig = ptr;
1249 cl = dups_cl(&default_line);
1254 ptr = read_word(ptr, shortcut, sizeof(shortcut));
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?? */
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);
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;
1272 fprintf(stderr, " Shc : @reboot\n");
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);
1282 fprintf(stderr, " Shc : @yearly\n");
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);
1292 fprintf(stderr, " Shc : @monthly\n");
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 */
1303 fprintf(stderr, " Shc : @weekly\n");
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);
1313 fprintf(stderr, " Shc : @daily\n");
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);
1323 fprintf(stderr, " Shc : @hourly\n");
1326 /* this is not a shortcut line but a normal @-line: */
1331 /* check for inline runas */
1332 ptr = check_username(ptr, cf, cl);
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",
1340 if ( strcmp(cl->cl_shell, "\0") == 0 ) {
1341 fprintf(stderr, "%s:%d: No shell command: skipping line.\n",
1343 Free_safe(cl->cl_shell);
1347 #ifndef USE_SENDMAIL
1348 clear_mail(cl->cl_option);
1349 clear_mailzerolength(cl->cl_option);
1352 cl->cl_next = cf->cf_line_base;
1353 cf->cf_line_base = cl;
1356 fprintf(stderr, " Cmd \"%s\" (shortcut)\n", cl->cl_shell);
1361 need_correction = 1;
1366 read_freq(char *ptr, cf_t *cf)
1367 /* read a freq entry, and append a line to cf */
1371 cl = dups_cl(&default_line);
1373 cl->cl_first = -1; /* 0 is a valid value, so we have to use -1 to detect unset */
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);
1388 else if ( isalnum( (int) *ptr) ) {
1389 if ( (ptr = read_opt(ptr, cl)) == NULL )
1395 /* we set this here, because it may be unset by read_opt (reset option) */
1397 set_freq(cl->cl_option);
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) " : "");
1407 if ( cl->cl_timefreq == 0) {
1408 fprintf(stderr, "%s:%d: no freq specified: skipping line.\n",
1413 if ( cl->cl_first == -1 )
1414 /* time before first execution was not specified explicitely */
1415 cl->cl_first = cl->cl_timefreq;
1417 /* check for inline runas */
1418 ptr = check_username(ptr, cf, cl);
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",
1426 if ( strcmp(cl->cl_shell, "\0") == 0 ) {
1427 fprintf(stderr, "%s:%d: No shell command: skipping line.\n",
1429 Free_safe(cl->cl_shell);
1433 #ifndef USE_SENDMAIL
1434 clear_mail(cl->cl_option);
1435 clear_forcemail(cl->cl_option);
1438 cl->cl_next = cf->cf_line_base;
1439 cf->cf_line_base = cl;
1442 fprintf(stderr, " Cmd \"%s\", timefreq %ld, first %ld\n",
1443 cl->cl_shell, cl->cl_timefreq, (long int)cl->cl_first);
1449 need_correction = 1;
1455 #define R_field(PTR, ARY, MAX, ARYCONST, DESCRP) \
1456 if((PTR = read_field(PTR, ARY, MAX, ARYCONST)) == NULL) { \
1458 fprintf(stderr, "\n"); \
1459 fprintf(stderr, "%s:%d: Error while reading " DESCRP " field: " \
1460 "skipping line.\n", file_name, line); \
1467 read_arys(char *ptr, cf_t *cf)
1468 /* read a run freq number plus a normal fcron line */
1473 cl = dups_cl(&default_line);
1475 /* set cl_remain if not specified */
1476 if ( *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);
1485 fprintf(stderr, "%s:%d: runfreq must be 2 or more :"
1486 " skipping line.\n", file_name, line);
1489 cl->cl_runfreq = (unsigned short) i;
1492 else if ( isalnum( (int) *ptr) )
1493 if ( (ptr = read_opt(ptr, cl)) == NULL ) {
1499 cl->cl_remain = cl->cl_runfreq;
1501 /* we set this here, because it may be unset by read_opt (reset option) */
1502 set_td(cl->cl_option);
1505 fprintf(stderr, " ");
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");
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);
1520 /* check for inline runas */
1521 ptr = check_username(ptr, cf, cl);
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",
1529 if ( strcmp(cl->cl_shell, "\0") == 0 ) {
1530 fprintf(stderr, "%s:%d: No shell command: skipping line.\n",
1532 Free_safe(cl->cl_shell);
1536 #ifndef USE_SENDMAIL
1537 clear_mail(cl->cl_option);
1538 clear_forcemail(cl->cl_option);
1541 cl->cl_next = cf->cf_line_base;
1542 cf->cf_line_base = cl;
1545 fprintf(stderr, " Cmd \"%s\"\n", cl->cl_shell);
1549 need_correction = 1;
1556 read_period(char *ptr, cf_t *cf)
1557 /* read a line to run periodically (i.e. once a day, once a week, etc) */
1560 short int remain = 8;
1562 cl = dups_cl(&default_line);
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;
1571 /* set cl_remain if not specified */
1572 if ( (ptr = read_opt(ptr, cl)) == NULL ) {
1577 /* we set this here, because it may be unset by read_opt (reset option) */
1578 set_td(cl->cl_option);
1581 fprintf(stderr, " ");
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;
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);
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);
1609 /* check for inline runas */
1610 ptr = check_username(ptr, cf, cl);
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",
1618 if ( strcmp(cl->cl_shell, "\0") == 0 ) {
1619 fprintf(stderr, "%s:%d: No shell command: skipping line.\n",
1621 Free_safe(cl->cl_shell);
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]);
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;
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;
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;
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) )
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;
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;
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;
1665 fprintf(stderr, "%s:%d: periodical line with no intervals: "
1666 "skipping line.\n", file_name, line);
1671 #ifndef USE_SENDMAIL
1672 clear_mail(cl->cl_option);
1673 clear_forcemail(cl->cl_option);
1676 cl->cl_next = cf->cf_line_base;
1677 cf->cf_line_base = cl;
1680 fprintf(stderr, " Cmd \"%s\"\n", cl->cl_shell);
1684 need_correction = 1;
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 */
1698 if ( isalpha( (int) *ptr) ) {
1700 if ( names == NULL ) {
1701 need_correction = 1;
1705 /* set string to lower case */
1706 for ( i = 0; i < strlen(names[0]); i++ )
1707 *(ptr+i) = tolower( *(ptr+i) );
1709 for (i = 0; names[i]; ++i)
1710 if (strncmp(ptr, names[i], strlen(names[i])) == 0) {
1712 ptr += strlen(names[i]);
1717 /* string is not in name list */
1718 need_correction = 1;
1723 while ( isdigit( (int) *ptr) || *ptr == '.') {
1725 if ( *ptr == '.' && ptr++ && i++ > 0 )
1727 if ( i > 0 && --decimal < 0 ) {
1728 /* the decimal number is exceeded : we round off,
1729 * skip the other decimals and return */
1732 while ( isdigit( (int) *(++ptr)) ) ;
1740 need_correction = 1;
1749 *num *= 10 * decimal;
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 */
1767 while ( (*ptr != ' ') && (*ptr != '\t') && (*ptr != '\0') ) {
1769 start = stop = step = 0 ;
1771 /* there may be a "," */
1775 if ( *ptr == '*' ) {
1776 /* we have to fill everything (may be modified by a step ) */
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;
1784 ptr = get_num(ptr, &start, max, 0, names);
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 */
1793 if (*ptr == ',' || *ptr == ' ' || *ptr == '\t') {
1794 /* this is a single number : set up array and continue */
1796 fprintf(stderr, " %d", start);
1797 bit_set(ary, start);
1801 /* check for a dash */
1802 else if ( *ptr == '-' ) {
1804 ptr = get_num(ptr, &stop, max, 0, names);
1806 /* we reached the end of the string to parse */
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 */
1815 need_correction = 1;
1820 /* check for step size */
1821 if ( *ptr == '/' ) {
1823 if ((ptr = get_num(ptr, &step, max, 0, names))==NULL || step == 0)
1826 /* step undefined : default is 0 */
1831 fprintf(stderr, " %d-%d/%d", start, stop, step);
1834 for (i = start; i <= stop; i += step)
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)
1842 for (i -= (field_max + 1); i <= stop; i += step)
1846 /* finally, remove unwanted values */
1847 while ( *ptr == '~' ) {
1850 if ( (ptr = get_num(ptr, &rm, max, 0, names)) == NULL )
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 */
1859 fprintf(stderr, " ~%d", rm);
1862 /* if we remove one value of Sunday, remove the other */
1863 if (max == 7 && rm == 0) {
1866 fprintf(stderr, " ~%d", 7);
1868 else if (max == 7 && rm == 7) {
1871 fprintf(stderr, " ~%d", 0);
1878 /* Sunday is both 0 and 7 : if one is set, set the other */
1880 if ( bit_test(ary, 0) )
1882 else if ( bit_test(ary, 7) )
1889 fprintf(stderr, " #");
1895 delete_file(const char *user_name)
1896 /* free a file if user_name is not null
1897 * otherwise free all files */
1900 cf_t *prev_file = NULL;
1902 cl_t *cur_line = NULL;
1905 while ( file != NULL) {
1906 if (strcmp(user_name, file->cf_user) == 0) {
1909 cur_line = file->cf_line_base;
1910 while ( (line = cur_line) != NULL) {
1911 cur_line = line->cl_next;
1918 file = file->cf_next;
1923 /* file not in list */
1926 /* remove file from list */
1927 if (prev_file == NULL)
1928 file_base = file->cf_next;
1930 prev_file->cf_next = file->cf_next;
1932 /* free env variables */
1933 env_list_destroy(file->cf_env_list);
1935 /* finally free file itself */
1936 Free_safe(file->cf_user);
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 */
1949 fprintf(stderr, "Saving ...\n");
1951 for (file = file_base; file; file = file->cf_next) {
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) */
1959 if ( save_file_safe(file, path, "fcrontab", asuid, fcrontab_gid, 0) == ERR )
1962 if ( save_file_safe(file, path, "fcrontab", fcrontab_uid, fcrontab_gid, 0) == ERR )