23 |
#endif |
#endif |
24 |
|
|
25 |
|
|
26 |
#ifndef CRONTABS |
#define TMPDIR CONFIG_FEATURE_CROND_DIR |
27 |
#define CRONTABS "/var/spool/cron/crontabs" |
#define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs" |
|
#endif |
|
|
#ifndef TMPDIR |
|
|
#define TMPDIR "/var/spool/cron" |
|
|
#endif |
|
28 |
#ifndef SENDMAIL |
#ifndef SENDMAIL |
29 |
#define SENDMAIL "sendmail" |
#define SENDMAIL "sendmail" |
30 |
#endif |
#endif |
31 |
#ifndef SENDMAIL_ARGS |
#ifndef SENDMAIL_ARGS |
32 |
#define SENDMAIL_ARGS "-ti", "oem" |
#define SENDMAIL_ARGS "-ti", NULL |
33 |
#endif |
#endif |
34 |
#ifndef CRONUPDATE |
#ifndef CRONUPDATE |
35 |
#define CRONUPDATE "cron.update" |
#define CRONUPDATE "cron.update" |
55 |
#if ENABLE_FEATURE_CROND_CALL_SENDMAIL |
#if ENABLE_FEATURE_CROND_CALL_SENDMAIL |
56 |
int cl_MailPos; /* 'empty file' size */ |
int cl_MailPos; /* 'empty file' size */ |
57 |
smallint cl_MailFlag; /* running pid is for mail */ |
smallint cl_MailFlag; /* running pid is for mail */ |
58 |
char *cl_MailTo; /* whom to mail results */ |
char *cl_MailTo; /* whom to mail results */ |
59 |
#endif |
#endif |
60 |
/* ordered by size, not in natural order. makes code smaller: */ |
/* ordered by size, not in natural order. makes code smaller: */ |
61 |
char cl_Dow[7]; /* 0-6, beginning sunday */ |
char cl_Dow[7]; /* 0-6, beginning sunday */ |
122 |
static void DeleteFile(const char *userName); |
static void DeleteFile(const char *userName); |
123 |
|
|
124 |
|
|
125 |
|
/* 0 is the most verbose, default 8 */ |
126 |
#define LVL5 "\x05" |
#define LVL5 "\x05" |
127 |
#define LVL7 "\x07" |
#define LVL7 "\x07" |
128 |
#define LVL8 "\x08" |
#define LVL8 "\x08" |
|
#define LVL9 "\x09" |
|
129 |
#define WARN9 "\x49" |
#define WARN9 "\x49" |
130 |
#define DIE9 "\xc9" |
#define DIE9 "\xc9" |
131 |
/* level >= 20 is "error" */ |
/* level >= 20 is "error" */ |
132 |
#define ERR20 "\x14" |
#define ERR20 "\x14" |
133 |
|
|
134 |
|
static void crondlog(const char *ctl, ...) __attribute__ ((format (printf, 1, 2))); |
135 |
static void crondlog(const char *ctl, ...) |
static void crondlog(const char *ctl, ...) |
136 |
{ |
{ |
137 |
va_list va; |
va_list va; |
143 |
/* Syslog mode: all to syslog (logmode = LOGMODE_SYSLOG), */ |
/* Syslog mode: all to syslog (logmode = LOGMODE_SYSLOG), */ |
144 |
if (!DebugOpt && LogFile) { |
if (!DebugOpt && LogFile) { |
145 |
/* Otherwise (log to file): we reopen log file at every write: */ |
/* Otherwise (log to file): we reopen log file at every write: */ |
146 |
int logfd = open3_or_warn(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600); |
int logfd = open3_or_warn(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0666); |
147 |
if (logfd >= 0) |
if (logfd >= 0) |
148 |
xmove_fd(logfd, STDERR_FILENO); |
xmove_fd(logfd, STDERR_FILENO); |
149 |
} |
} |
150 |
// TODO: ERR -> error, WARN -> warning, LVL -> info |
/* When we log to syslog, level > 8 is logged at LOG_ERR |
151 |
bb_verror_msg(ctl + 1, va, /* strerr: */ NULL); |
* syslog level, level <= 8 is logged at LOG_INFO. */ |
152 |
|
if (level > 8) { |
153 |
|
bb_verror_msg(ctl + 1, va, /* strerr: */ NULL); |
154 |
|
} else { |
155 |
|
char *msg = NULL; |
156 |
|
vasprintf(&msg, ctl + 1, va); |
157 |
|
bb_info_msg("%s: %s", applet_name, msg); |
158 |
|
free(msg); |
159 |
|
} |
160 |
} |
} |
161 |
va_end(va); |
va_end(va); |
162 |
if (ctl[0] & 0x80) |
if (ctl[0] & 0x80) |
166 |
int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
167 |
int crond_main(int argc UNUSED_PARAM, char **argv) |
int crond_main(int argc UNUSED_PARAM, char **argv) |
168 |
{ |
{ |
169 |
unsigned opt; |
unsigned opts; |
170 |
|
|
171 |
INIT_G(); |
INIT_G(); |
172 |
|
|
173 |
/* "-b after -f is ignored", and so on for every pair a-b */ |
/* "-b after -f is ignored", and so on for every pair a-b */ |
174 |
opt_complementary = "f-b:b-f:S-L:L-S" USE_FEATURE_CROND_D(":d-l") |
opt_complementary = "f-b:b-f:S-L:L-S" IF_FEATURE_CROND_D(":d-l") |
175 |
":l+:d+"; /* -l and -d have numeric param */ |
":l+:d+"; /* -l and -d have numeric param */ |
176 |
opt = getopt32(argv, "l:L:fbSc:" USE_FEATURE_CROND_D("d:"), |
opts = getopt32(argv, "l:L:fbSc:" IF_FEATURE_CROND_D("d:"), |
177 |
&LogLevel, &LogFile, &CDir |
&LogLevel, &LogFile, &CDir |
178 |
USE_FEATURE_CROND_D(,&LogLevel)); |
IF_FEATURE_CROND_D(,&LogLevel)); |
179 |
/* both -d N and -l N set the same variable: LogLevel */ |
/* both -d N and -l N set the same variable: LogLevel */ |
180 |
|
|
181 |
if (!(opt & OPT_f)) { |
if (!(opts & OPT_f)) { |
182 |
/* close stdin, stdout, stderr. |
/* close stdin, stdout, stderr. |
183 |
* close unused descriptors - don't need them. */ |
* close unused descriptors - don't need them. */ |
184 |
bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); |
bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); |
185 |
} |
} |
186 |
|
|
187 |
if (!DebugOpt && LogFile == NULL) { |
if (!(opts & OPT_d) && LogFile == NULL) { |
188 |
/* logging to syslog */ |
/* logging to syslog */ |
189 |
openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON); |
openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON); |
190 |
logmode = LOGMODE_SYSLOG; |
logmode = LOGMODE_SYSLOG; |
193 |
xchdir(CDir); |
xchdir(CDir); |
194 |
//signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */ |
//signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */ |
195 |
xsetenv("SHELL", DEFAULT_SHELL); /* once, for all future children */ |
xsetenv("SHELL", DEFAULT_SHELL); /* once, for all future children */ |
196 |
crondlog(LVL9 "crond (busybox "BB_VER") started, log level %d", LogLevel); |
crondlog(LVL8 "crond (busybox "BB_VER") started, log level %d", LogLevel); |
197 |
SynchronizeDir(); |
SynchronizeDir(); |
198 |
|
|
199 |
/* main loop - synchronize to 1 second after the minute, minimum sleep |
/* main loop - synchronize to 1 second after the minute, minimum sleep |
200 |
* of 1 second. */ |
* of 1 second. */ |
201 |
{ |
{ |
202 |
time_t t1 = time(NULL); |
time_t t1 = time(NULL); |
|
time_t t2; |
|
|
long dt; |
|
203 |
int rescan = 60; |
int rescan = 60; |
204 |
int sleep_time = 60; |
int sleep_time = 60; |
205 |
|
|
206 |
write_pidfile("/var/run/crond.pid"); |
write_pidfile("/var/run/crond.pid"); |
207 |
for (;;) { |
for (;;) { |
208 |
|
time_t t2; |
209 |
|
long dt; |
210 |
|
|
211 |
sleep((sleep_time + 1) - (time(NULL) % sleep_time)); |
sleep((sleep_time + 1) - (time(NULL) % sleep_time)); |
212 |
|
|
213 |
t2 = time(NULL); |
t2 = time(NULL); |
237 |
if (DebugOpt) |
if (DebugOpt) |
238 |
crondlog(LVL5 "wakeup dt=%ld", dt); |
crondlog(LVL5 "wakeup dt=%ld", dt); |
239 |
if (dt < -60 * 60 || dt > 60 * 60) { |
if (dt < -60 * 60 || dt > 60 * 60) { |
240 |
crondlog(WARN9 "time disparity of %d minutes detected", dt / 60); |
crondlog(WARN9 "time disparity of %ld minutes detected", dt / 60); |
241 |
} else if (dt > 0) { |
} else if (dt > 0) { |
242 |
TestJobs(t1, t2); |
TestJobs(t1, t2); |
243 |
RunJobs(); |
RunJobs(); |
249 |
} |
} |
250 |
} |
} |
251 |
t1 = t2; |
t1 = t2; |
252 |
} |
} /* for (;;) */ |
253 |
} |
} |
254 |
|
|
255 |
return 0; /* not reached */ |
return 0; /* not reached */ |
256 |
} |
} |
257 |
|
|
259 |
/* We set environment *before* vfork (because we want to use vfork), |
/* We set environment *before* vfork (because we want to use vfork), |
260 |
* so we cannot use setenv() - repeated calls to setenv() may leak memory! |
* so we cannot use setenv() - repeated calls to setenv() may leak memory! |
261 |
* Using putenv(), and freeing memory after unsetenv() won't leak */ |
* Using putenv(), and freeing memory after unsetenv() won't leak */ |
262 |
static void safe_setenv4(char **pvar_val, const char *var, const char *val /*, int len*/) |
static void safe_setenv(char **pvar_val, const char *var, const char *val) |
263 |
{ |
{ |
|
const int len = 4; /* both var names are 4 char long */ |
|
264 |
char *var_val = *pvar_val; |
char *var_val = *pvar_val; |
265 |
|
|
266 |
if (var_val) { |
if (var_val) { |
267 |
var_val[len] = '\0'; /* nuke '=' */ |
bb_unsetenv(var_val); |
|
unsetenv(var_val); |
|
268 |
free(var_val); |
free(var_val); |
269 |
} |
} |
270 |
*pvar_val = xasprintf("%s=%s", var, val); |
*pvar_val = xasprintf("%s=%s", var, val); |
275 |
static void SetEnv(struct passwd *pas) |
static void SetEnv(struct passwd *pas) |
276 |
{ |
{ |
277 |
#if SETENV_LEAKS |
#if SETENV_LEAKS |
278 |
safe_setenv4(&env_var_user, "USER", pas->pw_name); |
safe_setenv(&env_var_user, "USER", pas->pw_name); |
279 |
safe_setenv4(&env_var_home, "HOME", pas->pw_dir); |
safe_setenv(&env_var_home, "HOME", pas->pw_dir); |
280 |
/* if we want to set user's shell instead: */ |
/* if we want to set user's shell instead: */ |
281 |
/*safe_setenv(env_var_user, "SHELL", pas->pw_shell, 5);*/ |
/*safe_setenv(env_var_user, "SHELL", pas->pw_shell);*/ |
282 |
#else |
#else |
283 |
xsetenv("USER", pas->pw_name); |
xsetenv("USER", pas->pw_name); |
284 |
xsetenv("HOME", pas->pw_dir); |
xsetenv("HOME", pas->pw_dir); |
292 |
/* careful: we're after vfork! */ |
/* careful: we're after vfork! */ |
293 |
change_identity(pas); /* - initgroups, setgid, setuid */ |
change_identity(pas); /* - initgroups, setgid, setuid */ |
294 |
if (chdir(pas->pw_dir) < 0) { |
if (chdir(pas->pw_dir) < 0) { |
295 |
crondlog(LVL9 "can't chdir(%s)", pas->pw_dir); |
crondlog(WARN9 "can't chdir(%s)", pas->pw_dir); |
296 |
if (chdir(TMPDIR) < 0) { |
if (chdir(TMPDIR) < 0) { |
297 |
crondlog(DIE9 "can't chdir(%s)", TMPDIR); /* exits */ |
crondlog(DIE9 "can't chdir(%s)", TMPDIR); /* exits */ |
298 |
} |
} |
331 |
skip = 1; |
skip = 1; |
332 |
++ptr; |
++ptr; |
333 |
} else if (isdigit(*ptr)) { |
} else if (isdigit(*ptr)) { |
334 |
|
char *endp; |
335 |
if (n1 < 0) { |
if (n1 < 0) { |
336 |
n1 = strtol(ptr, &ptr, 10) + off; |
n1 = strtol(ptr, &endp, 10) + off; |
337 |
} else { |
} else { |
338 |
n2 = strtol(ptr, &ptr, 10) + off; |
n2 = strtol(ptr, &endp, 10) + off; |
339 |
} |
} |
340 |
|
ptr = endp; /* gcc likes temp var for &endp */ |
341 |
skip = 1; |
skip = 1; |
342 |
} else if (names) { |
} else if (names) { |
343 |
int i; |
int i; |
374 |
n2 = n1; |
n2 = n1; |
375 |
} |
} |
376 |
if (*ptr == '/') { |
if (*ptr == '/') { |
377 |
skip = strtol(ptr + 1, &ptr, 10); |
char *endp; |
378 |
|
skip = strtol(ptr + 1, &endp, 10); |
379 |
|
ptr = endp; /* gcc likes temp var for &endp */ |
380 |
} |
} |
381 |
|
|
382 |
/* |
/* |
654 |
/* Find jobs > t1 and <= t2 */ |
/* Find jobs > t1 and <= t2 */ |
655 |
|
|
656 |
for (t = t1 - t1 % 60; t <= t2; t += 60) { |
for (t = t1 - t1 % 60; t <= t2; t += 60) { |
657 |
struct tm *tp; |
struct tm *ptm; |
658 |
CronFile *file; |
CronFile *file; |
659 |
CronLine *line; |
CronLine *line; |
660 |
|
|
661 |
if (t <= t1) |
if (t <= t1) |
662 |
continue; |
continue; |
663 |
|
|
664 |
tp = localtime(&t); |
ptm = localtime(&t); |
665 |
for (file = FileBase; file; file = file->cf_Next) { |
for (file = FileBase; file; file = file->cf_Next) { |
666 |
if (DebugOpt) |
if (DebugOpt) |
667 |
crondlog(LVL5 "file %s:", file->cf_User); |
crondlog(LVL5 "file %s:", file->cf_User); |
670 |
for (line = file->cf_LineBase; line; line = line->cl_Next) { |
for (line = file->cf_LineBase; line; line = line->cl_Next) { |
671 |
if (DebugOpt) |
if (DebugOpt) |
672 |
crondlog(LVL5 " line %s", line->cl_Shell); |
crondlog(LVL5 " line %s", line->cl_Shell); |
673 |
if (line->cl_Mins[tp->tm_min] && line->cl_Hrs[tp->tm_hour] |
if (line->cl_Mins[ptm->tm_min] && line->cl_Hrs[ptm->tm_hour] |
674 |
&& (line->cl_Days[tp->tm_mday] || line->cl_Dow[tp->tm_wday]) |
&& (line->cl_Days[ptm->tm_mday] || line->cl_Dow[ptm->tm_wday]) |
675 |
&& line->cl_Mons[tp->tm_mon] |
&& line->cl_Mons[ptm->tm_mon] |
676 |
) { |
) { |
677 |
if (DebugOpt) { |
if (DebugOpt) { |
678 |
crondlog(LVL5 " job: %d %s", |
crondlog(LVL5 " job: %d %s", |
771 |
/* prepare things before vfork */ |
/* prepare things before vfork */ |
772 |
pas = getpwnam(user); |
pas = getpwnam(user); |
773 |
if (!pas) { |
if (!pas) { |
774 |
crondlog(LVL9 "can't get uid for %s", user); |
crondlog(WARN9 "can't get uid for %s", user); |
775 |
goto err; |
goto err; |
776 |
} |
} |
777 |
SetEnv(pas); |
SetEnv(pas); |
790 |
} |
} |
791 |
/* crond 3.0pl1-100 puts tasks in separate process groups */ |
/* crond 3.0pl1-100 puts tasks in separate process groups */ |
792 |
bb_setpgrp(); |
bb_setpgrp(); |
793 |
execlp(prog, prog, cmd, arg, NULL); |
execlp(prog, prog, cmd, arg, (char *) NULL); |
794 |
crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user, prog, cmd, arg); |
crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user, prog, cmd, arg); |
795 |
if (mail_filename) { |
if (mail_filename) { |
796 |
fdprintf(1, "Exec failed: %s -c %s\n", prog, arg); |
fdprintf(1, "Exec failed: %s -c %s\n", prog, arg); |
845 |
line->cl_Shell); |
line->cl_Shell); |
846 |
line->cl_MailPos = lseek(mailFd, 0, SEEK_CUR); |
line->cl_MailPos = lseek(mailFd, 0, SEEK_CUR); |
847 |
} else { |
} else { |
848 |
crondlog(ERR20 "cannot create mail file %s for user %s, " |
crondlog(ERR20 "can't create mail file %s for user %s, " |
849 |
"discarding output", mailFile, user); |
"discarding output", mailFile, user); |
850 |
} |
} |
851 |
} |
} |
911 |
/* prepare things before vfork */ |
/* prepare things before vfork */ |
912 |
pas = getpwnam(user); |
pas = getpwnam(user); |
913 |
if (!pas) { |
if (!pas) { |
914 |
crondlog(LVL9 "can't get uid for %s", user); |
crondlog(WARN9 "can't get uid for %s", user); |
915 |
goto err; |
goto err; |
916 |
} |
} |
917 |
SetEnv(pas); |
SetEnv(pas); |
927 |
} |
} |
928 |
/* crond 3.0pl1-100 puts tasks in separate process groups */ |
/* crond 3.0pl1-100 puts tasks in separate process groups */ |
929 |
bb_setpgrp(); |
bb_setpgrp(); |
930 |
execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, NULL); |
execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, (char *) NULL); |
931 |
crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user, |
crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user, |
932 |
DEFAULT_SHELL, "-c", line->cl_Shell); |
DEFAULT_SHELL, "-c", line->cl_Shell); |
933 |
_exit(EXIT_SUCCESS); |
_exit(EXIT_SUCCESS); |