2017-05-08 15:30:03 +02:00
|
|
|
/* Copyright 1988,1990,1993,1994 by Paul Vixie
|
|
|
|
* All rights reserved
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
|
|
|
|
* Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
|
|
|
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Modified 2010/09/12 by Colin Dean, Durham University IT Service,
|
|
|
|
* to add clustering support.
|
|
|
|
*/
|
|
|
|
|
2019-08-06 18:08:05 +02:00
|
|
|
#include "config.h"
|
|
|
|
|
2017-05-08 15:30:03 +02:00
|
|
|
#define MAIN_PROGRAM
|
|
|
|
|
2019-08-06 18:08:05 +02:00
|
|
|
#include <errno.h>
|
|
|
|
#include <langinfo.h>
|
|
|
|
#include <locale.h>
|
|
|
|
#include <pwd.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/time.h>
|
2023-07-01 12:15:55 +02:00
|
|
|
#include <fcntl.h>
|
2019-08-06 18:08:05 +02:00
|
|
|
|
|
|
|
#ifdef WITH_INOTIFY
|
|
|
|
# include <sys/inotify.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "cronie_common.h"
|
|
|
|
#include "funcs.h"
|
|
|
|
#include "globals.h"
|
|
|
|
#include "pathnames.h"
|
2017-05-08 15:30:03 +02:00
|
|
|
|
|
|
|
#if defined WITH_INOTIFY
|
|
|
|
int inotify_enabled;
|
|
|
|
#else
|
|
|
|
# define inotify_enabled 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
enum timejump { negative, small, medium, large };
|
|
|
|
|
2019-08-06 18:08:05 +02:00
|
|
|
static void usage(void) ATTRIBUTE_NORETURN,
|
2017-05-08 15:30:03 +02:00
|
|
|
run_reboot_jobs(cron_db *),
|
|
|
|
find_jobs(int, cron_db *, int, int, long),
|
|
|
|
set_time(int),
|
|
|
|
cron_sleep(int, cron_db *),
|
|
|
|
sigchld_handler(int),
|
2021-08-09 15:08:54 +02:00
|
|
|
sighup_handler(int ATTRIBUTE_UNUSED),
|
|
|
|
sigchld_reaper(void),
|
|
|
|
sigintterm_handler(int ATTRIBUTE_UNUSED), parse_args(int c, char *v[]);
|
2017-05-08 15:30:03 +02:00
|
|
|
|
2019-08-06 18:08:05 +02:00
|
|
|
static volatile sig_atomic_t got_sighup, got_sigchld, got_sigintterm;
|
2017-05-08 15:30:03 +02:00
|
|
|
static int timeRunning, virtualTime, clockTime;
|
|
|
|
static long GMToff;
|
|
|
|
static int DisableInotify;
|
|
|
|
|
|
|
|
#if defined WITH_INOTIFY
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note that inotify isn't safe to use with clustering, as changes made
|
|
|
|
* to a shared filesystem on one system cannot be relied on to be notified
|
|
|
|
* on another system, so use of inotify is disabled at runtime if run with
|
|
|
|
* clustering enabled.
|
|
|
|
*/
|
|
|
|
|
2019-08-06 18:08:05 +02:00
|
|
|
# if defined ENABLE_SYSCRONTAB
|
|
|
|
# define NUM_WATCHES 3
|
2017-05-08 15:30:03 +02:00
|
|
|
|
|
|
|
int wd[NUM_WATCHES];
|
|
|
|
const char *watchpaths[NUM_WATCHES] = {SPOOL_DIR, SYS_CROND_DIR, SYSCRONTAB};
|
2019-08-06 18:08:05 +02:00
|
|
|
# else
|
|
|
|
# define NUM_WATCHES 2
|
|
|
|
int wd[NUM_WATCHES];
|
|
|
|
const char *watchpaths[NUM_WATCHES] = {SPOOL_DIR, SYS_CROND_DIR};
|
|
|
|
# endif
|
|
|
|
|
|
|
|
static void reset_watches(void) {
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < sizeof (wd) / sizeof (wd[0]); ++i) {
|
|
|
|
wd[i] = -2;
|
|
|
|
}
|
|
|
|
}
|
2017-05-08 15:30:03 +02:00
|
|
|
|
|
|
|
void set_cron_unwatched(int fd) {
|
2019-08-06 18:08:05 +02:00
|
|
|
size_t i;
|
2017-05-08 15:30:03 +02:00
|
|
|
|
|
|
|
for (i = 0; i < sizeof (wd) / sizeof (wd[0]); ++i) {
|
2019-08-06 18:08:05 +02:00
|
|
|
if (wd[i] > 0) {
|
2017-05-08 15:30:03 +02:00
|
|
|
inotify_rm_watch(fd, wd[i]);
|
|
|
|
wd[i] = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_cron_watched(int fd) {
|
|
|
|
pid_t pid = getpid();
|
2019-08-06 18:08:05 +02:00
|
|
|
size_t i;
|
2017-05-08 15:30:03 +02:00
|
|
|
|
|
|
|
if (fd < 0) {
|
|
|
|
inotify_enabled = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < sizeof (wd) / sizeof (wd[0]); ++i) {
|
|
|
|
int w;
|
|
|
|
|
2019-08-06 18:08:05 +02:00
|
|
|
w = inotify_add_watch(fd, watchpaths[i],
|
|
|
|
IN_CREATE | IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO |
|
|
|
|
IN_MOVED_FROM | IN_MOVE_SELF | IN_DELETE | IN_DELETE_SELF);
|
|
|
|
if (w < 0 && errno != ENOENT) {
|
|
|
|
if (wd[i] != -1) {
|
|
|
|
log_it("CRON", pid, "This directory or file can't be watched",
|
|
|
|
watchpaths[i], errno);
|
|
|
|
log_it("CRON", pid, "INFO", "running without inotify support",
|
|
|
|
0);
|
2017-05-08 15:30:03 +02:00
|
|
|
}
|
2019-08-06 18:08:05 +02:00
|
|
|
inotify_enabled = 0;
|
|
|
|
set_cron_unwatched(fd);
|
|
|
|
return;
|
2017-05-08 15:30:03 +02:00
|
|
|
}
|
2019-08-06 18:08:05 +02:00
|
|
|
wd[i] = w;
|
2017-05-08 15:30:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!inotify_enabled) {
|
|
|
|
log_it("CRON", pid, "INFO", "running with inotify support", 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
inotify_enabled = 1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void handle_signals(cron_db * database) {
|
|
|
|
if (got_sighup) {
|
|
|
|
got_sighup = 0;
|
|
|
|
#if defined WITH_INOTIFY
|
|
|
|
/* watches must be reinstated on reload */
|
|
|
|
if (inotify_enabled && (EnableClustering != 1)) {
|
|
|
|
set_cron_unwatched(database->ifd);
|
2019-08-06 18:08:05 +02:00
|
|
|
reset_watches();
|
2017-05-08 15:30:03 +02:00
|
|
|
inotify_enabled = 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
database->mtime = (time_t) 0;
|
|
|
|
log_close();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (got_sigchld) {
|
|
|
|
got_sigchld = 0;
|
|
|
|
sigchld_reaper();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usage(void) {
|
|
|
|
const char **dflags;
|
|
|
|
|
2019-08-06 18:08:05 +02:00
|
|
|
fprintf(stderr, "Usage:\n");
|
|
|
|
fprintf(stderr, " %s [options]\n", ProgramName);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
fprintf(stderr, "Options:\n");
|
|
|
|
fprintf(stderr, " -h print this message \n");
|
|
|
|
fprintf(stderr, " -i deamon runs without inotify support\n");
|
|
|
|
fprintf(stderr, " -m <comm> off, or specify preferred client for sending mails\n");
|
|
|
|
fprintf(stderr, " -n run in foreground\n");
|
2023-07-01 12:15:55 +02:00
|
|
|
fprintf(stderr, " -f run in foreground, the same as -n\n");
|
2019-08-06 18:08:05 +02:00
|
|
|
fprintf(stderr, " -p permit any crontab\n");
|
2023-07-01 12:15:55 +02:00
|
|
|
fprintf(stderr, " -P inherit PATH from environment instead of using default value");
|
|
|
|
fprintf(stderr, " of \"%s\"\n", _PATH_DEFPATH);
|
2019-08-06 18:08:05 +02:00
|
|
|
fprintf(stderr, " -c enable clustering support\n");
|
|
|
|
fprintf(stderr, " -s log into syslog instead of sending mails\n");
|
|
|
|
fprintf(stderr, " -V print version and exit\n");
|
|
|
|
fprintf(stderr, " -x <flag> print debug information\n");
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
fprintf(stderr, "Debugging flags are: ");
|
2017-05-08 15:30:03 +02:00
|
|
|
for (dflags = DebugFlagNames; *dflags; dflags++)
|
2019-08-06 18:08:05 +02:00
|
|
|
fprintf(stderr, "%s%s", *dflags, dflags[1] ? "," : "\n");
|
2017-05-08 15:30:03 +02:00
|
|
|
exit(ERROR_EXIT);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
|
|
struct sigaction sact;
|
|
|
|
cron_db database;
|
|
|
|
int fd;
|
|
|
|
char *cs;
|
|
|
|
pid_t pid = getpid();
|
|
|
|
long oldGMToff;
|
2019-08-06 18:08:05 +02:00
|
|
|
struct timeval tv;
|
|
|
|
struct timezone tz;
|
|
|
|
char buf[256];
|
|
|
|
|
|
|
|
if ((ProgramName=strrchr(argv[0], '/')) == NULL) {
|
|
|
|
ProgramName = argv[0];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
++ProgramName;
|
|
|
|
}
|
2017-05-08 15:30:03 +02:00
|
|
|
|
|
|
|
MailCmd[0] = '\0';
|
|
|
|
cron_default_mail_charset[0] = '\0';
|
|
|
|
|
|
|
|
setlocale(LC_ALL, "");
|
|
|
|
|
|
|
|
#if defined(BSD)
|
|
|
|
setlinebuf(stdout);
|
|
|
|
setlinebuf(stderr);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
SyslogOutput = 0;
|
|
|
|
NoFork = 0;
|
2019-08-06 18:08:05 +02:00
|
|
|
ChangePath = 1;
|
2017-05-08 15:30:03 +02:00
|
|
|
parse_args(argc, argv);
|
|
|
|
|
2019-08-06 18:08:05 +02:00
|
|
|
memset((char *) &sact, 0, sizeof sact);
|
2017-05-08 15:30:03 +02:00
|
|
|
sigemptyset(&sact.sa_mask);
|
|
|
|
sact.sa_flags = 0;
|
|
|
|
#ifdef SA_RESTART
|
|
|
|
sact.sa_flags |= SA_RESTART;
|
|
|
|
#endif
|
|
|
|
sact.sa_handler = sigchld_handler;
|
|
|
|
(void) sigaction(SIGCHLD, &sact, NULL);
|
|
|
|
sact.sa_handler = sighup_handler;
|
|
|
|
(void) sigaction(SIGHUP, &sact, NULL);
|
2019-08-06 18:08:05 +02:00
|
|
|
sact.sa_handler = sigintterm_handler;
|
2017-05-08 15:30:03 +02:00
|
|
|
(void) sigaction(SIGINT, &sact, NULL);
|
|
|
|
(void) sigaction(SIGTERM, &sact, NULL);
|
|
|
|
|
|
|
|
acquire_daemonlock(0);
|
|
|
|
set_cron_uid();
|
|
|
|
check_spool_dir();
|
|
|
|
|
2019-08-06 18:08:05 +02:00
|
|
|
if (ChangePath) {
|
|
|
|
if (setenv("PATH", _PATH_DEFPATH, 1) < 0) {
|
|
|
|
log_it("CRON", pid, "DEATH", "can't setenv PATH",
|
|
|
|
errno);
|
|
|
|
exit(1);
|
|
|
|
}
|
2017-05-08 15:30:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the default locale character set for the mail
|
|
|
|
* "Content-Type: ...; charset=" header
|
|
|
|
*/
|
|
|
|
setlocale(LC_ALL, ""); /* set locale to system defaults or to
|
|
|
|
* that specified by any LC_* env vars */
|
2021-08-09 15:08:54 +02:00
|
|
|
if ((cs = nl_langinfo(CODESET)) != NULL)
|
2019-08-06 18:08:05 +02:00
|
|
|
strncpy(cron_default_mail_charset, cs, MAX_ENVSTR-1);
|
2017-05-08 15:30:03 +02:00
|
|
|
else
|
|
|
|
strcpy(cron_default_mail_charset, "US-ASCII");
|
|
|
|
|
|
|
|
/* if there are no debug flags turned on, fork as a daemon should.
|
|
|
|
*/
|
|
|
|
if (DebugFlags) {
|
|
|
|
#if DEBUGGING
|
|
|
|
(void) fprintf(stderr, "[%ld] cron started\n", (long) getpid());
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else if (NoFork == 0) {
|
|
|
|
switch (fork()) {
|
|
|
|
case -1:
|
|
|
|
log_it("CRON", pid, "DEATH", "can't fork", errno);
|
|
|
|
exit(0);
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
/* child process */
|
|
|
|
(void) setsid();
|
|
|
|
if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) >= 0) {
|
|
|
|
(void) dup2(fd, STDIN);
|
|
|
|
(void) dup2(fd, STDOUT);
|
|
|
|
(void) dup2(fd, STDERR);
|
|
|
|
if (fd != STDERR)
|
|
|
|
(void) close(fd);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* parent process should just die */
|
|
|
|
_exit(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-06 18:08:05 +02:00
|
|
|
log_it("CRON", getpid(), "STARTUP", PACKAGE_VERSION, 0);
|
|
|
|
|
|
|
|
if (!SyslogOutput && MailCmd[0] == '\0' && access("/usr/sbin/sendmail", X_OK) != 0) {
|
2017-05-08 15:30:03 +02:00
|
|
|
SyslogOutput=1;
|
2019-08-06 18:08:05 +02:00
|
|
|
log_it("CRON", pid, "INFO","Syslog will be used instead of sendmail.", 0);
|
2017-05-08 15:30:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pid = getpid();
|
2019-08-06 18:08:05 +02:00
|
|
|
|
|
|
|
/* obtain a random scaling factor for RANDOM_DELAY */
|
|
|
|
if (gettimeofday(&tv, &tz) != 0)
|
|
|
|
tv.tv_usec = 0;
|
|
|
|
srandom((unsigned int)(pid + tv.tv_usec));
|
2023-07-01 12:15:55 +02:00
|
|
|
RandomScale = (double)random() / (double)(1lu << 31);
|
2019-08-06 18:08:05 +02:00
|
|
|
snprintf(buf, sizeof(buf), "RANDOM_DELAY will be scaled with factor %d%% if used.", (int)(RandomScale*100));
|
|
|
|
log_it("CRON", pid, "INFO", buf, 0);
|
|
|
|
|
2017-05-08 15:30:03 +02:00
|
|
|
acquire_daemonlock(0);
|
|
|
|
|
|
|
|
fd = -1;
|
|
|
|
#if defined WITH_INOTIFY
|
|
|
|
if (DisableInotify || EnableClustering) {
|
|
|
|
log_it("CRON", getpid(), "No inotify - daemon runs with -i or -c option",
|
|
|
|
"", 0);
|
|
|
|
}
|
|
|
|
else {
|
2019-08-06 18:08:05 +02:00
|
|
|
reset_watches();
|
2017-05-08 15:30:03 +02:00
|
|
|
database.ifd = fd = inotify_init();
|
|
|
|
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
|
|
|
if (fd < 0)
|
|
|
|
log_it("CRON", pid, "INFO", "Inotify init failed", errno);
|
|
|
|
set_cron_watched(fd);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2023-07-01 12:15:55 +02:00
|
|
|
database.head = NULL;
|
|
|
|
database.tail = NULL;
|
|
|
|
database.mtime = (time_t) 0;
|
|
|
|
|
|
|
|
load_database(&database);
|
|
|
|
|
2017-05-08 15:30:03 +02:00
|
|
|
set_time(TRUE);
|
|
|
|
run_reboot_jobs(&database);
|
|
|
|
timeRunning = virtualTime = clockTime;
|
|
|
|
oldGMToff = GMToff;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Too many clocks, not enough time (Al. Einstein)
|
|
|
|
* These clocks are in minutes since the epoch, adjusted for timezone.
|
|
|
|
* virtualTime: is the time it *would* be if we woke up
|
|
|
|
* promptly and nobody ever changed the clock. It is
|
|
|
|
* monotonically increasing... unless a timejump happens.
|
|
|
|
* At the top of the loop, all jobs for 'virtualTime' have run.
|
|
|
|
* timeRunning: is the time we last awakened.
|
|
|
|
* clockTime: is the time when set_time was last called.
|
|
|
|
*/
|
2019-08-06 18:08:05 +02:00
|
|
|
while (!got_sigintterm) {
|
2017-05-08 15:30:03 +02:00
|
|
|
int timeDiff;
|
|
|
|
enum timejump wakeupKind;
|
|
|
|
|
|
|
|
/* ... wait for the time (in minutes) to change ... */
|
|
|
|
do {
|
|
|
|
cron_sleep(timeRunning + 1, &database);
|
|
|
|
set_time(FALSE);
|
2019-08-06 18:08:05 +02:00
|
|
|
} while (!got_sigintterm && clockTime == timeRunning);
|
|
|
|
if (got_sigintterm)
|
|
|
|
break;
|
2017-05-08 15:30:03 +02:00
|
|
|
timeRunning = clockTime;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate how the current time differs from our virtual
|
|
|
|
* clock. Classify the change into one of 4 cases.
|
|
|
|
*/
|
|
|
|
timeDiff = timeRunning - virtualTime;
|
|
|
|
check_orphans(&database);
|
|
|
|
#if defined WITH_INOTIFY
|
|
|
|
if (inotify_enabled) {
|
|
|
|
check_inotify_database(&database);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (load_database(&database) && (EnableClustering != 1))
|
|
|
|
/* try reinstating the watches */
|
|
|
|
set_cron_watched(fd);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
load_database(&database);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* shortcut for the most common case */
|
|
|
|
if (timeDiff == 1) {
|
|
|
|
virtualTime = timeRunning;
|
|
|
|
oldGMToff = GMToff;
|
|
|
|
find_jobs(virtualTime, &database, TRUE, TRUE, oldGMToff);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (timeDiff > (3 * MINUTE_COUNT) || timeDiff < -(3 * MINUTE_COUNT))
|
|
|
|
wakeupKind = large;
|
|
|
|
else if (timeDiff > 5)
|
|
|
|
wakeupKind = medium;
|
|
|
|
else if (timeDiff > 0)
|
|
|
|
wakeupKind = small;
|
|
|
|
else
|
|
|
|
wakeupKind = negative;
|
|
|
|
|
|
|
|
switch (wakeupKind) {
|
|
|
|
case small:
|
|
|
|
/*
|
|
|
|
* case 1: timeDiff is a small positive number
|
|
|
|
* (wokeup late) run jobs for each virtual
|
|
|
|
* minute until caught up.
|
|
|
|
*/
|
|
|
|
Debug(DSCH, ("[%ld], normal case %d minutes to go\n",
|
2019-08-06 18:08:05 +02:00
|
|
|
(long) pid, timeDiff));
|
2017-05-08 15:30:03 +02:00
|
|
|
do {
|
|
|
|
if (job_runqueue())
|
|
|
|
sleep(10);
|
|
|
|
virtualTime++;
|
|
|
|
if (virtualTime >= timeRunning)
|
|
|
|
/* always run also the other timezone jobs in the last step */
|
|
|
|
oldGMToff = GMToff;
|
|
|
|
find_jobs(virtualTime, &database, TRUE, TRUE, oldGMToff);
|
|
|
|
} while (virtualTime < timeRunning);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case medium:
|
|
|
|
/*
|
|
|
|
* case 2: timeDiff is a medium-sized positive
|
|
|
|
* number, for example because we went to DST
|
|
|
|
* run wildcard jobs once, then run any
|
|
|
|
* fixed-time jobs that would otherwise be
|
|
|
|
* skipped if we use up our minute (possible,
|
|
|
|
* if there are a lot of jobs to run) go
|
|
|
|
* around the loop again so that wildcard jobs
|
|
|
|
* have a chance to run, and we do our
|
|
|
|
* housekeeping.
|
|
|
|
*/
|
|
|
|
Debug(DSCH, ("[%ld], DST begins %d minutes to go\n",
|
2019-08-06 18:08:05 +02:00
|
|
|
(long) pid, timeDiff));
|
2017-05-08 15:30:03 +02:00
|
|
|
/* run wildcard jobs for current minute */
|
|
|
|
find_jobs(timeRunning, &database, TRUE, FALSE, GMToff);
|
|
|
|
|
|
|
|
/* run fixed-time jobs for each minute missed */
|
|
|
|
do {
|
|
|
|
if (job_runqueue())
|
|
|
|
sleep(10);
|
|
|
|
virtualTime++;
|
|
|
|
if (virtualTime >= timeRunning)
|
|
|
|
/* always run also the other timezone jobs in the last step */
|
|
|
|
oldGMToff = GMToff;
|
|
|
|
find_jobs(virtualTime, &database, FALSE, TRUE, oldGMToff);
|
|
|
|
set_time(FALSE);
|
|
|
|
} while (virtualTime < timeRunning && clockTime == timeRunning);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case negative:
|
|
|
|
/*
|
|
|
|
* case 3: timeDiff is a small or medium-sized
|
|
|
|
* negative num, eg. because of DST ending.
|
|
|
|
* Just run the wildcard jobs. The fixed-time
|
|
|
|
* jobs probably have already run, and should
|
|
|
|
* not be repeated. Virtual time does not
|
|
|
|
* change until we are caught up.
|
|
|
|
*/
|
|
|
|
Debug(DSCH, ("[%ld], DST ends %d minutes to go\n",
|
2019-08-06 18:08:05 +02:00
|
|
|
(long) pid, timeDiff));
|
2017-05-08 15:30:03 +02:00
|
|
|
find_jobs(timeRunning, &database, TRUE, FALSE, GMToff);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* other: time has changed a *lot*,
|
|
|
|
* jump virtual time, and run everything
|
|
|
|
*/
|
2019-08-06 18:08:05 +02:00
|
|
|
Debug(DSCH, ("[%ld], clock jumped\n", (long) pid));
|
2017-05-08 15:30:03 +02:00
|
|
|
virtualTime = timeRunning;
|
|
|
|
oldGMToff = GMToff;
|
|
|
|
find_jobs(timeRunning, &database, TRUE, TRUE, GMToff);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Jobs to be run (if any) are loaded; clear the queue. */
|
|
|
|
job_runqueue();
|
|
|
|
|
|
|
|
handle_signals(&database);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined WITH_INOTIFY
|
|
|
|
if (inotify_enabled && (EnableClustering != 1))
|
|
|
|
set_cron_unwatched(fd);
|
|
|
|
|
|
|
|
if (fd >= 0 && close(fd) < 0)
|
|
|
|
log_it("CRON", pid, "INFO", "Inotify close failed", errno);
|
|
|
|
#endif
|
2019-08-06 18:08:05 +02:00
|
|
|
|
|
|
|
log_it("CRON", pid, "INFO", "Shutting down", 0);
|
|
|
|
|
|
|
|
(void) unlink(_PATH_CRON_PID);
|
|
|
|
|
|
|
|
return 0;
|
2017-05-08 15:30:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void run_reboot_jobs(cron_db * db) {
|
|
|
|
user *u;
|
|
|
|
entry *e;
|
|
|
|
int reboot;
|
|
|
|
pid_t pid = getpid();
|
|
|
|
|
|
|
|
/* lock exist - skip reboot jobs */
|
|
|
|
if (access(REBOOT_LOCK, F_OK) == 0) {
|
|
|
|
log_it("CRON", pid, "INFO",
|
|
|
|
"@reboot jobs will be run at computer's startup.", 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* lock doesn't exist - create lock, run reboot jobs */
|
|
|
|
if ((reboot = creat(REBOOT_LOCK, S_IRUSR & S_IWUSR)) < 0)
|
|
|
|
log_it("CRON", pid, "INFO", "Can't create lock for reboot jobs.",
|
|
|
|
errno);
|
|
|
|
else
|
|
|
|
close(reboot);
|
|
|
|
|
|
|
|
for (u = db->head; u != NULL; u = u->next) {
|
|
|
|
for (e = u->crontab; e != NULL; e = e->next) {
|
|
|
|
if (e->flags & WHEN_REBOOT)
|
|
|
|
job_add(e, u);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
(void) job_runqueue();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void find_jobs(int vtime, cron_db * db, int doWild, int doNonWild, long vGMToff) {
|
|
|
|
char *orig_tz, *job_tz;
|
|
|
|
struct tm *tm;
|
|
|
|
int minute, hour, dom, month, dow;
|
|
|
|
user *u;
|
|
|
|
entry *e;
|
|
|
|
|
|
|
|
/* The support for the job-specific timezones is not perfect. There will
|
|
|
|
* be jobs missed or run twice during the DST change in the job timezone.
|
|
|
|
* It is recommended not to schedule any jobs during the hour when
|
|
|
|
* the DST changes happen if job-specific timezones are used.
|
|
|
|
*
|
|
|
|
* Make 0-based values out of tm values so we can use them as indicies
|
|
|
|
*/
|
|
|
|
#define maketime(tz1, tz2) do { \
|
|
|
|
char *t = tz1; \
|
|
|
|
if (t != NULL && *t != '\0') { \
|
|
|
|
setenv("TZ", t, 1); \
|
|
|
|
tm = localtime(&virtualGMTSecond); \
|
|
|
|
} else { if ((tz2) != NULL) \
|
|
|
|
setenv("TZ", (tz2), 1); \
|
|
|
|
else \
|
|
|
|
unsetenv("TZ"); \
|
|
|
|
tm = gmtime(&virtualSecond); \
|
|
|
|
} \
|
|
|
|
minute = tm->tm_min -FIRST_MINUTE; \
|
|
|
|
hour = tm->tm_hour -FIRST_HOUR; \
|
|
|
|
dom = tm->tm_mday -FIRST_DOM; \
|
|
|
|
month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH; \
|
|
|
|
dow = tm->tm_wday -FIRST_DOW; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
orig_tz = getenv("TZ");
|
2019-08-06 18:08:05 +02:00
|
|
|
|
|
|
|
/* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the
|
|
|
|
* first and fifteenth AND every Sunday; '* * * * Sun' will run *only*
|
|
|
|
* on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this
|
|
|
|
* is why we keep 'e->dow_star' and 'e->dom_star'. yes, it's bizarre.
|
|
|
|
* like many bizarre things, it's the standard.
|
|
|
|
*/
|
|
|
|
for (u = db->head; u != NULL; u = u->next) {
|
2017-05-08 15:30:03 +02:00
|
|
|
for (e = u->crontab; e != NULL; e = e->next) {
|
2019-08-06 18:08:05 +02:00
|
|
|
time_t virtualSecond = (time_t)(vtime - e->delay) * (time_t)SECONDS_PER_MINUTE;
|
|
|
|
time_t virtualGMTSecond = virtualSecond - vGMToff;
|
|
|
|
job_tz = env_get("CRON_TZ", e->envp);
|
|
|
|
maketime(job_tz, orig_tz);
|
|
|
|
|
|
|
|
/* here we test whether time is NOW */
|
|
|
|
if (bit_test(e->minute, minute) &&
|
|
|
|
bit_test(e->hour, hour) &&
|
|
|
|
bit_test(e->month, month) &&
|
|
|
|
(((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
|
|
|
|
? (bit_test(e->dow, dow) && bit_test(e->dom, dom))
|
2017-05-08 15:30:03 +02:00
|
|
|
: (bit_test(e->dow, dow) || bit_test(e->dom, dom))
|
2019-08-06 18:08:05 +02:00
|
|
|
)
|
|
|
|
) {
|
|
|
|
if (job_tz != NULL && vGMToff != GMToff)
|
|
|
|
/* do not try to run the jobs from different timezones
|
|
|
|
* during the DST switch of the default timezone.
|
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if ((doNonWild &&
|
|
|
|
!(e->flags & (MIN_STAR | HR_STAR))) ||
|
|
|
|
(doWild && (e->flags & (MIN_STAR | HR_STAR))))
|
|
|
|
job_add(e, u); /*will add job, if it isn't in queue already for NOW. */
|
2017-05-08 15:30:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (orig_tz != NULL)
|
|
|
|
setenv("TZ", orig_tz, 1);
|
|
|
|
else
|
|
|
|
unsetenv("TZ");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set StartTime and clockTime to the current time.
|
|
|
|
* These are used for computing what time it really is right now.
|
|
|
|
* Note that clockTime is a unix wallclock time converted to minutes.
|
|
|
|
*/
|
|
|
|
static void set_time(int initialize) {
|
|
|
|
struct tm tm;
|
|
|
|
static int isdst;
|
|
|
|
|
|
|
|
StartTime = time(NULL);
|
|
|
|
|
|
|
|
/* We adjust the time to GMT so we can catch DST changes. */
|
|
|
|
tm = *localtime(&StartTime);
|
|
|
|
if (initialize || tm.tm_isdst != isdst) {
|
|
|
|
isdst = tm.tm_isdst;
|
|
|
|
GMToff = get_gmtoff(&StartTime, &tm);
|
2019-08-06 18:08:05 +02:00
|
|
|
Debug(DSCH, ("[%ld] GMToff=%ld\n", (long) getpid(), (long) GMToff));
|
2017-05-08 15:30:03 +02:00
|
|
|
}
|
2019-08-06 18:08:05 +02:00
|
|
|
clockTime = (int)((StartTime + GMToff) / (time_t) SECONDS_PER_MINUTE);
|
2017-05-08 15:30:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to just hit the next minute.
|
|
|
|
*/
|
|
|
|
static void cron_sleep(int target, cron_db * db) {
|
|
|
|
time_t t1, t2;
|
|
|
|
int seconds_to_wait;
|
|
|
|
|
|
|
|
t1 = time(NULL) + GMToff;
|
|
|
|
seconds_to_wait = (int) (target * SECONDS_PER_MINUTE - t1) + 1;
|
|
|
|
Debug(DSCH, ("[%ld] Target time=%ld, sec-to-wait=%d\n",
|
|
|
|
(long) getpid(), (long) target * SECONDS_PER_MINUTE,
|
2019-08-06 18:08:05 +02:00
|
|
|
seconds_to_wait));
|
2017-05-08 15:30:03 +02:00
|
|
|
|
2019-08-06 18:08:05 +02:00
|
|
|
while (seconds_to_wait > 0 && seconds_to_wait < 65) {
|
2017-05-08 15:30:03 +02:00
|
|
|
sleep((unsigned int) seconds_to_wait);
|
|
|
|
|
2019-08-06 18:08:05 +02:00
|
|
|
if (got_sigintterm)
|
|
|
|
return;
|
|
|
|
|
2017-05-08 15:30:03 +02:00
|
|
|
/*
|
|
|
|
* Check to see if we were interrupted by a signal.
|
|
|
|
* If so, service the signal(s) then continue sleeping
|
|
|
|
* where we left off.
|
|
|
|
*/
|
|
|
|
handle_signals(db);
|
|
|
|
|
|
|
|
t2 = time(NULL) + GMToff;
|
|
|
|
seconds_to_wait -= (int) (t2 - t1);
|
|
|
|
t1 = t2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-09 15:08:54 +02:00
|
|
|
static void sighup_handler(int x ATTRIBUTE_UNUSED) {
|
2017-05-08 15:30:03 +02:00
|
|
|
got_sighup = 1;
|
|
|
|
}
|
|
|
|
|
2021-08-09 15:08:54 +02:00
|
|
|
static void sigchld_handler(int x ATTRIBUTE_UNUSED) {
|
2017-05-08 15:30:03 +02:00
|
|
|
got_sigchld = 1;
|
|
|
|
}
|
|
|
|
|
2021-08-09 15:08:54 +02:00
|
|
|
static void sigintterm_handler(int x ATTRIBUTE_UNUSED) {
|
2019-08-06 18:08:05 +02:00
|
|
|
got_sigintterm = 1;
|
2017-05-08 15:30:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void sigchld_reaper(void) {
|
|
|
|
WAIT_T waiter;
|
|
|
|
PID_T pid;
|
|
|
|
|
|
|
|
do {
|
|
|
|
pid = waitpid(-1, &waiter, WNOHANG);
|
|
|
|
switch (pid) {
|
|
|
|
case -1:
|
|
|
|
if (errno == EINTR)
|
|
|
|
continue;
|
2019-08-06 18:08:05 +02:00
|
|
|
Debug(DPROC, ("[%ld] sigchld...no children\n", (long) getpid()));
|
|
|
|
break;
|
2017-05-08 15:30:03 +02:00
|
|
|
case 0:
|
2019-08-06 18:08:05 +02:00
|
|
|
Debug(DPROC, ("[%ld] sigchld...no dead kids\n", (long) getpid()));
|
|
|
|
break;
|
2017-05-08 15:30:03 +02:00
|
|
|
default:
|
|
|
|
Debug(DPROC,
|
|
|
|
("[%ld] sigchld...pid #%ld died, stat=%d\n",
|
2019-08-06 18:08:05 +02:00
|
|
|
(long) getpid(), (long) pid, WEXITSTATUS(waiter)));
|
|
|
|
break;
|
2017-05-08 15:30:03 +02:00
|
|
|
}
|
|
|
|
} while (pid > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parse_args(int argc, char *argv[]) {
|
|
|
|
int argch;
|
|
|
|
|
2023-07-01 12:15:55 +02:00
|
|
|
while (-1 != (argch = getopt(argc, argv, "hnfpsiPx:m:cV"))) {
|
2017-05-08 15:30:03 +02:00
|
|
|
switch (argch) {
|
|
|
|
case 'x':
|
|
|
|
if (!set_debug_flags(optarg))
|
|
|
|
usage();
|
|
|
|
break;
|
|
|
|
case 'n':
|
2023-07-01 12:15:55 +02:00
|
|
|
case 'f':
|
2017-05-08 15:30:03 +02:00
|
|
|
NoFork = 1;
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
PermitAnyCrontab = 1;
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
SyslogOutput = 1;
|
|
|
|
break;
|
|
|
|
case 'i':
|
|
|
|
DisableInotify = 1;
|
|
|
|
break;
|
2019-08-06 18:08:05 +02:00
|
|
|
case 'P':
|
|
|
|
ChangePath = 0;
|
|
|
|
break;
|
2017-05-08 15:30:03 +02:00
|
|
|
case 'm':
|
|
|
|
strncpy(MailCmd, optarg, MAX_COMMAND);
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
EnableClustering = 1;
|
|
|
|
break;
|
2019-08-06 18:08:05 +02:00
|
|
|
case 'V':
|
|
|
|
puts(PACKAGE_STRING);
|
|
|
|
exit(EXIT_SUCCESS);
|
2017-05-08 15:30:03 +02:00
|
|
|
case 'h':
|
|
|
|
default:
|
|
|
|
usage();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|