/* * FCRON - periodic command scheduler * * Copyright 2000-2014 Thibault Godouet * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * The GNU General Public License can also be found in the file * `LICENSE' that comes with the fcron source distribution. */ #include "fcrontab.h" #include "fileconf.h" char *get_string(char *ptr); int get_line(char *str, size_t size, FILE * file); void init_default_line(cl_t * cl, cf_t * cf); char *get_time(char *ptr, time_t * time, int zero_allowed); char *get_num(char *ptr, int *num, int max, short int decimal, const char **names); char *get_nice(char *ptr, int *nice); char *get_bool(char *ptr, int *i); char *read_field(char *ptr, bitstr_t * ary, int max, const char **names); void read_freq(char *ptr, cf_t * cf); void read_arys(char *ptr, cf_t * cf); void read_period(char *ptr, cf_t * cf); int read_shortcut(char *ptr, cf_t * cf); void read_env(char *ptr, cf_t * cf); char *read_opt(char *ptr, cl_t * cl); char *check_username(char *ptr, cf_t * cf, cl_t * cl); size_t option_strlen(char *value); char need_correction; cl_t default_line; /* default options for a line */ char *file_name; int line; /* warning : all names must have the same length */ const char *dows_ary[] = { "sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL }; /* warning : all names must have the same length */ const char *mons_ary[] = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec", NULL }; #define GET_LINE_EOF 999 char * get_string(char *ptr) /* read string pointed by ptr, remove blanks and manage * string placed in quotes */ /* return NULL on mismatched quotes */ { char quote = 0; int length = 0; char *rtn_string = NULL; if (*ptr == '\"' || *ptr == '\'') { quote = *ptr; ptr++; } length = remove_blanks(ptr); if (quote != 0) { if (*(ptr + length - 1) == quote) *(ptr + length - 1) = '\0'; else { /* mismatched quotes */ need_correction = 1; return NULL; } } Set(rtn_string, ptr); return rtn_string; } int get_line(char *str, size_t size, FILE * file) /* similar to fgets, but increase line if necessary, * and continue over an "\" followed by an "\n" char */ { size_t size_max = size - 1; int i = 0; int c; while (i < size_max) { switch (c = getc(file)) { case '\n': /* check if the \n char is preceded by a "\" char : * in this case, suppress the "\", don't copy the \n, * and continue */ if (i > 0 && *(str + i - 1) == '\\') { i--; line++; continue; } else { *(str + i) = (char)'\0'; return OK; } break; case EOF: *(str + i) = (char)'\0'; /* we couldn't return EOF ( equal to ERR by default ) * nor ERR, which is used for another error */ return GET_LINE_EOF; default: *(str + i) = (char)c; i++; } } /* line is too long : goto next line and return ERR */ while (((c = getc(file)) != EOF) && (c != '\n')) ; line++; need_correction = 1; return ERR; } void init_default_line(cl_t * cl, cf_t * cf) /* clear all context/options from cl */ { bzero(cl, sizeof(cl_t)); Set(cl->cl_runas, runas); Set(cl->cl_mailto, runas); Free_safe(cl->cl_tz); set_default_opt(cl->cl_option); cl->cl_file = cf; } int read_file(char *filename, int fd) /* read file "name" and append cf_t list */ { cf_t *cf = NULL; FILE *file = NULL; char buf[LINE_LEN]; int max_lines; int max_entries = MAXENTRIES; int entries = 0; char *ptr = NULL; int ret; bzero(buf, sizeof(buf)); need_correction = 0; line = 1; file_name = filename; /* open file */ if ((file = fdopen(fd, "r")) == NULL) { fprintf(stderr, "Could not open \"%s\": %s\n", file_name, strerror(errno)); return ERR; } /* Rewind, just in case */ rewind(file); Alloc(cf, cf_t); cf->cf_env_list = env_list_init(); Set(cf->cf_user, user); init_default_line(&default_line, cf); if (debug_opt) fprintf(stderr, "FILE %s\n", file_name); if (strcmp(runas, ROOTNAME) == 0) max_entries = 65535; /* max_lines acts here as a security counter to avoid endless loop. */ max_lines = (max_entries * 10) + 10; while (entries < max_entries && line <= max_lines) { ret = get_line(buf, sizeof(buf), file); if (ret == ERR) { fprintf(stderr, "%s:%d: Line is too long (more than %d): skipping line.\n", file_name, line, (int)sizeof(buf)); continue; } ptr = buf; Skip_blanks(ptr); if (debug_opt && *ptr != '#' && *ptr != '\0') fprintf(stderr, " %s\n", buf); switch (*ptr) { case '#': case '\0': /* comments or empty line: skipping */ break; case '@': /* if it is not a shortcut line then read_shortcut() won't do anything. */ if (!read_shortcut(ptr, cf)) read_freq(ptr, cf); entries++; break; case '&': read_arys(ptr, cf); entries++; break; case '%': read_period(ptr, cf); entries++; break; case '!': ptr = read_opt(ptr, &default_line); if (ptr != NULL && *ptr != '\0') { fprintf(stderr, "%s:%d: Syntax error: string \"%s\" ignored\n", file_name, line, ptr); need_correction = 1; } break; default: if (isdigit((int)*ptr) || *ptr == '*') { read_arys(ptr, cf); entries++; } else read_env(ptr, cf); } line++; if (ret != OK) /* in this case, ret == GET_LINE_EOF : * no more lines, so we exit the loop */ break; } if (entries == max_entries) { error("%s:%d: maximum number of entries (%d) has been reached by %s", file_name, line, user); fprintf(stderr, "Anything after this line will be ignored\n"); } else if (line == max_lines) error("%s:%d: maximum number of lines (%d) has been reached by %s", file_name, line, user); cf->cf_next = file_base; file_base = cf; /* don't close as underlying fd may still be used by calling function */ if (fflush(file) != 0) error_e("could not fflush() file_name"); Free_safe(default_line.cl_runas); Free_safe(default_line.cl_mailto); Free_safe(default_line.cl_tz); if (!need_correction) return OK; else return 2; } void read_env(char *ptr, cf_t * cf) /* append env variable list. * (remove blanks) */ { char name[LINE_LEN]; int j = 0; char *val = NULL; bzero(name, sizeof(name)); /* copy env variable's name */ while ((isalnum((int)*ptr) || *ptr == '_') && *ptr != '=' && !isspace((int)*ptr) && j < sizeof(name)) { name[j++] = *ptr; ptr++; } name[j] = '\0'; if (name[0] == '\0') goto error; /* skip '=' and spaces around */ while (isspace((int)*ptr)) ptr++; /* if j == 0 name is a zero length string */ if (*ptr++ != '=' || j == 0) goto error; while (isspace((int)*ptr)) ptr++; /* get value */ if ((val = get_string(ptr)) == NULL) { fprintf(stderr, "%s:%d: Mismatched quotes: skipping line.\n", file_name, line); need_correction = 1; return; } if (debug_opt) fprintf(stderr, " Env : '%s=%s'\n", name, val); /* we ignore USER/LOGNAME's assignment */ if (strcmp(name, "USER") == 0 || strcmp(name, "LOGNAME") == 0) { fprintf(stderr, "%s:%d: USER or LOGNAME assignement is not allowed: ignored.\n", file_name, line); return; } /* the MAILTO assignment is, in fact, an fcron option : * we don't store it in the same way. */ /* please note that we check if the mailto is valid in conf.c */ if (strcmp(name, "MAILTO") == 0) { if (strcmp(val, "\0") == 0) { clear_mail(default_line.cl_option); clear_mailzerolength(default_line.cl_option); } else { Set(default_line.cl_mailto, val); set_mail(default_line.cl_option); } } else { env_list_setenv(cf->cf_env_list, name, val, 1); } Free_safe(val); return; error: fprintf(stderr, "%s:%d: Syntax error: skipping line.\n", file_name, line); need_correction = 1; return; } char * get_nice(char *ptr, int *nice) /* read a nice value and put it in variable nice */ { char negative = 0; if (*ptr == '-') { negative = 1; ptr++; } if ((ptr = get_num(ptr, nice, 20, 0, NULL)) == NULL) return NULL; if (negative == 1) { if (getuid() != rootuid) { fprintf(stderr, "must be privileged to use a negative argument " "with nice: set to 0\n"); need_correction = 1; *nice = 0; } *nice *= (-1); } return ptr; } size_t option_strlen(char *value) /* return the length of the string value of an option */ { char *ptr = value; size_t len = 0; /* look for the end of the option value */ while (ptr != NULL && *ptr != ')' && *ptr != '\0') { ptr++; len++; } return len; } int assign_option_string(char **var, char *value) /* Read the value of an option: if non-empty, assign it to the var, * otherwise set to NULL. * Returns the length of the value (0 if empty), or -1 on error. */ { char start = value[0]; char end = '\0'; size_t len = 0; Free_safe(*var); len = option_strlen(value); if (len <= 0) { return len; } end = value[len - 1]; /* spaces and quotes are not allowed before or after the value */ if (isspace((int)start) || isspace((int)end) || start == '\'' || start == '"' || end == '\'' || end == '"') { return -1; } *var = strndup2(value, len); return len; } char * get_bool(char *ptr, int *i) /* get a bool value : either true (1) or false (0) * return NULL on error */ { if (*ptr == '1') goto true; else if (*ptr == '0') goto false; else if (strncmp(ptr, "true", 4) == 0) { ptr += 3; goto true; } else if (strncmp(ptr, "yes", 3) == 0) { ptr += 2; goto true; } else if (strncmp(ptr, "false", 5) == 0) { ptr += 4; goto false; } else if (strncmp(ptr, "no", 2) == 0) { ptr += 1; goto false; } else return NULL; true: *i = 1; ptr++; return ptr; false: *i = 0; ptr++; return ptr; } char * read_opt(char *ptr, cl_t * cl) /* read one or several options and fill in the field "option" */ { char opt_name[20]; int i; char in_brackets; #define Handle_err \ { \ fprintf(stderr, "%s:%d: Argument(s) for option \"%s\" not valid: " \ "skipping end of line.\n", file_name, line, opt_name); \ need_correction = 1; \ return NULL; \ } if (*ptr == '!') ptr++; do { i = 0; bzero(opt_name, sizeof(opt_name)); while (isalnum((int)*ptr) && i < sizeof(opt_name)) opt_name[i++] = *ptr++; i = 1; in_brackets = 0; if (*ptr == '(') { in_brackets = 1; ptr++; /* spaces are not allowed -- make sure there is no leading space. */ if (isspace((int)*ptr)) { Handle_err; } } /* global options for a file */ if (strcmp(opt_name, "tzdiff") == 0) { char negative = 0; if (!in_brackets) Handle_err; if (*ptr == '-') { negative = 1; ptr++; } if ((ptr = get_num(ptr, &i, 24, 0, NULL)) == NULL) Handle_err; if (negative) cl->cl_file->cf_tzdiff = (-i); else cl->cl_file->cf_tzdiff = i; if (debug_opt) fprintf(stderr, " Opt : \"%s\" (-)%d\n", opt_name, i); } /* options related to a line (or a set of lines) */ else if (strcmp(opt_name, "timezone") == 0) { int len = -1; if (!in_brackets) { Handle_err; } /* assign_option_string() will set cl_tz to NULL is the value is empty, * which means "use the system timezone" */ len = assign_option_string(&(cl->cl_tz), ptr); if (len < 0) { Handle_err; } else { ptr += len; } if (debug_opt) { fprintf(stderr, " Opt : \"%s\" \"%s\"\n", opt_name, cl->cl_tz); } } else if (strcmp(opt_name, "s") == 0 || strcmp(opt_name, "serial") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) clear_serial(cl->cl_option); else set_serial(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "serialonce") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) set_serial_sev(cl->cl_option); else clear_serial_sev(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "lavgonce") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) set_lavg_sev(cl->cl_option); else clear_lavg_sev(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "exesev") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) clear_exe_sev(cl->cl_option); else set_exe_sev(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "b") == 0 || strcmp(opt_name, "bootrun") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) clear_bootrun(cl->cl_option); else set_bootrun(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "rebootreset") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) clear_rebootreset(cl->cl_option); else set_rebootreset(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "runatreboot") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) clear_runatreboot(cl->cl_option); else set_runatreboot(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "runonce") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) clear_runonce(cl->cl_option); else set_runonce(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "reset") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 1) { init_default_line(cl, cl->cl_file); } if (debug_opt) fprintf(stderr, " Opt : \"%s\"\n", opt_name); } else if (strcmp(opt_name, "f") == 0 || strcmp(opt_name, "first") == 0) { if (!in_brackets || (ptr = get_time(ptr, &(cl->cl_first), 1)) == NULL) Handle_err; if (debug_opt) fprintf(stderr, " Opt : \"%s\" %ld\n", opt_name, (long int)cl->cl_first); } else if (strcmp(opt_name, "r") == 0 || strcmp(opt_name, "runfreq") == 0) { if (cl->cl_runfreq == 1) { fprintf(stderr, "cannot change runfreq value in a %%-line"); Handle_err; } if (!in_brackets || (ptr = get_num(ptr, &i, USHRT_MAX, 0, NULL)) == NULL) Handle_err; if (i <= 1) { fprintf(stderr, "runfreq must be 2 or more.\n"); Handle_err; } cl->cl_runfreq = i; if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "strict") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) clear_strict(cl->cl_option); else set_strict(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "noticenotrun") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) clear_notice_notrun(cl->cl_option); else set_notice_notrun(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "lavg") == 0) { if (!in_brackets || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL) Handle_err; cl->cl_lavg[0] = i; if (debug_opt) fprintf(stderr, " Opt : 'lavg1' %d\n", i); if (*ptr++ != ',') Handle_err; if (!in_brackets || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL) Handle_err; cl->cl_lavg[1] = i; if (debug_opt) fprintf(stderr, " Opt : 'lavg5' %d\n", i); if (*ptr++ != ',') Handle_err; if (!in_brackets || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL) Handle_err; cl->cl_lavg[2] = i; if (debug_opt) fprintf(stderr, " Opt : 'lavg15' %d\n", i); if (cl->cl_lavg[0] || cl->cl_lavg[1] || cl->cl_lavg[2]) set_lavg(cl->cl_option); else clear_lavg(cl->cl_option); #ifdef NOLOADAVG warn("As fcron has been compiled with no procfs support,\n" "you will not be able to use the lavg* options"); #endif /* NOLOADAVG */ } else if (strcmp(opt_name, "lavg1") == 0) { if (!in_brackets || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL) Handle_err; cl->cl_lavg[0] = i; if (cl->cl_lavg[0] || cl->cl_lavg[1] || cl->cl_lavg[2]) set_lavg(cl->cl_option); else clear_lavg(cl->cl_option); #if NOLOADAVG warn("As fcron has been compiled with no procfs support,\n" "you will not be able to use the lavg* options"); #endif /* NOLOADAVG */ if (debug_opt) fprintf(stderr, " Opt : 'lavg1' %d\n", i); } else if (strcmp(opt_name, "lavg5") == 0) { if (!in_brackets || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL) Handle_err; cl->cl_lavg[1] = i; if (cl->cl_lavg[0] || cl->cl_lavg[1] || cl->cl_lavg[2]) set_lavg(cl->cl_option); else clear_lavg(cl->cl_option); #ifdef NOLOADAVG warn("As fcron has been compiled with no procfs support,\n" "you will not be able to use the lavg* options"); #endif /* NOLOADAVG = 0 */ if (debug_opt) fprintf(stderr, " Opt : 'lavg5' %d\n", i); } else if (strcmp(opt_name, "lavg15") == 0) { if (!in_brackets || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL) Handle_err; cl->cl_lavg[2] = i; if (cl->cl_lavg[0] || cl->cl_lavg[1] || cl->cl_lavg[2]) set_lavg(cl->cl_option); else clear_lavg(cl->cl_option); #ifdef NOLOADAVG warn("As fcron has been compiled with no procfs support,\n" "you will not be able to use the lavg* options"); #endif /* NOLOADAVG = 0 */ if (debug_opt) fprintf(stderr, " Opt : 'lavg15' %d\n", i); } else if (strcmp(opt_name, "lavgand") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) set_lor(cl->cl_option); else set_land(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "lavgor") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) set_land(cl->cl_option); else set_lor(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "u") == 0 || strcmp(opt_name, "until") == 0) { if (!in_brackets || (ptr = get_time(ptr, &(cl->cl_until), 0)) == NULL) Handle_err; if (debug_opt) fprintf(stderr, " Opt : \"%s\" %ld\n", opt_name, (long int)cl->cl_until); } else if (strcmp(opt_name, "m") == 0 || strcmp(opt_name, "mail") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) { clear_mail(cl->cl_option); clear_mailzerolength(cl->cl_option); } else set_mail(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "forcemail") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) clear_mailzerolength(cl->cl_option); else { set_mailzerolength(cl->cl_option); set_mail(cl->cl_option); } if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "mailto") == 0) { int len = -1; if (!in_brackets) { Handle_err; } /* assign_option_string() set the value to NULL if the length is zero. * However cl_mailto must not be NULL (as expected in * conf.c:add_line_to_file()), so we check if the length is >= 0 * before calling assign_option_string() */ /* Also please note that we check if the mailto is valid in conf.c */ len = option_strlen(ptr); if (len <= 0) { clear_mail(cl->cl_option); clear_mailzerolength(cl->cl_option); if (debug_opt) { fprintf(stderr, " Opt : \"mail\" 0\n"); fprintf(stderr, " Opt : \"forcemail\" 0\n"); } } else { len = assign_option_string(&(cl->cl_mailto), ptr); if (len < 0) { Handle_err; } else { ptr += len; set_mail(cl->cl_option); } } if (debug_opt) fprintf(stderr, " Opt : \"%s\" \"%s\"\n", opt_name, cl->cl_mailto); } else if (strcmp(opt_name, "erroronlymail") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) clear_erroronlymail(cl->cl_option); else set_erroronlymail(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "dayand") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) set_dayor(cl->cl_option); else set_dayand(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "dayor") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) set_dayand(cl->cl_option); else set_dayor(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "nolog") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) clear_nolog(cl->cl_option); else set_nolog(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "volatile") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) clear_volatile(cl->cl_option); else set_volatile(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "stdout") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) clear_stdout(cl->cl_option); else set_stdout(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "n") == 0 || strcmp(opt_name, "nice") == 0) { if (!in_brackets || (ptr = get_nice(ptr, &i)) == NULL) Handle_err; cl->cl_nice = (char)i; if (debug_opt) fprintf(stderr, " Opt : \"%s\" (-)%d\n", opt_name, i); } else if (strcmp(opt_name, "runas") == 0) { int len = -1; char *runas = NULL; struct passwd *pas; if (!in_brackets) { Handle_err; } len = assign_option_string(&runas, ptr); if (len <= 0) { Handle_err; } else { ptr += len; } if (getuid() != rootuid) { fprintf(stderr, "must be privileged to use option runas: " "skipping option\n"); need_correction = 1; } else if (len > USER_NAME_LEN) { fprintf(stderr, "runas: user name \"%s\" longer than %d" "characters: skipping option\n", runas, USER_NAME_LEN); need_correction = 1; } else if ((pas = getpwnam(runas)) == NULL) { fprintf(stderr, "runas: \"%s\" is not in passwd file : " "ignored", runas); need_correction = 1; } if (need_correction) { Free_safe(runas); } else { /* only set cl_runas if all is well, as cl_runas MUST always * be set to a valid value */ Set(cl->cl_runas, runas); } /* all good */ if (debug_opt) fprintf(stderr, " Opt : \"%s\" %s ptr=%p\n", opt_name, cl->cl_runas, cl->cl_runas); } else if (strcmp(opt_name, "random") == 0) { if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL) Handle_err; if (i == 0) clear_random(cl->cl_option); else set_random(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } else if (strcmp(opt_name, "jitter") == 0) { if (!in_brackets || (ptr = get_num(ptr, &i, UCHAR_MAX, 0, NULL)) == NULL) Handle_err; cl->cl_jitter = i; if (debug_opt) fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i); } /* handle %-line : we check if we are really in a %-line (which we do not do * for other options), because writing "&hourly" in a fcrontab results in an * error (hourly ignored) hard to find, and, in any case, annoying. */ else if (cl->cl_runfreq == 1) { /* options to run once per interval : * ignore every fields below the limit */ if (strcmp(opt_name, "mins") == 0) { /* nothing to do */ if (debug_opt) fprintf(stderr, " Opt : \"%s\"\n", opt_name); } else if (strcmp(opt_name, "hours") == 0) { set_freq_mins(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\"\n", opt_name); } else if (strcmp(opt_name, "days") == 0) { set_freq_mins(cl->cl_option); set_freq_hrs(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\"\n", opt_name); } else if (strcmp(opt_name, "mons") == 0) { set_freq_mins(cl->cl_option); set_freq_hrs(cl->cl_option); set_freq_days(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\"\n", opt_name); } else if (strcmp(opt_name, "dow") == 0) { set_freq_mins(cl->cl_option); set_freq_hrs(cl->cl_option); set_freq_days(cl->cl_option); set_freq_mons(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\"\n", opt_name); } /* run once an element of the selected field * (once an hour, once a day, etc) */ else if (strcmp(opt_name, "hourly") == 0) { set_freq_hrs(cl->cl_option); set_freq_periodically(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\"\n", opt_name); } else if (strcmp(opt_name, "daily") == 0) { set_freq_days(cl->cl_option); set_freq_periodically(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\"\n", opt_name); } else if (strcmp(opt_name, "monthly") == 0) { set_freq_mons(cl->cl_option); set_freq_periodically(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\"\n", opt_name); } else if (strcmp(opt_name, "weekly") == 0) { set_freq_dow(cl->cl_option); set_freq_periodically(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\"\n", opt_name); } /* run once an element of the selected field * from middle to middle of that field * (ie once from 12h to 12h the following day) */ else if (strcmp(opt_name, "midhourly") == 0) { set_freq_hrs(cl->cl_option); set_freq_periodically(cl->cl_option); set_freq_mid(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\"\n", opt_name); } else if (strcmp(opt_name, "middaily") == 0 || strcmp(opt_name, "nightly") == 0) { set_freq_days(cl->cl_option); set_freq_periodically(cl->cl_option); set_freq_mid(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\"\n", opt_name); } else if (strcmp(opt_name, "midmonthly") == 0) { set_freq_mons(cl->cl_option); set_freq_periodically(cl->cl_option); set_freq_mid(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\"\n", opt_name); } else if (strcmp(opt_name, "midweekly") == 0) { set_freq_dow(cl->cl_option); set_freq_periodically(cl->cl_option); set_freq_mid(cl->cl_option); if (debug_opt) fprintf(stderr, " Opt : \"%s\"\n", opt_name); } } else { fprintf(stderr, "%s:%d: Option \"%s\" unknown: " "skipping option.\n", file_name, line, opt_name); need_correction = 1; } if (in_brackets) { if (*ptr != ')') { Handle_err} else ptr++; } } while (*ptr == ',' && ptr++); Skip_blanks(ptr); return ptr; } char * get_time(char *ptr, time_t * time, int zero_allowed) /* convert time read in string in time_t format */ { time_t sum; *time = 0; while ((*ptr != ' ') && (*ptr != '\t') && (*ptr != '\0') && (*ptr != ')')) { sum = 0; while (isdigit((int)*ptr)) { sum *= 10; sum += *ptr - 48; ptr++; } /* interpret multipliers */ switch (*ptr) { case 'm': /* months */ sum *= 4; case 'w': /* weeks */ sum *= 7; case 'd': /* days */ sum *= 24; case 'h': /* hours */ sum *= 3600; case 's': /* seconds */ ptr++; break; case ' ': case '\t': case ')': sum *= 60; /* minutes */ break; default: need_correction = 1; return NULL; } *time += sum; } Skip_blanks(ptr); if (*time == 0 && !zero_allowed) { need_correction = 1; return NULL; } else return ptr; } char * check_username(char *ptr, cf_t * cf, cl_t * cl) /* check ptr to see if the first word is a username, returns new ptr */ { short int indx = 0; char username[USER_NAME_LEN]; struct passwd *userpwent; /* check to see if next word is a username */ /* we don't allow quotes, to be able to distinguish a user name from * a command line (where quotes are allowed) */ while (isalnum((int)ptr[indx]) || ptr[indx] == '-' || ptr[indx] == '_') indx++; if (indx >= USER_NAME_LEN) indx = USER_NAME_LEN - 1; strncpy(username, ptr, indx); username[indx] = '\0'; if ((userpwent = getpwnam(username)) != NULL) { /* found the user */ ptr = ptr + indx; /* move ptr to the next word */ Skip_blanks(ptr); if (getuid() != rootuid) { fprintf(stderr, "must be privileged to run as another user : " "ignoring\n"); } else { Set(cl->cl_runas, username); if (debug_opt) fprintf(stderr, " Opt : inline_runas %s\n", username); } } return ptr; } char * read_word(char *ptr, char *buf, size_t buf_size) /* copy a word to buf with a max_size of buf_size and return a pointer * to the next char after the word */ { int i = 0; bzero(buf, buf_size); while (isalnum((int)*ptr) && i < buf_size) buf[i++] = *ptr++; return ptr; } int read_shortcut(char *ptr, cf_t * cf) /* try to read a shortcut entry, and if it is one then append a line to cf * Return 1 if that was a shortcut entry. If it wasn't, return 0 and make sure * ptr is back to its orig value. */ { cl_t *cl = NULL; char shortcut[20]; char *ptr_orig = ptr; cl = dups_cl(&default_line); /* skip the @ */ ptr++; ptr = read_word(ptr, shortcut, sizeof(shortcut)); /* time&date by default -- we'll switch to freq if @reboot */ set_td(cl->cl_option); cl->cl_remain = cl->cl_runfreq; /* FIXME: necessary?? */ if (strcmp(shortcut, "reboot") == 0) { set_freq(cl->cl_option); set_runatreboot(cl->cl_option); set_runonce(cl->cl_option); clear_volatile(cl->cl_option); cl->cl_runfreq = 0; cl->cl_first = 0; /* the job will not be rescheduled after the first execution (flag is_hasrun), * we set timefreq to LONG_MAX just in case */ cl->cl_timefreq = LONG_MAX; if (debug_opt) fprintf(stderr, " Shc : @reboot\n"); } else if (strcmp(shortcut, "yearly") == 0 || strcmp(shortcut, "annually") == 0) { bit_set(cl->cl_mins, 0); bit_set(cl->cl_hrs, 0); bit_set(cl->cl_days, 1); bit_set(cl->cl_mons, 0); bit_nset(cl->cl_dow, 0, 7); if (debug_opt) fprintf(stderr, " Shc : @yearly\n"); } else if (strcmp(shortcut, "monthly") == 0) { bit_set(cl->cl_mins, 0); bit_set(cl->cl_hrs, 0); bit_set(cl->cl_days, 1); bit_nset(cl->cl_mons, 0, 11); bit_nset(cl->cl_dow, 0, 7); if (debug_opt) fprintf(stderr, " Shc : @monthly\n"); } else if (strcmp(shortcut, "weekly") == 0) { bit_set(cl->cl_mins, 0); bit_set(cl->cl_hrs, 0); bit_nset(cl->cl_days, 0, 31); bit_nset(cl->cl_mons, 0, 11); bit_set(cl->cl_dow, 0); bit_set(cl->cl_dow, 7); /* 0 and 7 are both sunday */ if (debug_opt) fprintf(stderr, " Shc : @weekly\n"); } else if (strcmp(shortcut, "daily") == 0 || strcmp(shortcut, "midnight") == 0) { bit_set(cl->cl_mins, 0); bit_set(cl->cl_hrs, 0); bit_nset(cl->cl_days, 0, 31); bit_nset(cl->cl_mons, 0, 11); bit_nset(cl->cl_dow, 0, 7); if (debug_opt) fprintf(stderr, " Shc : @daily\n"); } else if (strcmp(shortcut, "hourly") == 0) { bit_set(cl->cl_mins, 0); bit_nset(cl->cl_hrs, 0, 23); bit_nset(cl->cl_days, 0, 31); bit_nset(cl->cl_mons, 0, 11); bit_nset(cl->cl_dow, 0, 7); if (debug_opt) fprintf(stderr, " Shc : @hourly\n"); } else { /* this is not a shortcut line but a normal @-line: */ ptr = ptr_orig; return 0; } /* The next char must be a space (no other option allowed when using * a shortcut: if the user wants to use options, they should use the * native fcron lines */ if (!isspace((int)*ptr)) { fprintf(stderr, "%s:%d: No space after shortcut: skipping line.\n", file_name, line); goto exiterr; } /* skip spaces before the username / shell command */ while (isspace((int)*ptr)) ptr++; /* check for inline runas */ ptr = check_username(ptr, cf, cl); /* get cl_shell field ( remove trailing blanks ) */ if ((cl->cl_shell = get_string(ptr)) == NULL) { fprintf(stderr, "%s:%d: Mismatched quotes: skipping line.\n", file_name, line); goto exiterr; } if (strcmp(cl->cl_shell, "\0") == 0) { fprintf(stderr, "%s:%d: No shell command: skipping line.\n", file_name, line); Free_safe(cl->cl_shell); goto exiterr; } #ifndef USE_SENDMAIL clear_mail(cl->cl_option); clear_mailzerolength(cl->cl_option); #endif cl->cl_next = cf->cf_line_base; cf->cf_line_base = cl; if (debug_opt) fprintf(stderr, " Cmd \"%s\" (shortcut)\n", cl->cl_shell); return 1; exiterr: Free_safe(cl); need_correction = 1; return 1; } void read_freq(char *ptr, cf_t * cf) /* read a freq entry, and append a line to cf */ { cl_t *cl = NULL; cl = dups_cl(&default_line); cl->cl_first = -1; /* 0 is a valid value, so we have to use -1 to detect unset */ /* skip the @ */ ptr++; /* get the time before first execution or the options */ if (isdigit((int)*ptr)) { if ((ptr = get_time(ptr, &(cl->cl_first), 1)) == NULL) { fprintf(stderr, "%s:%d: Error while reading first delay:" " skipping line.\n", file_name, line); goto exiterr; } Skip_blanks(ptr); } else if (isalnum((int)*ptr)) { if ((ptr = read_opt(ptr, cl)) == NULL) goto exiterr; } else Skip_blanks(ptr); /* we set this here, because it may be unset by read_opt (reset option) */ cl->cl_runfreq = 0; set_freq(cl->cl_option); /* then cl_timefreq */ if ((ptr = get_time(ptr, (time_t *) & (cl->cl_timefreq), 0)) == NULL || cl->cl_timefreq < 1) { fprintf(stderr, "%s:%d: Error while reading frequency %s: skipping line.\n", file_name, line, (cl->cl_timefreq < 10) ? "(lower than 10s) " : ""); goto exiterr; } if (cl->cl_timefreq == 0) { fprintf(stderr, "%s:%d: no freq specified: skipping line.\n", file_name, line); goto exiterr; } if (cl->cl_first == -1) /* time before first execution was not specified explicitely */ cl->cl_first = cl->cl_timefreq; /* check for inline runas */ ptr = check_username(ptr, cf, cl); /* get cl_shell field ( remove trailing blanks ) */ if ((cl->cl_shell = get_string(ptr)) == NULL) { fprintf(stderr, "%s:%d: Mismatched quotes: skipping line.\n", file_name, line); goto exiterr; } if (strcmp(cl->cl_shell, "\0") == 0) { fprintf(stderr, "%s:%d: No shell command: skipping line.\n", file_name, line); Free_safe(cl->cl_shell); goto exiterr; } #ifndef USE_SENDMAIL clear_mail(cl->cl_option); clear_mailzerolength(cl->cl_option); #endif cl->cl_next = cf->cf_line_base; cf->cf_line_base = cl; if (debug_opt) fprintf(stderr, " Cmd \"%s\", timefreq %ld, first %ld\n", cl->cl_shell, cl->cl_timefreq, (long int)cl->cl_first); return; exiterr: free_line(cl); need_correction = 1; return; } #define R_field(PTR, ARY, MAX, ARYCONST, DESCRP) \ if((PTR = read_field(PTR, ARY, MAX, ARYCONST)) == NULL) { \ if (debug_opt) \ fprintf(stderr, "\n"); \ fprintf(stderr, "%s:%d: Error while reading " DESCRP " field: " \ "skipping line.\n", file_name, line); \ Free_safe(cl); \ return; \ } void read_arys(char *ptr, cf_t * cf) /* read a run freq number plus a normal fcron line */ { cl_t *cl = NULL; int i = 0; cl = dups_cl(&default_line); /* set cl_remain if not specified */ if (*ptr == '&') { ptr++; if (isdigit((int)*ptr)) { if ((ptr = get_num(ptr, &i, USHRT_MAX, 0, NULL)) == NULL) { fprintf(stderr, "%s:%d: Error while reading runfreq:" " skipping line.\n", file_name, line); goto exiterr; } else { if (i <= 1) { fprintf(stderr, "%s:%d: runfreq must be 2 or more :" " skipping line.\n", file_name, line); goto exiterr; } cl->cl_runfreq = (unsigned short)i; } } else if (isalnum((int)*ptr)) if ((ptr = read_opt(ptr, cl)) == NULL) { goto exiterr; } Skip_blanks(ptr); } cl->cl_remain = cl->cl_runfreq; /* we set this here, because it may be unset by read_opt (reset option) */ set_td(cl->cl_option); if (debug_opt) fprintf(stderr, " "); /* get the fields (check for errors) */ R_field(ptr, cl->cl_mins, 59, NULL, "minutes"); R_field(ptr, cl->cl_hrs, 23, NULL, "hours"); R_field(ptr, cl->cl_days, 31, NULL, "days"); /* month are defined by user from 1 to 12 : max is 12 */ R_field(ptr, cl->cl_mons, 12, mons_ary, "months"); R_field(ptr, cl->cl_dow, 7, dows_ary, "days of week"); if (debug_opt) /* if debug_opt is set, we print informations in read_field function, * but no end line : we print it here */ fprintf(stderr, " remain %d\n", cl->cl_remain); /* check for inline runas */ ptr = check_username(ptr, cf, cl); /* get the shell command (remove trailing blanks) */ if ((cl->cl_shell = get_string(ptr)) == NULL) { fprintf(stderr, "%s:%d: Mismatched quotes: skipping line.\n", file_name, line); goto exiterr; } if (strcmp(cl->cl_shell, "\0") == 0) { fprintf(stderr, "%s:%d: No shell command: skipping line.\n", file_name, line); Free_safe(cl->cl_shell); goto exiterr; } #ifndef USE_SENDMAIL clear_mail(cl->cl_option); clear_mailzerolength(cl->cl_option); #endif cl->cl_next = cf->cf_line_base; cf->cf_line_base = cl; if (debug_opt) fprintf(stderr, " Cmd \"%s\"\n", cl->cl_shell); return; exiterr: need_correction = 1; free_line(cl); return; } void read_period(char *ptr, cf_t * cf) /* read a line to run periodically (i.e. once a day, once a week, etc) */ { cl_t *cl = NULL; short int remain = 8; cl = dups_cl(&default_line); /* skip the % */ ptr++; /* a runfreq set to 1 means : this is a periodical line * (runfreq cannot be changed by read_opt() if already set to 1) */ cl->cl_remain = cl->cl_runfreq = 1; /* set cl_remain if not specified */ if ((ptr = read_opt(ptr, cl)) == NULL) { goto exiterr; } Skip_blanks(ptr); /* we set this here, because it may be unset by read_opt (reset option) */ set_td(cl->cl_option); if (debug_opt) fprintf(stderr, " "); if (is_freq_periodically(cl->cl_option)) { if (is_freq_mins(cl->cl_option)) remain = 0; else if (is_freq_hrs(cl->cl_option)) remain = 1; else if (is_freq_days(cl->cl_option)) remain = 2; else if (is_freq_mons(cl->cl_option)) remain = 3; else if (is_freq_dow(cl->cl_option)) remain = 2; } /* get the fields (check for errors) */ if (remain-- > 0) { R_field(ptr, cl->cl_mins, 59, NULL, "minutes")} else bit_nset(cl->cl_mins, 0, 59); if (remain-- > 0) { R_field(ptr, cl->cl_hrs, 23, NULL, "hours")} else bit_nset(cl->cl_hrs, 0, 23); if (remain-- > 0) { R_field(ptr, cl->cl_days, 31, NULL, "days")} else bit_nset(cl->cl_days, 1, 32); /* month are defined by user from 1 to 12 : max is 12 */ if (remain-- > 0) { R_field(ptr, cl->cl_mons, 12, mons_ary, "months")} else bit_nset(cl->cl_mons, 0, 11); if (remain-- > 0) { R_field(ptr, cl->cl_dow, 7, dows_ary, "days of week")} else bit_nset(cl->cl_dow, 0, 7); if (debug_opt) /* if debug_opt is set, we print informations in read_field function, * but no end line : we print it here */ fprintf(stderr, " remain %d\n", cl->cl_remain); /* check for inline runas */ ptr = check_username(ptr, cf, cl); /* get the shell command (remove trailing blanks) */ if ((cl->cl_shell = get_string(ptr)) == NULL) { fprintf(stderr, "%s:%d: Mismatched quotes: skipping line.\n", file_name, line); goto exiterr; } if (strcmp(cl->cl_shell, "\0") == 0) { fprintf(stderr, "%s:%d: No shell command: skipping line.\n", file_name, line); Free_safe(cl->cl_shell); goto exiterr; } else if (cl->cl_shell[0] == '*' || isdigit((int)cl->cl_shell[0])) fprintf(stderr, "%s:%d: Warning : shell command beginning by '%c'.\n", file_name, line, cl->cl_shell[0]); /* check for non matching if option runfreq is set to 1 */ if (!is_freq_periodically(cl->cl_option)) { const size_t s_mins = 60, s_hrs = 24; const size_t s_days = 32, s_mons = 12; const size_t s_dow = 8; int j = 0; if (!is_freq_mins(cl->cl_option)) { bit_ffc(cl->cl_mins, s_mins, &j); if (j != -1 && j < s_mins) goto ok; } if (!is_freq_hrs(cl->cl_option)) { bit_ffc(cl->cl_hrs, s_hrs, &j); if (j != -1 && j < s_hrs) goto ok; } if (!is_freq_days(cl->cl_option)) { bit_ffc(cl->cl_days, s_days, &j); if (j != -1 && j < s_days) { if (is_dayand(cl->cl_option)) goto ok; else { if (!is_freq_dow(cl->cl_option)) { bit_ffc(cl->cl_dow, s_dow, &j); if (j != -1 && j < s_dow) goto ok; } } } } if (!is_freq_mons(cl->cl_option)) { bit_ffc(cl->cl_mons, s_mons, &j); if (j != -1 && j < s_mons) goto ok; } if (!is_freq_dow(cl->cl_option)) { bit_ffc(cl->cl_dow, s_dow, &j); if (j != -1 && j < s_dow && is_dayand(cl->cl_option)) goto ok; } fprintf(stderr, "%s:%d: periodical line with no intervals: " "skipping line.\n", file_name, line); goto exiterr; } ok: #ifndef USE_SENDMAIL clear_mail(cl->cl_option); clear_mailzerolength(cl->cl_option); #endif cl->cl_next = cf->cf_line_base; cf->cf_line_base = cl; if (debug_opt) fprintf(stderr, " Cmd \"%s\"\n", cl->cl_shell); return; exiterr: need_correction = 1; free_line(cl); return; } char * get_num(char *ptr, int *num, int max, short int decimal, const char **names) /* read a string's number and return it under int format. * Also check if that number is less than max */ { int i = 0; *num = 0; if (isalpha((int)*ptr)) { if (names == NULL) { need_correction = 1; return NULL; } /* set string to lower case */ for (i = 0; i < strlen(names[0]); i++) *(ptr + i) = tolower(*(ptr + i)); for (i = 0; names[i]; ++i) if (strncmp(ptr, names[i], strlen(names[i])) == 0) { *num = i; ptr += strlen(names[i]); return ptr; break; } /* string is not in name list */ need_correction = 1; return NULL; } else { while (isdigit((int)*ptr) || *ptr == '.') { if (*ptr == '.' && ptr++ && i++ > 0) return NULL; if (i > 0 && --decimal < 0) { /* the decimal number is exceeded : we round off, * skip the other decimals and return */ if (*ptr >= '5') *num += 1; while (isdigit((int)*(++ptr))) ; ptr--; } else { *num *= 10; *num += *ptr - 48; } if (*num > max) { need_correction = 1; return NULL; } ptr++; } if (decimal > 0) *num *= 10 * decimal; } return ptr; } char * read_field(char *ptr, bitstr_t * ary, int max, const char **names) /* read a field like "2,5-8,10-20/2,21-30~25" and fill ary */ { int start = 0; int stop = 0; int step = 0; int rm = 0; int i = 0; while ((*ptr != ' ') && (*ptr != '\t') && (*ptr != '\0')) { start = stop = step = 0; /* there may be a "," */ if (*ptr == ',') ptr++; if (*ptr == '*') { /* we have to fill everything (may be modified by a step ) */ start = 0; /* user set month from 1 to 12, but we manage it internally * as a number from 0 to 11 */ stop = (max == 12) ? 11 : max; ptr++; } else { ptr = get_num(ptr, &start, max, 0, names); if (ptr == NULL) return NULL; if (max == 12) /* this number is part of the month field. * user set it from 1 to 12, but we manage it internally * as a number from 0 to 11 : we remove 1 to start */ start -= 1; if (*ptr == ',' || *ptr == ' ' || *ptr == '\t') { /* this is a single number : set up array and continue */ if (debug_opt) fprintf(stderr, " %d", start); bit_set(ary, start); continue; } /* check for a dash */ else if (*ptr == '-') { ptr++; ptr = get_num(ptr, &stop, max, 0, names); if (ptr == NULL) /* we reached the end of the string to parse */ return NULL; if (max == 12) /* this number is part of the month field. * user set it from 1 to 12, but we manage it internally * as a number from 0 to 11 : we remove 1 to stop */ stop -= 1; } else { /* syntax error */ need_correction = 1; return NULL; } } /* check for step size */ if (*ptr == '/') { ptr++; if ((ptr = get_num(ptr, &step, max, 0, names)) == NULL || step == 0) return NULL; } else /* step undefined : default is 0 */ step = 1; /* fill array */ if (debug_opt) fprintf(stderr, " %d-%d/%d", start, stop, step); if (start < stop) for (i = start; i <= stop; i += step) bit_set(ary, i); else { short int field_max = (max == 12) ? 11 : max; /* this is a field like (for hours field) "22-3" : * we should set from 22 to 3 (not from 3 to 22 or nothing :)) ) */ for (i = start; i <= field_max; i += step) bit_set(ary, i); for (i -= (field_max + 1); i <= stop; i += step) bit_set(ary, i); } /* finally, remove unwanted values */ while (*ptr == '~') { ptr++; rm = 0; if ((ptr = get_num(ptr, &rm, max, 0, names)) == NULL) return NULL; if (max == 12) /* this number is part of the month field. * user set it from 1 to 12, but we manage it internally * as a number from 0 to 11 : we remove 1 to rm */ rm -= 1; if (debug_opt) fprintf(stderr, " ~%d", rm); bit_clear(ary, rm); /* if we remove one value of Sunday, remove the other */ if (max == 7 && rm == 0) { bit_clear(ary, 7); if (debug_opt) fprintf(stderr, " ~%d", 7); } else if (max == 7 && rm == 7) { bit_clear(ary, 0); if (debug_opt) fprintf(stderr, " ~%d", 0); } } } /* Sunday is both 0 and 7 : if one is set, set the other */ if (max == 7) { if (bit_test(ary, 0)) bit_set(ary, 7); else if (bit_test(ary, 7)) bit_set(ary, 0); } Skip_blanks(ptr); if (debug_opt) fprintf(stderr, " #"); return ptr; } void delete_file(const char *user_name) /* free a file if user_name is not null * otherwise free all files */ { cf_t *file = NULL; cf_t *prev_file = NULL; cl_t *line = NULL; cl_t *cur_line = NULL; file = file_base; while (file != NULL) { if (strcmp(user_name, file->cf_user) == 0) { /* free lines */ cur_line = file->cf_line_base; while ((line = cur_line) != NULL) { cur_line = line->cl_next; free_line(line); } break; } else { prev_file = file; file = file->cf_next; } } if (file == NULL) /* file not in list */ return; /* remove file from list */ if (prev_file == NULL) file_base = file->cf_next; else prev_file->cf_next = file->cf_next; /* free env variables */ env_list_destroy(file->cf_env_list); /* finally free file itself */ Free_safe(file->cf_user); Free_safe(file); } int save_file(char *path) /* Store the informations relatives to the executions * of tasks at a defined frequency of system's running time */ { cf_t *file = NULL; if (debug_opt) fprintf(stderr, "Saving ...\n"); for (file = file_base; file; file = file->cf_next) { /* save_file() is run under user's rights. * If fcrontab is run by root for a normal user, we must change the file's * ownership to this user, in order to make fcron check the runas fields. * (a malicious user could put a runas(root) and wait for the fcrontab to be * installed by root) */ #ifdef USE_SETE_ID if (save_file_safe(file, path, "fcrontab", asuid, fcrontab_gid, 0) == ERR) return ERR; #else if (save_file_safe (file, path, "fcrontab", fcrontab_uid, fcrontab_gid, 0) == ERR) return ERR; #endif } return OK; }