crond: support @daily etc
function old new delta start_jobs - 348 +348 load_crontab 766 936 +170 static.SpecAry - 96 +96 crond_main 1424 1134 -290 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 1/1 up/down: 614/-290) Total: 324 bytes Based on patch by Jonathan Kolb <kolbyjack@gmail.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
1572f520cc
commit
75fbea3879
@ -35,6 +35,22 @@
|
|||||||
//config: help
|
//config: help
|
||||||
//config: Command output will be sent to corresponding user via email.
|
//config: Command output will be sent to corresponding user via email.
|
||||||
//config:
|
//config:
|
||||||
|
//config:config FEATURE_CROND_SPECIAL_TIMES
|
||||||
|
//config: bool "Support special times (@reboot, @daily, etc) in crontabs"
|
||||||
|
//config: default y
|
||||||
|
//config: depends on CROND
|
||||||
|
//config: help
|
||||||
|
//config: string meaning
|
||||||
|
//config: ------ -------
|
||||||
|
//config: @reboot Run once, at startup
|
||||||
|
//config: @yearly Run once a year: "0 0 1 1 *"
|
||||||
|
//config: @annually Same as @yearly: "0 0 1 1 *"
|
||||||
|
//config: @monthly Run once a month: "0 0 1 * *"
|
||||||
|
//config: @weekly Run once a week: "0 0 * * 0"
|
||||||
|
//config: @daily Run once a day: "0 0 * * *"
|
||||||
|
//config: @midnight Same as @daily: "0 0 * * *"
|
||||||
|
//config: @hourly Run once an hour: "0 * * * *"
|
||||||
|
//config:
|
||||||
//config:config FEATURE_CROND_DIR
|
//config:config FEATURE_CROND_DIR
|
||||||
//config: string "crond spool directory"
|
//config: string "crond spool directory"
|
||||||
//config: default "/var/spool/cron"
|
//config: default "/var/spool/cron"
|
||||||
@ -74,6 +90,7 @@
|
|||||||
|
|
||||||
#define CRON_DIR CONFIG_FEATURE_CROND_DIR
|
#define CRON_DIR CONFIG_FEATURE_CROND_DIR
|
||||||
#define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs"
|
#define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs"
|
||||||
|
#define CRON_REBOOT CONFIG_PID_FILE_PATH "/crond.reboot"
|
||||||
#ifndef SENDMAIL
|
#ifndef SENDMAIL
|
||||||
# define SENDMAIL "sendmail"
|
# define SENDMAIL "sendmail"
|
||||||
#endif
|
#endif
|
||||||
@ -101,6 +118,8 @@ typedef struct CronLine {
|
|||||||
struct CronLine *cl_next;
|
struct CronLine *cl_next;
|
||||||
char *cl_cmd; /* shell command */
|
char *cl_cmd; /* shell command */
|
||||||
pid_t cl_pid; /* >0:running, <0:needs to be started in this minute, 0:dormant */
|
pid_t cl_pid; /* >0:running, <0:needs to be started in this minute, 0:dormant */
|
||||||
|
#define START_ME_REBOOT -2
|
||||||
|
#define START_ME_NORMAL -1
|
||||||
#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
|
#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
|
||||||
int cl_empty_mail_size; /* size of mail header only, 0 if no mailfile */
|
int cl_empty_mail_size; /* size of mail header only, 0 if no mailfile */
|
||||||
char *cl_mailto; /* whom to mail results, may be NULL */
|
char *cl_mailto; /* whom to mail results, may be NULL */
|
||||||
@ -452,6 +471,59 @@ static void load_crontab(const char *fileName)
|
|||||||
shell = xstrdup(&tokens[0][6]);
|
shell = xstrdup(&tokens[0][6]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
#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,
|
||||||
|
* find the entire command in unmodified string:
|
||||||
|
*/
|
||||||
|
tokens[5] = strstr(
|
||||||
|
skip_non_whitespace(skip_whitespace(parser->data)),
|
||||||
|
/* ^^^^ avoids mishandling e.g. "@daily aily PARAM" */
|
||||||
|
tokens[1]
|
||||||
|
);
|
||||||
|
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
|
||||||
//TODO: handle HOME= too? "man crontab" says:
|
//TODO: handle HOME= too? "man crontab" says:
|
||||||
//name = value
|
//name = value
|
||||||
//
|
//
|
||||||
@ -468,18 +540,30 @@ static void load_crontab(const char *fileName)
|
|||||||
/* check if a minimum of tokens is specified */
|
/* check if a minimum of tokens is specified */
|
||||||
if (n < 6)
|
if (n < 6)
|
||||||
continue;
|
continue;
|
||||||
|
IF_FEATURE_CROND_SPECIAL_TIMES(
|
||||||
|
got_it:
|
||||||
|
)
|
||||||
*pline = line = xzalloc(sizeof(*line));
|
*pline = line = xzalloc(sizeof(*line));
|
||||||
/* parse date ranges */
|
#if ENABLE_FEATURE_CROND_SPECIAL_TIMES
|
||||||
ParseField(file->cf_username, line->cl_Mins, 60, 0, NULL, tokens[0]);
|
if (tokens[0][0] == '@') { /* "@reboot" line */
|
||||||
ParseField(file->cf_username, line->cl_Hrs, 24, 0, NULL, tokens[1]);
|
file->cf_wants_starting = 1;
|
||||||
ParseField(file->cf_username, line->cl_Days, 32, 0, NULL, tokens[2]);
|
line->cl_pid = START_ME_REBOOT; /* wants to start */
|
||||||
ParseField(file->cf_username, line->cl_Mons, 12, -1, MonAry, tokens[3]);
|
/* line->cl_Mins/Hrs/etc stay zero: never match any time */
|
||||||
ParseField(file->cf_username, line->cl_Dow, 7, 0, DowAry, tokens[4]);
|
} else
|
||||||
/*
|
#endif
|
||||||
* fix days and dow - if one is not "*" and the other
|
{
|
||||||
* is "*", the other is set to 0, and vise-versa
|
/* parse date ranges */
|
||||||
*/
|
ParseField(file->cf_username, line->cl_Mins, 60, 0, NULL, tokens[0]);
|
||||||
FixDayDow(line);
|
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
|
#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
|
||||||
/* copy mailto (can be NULL) */
|
/* copy mailto (can be NULL) */
|
||||||
line->cl_mailto = xstrdup(mailTo);
|
line->cl_mailto = xstrdup(mailTo);
|
||||||
@ -664,7 +748,7 @@ fork_job(const char *user, int mailFd, CronLine *line, bool run_sendmail)
|
|||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void start_one_job(const char *user, CronLine *line)
|
static pid_t start_one_job(const char *user, CronLine *line)
|
||||||
{
|
{
|
||||||
char mailFile[128];
|
char mailFile[128];
|
||||||
int mailFd = -1;
|
int mailFd = -1;
|
||||||
@ -698,6 +782,8 @@ static void start_one_job(const char *user, CronLine *line)
|
|||||||
free(mailFile2);
|
free(mailFile2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return line->cl_pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -748,7 +834,7 @@ static void process_finished_job(const char *user, CronLine *line)
|
|||||||
|
|
||||||
#else /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */
|
#else /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */
|
||||||
|
|
||||||
static void start_one_job(const char *user, CronLine *line)
|
static pid_t start_one_job(const char *user, CronLine *line)
|
||||||
{
|
{
|
||||||
const char *shell;
|
const char *shell;
|
||||||
struct passwd *pas;
|
struct passwd *pas;
|
||||||
@ -782,6 +868,7 @@ static void start_one_job(const char *user, CronLine *line)
|
|||||||
pid = 0;
|
pid = 0;
|
||||||
}
|
}
|
||||||
line->cl_pid = pid;
|
line->cl_pid = pid;
|
||||||
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define process_finished_job(user, line) ((line)->cl_pid = 0)
|
#define process_finished_job(user, line) ((line)->cl_pid = 0)
|
||||||
@ -825,7 +912,7 @@ static void flag_starting_jobs(time_t t1, time_t t2)
|
|||||||
log8("user %s: process already running: %s",
|
log8("user %s: process already running: %s",
|
||||||
file->cf_username, line->cl_cmd);
|
file->cf_username, line->cl_cmd);
|
||||||
} else if (line->cl_pid == 0) {
|
} else if (line->cl_pid == 0) {
|
||||||
line->cl_pid = -1;
|
line->cl_pid = START_ME_NORMAL;
|
||||||
file->cf_wants_starting = 1;
|
file->cf_wants_starting = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -834,7 +921,20 @@ static void flag_starting_jobs(time_t t1, time_t t2)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void start_jobs(void)
|
#if ENABLE_FEATURE_CROND_SPECIAL_TIMES
|
||||||
|
static int touch_reboot_file(void)
|
||||||
|
{
|
||||||
|
int fd = open(CRON_REBOOT, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0000);
|
||||||
|
if (fd >= 0) {
|
||||||
|
close(fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* File (presumably) exists - this is not the first run after reboot */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void start_jobs(int wants_start)
|
||||||
{
|
{
|
||||||
CronFile *file;
|
CronFile *file;
|
||||||
CronLine *line;
|
CronLine *line;
|
||||||
@ -846,11 +946,10 @@ static void start_jobs(void)
|
|||||||
file->cf_wants_starting = 0;
|
file->cf_wants_starting = 0;
|
||||||
for (line = file->cf_lines; line; line = line->cl_next) {
|
for (line = file->cf_lines; line; line = line->cl_next) {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
if (line->cl_pid >= 0)
|
if (line->cl_pid != wants_start)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
start_one_job(file->cf_username, line);
|
pid = start_one_job(file->cf_username, line);
|
||||||
pid = line->cl_pid;
|
|
||||||
log8("USER %s pid %3d cmd %s",
|
log8("USER %s pid %3d cmd %s",
|
||||||
file->cf_username, (int)pid, line->cl_cmd);
|
file->cf_username, (int)pid, line->cl_cmd);
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
@ -950,6 +1049,10 @@ int crond_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
log8("crond (busybox "BB_VER") started, log level %d", G.log_level);
|
log8("crond (busybox "BB_VER") started, log level %d", G.log_level);
|
||||||
rescan_crontab_dir();
|
rescan_crontab_dir();
|
||||||
write_pidfile(CONFIG_PID_FILE_PATH "/crond.pid");
|
write_pidfile(CONFIG_PID_FILE_PATH "/crond.pid");
|
||||||
|
#if ENABLE_FEATURE_CROND_SPECIAL_TIMES
|
||||||
|
if (touch_reboot_file())
|
||||||
|
start_jobs(START_ME_REBOOT); /* start @reboot entries, if any */
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Main loop */
|
/* Main loop */
|
||||||
t2 = time(NULL);
|
t2 = time(NULL);
|
||||||
@ -1002,7 +1105,7 @@ int crond_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
} else if (dt > 0) {
|
} else if (dt > 0) {
|
||||||
/* Usual case: time advances forward, as expected */
|
/* Usual case: time advances forward, as expected */
|
||||||
flag_starting_jobs(t1, t2);
|
flag_starting_jobs(t1, t2);
|
||||||
start_jobs();
|
start_jobs(START_ME_NORMAL);
|
||||||
sleep_time = 60;
|
sleep_time = 60;
|
||||||
if (check_completions() > 0) {
|
if (check_completions() > 0) {
|
||||||
/* some jobs are still running */
|
/* some jobs are still running */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user