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:
		@@ -35,6 +35,22 @@
 | 
			
		||||
//config:	help
 | 
			
		||||
//config:	  Command output will be sent to corresponding user via email.
 | 
			
		||||
//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:	string "crond spool directory"
 | 
			
		||||
//config:	default "/var/spool/cron"
 | 
			
		||||
@@ -74,6 +90,7 @@
 | 
			
		||||
 | 
			
		||||
#define CRON_DIR        CONFIG_FEATURE_CROND_DIR
 | 
			
		||||
#define CRONTABS        CONFIG_FEATURE_CROND_DIR "/crontabs"
 | 
			
		||||
#define CRON_REBOOT     CONFIG_PID_FILE_PATH "/crond.reboot"
 | 
			
		||||
#ifndef SENDMAIL
 | 
			
		||||
# define SENDMAIL       "sendmail"
 | 
			
		||||
#endif
 | 
			
		||||
@@ -101,6 +118,8 @@ typedef struct CronLine {
 | 
			
		||||
	struct CronLine *cl_next;
 | 
			
		||||
	char *cl_cmd;                   /* shell command */
 | 
			
		||||
	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
 | 
			
		||||
	int cl_empty_mail_size;         /* size of mail header only, 0 if no mailfile */
 | 
			
		||||
	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]);
 | 
			
		||||
				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:
 | 
			
		||||
//name = value
 | 
			
		||||
//
 | 
			
		||||
@@ -468,18 +540,30 @@ static void load_crontab(const char *fileName)
 | 
			
		||||
			/* check if a minimum of tokens is specified */
 | 
			
		||||
			if (n < 6)
 | 
			
		||||
				continue;
 | 
			
		||||
 IF_FEATURE_CROND_SPECIAL_TIMES(
 | 
			
		||||
  got_it:
 | 
			
		||||
 )
 | 
			
		||||
			*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_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);
 | 
			
		||||
@@ -664,7 +748,7 @@ fork_job(const char *user, int mailFd, CronLine *line, bool run_sendmail)
 | 
			
		||||
	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];
 | 
			
		||||
	int mailFd = -1;
 | 
			
		||||
@@ -698,6 +782,8 @@ static void start_one_job(const char *user, CronLine *line)
 | 
			
		||||
			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 */
 | 
			
		||||
 | 
			
		||||
static void start_one_job(const char *user, CronLine *line)
 | 
			
		||||
static pid_t start_one_job(const char *user, CronLine *line)
 | 
			
		||||
{
 | 
			
		||||
	const char *shell;
 | 
			
		||||
	struct passwd *pas;
 | 
			
		||||
@@ -782,6 +868,7 @@ static void start_one_job(const char *user, CronLine *line)
 | 
			
		||||
		pid = 0;
 | 
			
		||||
	}
 | 
			
		||||
	line->cl_pid = pid;
 | 
			
		||||
	return pid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#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",
 | 
			
		||||
							file->cf_username, line->cl_cmd);
 | 
			
		||||
					} else if (line->cl_pid == 0) {
 | 
			
		||||
						line->cl_pid = -1;
 | 
			
		||||
						line->cl_pid = START_ME_NORMAL;
 | 
			
		||||
						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;
 | 
			
		||||
	CronLine *line;
 | 
			
		||||
@@ -846,11 +946,10 @@ static void start_jobs(void)
 | 
			
		||||
		file->cf_wants_starting = 0;
 | 
			
		||||
		for (line = file->cf_lines; line; line = line->cl_next) {
 | 
			
		||||
			pid_t pid;
 | 
			
		||||
			if (line->cl_pid >= 0)
 | 
			
		||||
			if (line->cl_pid != wants_start)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			start_one_job(file->cf_username, line);
 | 
			
		||||
			pid = line->cl_pid;
 | 
			
		||||
			pid = start_one_job(file->cf_username, line);
 | 
			
		||||
			log8("USER %s pid %3d cmd %s",
 | 
			
		||||
				file->cf_username, (int)pid, line->cl_cmd);
 | 
			
		||||
			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);
 | 
			
		||||
	rescan_crontab_dir();
 | 
			
		||||
	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 */
 | 
			
		||||
	t2 = time(NULL);
 | 
			
		||||
@@ -1002,7 +1105,7 @@ int crond_main(int argc UNUSED_PARAM, char **argv)
 | 
			
		||||
		} else if (dt > 0) {
 | 
			
		||||
			/* Usual case: time advances forward, as expected */
 | 
			
		||||
			flag_starting_jobs(t1, t2);
 | 
			
		||||
			start_jobs();
 | 
			
		||||
			start_jobs(START_ME_NORMAL);
 | 
			
		||||
			sleep_time = 60;
 | 
			
		||||
			if (check_completions() > 0) {
 | 
			
		||||
				/* some jobs are still running */
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user