static void load_crontab(const char *fileName) { struct parser_t *parser; struct stat sbuf; int maxLines; char *tokens[6]; #if ENABLE_FEATURE_CROND_CALL_SENDMAIL char *mailTo = NULL; #endif char *shell = NULL; delete_cronfile(fileName); if (!getpwnam(fileName)) { log7("ignoring file '%s' (no such user)", fileName); return; } parser = config_open(fileName); if (!parser) return; maxLines = (strcmp(fileName, "root") == 0) ? 65535 : MAXLINES; if (fstat(fileno(parser->fp), &sbuf) == 0 && sbuf.st_uid == DAEMON_UID) { CronFile *file = xzalloc(sizeof(CronFile)); CronLine **pline; int n; file->cf_username = xstrdup(fileName); pline = &file->cf_lines; while (1) { CronLine *line; if (!--maxLines) { bb_error_msg("user %s: too many lines", fileName); break; } n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY); if (!n) break; log5("user:%s entry:%s", fileName, parser->data); /* check if line is setting MAILTO= */ if (is_prefixed_with(tokens[0], "MAILTO=")) { #if ENABLE_FEATURE_CROND_CALL_SENDMAIL free(mailTo); mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL; #endif /* otherwise just ignore such lines */ continue; } if (is_prefixed_with(tokens[0], "SHELL=")) { free(shell); shell = xstrdup(&tokens[0][6]); continue; } //TODO: handle HOME= too? "man crontab" says: //name = value // //where the spaces around the equal-sign (=) are optional, and any subsequent //non-leading spaces in value will be part of the value assigned to name. //The value string may be placed in quotes (single or double, but matching) //to preserve leading or trailing blanks. // //Several environment variables are set up automatically by the cron(8) daemon. //SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd //line of the crontab's owner. HOME and SHELL may be overridden by settings //in the crontab; LOGNAME may not. /* check if a minimum of tokens is specified */ if (n < 6) continue; *pline = line = xzalloc(sizeof(*line)); /* parse date ranges */ ParseField(file->cf_username, line->cl_Mins, 60, 0, NULL, tokens[0]); ParseField(file->cf_username, line->cl_Hrs, 24, 0, NULL, tokens[1]); ParseField(file->cf_username, line->cl_Days, 32, 0, NULL, tokens[2]); ParseField(file->cf_username, line->cl_Mons, 12, -1, MonAry, tokens[3]); ParseField(file->cf_username, line->cl_Dow, 7, 0, DowAry, tokens[4]); /* * fix days and dow - if one is not "*" and the other * is "*", the other is set to 0, and vise-versa */ FixDayDow(line); #if ENABLE_FEATURE_CROND_CALL_SENDMAIL /* copy mailto (can be NULL) */ line->cl_mailto = xstrdup(mailTo); #endif line->cl_shell = xstrdup(shell); /* copy command */ line->cl_cmd = xstrdup(tokens[5]); pline = &line->cl_next; //bb_error_msg("M[%s]F[%s][%s][%s][%s][%s][%s]", mailTo, tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]); } *pline = NULL; file->cf_next = G.cron_files; G.cron_files = file; } config_close(parser); #if ENABLE_FEATURE_CROND_CALL_SENDMAIL free(mailTo); #endif free(shell); }
static void SynchronizeFile(const char *fileName) { int maxEntries = MAXLINES; int maxLines; char buf[1024]; if (strcmp(fileName, "root") == 0) { maxEntries = 65535; } maxLines = maxEntries * 10; if (fileName) { FILE *fi; DeleteFile(fileName); fi = fopen(fileName, "r"); if (fi != NULL) { struct stat sbuf; if (fstat(fileno(fi), &sbuf) == 0 && sbuf.st_uid == DaemonUid) { CronFile *file = calloc(1, sizeof(CronFile)); CronLine **pline; file->cf_User = strdup(fileName); pline = &file->cf_LineBase; while (fgets(buf, sizeof(buf), fi) != NULL && --maxLines) { CronLine line; char *ptr; trim(buf); if (buf[0] == 0 || buf[0] == '#') { continue; } if (--maxEntries == 0) { break; } memset(&line, 0, sizeof(line)); #ifdef FEATURE_DEBUG_OPT if (DebugOpt) { crondlog("\111User %s Entry %s\n", fileName, buf); } #endif /* parse date ranges */ ptr = ParseField(file->cf_User, line.cl_Mins, 60, 0, NULL, buf); ptr = ParseField(file->cf_User, line.cl_Hrs, 24, 0, NULL, ptr); ptr = ParseField(file->cf_User, line.cl_Days, 32, 0, NULL, ptr); ptr = ParseField(file->cf_User, line.cl_Mons, 12, -1, MonAry, ptr); ptr = ParseField(file->cf_User, line.cl_Dow, 7, 0, DowAry, ptr); /* check failure */ if (ptr == NULL) { continue; } /* * fix days and dow - if one is not * and the other * is *, the other is set to 0, and vise-versa */ FixDayDow(&line); *pline = calloc(1, sizeof(CronLine)); **pline = line; /* copy command */ (*pline)->cl_Shell = strdup(ptr); #ifdef FEATURE_DEBUG_OPT if (DebugOpt) { crondlog("\111 Command %s\n", ptr); } #endif pline = &((*pline)->cl_Next); } *pline = NULL; file->cf_Next = FileBase; FileBase = file; if (maxLines == 0 || maxEntries == 0) { crondlog("\111Maximum number of lines reached for user %s\n", fileName); } } fclose(fi); } } }
static void SynchronizeFile(const char *fileName) { FILE *fi; struct stat sbuf; int maxEntries; int maxLines; char buf[1024]; #if ENABLE_FEATURE_CROND_CALL_SENDMAIL char *mailTo = NULL; #endif if (!fileName) return; DeleteFile(fileName); fi = fopen(fileName, "r"); if (!fi) return; maxEntries = MAXLINES; if (strcmp(fileName, "root") == 0) { maxEntries = 65535; } maxLines = maxEntries * 10; if (fstat(fileno(fi), &sbuf) == 0 && sbuf.st_uid == DaemonUid) { CronFile *file = xzalloc(sizeof(CronFile)); CronLine **pline; file->cf_User = xstrdup(fileName); pline = &file->cf_LineBase; while (fgets(buf, sizeof(buf), fi) != NULL && --maxLines) { CronLine *line; char *ptr; trim(buf); if (buf[0] == '\0' || buf[0] == '#') { continue; } if (--maxEntries == 0) { break; } if (DebugOpt) { crondlog(LVL5 "user:%s entry:%s", fileName, buf); } /* check if line is setting MAILTO= */ if (0 == strncmp("MAILTO=", buf, 7)) { #if ENABLE_FEATURE_CROND_CALL_SENDMAIL free(mailTo); mailTo = (buf[7]) ? xstrdup(buf+7) : NULL; #endif /* otherwise just ignore such lines */ continue; } *pline = line = xzalloc(sizeof(CronLine)); /* parse date ranges */ ptr = ParseField(file->cf_User, line->cl_Mins, 60, 0, NULL, buf); ptr = ParseField(file->cf_User, line->cl_Hrs, 24, 0, NULL, ptr); ptr = ParseField(file->cf_User, line->cl_Days, 32, 0, NULL, ptr); ptr = ParseField(file->cf_User, line->cl_Mons, 12, -1, MonAry, ptr); ptr = ParseField(file->cf_User, line->cl_Dow, 7, 0, DowAry, ptr); /* check failure */ if (ptr == NULL) { free(line); continue; } /* * fix days and dow - if one is not "*" and the other * is "*", the other is set to 0, and vise-versa */ FixDayDow(line); #if ENABLE_FEATURE_CROND_CALL_SENDMAIL /* copy mailto (can be NULL) */ line->cl_MailTo = xstrdup(mailTo); #endif /* copy command */ line->cl_Shell = xstrdup(ptr); if (DebugOpt) { crondlog(LVL5 " command:%s", ptr); } pline = &line->cl_Next; } *pline = NULL; file->cf_Next = FileBase; FileBase = file; if (maxLines == 0 || maxEntries == 0) { crondlog(WARN9 "user %s: too many lines", fileName); } } fclose(fi); }
static void load_crontab(const char *fileName) { struct parser_t *parser; struct stat sbuf; int maxLines; char *tokens[6]; #if ENABLE_FEATURE_CROND_CALL_SENDMAIL char *mailTo = NULL; #endif char *shell = NULL; delete_cronfile(fileName); if (!getpwnam(fileName)) { log7("ignoring file '%s' (no such user)", fileName); return; } parser = config_open(fileName); if (!parser) return; maxLines = (strcmp(fileName, "root") == 0) ? 65535 : MAXLINES; if (fstat(fileno(parser->fp), &sbuf) == 0 && sbuf.st_uid == DAEMON_UID) { CronFile *file = xzalloc(sizeof(CronFile)); CronLine **pline; int n; file->cf_username = xstrdup(fileName); pline = &file->cf_lines; while (1) { CronLine *line; if (!--maxLines) { bb_error_msg("user %s: too many lines", fileName); break; } n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY); if (!n) break; log5("user:%s entry:%s", fileName, parser->data); /* check if line is setting MAILTO= */ if (is_prefixed_with(tokens[0], "MAILTO=")) { #if ENABLE_FEATURE_CROND_CALL_SENDMAIL free(mailTo); mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL; #endif /* otherwise just ignore such lines */ continue; } if (is_prefixed_with(tokens[0], "SHELL=")) { free(shell); shell = xstrdup(&tokens[0][6]); continue; } //TODO: handle HOME= too? "man crontab" says: //name = value // //where the spaces around the equal-sign (=) are optional, and any subsequent //non-leading spaces in value will be part of the value assigned to name. //The value string may be placed in quotes (single or double, but matching) //to preserve leading or trailing blanks. // //Several environment variables are set up automatically by the cron(8) daemon. //SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd //line of the crontab's owner. HOME and SHELL may be overridden by settings //in the crontab; LOGNAME may not. #if ENABLE_FEATURE_CROND_SPECIAL_TIMES if (tokens[0][0] == '@') { /* * "@daily /a/script/to/run PARAM1 PARAM2..." */ typedef struct SpecialEntry { const char *name; const char tokens[8]; } SpecialEntry; static const SpecialEntry SpecAry[] = { /* hour day month weekday */ { "yearly", "0\0" "1\0" "1\0" "*" }, { "annually", "0\0" "1\0" "1\0" "*" }, { "monthly", "0\0" "1\0" "*\0" "*" }, { "weekly", "0\0" "*\0" "*\0" "0" }, { "daily", "0\0" "*\0" "*\0" "*" }, { "midnight", "0\0" "*\0" "*\0" "*" }, { "hourly", "*\0" "*\0" "*\0" "*" }, { "reboot", "" }, }; const SpecialEntry *e = SpecAry; if (n < 2) continue; for (;;) { if (strcmp(e->name, tokens[0] + 1) == 0) { /* * tokens[1] is only the first word of command, * can'r use it. * find the entire command in unmodified string: */ tokens[5] = skip_whitespace( skip_non_whitespace( skip_whitespace(parser->data))); if (e->tokens[0]) { char *et = (char*)e->tokens; /* minute is "0" for all specials */ tokens[0] = (char*)"0"; tokens[1] = et; tokens[2] = et + 2; tokens[3] = et + 4; tokens[4] = et + 6; } goto got_it; } if (!e->tokens[0]) break; e++; } continue; /* bad line (unrecognized '@foo') */ } #endif /* check if a minimum of tokens is specified */ if (n < 6) continue; IF_FEATURE_CROND_SPECIAL_TIMES( got_it: ) *pline = line = xzalloc(sizeof(*line)); #if ENABLE_FEATURE_CROND_SPECIAL_TIMES if (tokens[0][0] == '@') { /* "@reboot" line */ file->cf_wants_starting = 1; line->cl_pid = START_ME_REBOOT; /* wants to start */ /* line->cl_Mins/Hrs/etc stay zero: never match any time */ } else #endif { /* parse date ranges */ ParseField(file->cf_username, line->cl_Mins, 60, 0, NULL, tokens[0]); ParseField(file->cf_username, line->cl_Hrs, 24, 0, NULL, tokens[1]); ParseField(file->cf_username, line->cl_Days, 32, 0, NULL, tokens[2]); ParseField(file->cf_username, line->cl_Mons, 12, -1, MonAry, tokens[3]); ParseField(file->cf_username, line->cl_Dow, 7, 0, DowAry, tokens[4]); /* * fix days and dow - if one is not "*" and the other * is "*", the other is set to 0, and vise-versa */ FixDayDow(line); } #if ENABLE_FEATURE_CROND_CALL_SENDMAIL /* copy mailto (can be NULL) */ line->cl_mailto = xstrdup(mailTo); #endif line->cl_shell = xstrdup(shell); /* copy command */ line->cl_cmd = xstrdup(tokens[5]); pline = &line->cl_next; //bb_error_msg("M[%s]F[%s][%s][%s][%s][%s][%s]", mailTo, tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]); } *pline = NULL; file->cf_next = G.cron_files; G.cron_files = file; }
void SynchronizeFile(const char *dpath, const char *fileName, const char *userName) { CronFile **pfile; CronFile *file; int maxEntries; int maxLines; char buf[RW_BUFFER]; /* max length for crontab lines */ char *path; FILE *fi; /* * Limit entries */ if (strcmp(userName, "root") == 0) maxEntries = 65535; else maxEntries = MAXLINES; maxLines = maxEntries * 10; /* * Delete any existing copy of this CronFile */ pfile = &FileBase; while ((file = *pfile) != NULL) { if (file->cf_Deleted == 0 && strcmp(file->cf_DPath, dpath) == 0 && strcmp(file->cf_FileName, fileName) == 0 ) { DeleteFile(pfile); } else { pfile = &file->cf_Next; } } if (!(path = concat(dpath, "/", fileName, NULL))) { errno = ENOMEM; perror("SynchronizeFile"); exit(1); } if ((fi = fopen(path, "r")) != NULL) { struct stat sbuf; if (fstat(fileno(fi), &sbuf) == 0 && sbuf.st_uid == DaemonUid) { CronFile *file = calloc(1, sizeof(CronFile)); CronLine **pline; time_t tnow = time(NULL); tnow -= tnow % 60; file->cf_UserName = strdup(userName); file->cf_FileName = strdup(fileName); file->cf_DPath = strdup(dpath); pline = &file->cf_LineBase; /* fgets reads at most size-1 chars until \n or EOF, then adds a\0; \n if present is stored in buf */ while (fgets(buf, sizeof(buf), fi) != NULL && --maxLines) { CronLine line; char *ptr = buf; int len; while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') ++ptr; len = strlen(ptr); if (len && ptr[len-1] == '\n') ptr[--len] = 0; if (*ptr == 0 || *ptr == '#') continue; if (--maxEntries == 0) break; memset(&line, 0, sizeof(line)); if (DebugOpt) printlogf(LOG_DEBUG, "User %s Entry %s\n", userName, buf); if (*ptr == '@') { /* * parse @hourly, etc */ int j; line.cl_Delay = -1; ptr += 1; for (j = 0; FreqAry[j]; ++j) { if (strncmp(ptr, FreqAry[j], strlen(FreqAry[j])) == 0) { break; } } if (FreqAry[j]) { ptr += strlen(FreqAry[j]); switch(j) { case 0: /* noauto */ line.cl_Freq = -2; line.cl_Delay = 0; break; case 1: /* reboot */ line.cl_Freq = -1; line.cl_Delay = 0; break; case 2: line.cl_Freq = HOURLY_FREQ; break; case 3: line.cl_Freq = DAILY_FREQ; break; case 4: line.cl_Freq = WEEKLY_FREQ; break; case 5: line.cl_Freq = MONTHLY_FREQ; break; case 6: line.cl_Freq = YEARLY_FREQ; break; /* else line.cl_Freq will remain 0 */ } } if (!line.cl_Freq || (*ptr != ' ' && *ptr != '\t')) { printlogf(LOG_WARNING, "failed parsing crontab for user %s: %s\n", userName, buf); continue; } if (line.cl_Delay < 0) { /* * delays on @daily, @hourly, etc are 1/20 of the frequency * so they don't all start at once * this also affects how they behave when the job returns EAGAIN */ line.cl_Delay = line.cl_Freq / 20; line.cl_Delay -= line.cl_Delay % 60; if (line.cl_Delay == 0) line.cl_Delay = 60; /* all minutes are permitted */ for (j=0; j<60; ++j) line.cl_Mins[j] = 1; for (j=0; j<24; ++j) line.cl_Hrs[j] = 1; for (j=1; j<32; ++j) /* days are numbered 1..31 */ line.cl_Days[j] = 1; for (j=0; j<12; ++j) line.cl_Mons[j] = 1; } while (*ptr == ' ' || *ptr == '\t') ++ptr; } else { /* * parse date ranges */ ptr = ParseField(file->cf_UserName, line.cl_Mins, 60, 0, 1, NULL, ptr); ptr = ParseField(file->cf_UserName, line.cl_Hrs, 24, 0, 1, NULL, ptr); ptr = ParseField(file->cf_UserName, line.cl_Days, 32, 0, 1, NULL, ptr); ptr = ParseField(file->cf_UserName, line.cl_Mons, 12, -1, 1, MonAry, ptr); ptr = ParseField(file->cf_UserName, line.cl_Dow, 7, 0, 31, DowAry, ptr); /* * check failure */ if (ptr == NULL) continue; /* * fix days and dow - if one is not * and the other * is *, the other is set to 0, and vise-versa */ FixDayDow(&line); } /* check for ID=... and AFTER=... and FREQ=... */ do { if (strncmp(ptr, ID_TAG, strlen(ID_TAG)) == 0) { if (line.cl_JobName) { /* only assign ID_TAG once */ printlogf(LOG_WARNING, "failed parsing crontab for user %s: repeated %s\n", userName, ptr); ptr = NULL; } else { ptr += strlen(ID_TAG); /* * name = strsep(&ptr, seps): * return name = ptr, and if ptr contains sep chars, overwrite first with 0 and point ptr to next char * else set ptr=NULL */ if (!(line.cl_Description = concat("job ", strsep(&ptr, " \t"), NULL))) { errno = ENOMEM; perror("SynchronizeFile"); exit(1); } line.cl_JobName = line.cl_Description + 4; if (!ptr) printlogf(LOG_WARNING, "failed parsing crontab for user %s: no command after %s%s\n", userName, ID_TAG, line.cl_JobName); } } else if (strncmp(ptr, FREQ_TAG, strlen(FREQ_TAG)) == 0) { if (line.cl_Freq) { /* only assign FREQ_TAG once */ printlogf(LOG_WARNING, "failed parsing crontab for user %s: repeated %s\n", userName, ptr); ptr = NULL; } else { char *base = ptr; ptr += strlen(FREQ_TAG); ptr = ParseInterval(&line.cl_Freq, ptr); if (ptr && *ptr == '/') ptr = ParseInterval(&line.cl_Delay, ++ptr); else line.cl_Delay = line.cl_Freq; if (!ptr) { printlogf(LOG_WARNING, "failed parsing crontab for user %s: %s\n", userName, base); } else if (*ptr != ' ' && *ptr != '\t') { printlogf(LOG_WARNING, "failed parsing crontab for user %s: no command after %s\n", userName, base); ptr = NULL; } } } else if (strncmp(ptr, WAIT_TAG, strlen(WAIT_TAG)) == 0) { if (line.cl_Waiters) { /* only assign WAIT_TAG once */ printlogf(LOG_WARNING, "failed parsing crontab for user %s: repeated %s\n", userName, ptr); ptr = NULL; } else { short more = 1; char *name; ptr += strlen(WAIT_TAG); do { CronLine *job, **pjob; if (strcspn(ptr,",") < strcspn(ptr," \t")) name = strsep(&ptr, ","); else { more = 0; name = strsep(&ptr, " \t"); } if (!ptr || *ptr == 0) { /* unexpectedly this was the last token in buf; so abort */ printlogf(LOG_WARNING, "failed parsing crontab for user %s: no command after %s%s\n", userName, WAIT_TAG, name); ptr = NULL; } else { int waitfor = 0; char *w, *wsave; if ((w = strchr(name, '/')) != NULL) { wsave = w++; w = ParseInterval(&waitfor, w); if (!w || *w != 0) { printlogf(LOG_WARNING, "failed parsing crontab for user %s: %s%s\n", userName, WAIT_TAG, name); ptr = NULL; } else /* truncate name */ *wsave = 0; } if (ptr) { /* look for a matching CronLine */ pjob = &file->cf_LineBase; while ((job = *pjob) != NULL) { if (job->cl_JobName && strcmp(job->cl_JobName, name) == 0) { CronWaiter *waiter = malloc(sizeof(CronWaiter)); CronNotifier *notif = malloc(sizeof(CronNotifier)); waiter->cw_Flag = -1; waiter->cw_MaxWait = waitfor; waiter->cw_NotifLine = job; waiter->cw_Notifier = notif; waiter->cw_Next = line.cl_Waiters; /* add to head of line.cl_Waiters */ line.cl_Waiters = waiter; notif->cn_Waiter = waiter; notif->cn_Next = job->cl_Notifs; /* add to head of job->cl_Notifs */ job->cl_Notifs = notif; break; } else pjob = &job->cl_Next; } if (!job) { printlogf(LOG_WARNING, "failed parsing crontab for user %s: unknown job %s\n", userName, name); /* we can continue parsing this line, we just don't install any CronWaiter for the requested job */ } } } } while (ptr && more); } } else break; if (!ptr) break; while (*ptr == ' ' || *ptr == '\t') ++ptr; } while (!line.cl_JobName || !line.cl_Waiters || !line.cl_Freq); if (line.cl_JobName && (!ptr || *line.cl_JobName == 0)) { /* we're aborting, or ID= was empty */ free(line.cl_Description); line.cl_Description = NULL; line.cl_JobName = NULL; } if (ptr && line.cl_Delay > 0 && !line.cl_JobName) { printlogf(LOG_WARNING, "failed parsing crontab for user %s: writing timestamp requires job %s to be named\n", userName, ptr); ptr = NULL; } if (!ptr) { /* couldn't parse so we abort; free any cl_Waiters */ if (line.cl_Waiters) { CronWaiter **pwaiters, *waiters; pwaiters = &line.cl_Waiters; while ((waiters = *pwaiters) != NULL) { *pwaiters = waiters->cw_Next; /* leave the Notifier allocated but disabled */ waiters->cw_Notifier->cn_Waiter = NULL; free(waiters); } } continue; } /* now we've added any ID=... or AFTER=... */ /* * copy command string */ line.cl_Shell = strdup(ptr); if (line.cl_Delay > 0) { if (!(line.cl_Timestamp = concat(TSDir, "/", userName, ".", line.cl_JobName, NULL))) { errno = ENOMEM; perror("SynchronizeFile"); exit(1); } line.cl_NotUntil = tnow + line.cl_Delay; } if (line.cl_JobName) { if (DebugOpt) printlogf(LOG_DEBUG, " Command %s Job %s\n", line.cl_Shell, line.cl_JobName); } else { /* when cl_JobName is NULL, we point cl_Description to cl_Shell */ line.cl_Description = line.cl_Shell; if (DebugOpt) printlogf(LOG_DEBUG, " Command %s\n", line.cl_Shell); } *pline = calloc(1, sizeof(CronLine)); /* copy working CronLine to newly allocated one */ **pline = line; pline = &((*pline)->cl_Next); } *pline = NULL; file->cf_Next = FileBase; FileBase = file; if (maxLines == 0 || maxEntries == 0) printlogf(LOG_WARNING, "maximum number of lines reached for user %s\n", userName); } fclose(fi); } free(path); }