diff options
Diffstat (limited to 'miscutils')
-rw-r--r-- | miscutils/crond.c | 452 |
1 files changed, 221 insertions, 231 deletions
diff --git a/miscutils/crond.c b/miscutils/crond.c index 49ecf745e..06d72ced9 100644 --- a/miscutils/crond.c +++ b/miscutils/crond.c @@ -17,25 +17,25 @@ /* glibc frees previous setenv'ed value when we do next setenv() * of the same variable. uclibc does not do this! */ #if (defined(__GLIBC__) && !defined(__UCLIBC__)) /* || OTHER_SAFE_LIBC... */ -#define SETENV_LEAKS 0 +# define SETENV_LEAKS 0 #else -#define SETENV_LEAKS 1 +# define SETENV_LEAKS 1 #endif #define TMPDIR CONFIG_FEATURE_CROND_DIR #define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs" #ifndef SENDMAIL -#define SENDMAIL "sendmail" +# define SENDMAIL "sendmail" #endif #ifndef SENDMAIL_ARGS -#define SENDMAIL_ARGS "-ti", NULL +# define SENDMAIL_ARGS "-ti", NULL #endif #ifndef CRONUPDATE -#define CRONUPDATE "cron.update" +# define CRONUPDATE "cron.update" #endif #ifndef MAXLINES -#define MAXLINES 256 /* max lines in non-root crontabs */ +# define MAXLINES 256 /* max lines in non-root crontabs */ #endif @@ -108,20 +108,6 @@ struct globals { } while (0) -static void CheckUpdates(void); -static void SynchronizeDir(void); -static int TestJobs(time_t t1, time_t t2); -static void RunJobs(void); -static int CheckJobs(void); -static void RunJob(const char *user, CronLine *line); -#if ENABLE_FEATURE_CROND_CALL_SENDMAIL -static void EndJob(const char *user, CronLine *line); -#else -#define EndJob(user, line) ((line)->cl_Pid = 0) -#endif -static void DeleteFile(const char *userName); - - /* 0 is the most verbose, default 8 */ #define LVL5 "\x05" #define LVL7 "\x07" @@ -163,101 +149,6 @@ static void crondlog(const char *ctl, ...) exit(20); } -int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -int crond_main(int argc UNUSED_PARAM, char **argv) -{ - time_t t2; - int rescan; - int sleep_time; - unsigned opts; - - INIT_G(); - - /* "-b after -f is ignored", and so on for every pair a-b */ - opt_complementary = "f-b:b-f:S-L:L-S" IF_FEATURE_CROND_D(":d-l") - ":l+:d+"; /* -l and -d have numeric param */ - opts = getopt32(argv, "l:L:fbSc:" IF_FEATURE_CROND_D("d:"), - &LogLevel, &LogFile, &CDir - IF_FEATURE_CROND_D(,&LogLevel)); - /* both -d N and -l N set the same variable: LogLevel */ - - if (!(opts & OPT_f)) { - /* close stdin, stdout, stderr. - * close unused descriptors - don't need them. */ - bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); - } - - if (!(opts & OPT_d) && LogFile == NULL) { - /* logging to syslog */ - openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON); - logmode = LOGMODE_SYSLOG; - } - - xchdir(CDir); - //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */ - xsetenv("SHELL", DEFAULT_SHELL); /* once, for all future children */ - crondlog(LVL8 "crond (busybox "BB_VER") started, log level %d", LogLevel); - SynchronizeDir(); - write_pidfile("/var/run/crond.pid"); - - /* main loop - synchronize to 1 second after the minute, minimum sleep - * of 1 second. */ - t2 = time(NULL); - rescan = 60; - sleep_time = 60; - for (;;) { - time_t t1; - long dt; - - t1 = t2; - sleep((sleep_time + 1) - (time(NULL) % sleep_time)); - - t2 = time(NULL); - dt = (long)t2 - (long)t1; - - /* - * The file 'cron.update' is checked to determine new cron - * jobs. The directory is rescanned once an hour to deal - * with any screwups. - * - * Check for time jump. Disparities over an hour either way - * result in resynchronization. A negative disparity - * less than an hour causes us to effectively sleep until we - * match the original time (i.e. no re-execution of jobs that - * have just been run). A positive disparity less than - * an hour causes intermediate jobs to be run, but only once - * in the worst case. - * - * When running jobs, the inequality used is greater but not - * equal to t1, and less then or equal to t2. - */ - if (--rescan == 0) { - rescan = 60; - SynchronizeDir(); - } - CheckUpdates(); - if (DebugOpt) - crondlog(LVL5 "wakeup dt=%ld", dt); - if (dt < -60 * 60 || dt > 60 * 60) { - crondlog(WARN9 "time disparity of %ld minutes detected", dt / 60); - /* and we do not run any jobs in this case */ - } else if (dt > 0) { - /* Usual case: time advances forwad, as expected */ - TestJobs(t1, t2); - RunJobs(); - sleep(5); - if (CheckJobs() > 0) { - sleep_time = 10; - } else { - sleep_time = 60; - } - } - /* else: time jumped back, do not run any jobs */ - } /* for (;;) */ - - return 0; /* not reached */ -} - #if SETENV_LEAKS /* We set environment *before* vfork (because we want to use vfork), * so we cannot use setenv() - repeated calls to setenv() may leak memory! @@ -452,6 +343,48 @@ static void FixDayDow(CronLine *line) } } +/* + * DeleteFile() - delete user database + * + * Note: multiple entries for same user may exist if we were unable to + * completely delete a database due to running processes. + */ +static void DeleteFile(const char *userName) +{ + CronFile **pfile = &FileBase; + CronFile *file; + + while ((file = *pfile) != NULL) { + if (strcmp(userName, file->cf_User) == 0) { + CronLine **pline = &file->cf_LineBase; + CronLine *line; + + file->cf_Running = 0; + file->cf_Deleted = 1; + + while ((line = *pline) != NULL) { + if (line->cl_Pid > 0) { + file->cf_Running = 1; + pline = &line->cl_Next; + } else { + *pline = line->cl_Next; + free(line->cl_Shell); + free(line); + } + } + if (file->cf_Running == 0) { + *pfile = file->cf_Next; + free(file->cf_User); + free(file); + } else { + pfile = &file->cf_Next; + } + } else { + pfile = &file->cf_Next; + } + } +} + static void SynchronizeFile(const char *fileName) { struct parser_t *parser; @@ -600,51 +533,7 @@ static void SynchronizeDir(void) } /* - * DeleteFile() - delete user database - * - * Note: multiple entries for same user may exist if we were unable to - * completely delete a database due to running processes. - */ -static void DeleteFile(const char *userName) -{ - CronFile **pfile = &FileBase; - CronFile *file; - - while ((file = *pfile) != NULL) { - if (strcmp(userName, file->cf_User) == 0) { - CronLine **pline = &file->cf_LineBase; - CronLine *line; - - file->cf_Running = 0; - file->cf_Deleted = 1; - - while ((line = *pline) != NULL) { - if (line->cl_Pid > 0) { - file->cf_Running = 1; - pline = &line->cl_Next; - } else { - *pline = line->cl_Next; - free(line->cl_Shell); - free(line); - } - } - if (file->cf_Running == 0) { - *pfile = file->cf_Next; - free(file->cf_User); - free(file); - } else { - pfile = &file->cf_Next; - } - } else { - pfile = &file->cf_Next; - } - } -} - -/* - * TestJobs() - * - * determine which jobs need to be run. Under normal conditions, the + * Determine which jobs need to be run. Under normal conditions, the * period is about a minute (one scan). Worst case it will be one * hour (60 scans). */ @@ -695,35 +584,63 @@ static int TestJobs(time_t t1, time_t t2) return nJobs; } -static void RunJobs(void) +#if ENABLE_FEATURE_CROND_CALL_SENDMAIL +static void +ForkJob(const char *user, CronLine *line, int mailFd, + const char *prog, const char *cmd, const char *arg, + const char *mail_filename); +/* + * EndJob - called when job terminates and when mail terminates + */ +static void EndJob(const char *user, CronLine *line) { - CronFile *file; - CronLine *line; + int mailFd; + char mailFile[128]; + struct stat sbuf; - for (file = FileBase; file; file = file->cf_Next) { - if (!file->cf_Ready) - continue; + /* No job */ + if (line->cl_Pid <= 0) { + line->cl_Pid = 0; + return; + } - file->cf_Ready = 0; - for (line = file->cf_LineBase; line; line = line->cl_Next) { - if (line->cl_Pid >= 0) - continue; + /* + * End of job and no mail file + * End of sendmail job + */ + snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, line->cl_Pid); + line->cl_Pid = 0; - RunJob(file->cf_User, line); - crondlog(LVL8 "USER %s pid %3d cmd %s", - file->cf_User, (int)line->cl_Pid, line->cl_Shell); - if (line->cl_Pid < 0) { - file->cf_Ready = 1; - } else if (line->cl_Pid > 0) { - file->cf_Running = 1; - } - } + if (line->cl_MailFlag == 0) { + return; } + line->cl_MailFlag = 0; + + /* + * End of primary job - check for mail file. If size has increased and + * the file is still valid, we sendmail it. + */ + mailFd = open(mailFile, O_RDONLY); + unlink(mailFile); + if (mailFd < 0) { + return; + } + + if (fstat(mailFd, &sbuf) < 0 || sbuf.st_uid != DaemonUid + || sbuf.st_nlink != 0 || sbuf.st_size == line->cl_MailPos + || !S_ISREG(sbuf.st_mode) + ) { + close(mailFd); + return; + } + if (line->cl_MailTo) + ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL); } +#else +# define EndJob(user, line) ((line)->cl_Pid = 0) +#endif /* - * CheckJobs() - check for job completion - * * Check for job completion, return number of jobs still running after * all done. */ @@ -855,55 +772,7 @@ static void RunJob(const char *user, CronLine *line) ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile); } -/* - * EndJob - called when job terminates and when mail terminates - */ -static void EndJob(const char *user, CronLine *line) -{ - int mailFd; - char mailFile[128]; - struct stat sbuf; - - /* No job */ - if (line->cl_Pid <= 0) { - line->cl_Pid = 0; - return; - } - - /* - * End of job and no mail file - * End of sendmail job - */ - snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, line->cl_Pid); - line->cl_Pid = 0; - - if (line->cl_MailFlag == 0) { - return; - } - line->cl_MailFlag = 0; - - /* - * End of primary job - check for mail file. If size has increased and - * the file is still valid, we sendmail it. - */ - mailFd = open(mailFile, O_RDONLY); - unlink(mailFile); - if (mailFd < 0) { - return; - } - - if (fstat(mailFd, &sbuf) < 0 || sbuf.st_uid != DaemonUid - || sbuf.st_nlink != 0 || sbuf.st_size == line->cl_MailPos - || !S_ISREG(sbuf.st_mode) - ) { - close(mailFd); - return; - } - if (line->cl_MailTo) - ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL); -} - -#else /* crond without sendmail */ +#else /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */ static void RunJob(const char *user, CronLine *line) { @@ -943,4 +812,125 @@ static void RunJob(const char *user, CronLine *line) line->cl_Pid = pid; } -#endif /* ENABLE_FEATURE_CROND_CALL_SENDMAIL */ +#endif /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */ + +static void RunJobs(void) +{ + CronFile *file; + CronLine *line; + + for (file = FileBase; file; file = file->cf_Next) { + if (!file->cf_Ready) + continue; + + file->cf_Ready = 0; + for (line = file->cf_LineBase; line; line = line->cl_Next) { + if (line->cl_Pid >= 0) + continue; + + RunJob(file->cf_User, line); + crondlog(LVL8 "USER %s pid %3d cmd %s", + file->cf_User, (int)line->cl_Pid, line->cl_Shell); + if (line->cl_Pid < 0) { + file->cf_Ready = 1; + } else if (line->cl_Pid > 0) { + file->cf_Running = 1; + } + } + } +} + +int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int crond_main(int argc UNUSED_PARAM, char **argv) +{ + time_t t2; + int rescan; + int sleep_time; + unsigned opts; + + INIT_G(); + + /* "-b after -f is ignored", and so on for every pair a-b */ + opt_complementary = "f-b:b-f:S-L:L-S" IF_FEATURE_CROND_D(":d-l") + ":l+:d+"; /* -l and -d have numeric param */ + opts = getopt32(argv, "l:L:fbSc:" IF_FEATURE_CROND_D("d:"), + &LogLevel, &LogFile, &CDir + IF_FEATURE_CROND_D(,&LogLevel)); + /* both -d N and -l N set the same variable: LogLevel */ + + if (!(opts & OPT_f)) { + /* close stdin, stdout, stderr. + * close unused descriptors - don't need them. */ + bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); + } + + if (!(opts & OPT_d) && LogFile == NULL) { + /* logging to syslog */ + openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON); + logmode = LOGMODE_SYSLOG; + } + + xchdir(CDir); + //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */ + xsetenv("SHELL", DEFAULT_SHELL); /* once, for all future children */ + crondlog(LVL8 "crond (busybox "BB_VER") started, log level %d", LogLevel); + SynchronizeDir(); + write_pidfile("/var/run/crond.pid"); + + /* main loop - synchronize to 1 second after the minute, minimum sleep + * of 1 second. */ + t2 = time(NULL); + rescan = 60; + sleep_time = 60; + for (;;) { + time_t t1; + long dt; + + t1 = t2; + sleep((sleep_time + 1) - (time(NULL) % sleep_time)); + + t2 = time(NULL); + dt = (long)t2 - (long)t1; + + /* + * The file 'cron.update' is checked to determine new cron + * jobs. The directory is rescanned once an hour to deal + * with any screwups. + * + * Check for time jump. Disparities over an hour either way + * result in resynchronization. A negative disparity + * less than an hour causes us to effectively sleep until we + * match the original time (i.e. no re-execution of jobs that + * have just been run). A positive disparity less than + * an hour causes intermediate jobs to be run, but only once + * in the worst case. + * + * When running jobs, the inequality used is greater but not + * equal to t1, and less then or equal to t2. + */ + if (--rescan == 0) { + rescan = 60; + SynchronizeDir(); + } + CheckUpdates(); + if (DebugOpt) + crondlog(LVL5 "wakeup dt=%ld", dt); + if (dt < -60 * 60 || dt > 60 * 60) { + crondlog(WARN9 "time disparity of %ld minutes detected", dt / 60); + /* and we do not run any jobs in this case */ + } else if (dt > 0) { + /* Usual case: time advances forwad, as expected */ + TestJobs(t1, t2); + RunJobs(); + sleep(5); + if (CheckJobs() > 0) { + sleep_time = 10; + } else { + sleep_time = 60; + } + } + /* else: time jumped back, do not run any jobs */ + } /* for (;;) */ + + return 0; /* not reached */ +} |