blob: 2bed5a4b16a366977d5a51d994aaab5871da5c22 [file] [log] [blame]
Bernhard Reutner-Fischer2c998512006-04-12 18:09:26 +00001/* vi: set sw=4 ts=4: */
Eric Andersenf6f7bfb2002-10-22 12:24:59 +00002/*
3 * crond -d[#] -c <crondir> -f -b
4 *
5 * run as root, but NOT setuid root
6 *
7 * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
Denis Vlasenkob9c02dd2007-08-18 15:48:00 +00008 * (version 2.3.2)
Mike Frysingerf284c762006-04-16 20:38:26 +00009 * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000010 *
Mike Frysingerf284c762006-04-16 20:38:26 +000011 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000012 */
13
Denis Vlasenkob9c02dd2007-08-18 15:48:00 +000014#include "libbb.h"
Bernhard Reutner-Fischerf4701962008-01-27 12:50:12 +000015#include <syslog.h>
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000016
Denis Vlasenko4e6c8122008-03-12 22:10:25 +000017/* glibc frees previous setenv'ed value when we do next setenv()
18 * of the same variable. uclibc does not do this! */
19#if (defined(__GLIBC__) && !defined(__UCLIBC__)) /* || OTHER_SAFE_LIBC... */
20#define SETENV_LEAKS 0
21#else
22#define SETENV_LEAKS 1
23#endif
24
25
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000026#ifndef CRONTABS
27#define CRONTABS "/var/spool/cron/crontabs"
28#endif
29#ifndef TMPDIR
30#define TMPDIR "/var/spool/cron"
31#endif
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000032#ifndef SENDMAIL
Denis Vlasenkob9c02dd2007-08-18 15:48:00 +000033#define SENDMAIL "sendmail"
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000034#endif
35#ifndef SENDMAIL_ARGS
Denis Vlasenko6fa1ba32008-04-07 21:02:35 +000036# if ENABLE_SENDMAIL
37# define SENDMAIL_ARGS "localhost", line->cl_MailTo
38# else
39# define SENDMAIL_ARGS "-ti", "oem"
40# endif
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000041#endif
42#ifndef CRONUPDATE
43#define CRONUPDATE "cron.update"
44#endif
45#ifndef MAXLINES
Glenn L McGrath9079ad02004-02-22 04:44:21 +000046#define MAXLINES 256 /* max lines in non-root crontabs */
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000047#endif
48
Denis Vlasenko4e6c8122008-03-12 22:10:25 +000049
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000050typedef struct CronFile {
Glenn L McGrath9079ad02004-02-22 04:44:21 +000051 struct CronFile *cf_Next;
52 struct CronLine *cf_LineBase;
Denis Vlasenko4e6c8122008-03-12 22:10:25 +000053 char *cf_User; /* username */
54 smallint cf_Ready; /* bool: one or more jobs ready */
55 smallint cf_Running; /* bool: one or more jobs running */
56 smallint cf_Deleted; /* marked for deletion, ignore */
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000057} CronFile;
58
59typedef struct CronLine {
Glenn L McGrath9079ad02004-02-22 04:44:21 +000060 struct CronLine *cl_Next;
Denis Vlasenko4e6c8122008-03-12 22:10:25 +000061 char *cl_Shell; /* shell command */
62 pid_t cl_Pid; /* running pid, 0, or armed (-1) */
63#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
64 int cl_MailPos; /* 'empty file' size */
65 smallint cl_MailFlag; /* running pid is for mail */
Denis Vlasenko6fa1ba32008-04-07 21:02:35 +000066 char *cl_MailTo; /* whom to mail results */
Denis Vlasenko4e6c8122008-03-12 22:10:25 +000067#endif
68 /* ordered by size, not in natural order. makes code smaller: */
69 char cl_Dow[7]; /* 0-6, beginning sunday */
70 char cl_Mons[12]; /* 0-11 */
71 char cl_Hrs[24]; /* 0-23 */
72 char cl_Days[32]; /* 1-31 */
73 char cl_Mins[60]; /* 0-59 */
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000074} CronLine;
75
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000076
77#define DaemonUid 0
78
Denis Vlasenko4e6c8122008-03-12 22:10:25 +000079
80enum {
81 OPT_l = (1 << 0),
82 OPT_L = (1 << 1),
83 OPT_f = (1 << 2),
84 OPT_b = (1 << 3),
85 OPT_S = (1 << 4),
86 OPT_c = (1 << 5),
87 OPT_d = (1 << 6) * ENABLE_DEBUG_CROND_OPTION,
88};
Bernhard Reutner-Fischeref216292006-05-20 14:14:05 +000089#if ENABLE_DEBUG_CROND_OPTION
Denis Vlasenko4e6c8122008-03-12 22:10:25 +000090#define DebugOpt (option_mask32 & OPT_d)
91#else
92#define DebugOpt 0
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000093#endif
94
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000095
Denis Vlasenko4e6c8122008-03-12 22:10:25 +000096struct globals {
97 unsigned LogLevel; /* = 8; */
98 const char *LogFile;
99 const char *CDir; /* = CRONTABS; */
100 CronFile *FileBase;
101#if SETENV_LEAKS
102 char *env_var_user;
103 char *env_var_home;
104#endif
105};
106#define G (*(struct globals*)&bb_common_bufsiz1)
107#define LogLevel (G.LogLevel )
108#define LogFile (G.LogFile )
109#define CDir (G.CDir )
110#define FileBase (G.FileBase )
111#define env_var_user (G.env_var_user )
112#define env_var_home (G.env_var_home )
113#define INIT_G() do { \
114 LogLevel = 8; \
115 CDir = CRONTABS; \
116} while (0)
117
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000118
119static void CheckUpdates(void);
120static void SynchronizeDir(void);
121static int TestJobs(time_t t1, time_t t2);
122static void RunJobs(void);
123static int CheckJobs(void);
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000124static void RunJob(const char *user, CronLine *line);
Bernhard Reutner-Fischeref216292006-05-20 14:14:05 +0000125#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000126static void EndJob(const char *user, CronLine *line);
Eric Andersen35e643b2003-07-28 07:40:39 +0000127#else
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000128#define EndJob(user, line) ((line)->cl_Pid = 0)
Eric Andersen35e643b2003-07-28 07:40:39 +0000129#endif
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000130static void DeleteFile(const char *userName);
131
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000132
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000133#define LVL5 "\x05"
134#define LVL7 "\x07"
135#define LVL8 "\x08"
136#define LVL9 "\x09"
137#define WARN9 "\x49"
138#define DIE9 "\xc9"
139/* level >= 20 is "error" */
140#define ERR20 "\x14"
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000141
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000142static void crondlog(const char *ctl, ...)
Eric Andersen35e643b2003-07-28 07:40:39 +0000143{
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000144 va_list va;
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000145 int level = (ctl[0] & 0x1f);
Eric Andersen35e643b2003-07-28 07:40:39 +0000146
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000147 va_start(va, ctl);
Denis Vlasenko77ad97f2008-05-13 02:27:31 +0000148 if (level >= (int)LogLevel) {
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000149 /* Debug mode: all to (non-redirected) stderr, */
150 /* Syslog mode: all to syslog (logmode = LOGMODE_SYSLOG), */
151 if (!DebugOpt && LogFile) {
152 /* Otherwise (log to file): we reopen log file at every write: */
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000153 int logfd = open3_or_warn(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600);
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000154 if (logfd >= 0)
155 xmove_fd(logfd, STDERR_FILENO);
Eric Andersen35e643b2003-07-28 07:40:39 +0000156 }
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000157// TODO: ERR -> error, WARN -> warning, LVL -> info
158 bb_verror_msg(ctl + 1, va, /* strerr: */ NULL);
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000159 }
160 va_end(va);
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000161 if (ctl[0] & 0x80)
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000162 exit(20);
Eric Andersen35e643b2003-07-28 07:40:39 +0000163}
164
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000165int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000166int crond_main(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000167{
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000168 unsigned opt;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000169
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000170 INIT_G();
171
172 /* "-b after -f is ignored", and so on for every pair a-b */
173 opt_complementary = "f-b:b-f:S-L:L-S" USE_DEBUG_CROND_OPTION(":d-l")
174 ":l+:d+"; /* -l and -d have numeric param */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000175 opt = getopt32(argv, "l:L:fbSc:" USE_DEBUG_CROND_OPTION("d:"),
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000176 &LogLevel, &LogFile, &CDir
177 USE_DEBUG_CROND_OPTION(,&LogLevel));
178 /* both -d N and -l N set the same variable: LogLevel */
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000179
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000180 if (!(opt & OPT_f)) {
181 /* close stdin, stdout, stderr.
182 * close unused descriptors - don't need them. */
Denis Vlasenkoc52248e2008-03-20 14:04:30 +0000183 bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000184 }
185
186 if (!DebugOpt && LogFile == NULL) {
187 /* logging to syslog */
188 openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON);
189 logmode = LOGMODE_SYSLOG;
190 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000191
Denis Vlasenko5a142022007-03-26 13:20:54 +0000192 xchdir(CDir);
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000193 //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */
194 setenv("SHELL", DEFAULT_SHELL, 1); /* once, for all future children */
195 crondlog(LVL9 "crond (busybox "BB_VER") started, log level %d", LogLevel);
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000196 SynchronizeDir();
197
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000198 /* main loop - synchronize to 1 second after the minute, minimum sleep
199 * of 1 second. */
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000200 {
201 time_t t1 = time(NULL);
202 time_t t2;
203 long dt;
"Vladimir N. Oleynik"cd5c15d2006-01-30 13:36:03 +0000204 int rescan = 60;
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000205 int sleep_time = 60;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000206
Denis Vlasenko10457b92007-03-27 22:01:31 +0000207 write_pidfile("/var/run/crond.pid");
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000208 for (;;) {
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000209 sleep((sleep_time + 1) - (time(NULL) % sleep_time));
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000210
211 t2 = time(NULL);
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000212 dt = (long)t2 - (long)t1;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000213
214 /*
215 * The file 'cron.update' is checked to determine new cron
216 * jobs. The directory is rescanned once an hour to deal
217 * with any screwups.
218 *
219 * check for disparity. Disparities over an hour either way
220 * result in resynchronization. A reverse-indexed disparity
221 * less then an hour causes us to effectively sleep until we
222 * match the original time (i.e. no re-execution of jobs that
223 * have just been run). A forward-indexed disparity less then
224 * an hour causes intermediate jobs to be run, but only once
225 * in the worst case.
226 *
227 * when running jobs, the inequality used is greater but not
228 * equal to t1, and less then or equal to t2.
229 */
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000230 if (--rescan == 0) {
231 rescan = 60;
232 SynchronizeDir();
233 }
234 CheckUpdates();
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000235 if (DebugOpt)
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000236 crondlog(LVL5 "wakeup dt=%ld", dt);
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000237 if (dt < -60 * 60 || dt > 60 * 60) {
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000238 crondlog(WARN9 "time disparity of %d minutes detected", dt / 60);
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000239 } else if (dt > 0) {
240 TestJobs(t1, t2);
241 RunJobs();
242 sleep(5);
243 if (CheckJobs() > 0) {
244 sleep_time = 10;
245 } else {
246 sleep_time = 60;
247 }
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000248 }
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000249 t1 = t2;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000250 }
251 }
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000252 return 0; /* not reached */
Glenn L McGrathb89fcd42003-12-23 07:21:33 +0000253}
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000254
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000255#if SETENV_LEAKS
256/* We set environment *before* vfork (because we want to use vfork),
257 * so we cannot use setenv() - repeated calls to setenv() may leak memory!
258 * Using putenv(), and freeing memory after unsetenv() won't leak */
259static void safe_setenv4(char **pvar_val, const char *var, const char *val /*, int len*/)
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000260{
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000261 const int len = 4; /* both var names are 4 char long */
262 char *var_val = *pvar_val;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000263
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000264 if (var_val) {
265 var_val[len] = '\0'; /* nuke '=' */
266 unsetenv(var_val);
267 free(var_val);
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000268 }
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000269 *pvar_val = xasprintf("%s=%s", var, val);
270 putenv(*pvar_val);
271}
272#endif
273
274static void SetEnv(struct passwd *pas)
275{
276#if SETENV_LEAKS
277 safe_setenv4(&env_var_user, "USER", pas->pw_name);
278 safe_setenv4(&env_var_home, "HOME", pas->pw_dir);
279 /* if we want to set user's shell instead: */
280 /*safe_setenv(env_var_user, "SHELL", pas->pw_shell, 5);*/
281#else
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000282 setenv("USER", pas->pw_name, 1);
283 setenv("HOME", pas->pw_dir, 1);
Eric Andersen35e643b2003-07-28 07:40:39 +0000284#endif
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000285 /* currently, we use constant one: */
286 /*setenv("SHELL", DEFAULT_SHELL, 1); - done earlier */
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000287}
288
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000289static void ChangeUser(struct passwd *pas)
290{
291 /* careful: we're after vfork! */
292 change_identity(pas); /* - initgroups, setgid, setuid */
293 if (chdir(pas->pw_dir) < 0) {
294 crondlog(LVL9 "can't chdir(%s)", pas->pw_dir);
295 if (chdir(TMPDIR) < 0) {
296 crondlog(DIE9 "can't chdir(%s)", TMPDIR); /* exits */
297 }
298 }
299}
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000300
Denis Vlasenkof9000c52007-08-19 18:49:21 +0000301static const char DowAry[] ALIGN1 =
302 "sun""mon""tue""wed""thu""fri""sat"
303 /* "Sun""Mon""Tue""Wed""Thu""Fri""Sat" */
304;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000305
Denis Vlasenkof9000c52007-08-19 18:49:21 +0000306static const char MonAry[] ALIGN1 =
307 "jan""feb""mar""apr""may""jun""jul""aug""sep""oct""nov""dec"
308 /* "Jan""Feb""Mar""Apr""May""Jun""Jul""Aug""Sep""Oct""Nov""Dec" */
309;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000310
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000311static char *ParseField(char *user, char *ary, int modvalue, int off,
Denis Vlasenkof9000c52007-08-19 18:49:21 +0000312 const char *names, char *ptr)
313/* 'names' is a pointer to a set of 3-char abbreviations */
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000314{
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000315 char *base = ptr;
316 int n1 = -1;
317 int n2 = -1;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000318
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000319 if (base == NULL) {
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000320 return NULL;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000321 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000322
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000323 while (!isspace(*ptr)) {
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000324 int skip = 0;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000325
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000326 /* Handle numeric digit or symbol or '*' */
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000327 if (*ptr == '*') {
328 n1 = 0; /* everything will be filled */
329 n2 = modvalue - 1;
330 skip = 1;
331 ++ptr;
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000332 } else if (isdigit(*ptr)) {
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000333 if (n1 < 0) {
334 n1 = strtol(ptr, &ptr, 10) + off;
335 } else {
336 n2 = strtol(ptr, &ptr, 10) + off;
337 }
338 skip = 1;
339 } else if (names) {
340 int i;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000341
Denis Vlasenkof9000c52007-08-19 18:49:21 +0000342 for (i = 0; names[i]; i += 3) {
343 /* was using strncmp before... */
344 if (strncasecmp(ptr, &names[i], 3) == 0) {
345 ptr += 3;
346 if (n1 < 0) {
347 n1 = i / 3;
348 } else {
349 n2 = i / 3;
350 }
351 skip = 1;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000352 break;
353 }
354 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000355 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000356
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000357 /* handle optional range '-' */
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000358 if (skip == 0) {
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000359 crondlog(WARN9 "user %s: parse error at %s", user, base);
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000360 return NULL;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000361 }
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000362 if (*ptr == '-' && n2 < 0) {
363 ++ptr;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000364 continue;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000365 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000366
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000367 /*
368 * collapse single-value ranges, handle skipmark, and fill
369 * in the character array appropriately.
370 */
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000371 if (n2 < 0) {
372 n2 = n1;
373 }
374 if (*ptr == '/') {
375 skip = strtol(ptr + 1, &ptr, 10);
376 }
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000377
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000378 /*
379 * fill array, using a failsafe is the easiest way to prevent
380 * an endless loop
381 */
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000382 {
383 int s0 = 1;
384 int failsafe = 1024;
385
386 --n1;
387 do {
388 n1 = (n1 + 1) % modvalue;
389
390 if (--s0 == 0) {
391 ary[n1 % modvalue] = 1;
392 s0 = skip;
393 }
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000394 if (--failsafe == 0) {
395 crondlog(WARN9 "user %s: parse error at %s", user, base);
396 return NULL;
397 }
398 } while (n1 != n2);
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000399
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000400 }
401 if (*ptr != ',') {
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000402 break;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000403 }
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000404 ++ptr;
405 n1 = -1;
406 n2 = -1;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000407 }
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000408
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000409 if (!isspace(*ptr)) {
410 crondlog(WARN9 "user %s: parse error at %s", user, base);
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000411 return NULL;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000412 }
413
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000414 if (DebugOpt && (LogLevel <= 5)) { /* like LVL5 */
415 /* can't use crondlog, it inserts '\n' */
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000416 int i;
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000417 for (i = 0; i < modvalue; ++i)
418 fprintf(stderr, "%d", (unsigned char)ary[i]);
419 fputc('\n', stderr);
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000420 }
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000421 return skip_whitespace(ptr);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000422}
423
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000424static void FixDayDow(CronLine *line)
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000425{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000426 unsigned i;
"Vladimir N. Oleynik"cd5c15d2006-01-30 13:36:03 +0000427 int weekUsed = 0;
428 int daysUsed = 0;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000429
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000430 for (i = 0; i < ARRAY_SIZE(line->cl_Dow); ++i) {
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000431 if (line->cl_Dow[i] == 0) {
432 weekUsed = 1;
433 break;
434 }
435 }
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000436 for (i = 0; i < ARRAY_SIZE(line->cl_Days); ++i) {
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000437 if (line->cl_Days[i] == 0) {
438 daysUsed = 1;
439 break;
440 }
441 }
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000442 if (weekUsed != daysUsed) {
443 if (weekUsed)
444 memset(line->cl_Days, 0, sizeof(line->cl_Days));
445 else /* daysUsed */
446 memset(line->cl_Dow, 0, sizeof(line->cl_Dow));
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000447 }
448}
449
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000450static void SynchronizeFile(const char *fileName)
451{
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000452 FILE *fi;
453 struct stat sbuf;
454 int maxEntries;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000455 int maxLines;
456 char buf[1024];
Denis Vlasenko6fa1ba32008-04-07 21:02:35 +0000457#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
458 char *mailTo = NULL;
459#endif
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000460
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000461 if (!fileName)
462 return;
463
464 DeleteFile(fileName);
465 fi = fopen(fileName, "r");
466 if (!fi)
467 return;
468
469 maxEntries = MAXLINES;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000470 if (strcmp(fileName, "root") == 0) {
471 maxEntries = 65535;
472 }
473 maxLines = maxEntries * 10;
474
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000475 if (fstat(fileno(fi), &sbuf) == 0 && sbuf.st_uid == DaemonUid) {
476 CronFile *file = xzalloc(sizeof(CronFile));
477 CronLine **pline;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000478
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000479 file->cf_User = xstrdup(fileName);
480 pline = &file->cf_LineBase;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000481
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000482 while (fgets(buf, sizeof(buf), fi) != NULL && --maxLines) {
483 CronLine *line;
484 char *ptr;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000485
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000486 trim(buf);
487 if (buf[0] == '\0' || buf[0] == '#') {
488 continue;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000489 }
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000490 if (--maxEntries == 0) {
491 break;
492 }
493 if (DebugOpt) {
494 crondlog(LVL5 "user:%s entry:%s", fileName, buf);
495 }
Denis Vlasenko6fa1ba32008-04-07 21:02:35 +0000496 /* check if line is setting MAILTO= */
497 if (0 == strncmp("MAILTO=", buf, 7)) {
498#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
499 free(mailTo);
500 mailTo = (buf[7]) ? xstrdup(buf+7) : NULL;
501#endif /* otherwise just ignore such lines */
502 continue;
503 }
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000504 *pline = line = xzalloc(sizeof(CronLine));
505 /* parse date ranges */
506 ptr = ParseField(file->cf_User, line->cl_Mins, 60, 0, NULL, buf);
507 ptr = ParseField(file->cf_User, line->cl_Hrs, 24, 0, NULL, ptr);
508 ptr = ParseField(file->cf_User, line->cl_Days, 32, 0, NULL, ptr);
509 ptr = ParseField(file->cf_User, line->cl_Mons, 12, -1, MonAry, ptr);
510 ptr = ParseField(file->cf_User, line->cl_Dow, 7, 0, DowAry, ptr);
511 /* check failure */
512 if (ptr == NULL) {
513 free(line);
514 continue;
515 }
516 /*
Denis Vlasenko6fa1ba32008-04-07 21:02:35 +0000517 * fix days and dow - if one is not "*" and the other
518 * is "*", the other is set to 0, and vise-versa
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000519 */
520 FixDayDow(line);
Denis Vlasenko6fa1ba32008-04-07 21:02:35 +0000521#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
522 /* copy mailto (can be NULL) */
523 line->cl_MailTo = xstrdup(mailTo);
524#endif
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000525 /* copy command */
526 line->cl_Shell = xstrdup(ptr);
527 if (DebugOpt) {
528 crondlog(LVL5 " command:%s", ptr);
529 }
530 pline = &line->cl_Next;
531 }
532 *pline = NULL;
533
534 file->cf_Next = FileBase;
535 FileBase = file;
536
537 if (maxLines == 0 || maxEntries == 0) {
Denis Vlasenko6fa1ba32008-04-07 21:02:35 +0000538 crondlog(WARN9 "user %s: too many lines", fileName);
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000539 }
540 }
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000541 fclose(fi);
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000542}
543
544static void CheckUpdates(void)
545{
546 FILE *fi;
547 char buf[256];
548
549 fi = fopen(CRONUPDATE, "r");
550 if (fi != NULL) {
Denis Vlasenkocb448fe2008-02-17 14:28:53 +0000551 unlink(CRONUPDATE);
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000552 while (fgets(buf, sizeof(buf), fi) != NULL) {
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000553 /* use first word only */
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000554 SynchronizeFile(strtok(buf, " \t\r\n"));
555 }
556 fclose(fi);
557 }
558}
559
560static void SynchronizeDir(void)
561{
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000562 CronFile *file;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000563 /* Attempt to delete the database. */
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000564 again:
565 for (file = FileBase; file; file = file->cf_Next) {
566 if (!file->cf_Deleted) {
567 DeleteFile(file->cf_User);
568 goto again;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000569 }
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000570 }
571
572 /*
573 * Remove cron update file
574 *
575 * Re-chdir, in case directory was renamed & deleted, or otherwise
576 * screwed up.
577 *
578 * scan directory and add associated users
579 */
Denis Vlasenkocb448fe2008-02-17 14:28:53 +0000580 unlink(CRONUPDATE);
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000581 if (chdir(CDir) < 0) {
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000582 crondlog(DIE9 "can't chdir(%s)", CDir);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000583 }
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000584 {
585 DIR *dir = opendir(".");
586 struct dirent *den;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000587
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000588 if (!dir)
589 crondlog(DIE9 "can't chdir(%s)", "."); /* exits */
Denis Vlasenko6fa1ba32008-04-07 21:02:35 +0000590 while ((den = readdir(dir)) != NULL) {
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000591 if (strchr(den->d_name, '.') != NULL) {
592 continue;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000593 }
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000594 if (getpwnam(den->d_name)) {
595 SynchronizeFile(den->d_name);
596 } else {
597 crondlog(LVL7 "ignoring %s", den->d_name);
598 }
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000599 }
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000600 closedir(dir);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000601 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000602}
603
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000604/*
605 * DeleteFile() - delete user database
606 *
607 * Note: multiple entries for same user may exist if we were unable to
608 * completely delete a database due to running processes.
609 */
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000610static void DeleteFile(const char *userName)
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000611{
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000612 CronFile **pfile = &FileBase;
613 CronFile *file;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000614
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000615 while ((file = *pfile) != NULL) {
616 if (strcmp(userName, file->cf_User) == 0) {
617 CronLine **pline = &file->cf_LineBase;
618 CronLine *line;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000619
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000620 file->cf_Running = 0;
621 file->cf_Deleted = 1;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000622
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000623 while ((line = *pline) != NULL) {
624 if (line->cl_Pid > 0) {
625 file->cf_Running = 1;
626 pline = &line->cl_Next;
627 } else {
628 *pline = line->cl_Next;
629 free(line->cl_Shell);
630 free(line);
631 }
632 }
633 if (file->cf_Running == 0) {
634 *pfile = file->cf_Next;
635 free(file->cf_User);
636 free(file);
637 } else {
638 pfile = &file->cf_Next;
639 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000640 } else {
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000641 pfile = &file->cf_Next;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000642 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000643 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000644}
645
646/*
647 * TestJobs()
648 *
649 * determine which jobs need to be run. Under normal conditions, the
650 * period is about a minute (one scan). Worst case it will be one
651 * hour (60 scans).
652 */
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000653static int TestJobs(time_t t1, time_t t2)
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000654{
"Vladimir N. Oleynik"cd5c15d2006-01-30 13:36:03 +0000655 int nJobs = 0;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000656 time_t t;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000657
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000658 /* Find jobs > t1 and <= t2 */
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000659
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000660 for (t = t1 - t1 % 60; t <= t2; t += 60) {
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000661 struct tm *tp;
662 CronFile *file;
663 CronLine *line;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000664
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000665 if (t <= t1)
666 continue;
667
668 tp = localtime(&t);
669 for (file = FileBase; file; file = file->cf_Next) {
670 if (DebugOpt)
671 crondlog(LVL5 "file %s:", file->cf_User);
672 if (file->cf_Deleted)
673 continue;
674 for (line = file->cf_LineBase; line; line = line->cl_Next) {
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000675 if (DebugOpt)
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000676 crondlog(LVL5 " line %s", line->cl_Shell);
677 if (line->cl_Mins[tp->tm_min] && line->cl_Hrs[tp->tm_hour]
678 && (line->cl_Days[tp->tm_mday] || line->cl_Dow[tp->tm_wday])
679 && line->cl_Mons[tp->tm_mon]
680 ) {
681 if (DebugOpt) {
682 crondlog(LVL5 " job: %d %s",
683 (int)line->cl_Pid, line->cl_Shell);
684 }
685 if (line->cl_Pid > 0) {
686 crondlog(LVL8 "user %s: process already running: %s",
687 file->cf_User, line->cl_Shell);
688 } else if (line->cl_Pid == 0) {
689 line->cl_Pid = -1;
690 file->cf_Ready = 1;
691 ++nJobs;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000692 }
693 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000694 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000695 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000696 }
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000697 return nJobs;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000698}
699
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000700static void RunJobs(void)
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000701{
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000702 CronFile *file;
703 CronLine *line;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000704
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000705 for (file = FileBase; file; file = file->cf_Next) {
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000706 if (!file->cf_Ready)
707 continue;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000708
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000709 file->cf_Ready = 0;
710 for (line = file->cf_LineBase; line; line = line->cl_Next) {
711 if (line->cl_Pid >= 0)
712 continue;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000713
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000714 RunJob(file->cf_User, line);
715 crondlog(LVL8 "USER %s pid %3d cmd %s",
716 file->cf_User, (int)line->cl_Pid, line->cl_Shell);
717 if (line->cl_Pid < 0) {
718 file->cf_Ready = 1;
719 } else if (line->cl_Pid > 0) {
720 file->cf_Running = 1;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000721 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000722 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000723 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000724}
725
726/*
727 * CheckJobs() - check for job completion
728 *
729 * Check for job completion, return number of jobs still running after
730 * all done.
731 */
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000732static int CheckJobs(void)
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000733{
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000734 CronFile *file;
735 CronLine *line;
736 int nStillRunning = 0;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000737
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000738 for (file = FileBase; file; file = file->cf_Next) {
739 if (file->cf_Running) {
740 file->cf_Running = 0;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000741
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000742 for (line = file->cf_LineBase; line; line = line->cl_Next) {
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000743 int status, r;
744 if (line->cl_Pid <= 0)
745 continue;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000746
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000747 r = waitpid(line->cl_Pid, &status, WNOHANG);
748 if (r < 0 || r == line->cl_Pid) {
749 EndJob(file->cf_User, line);
750 if (line->cl_Pid) {
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000751 file->cf_Running = 1;
752 }
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000753 } else if (r == 0) {
754 file->cf_Running = 1;
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000755 }
756 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000757 }
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000758 nStillRunning += file->cf_Running;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000759 }
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000760 return nStillRunning;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000761}
762
Bernhard Reutner-Fischeref216292006-05-20 14:14:05 +0000763#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000764
765// TODO: sendmail should be _run-time_ option, not compile-time!
766
Eric Andersen35e643b2003-07-28 07:40:39 +0000767static void
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000768ForkJob(const char *user, CronLine *line, int mailFd,
Denis Vlasenko314820e2008-01-24 01:33:12 +0000769 const char *prog, const char *cmd, const char *arg,
770 const char *mail_filename)
Eric Andersen35e643b2003-07-28 07:40:39 +0000771{
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000772 struct passwd *pas;
773 pid_t pid;
Eric Andersen35e643b2003-07-28 07:40:39 +0000774
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000775 /* prepare things before vfork */
776 pas = getpwnam(user);
777 if (!pas) {
778 crondlog(LVL9 "can't get uid for %s", user);
779 goto err;
780 }
781 SetEnv(pas);
782
783 pid = vfork();
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000784 if (pid == 0) {
785 /* CHILD */
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000786 /* change running state to the user in question */
787 ChangeUser(pas);
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000788 if (DebugOpt) {
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000789 crondlog(LVL5 "child running %s", prog);
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000790 }
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000791 if (mailFd >= 0) {
Denis Vlasenko314820e2008-01-24 01:33:12 +0000792 xmove_fd(mailFd, mail_filename ? 1 : 0);
793 dup2(1, 2);
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000794 }
795 execl(prog, prog, cmd, arg, NULL);
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000796 crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user, prog, cmd, arg);
Denis Vlasenko314820e2008-01-24 01:33:12 +0000797 if (mail_filename) {
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000798 fdprintf(1, "Exec failed: %s -c %s\n", prog, arg);
799 }
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000800 _exit(EXIT_SUCCESS);
Denis Vlasenko314820e2008-01-24 01:33:12 +0000801 }
802
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000803 line->cl_Pid = pid;
Denis Vlasenko314820e2008-01-24 01:33:12 +0000804 if (pid < 0) {
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000805 /* FORK FAILED */
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000806 crondlog(ERR20 "can't vfork");
807 err:
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000808 line->cl_Pid = 0;
Denis Vlasenko314820e2008-01-24 01:33:12 +0000809 if (mail_filename) {
Denis Vlasenkocb448fe2008-02-17 14:28:53 +0000810 unlink(mail_filename);
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000811 }
Denis Vlasenko314820e2008-01-24 01:33:12 +0000812 } else if (mail_filename) {
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000813 /* PARENT, FORK SUCCESS
814 * rename mail-file based on pid of process
815 */
816 char mailFile2[128];
817
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000818 snprintf(mailFile2, sizeof(mailFile2), "%s/cron.%s.%d", TMPDIR, user, pid);
Denis Vlasenkocb448fe2008-02-17 14:28:53 +0000819 rename(mail_filename, mailFile2); // TODO: xrename?
Eric Andersen35e643b2003-07-28 07:40:39 +0000820 }
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000821
Eric Andersen35e643b2003-07-28 07:40:39 +0000822 /*
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000823 * Close the mail file descriptor.. we can't just leave it open in
824 * a structure, closing it later, because we might run out of descriptors
Eric Andersen35e643b2003-07-28 07:40:39 +0000825 */
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000826 if (mailFd >= 0) {
827 close(mailFd);
828 }
Eric Andersen35e643b2003-07-28 07:40:39 +0000829}
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000830
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000831static void RunJob(const char *user, CronLine *line)
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000832{
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000833 char mailFile[128];
Denis Vlasenko6fa1ba32008-04-07 21:02:35 +0000834 int mailFd = -1;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000835
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000836 line->cl_Pid = 0;
837 line->cl_MailFlag = 0;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000838
Denis Vlasenko6fa1ba32008-04-07 21:02:35 +0000839 if (line->cl_MailTo) {
840 /* open mail file - owner root so nobody can screw with it. */
841 snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, getpid());
842 mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000843
Denis Vlasenko6fa1ba32008-04-07 21:02:35 +0000844 if (mailFd >= 0) {
845 line->cl_MailFlag = 1;
846 fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", user,
847 line->cl_Shell);
848 line->cl_MailPos = lseek(mailFd, 0, SEEK_CUR);
849 } else {
850 crondlog(ERR20 "cannot create mail file %s for user %s, "
851 "discarding output", mailFile, user);
852 }
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000853 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000854
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000855 ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000856}
857
858/*
859 * EndJob - called when job terminates and when mail terminates
860 */
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000861static void EndJob(const char *user, CronLine *line)
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000862{
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000863 int mailFd;
864 char mailFile[128];
865 struct stat sbuf;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000866
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000867 /* No job */
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000868 if (line->cl_Pid <= 0) {
869 line->cl_Pid = 0;
870 return;
871 }
872
873 /*
874 * End of job and no mail file
875 * End of sendmail job
876 */
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000877 snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, line->cl_Pid);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000878 line->cl_Pid = 0;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000879
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000880 if (line->cl_MailFlag == 0) {
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000881 return;
882 }
883 line->cl_MailFlag = 0;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000884
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000885 /*
886 * End of primary job - check for mail file. If size has increased and
887 * the file is still valid, we sendmail it.
888 */
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000889 mailFd = open(mailFile, O_RDONLY);
Denis Vlasenkocb448fe2008-02-17 14:28:53 +0000890 unlink(mailFile);
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000891 if (mailFd < 0) {
892 return;
893 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000894
Denis Vlasenkob9c02dd2007-08-18 15:48:00 +0000895 if (fstat(mailFd, &sbuf) < 0 || sbuf.st_uid != DaemonUid
896 || sbuf.st_nlink != 0 || sbuf.st_size == line->cl_MailPos
897 || !S_ISREG(sbuf.st_mode)
898 ) {
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000899 close(mailFd);
900 return;
901 }
Denis Vlasenko6fa1ba32008-04-07 21:02:35 +0000902 if (line->cl_MailTo)
903 ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL);
Eric Andersen35e643b2003-07-28 07:40:39 +0000904}
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000905
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000906#else /* crond without sendmail */
907
908static void RunJob(const char *user, CronLine *line)
Eric Andersen35e643b2003-07-28 07:40:39 +0000909{
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000910 struct passwd *pas;
911 pid_t pid;
Eric Andersen35e643b2003-07-28 07:40:39 +0000912
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000913 /* prepare things before vfork */
914 pas = getpwnam(user);
915 if (!pas) {
916 crondlog(LVL9 "can't get uid for %s", user);
917 goto err;
918 }
919 SetEnv(pas);
920
921 /* fork as the user in question and run program */
922 pid = vfork();
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000923 if (pid == 0) {
924 /* CHILD */
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000925 /* change running state to the user in question */
926 ChangeUser(pas);
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000927 if (DebugOpt) {
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000928 crondlog(LVL5 "child running %s", DEFAULT_SHELL);
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000929 }
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000930 execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, NULL);
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000931 crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user,
932 DEFAULT_SHELL, "-c", line->cl_Shell);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000933 _exit(EXIT_SUCCESS);
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000934 }
935 if (pid < 0) {
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000936 /* FORK FAILED */
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000937 crondlog(ERR20 "can't vfork");
938 err:
Glenn L McGrath9079ad02004-02-22 04:44:21 +0000939 pid = 0;
940 }
941 line->cl_Pid = pid;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000942}
Denis Vlasenko4e6c8122008-03-12 22:10:25 +0000943
Bernhard Reutner-Fischeref216292006-05-20 14:14:05 +0000944#endif /* ENABLE_FEATURE_CROND_CALL_SENDMAIL */