summaryrefslogtreecommitdiff
path: root/miscutils
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-03-12 22:10:25 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-03-12 22:10:25 +0000
commit4e6c8120a53709c4a352db42e997303844c74584 (patch)
tree67494852dbb86ff0c25f161181bca156cd9a612e /miscutils
parent35e9c5d83ada1fea40798365a1b4c728f64ded98 (diff)
downloadbusybox-w32-4e6c8120a53709c4a352db42e997303844c74584.tar.gz
busybox-w32-4e6c8120a53709c4a352db42e997303844c74584.tar.bz2
busybox-w32-4e6c8120a53709c4a352db42e997303844c74584.zip
crond: make it NOMMU-capable
function old new delta safe_setenv4 - 62 +62 ForkJob 472 511 +39 change_identity 21 57 +36 ParseField 564 571 +7 SynchronizeDir 178 176 -2 LogLevel 4 - -4 LogFile 4 - -4 FileBase 4 - -4 DebugOpt 4 - -4 CDir 4 - -4 DeleteFile 141 132 -9 packed_usage 24248 24228 -20 crondlog 157 113 -44 change_identity_e2str 54 - -54 SynchronizeFile 729 671 -58 crond_main 1555 1404 -151 ------------------------------------------------------------------------------ (add/remove: 1/6 grow/shrink: 3/6 up/down: 144/-358) Total: -214 bytes
Diffstat (limited to 'miscutils')
-rw-r--r--miscutils/Config.in8
-rw-r--r--miscutils/crond.c745
2 files changed, 364 insertions, 389 deletions
diff --git a/miscutils/Config.in b/miscutils/Config.in
index ac1e2b57c..2cc6d9e80 100644
--- a/miscutils/Config.in
+++ b/miscutils/Config.in
@@ -106,15 +106,13 @@ config CROND
106 $ cat /var/spool/cron/crontabs/root 106 $ cat /var/spool/cron/crontabs/root
107 # Run daily cron jobs at 4:40 every day: 107 # Run daily cron jobs at 4:40 every day:
108 40 4 * * * /etc/cron/daily > /dev/null 2>&1 108 40 4 * * * /etc/cron/daily > /dev/null 2>&1
109 Note that Busybox binary must be setuid root for this applet to
110 work properly.
111 109
112config DEBUG_CROND_OPTION 110config DEBUG_CROND_OPTION
113 bool "Support debug option -d" 111 bool "Support option -d to redirect output to stderr"
114 depends on CROND 112 depends on CROND
115 default n 113 default n
116 help 114 help
117 Support option -d to enter debug mode. 115 -d sets loglevel to 0 (most verbose) and directs all output to stderr.
118 116
119config FEATURE_CROND_CALL_SENDMAIL 117config FEATURE_CROND_CALL_SENDMAIL
120 bool "Using /usr/sbin/sendmail?" 118 bool "Using /usr/sbin/sendmail?"
@@ -130,6 +128,8 @@ config CRONTAB
130 help 128 help
131 Crontab manipulates the crontab for a particular user. Only 129 Crontab manipulates the crontab for a particular user. Only
132 the superuser may specify a different user and/or crontab directory. 130 the superuser may specify a different user and/or crontab directory.
131 Note that Busybox binary must be setuid root for this applet to
132 work properly.
133 133
134config DC 134config DC
135 bool "dc" 135 bool "dc"
diff --git a/miscutils/crond.c b/miscutils/crond.c
index 9721a8a9a..6d4825e85 100644
--- a/miscutils/crond.c
+++ b/miscutils/crond.c
@@ -14,6 +14,15 @@
14#include "libbb.h" 14#include "libbb.h"
15#include <syslog.h> 15#include <syslog.h>
16 16
17/* 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
17#ifndef CRONTABS 26#ifndef CRONTABS
18#define CRONTABS "/var/spool/cron/crontabs" 27#define CRONTABS "/var/spool/cron/crontabs"
19#endif 28#endif
@@ -33,160 +42,169 @@
33#define MAXLINES 256 /* max lines in non-root crontabs */ 42#define MAXLINES 256 /* max lines in non-root crontabs */
34#endif 43#endif
35 44
45
36typedef struct CronFile { 46typedef struct CronFile {
37 struct CronFile *cf_Next; 47 struct CronFile *cf_Next;
38 struct CronLine *cf_LineBase; 48 struct CronLine *cf_LineBase;
39 char *cf_User; /* username */ 49 char *cf_User; /* username */
40 int cf_Ready; /* bool: one or more jobs ready */ 50 smallint cf_Ready; /* bool: one or more jobs ready */
41 int cf_Running; /* bool: one or more jobs running */ 51 smallint cf_Running; /* bool: one or more jobs running */
42 int cf_Deleted; /* marked for deletion, ignore */ 52 smallint cf_Deleted; /* marked for deletion, ignore */
43} CronFile; 53} CronFile;
44 54
45typedef struct CronLine { 55typedef struct CronLine {
46 struct CronLine *cl_Next; 56 struct CronLine *cl_Next;
47 char *cl_Shell; /* shell command */ 57 char *cl_Shell; /* shell command */
48 pid_t cl_Pid; /* running pid, 0, or armed (-1) */ 58 pid_t cl_Pid; /* running pid, 0, or armed (-1) */
49 int cl_MailFlag; /* running pid is for mail */ 59#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
50 int cl_MailPos; /* 'empty file' size */ 60 int cl_MailPos; /* 'empty file' size */
51 char cl_Mins[60]; /* 0-59 */ 61 smallint cl_MailFlag; /* running pid is for mail */
52 char cl_Hrs[24]; /* 0-23 */ 62#endif
53 char cl_Days[32]; /* 1-31 */ 63 /* ordered by size, not in natural order. makes code smaller: */
54 char cl_Mons[12]; /* 0-11 */ 64 char cl_Dow[7]; /* 0-6, beginning sunday */
55 char cl_Dow[7]; /* 0-6, beginning sunday */ 65 char cl_Mons[12]; /* 0-11 */
66 char cl_Hrs[24]; /* 0-23 */
67 char cl_Days[32]; /* 1-31 */
68 char cl_Mins[60]; /* 0-59 */
56} CronLine; 69} CronLine;
57 70
58#define RUN_RANOUT 1
59#define RUN_RUNNING 2
60#define RUN_FAILED 3
61 71
62#define DaemonUid 0 72#define DaemonUid 0
63 73
74
75enum {
76 OPT_l = (1 << 0),
77 OPT_L = (1 << 1),
78 OPT_f = (1 << 2),
79 OPT_b = (1 << 3),
80 OPT_S = (1 << 4),
81 OPT_c = (1 << 5),
82 OPT_d = (1 << 6) * ENABLE_DEBUG_CROND_OPTION,
83};
64#if ENABLE_DEBUG_CROND_OPTION 84#if ENABLE_DEBUG_CROND_OPTION
65static unsigned DebugOpt; 85#define DebugOpt (option_mask32 & OPT_d)
86#else
87#define DebugOpt 0
66#endif 88#endif
67 89
68static unsigned LogLevel = 8;
69static const char *LogFile;
70static const char *CDir = CRONTABS;
71 90
72static void startlogger(void); 91struct globals {
92 unsigned LogLevel; /* = 8; */
93 const char *LogFile;
94 const char *CDir; /* = CRONTABS; */
95 CronFile *FileBase;
96#if SETENV_LEAKS
97 char *env_var_user;
98 char *env_var_home;
99#endif
100};
101#define G (*(struct globals*)&bb_common_bufsiz1)
102#define LogLevel (G.LogLevel )
103#define LogFile (G.LogFile )
104#define CDir (G.CDir )
105#define FileBase (G.FileBase )
106#define env_var_user (G.env_var_user )
107#define env_var_home (G.env_var_home )
108#define INIT_G() do { \
109 LogLevel = 8; \
110 CDir = CRONTABS; \
111} while (0)
112
73 113
74static void CheckUpdates(void); 114static void CheckUpdates(void);
75static void SynchronizeDir(void); 115static void SynchronizeDir(void);
76static int TestJobs(time_t t1, time_t t2); 116static int TestJobs(time_t t1, time_t t2);
77static void RunJobs(void); 117static void RunJobs(void);
78static int CheckJobs(void); 118static int CheckJobs(void);
79 119static void RunJob(const char *user, CronLine *line);
80static void RunJob(const char *user, CronLine * line);
81
82#if ENABLE_FEATURE_CROND_CALL_SENDMAIL 120#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
83static void EndJob(const char *user, CronLine * line); 121static void EndJob(const char *user, CronLine *line);
84#else 122#else
85#define EndJob(user, line) line->cl_Pid = 0 123#define EndJob(user, line) ((line)->cl_Pid = 0)
86#endif 124#endif
87
88static void DeleteFile(const char *userName); 125static void DeleteFile(const char *userName);
89 126
90static CronFile *FileBase;
91 127
128#define LVL5 "\x05"
129#define LVL7 "\x07"
130#define LVL8 "\x08"
131#define LVL9 "\x09"
132#define WARN9 "\x49"
133#define DIE9 "\xc9"
134/* level >= 20 is "error" */
135#define ERR20 "\x14"
92 136
93static void crondlog(const char *ctl, ...) 137static void crondlog(const char *ctl, ...)
94{ 138{
95 va_list va; 139 va_list va;
96 const char *fmt; 140 int level = (ctl[0] & 0x1f);
97 int level = (int) (ctl[0] & 0xf);
98 int type = level == 20 ?
99 LOG_ERR : ((ctl[0] & 0100) ? LOG_WARNING : LOG_NOTICE);
100 141
101 va_start(va, ctl); 142 va_start(va, ctl);
102 fmt = ctl + 1;
103 if (level >= LogLevel) { 143 if (level >= LogLevel) {
104 144 /* Debug mode: all to (non-redirected) stderr, */
105#if ENABLE_DEBUG_CROND_OPTION 145 /* Syslog mode: all to syslog (logmode = LOGMODE_SYSLOG), */
106 if (DebugOpt) { 146 if (!DebugOpt && LogFile) {
107 vfprintf(stderr, fmt, va); 147 /* Otherwise (log to file): we reopen log file at every write: */
108 } else
109#endif
110 if (LogFile == NULL) {
111 vsyslog(type, fmt, va);
112 } else {
113#if !ENABLE_DEBUG_CROND_OPTION
114 int logfd = open(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600);
115#else
116 int logfd = open3_or_warn(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600); 148 int logfd = open3_or_warn(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600);
117#endif 149 if (logfd >= 0)
118 if (logfd >= 0) { 150 xmove_fd(logfd, STDERR_FILENO);
119 vdprintf(logfd, fmt, va);
120 close(logfd);
121 }
122 } 151 }
152// TODO: ERR -> error, WARN -> warning, LVL -> info
153 bb_verror_msg(ctl + 1, va, /* strerr: */ NULL);
123 } 154 }
124 va_end(va); 155 va_end(va);
125 if (ctl[0] & 0200) { 156 if (ctl[0] & 0x80)
126 exit(20); 157 exit(20);
127 }
128} 158}
129 159
130int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 160int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
131int crond_main(int ac, char **av) 161int crond_main(int ac, char **av)
132{ 162{
133 unsigned opt; 163 unsigned opt;
134 char *lopt, *Lopt, *copt;
135 USE_DEBUG_CROND_OPTION(char *dopt;)
136 164
137 opt_complementary = "f-b:b-f:S-L:L-S" USE_DEBUG_CROND_OPTION(":d-l"); 165 INIT_G();
138 opterr = 0; /* disable getopt 'errors' message. */ 166
167 /* "-b after -f is ignored", and so on for every pair a-b */
168 opt_complementary = "f-b:b-f:S-L:L-S" USE_DEBUG_CROND_OPTION(":d-l")
169 ":l+:d+"; /* -l and -d have numeric param */
139 opt = getopt32(av, "l:L:fbSc:" USE_DEBUG_CROND_OPTION("d:"), 170 opt = getopt32(av, "l:L:fbSc:" USE_DEBUG_CROND_OPTION("d:"),
140 &lopt, &Lopt, &copt USE_DEBUG_CROND_OPTION(, &dopt)); 171 &LogLevel, &LogFile, &CDir
141 if (opt & 1) /* -l */ 172 USE_DEBUG_CROND_OPTION(,&LogLevel));
142 LogLevel = xatou(lopt); 173 /* both -d N and -l N set the same variable: LogLevel */
143 if (opt & 2) /* -L */
144 if (*Lopt)
145 LogFile = Lopt;
146 if (opt & 32) /* -c */
147 if (*copt)
148 CDir = copt;
149#if ENABLE_DEBUG_CROND_OPTION
150 if (opt & 64) { /* -d */
151 DebugOpt = xatou(dopt);
152 LogLevel = 0;
153 }
154#endif
155 174
156 /* close stdin and stdout, stderr. 175 if (!(opt & OPT_f)) {
157 * close unused descriptors - don't need. 176 /* close stdin, stdout, stderr.
158 * optional detach from controlling terminal 177 * close unused descriptors - don't need them. */
159 */
160 if (!(opt & 4))
161 bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, av); 178 bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, av);
179 }
162 180
163 xchdir(CDir); 181 if (!DebugOpt && LogFile == NULL) {
164 signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */ 182 /* logging to syslog */
165 183 openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON);
166 startlogger(); /* need if syslog mode selected */ 184 logmode = LOGMODE_SYSLOG;
167 185 }
168 /*
169 * main loop - synchronize to 1 second after the minute, minimum sleep
170 * of 1 second.
171 */
172 crondlog("\011%s " BB_VER " started, log level %d\n",
173 applet_name, LogLevel);
174 186
187 xchdir(CDir);
188 //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */
189 setenv("SHELL", DEFAULT_SHELL, 1); /* once, for all future children */
190 crondlog(LVL9 "crond (busybox "BB_VER") started, log level %d", LogLevel);
175 SynchronizeDir(); 191 SynchronizeDir();
176 192
193 /* main loop - synchronize to 1 second after the minute, minimum sleep
194 * of 1 second. */
177 { 195 {
178 time_t t1 = time(NULL); 196 time_t t1 = time(NULL);
179 time_t t2; 197 time_t t2;
180 long dt; 198 long dt;
181 int rescan = 60; 199 int rescan = 60;
182 short sleep_time = 60; 200 int sleep_time = 60;
183 201
184 write_pidfile("/var/run/crond.pid"); 202 write_pidfile("/var/run/crond.pid");
185 for (;;) { 203 for (;;) {
186 sleep((sleep_time + 1) - (short) (time(NULL) % sleep_time)); 204 sleep((sleep_time + 1) - (time(NULL) % sleep_time));
187 205
188 t2 = time(NULL); 206 t2 = time(NULL);
189 dt = t2 - t1; 207 dt = (long)t2 - (long)t1;
190 208
191 /* 209 /*
192 * The file 'cron.update' is checked to determine new cron 210 * The file 'cron.update' is checked to determine new cron
@@ -204,19 +222,15 @@ int crond_main(int ac, char **av)
204 * when running jobs, the inequality used is greater but not 222 * when running jobs, the inequality used is greater but not
205 * equal to t1, and less then or equal to t2. 223 * equal to t1, and less then or equal to t2.
206 */ 224 */
207
208 if (--rescan == 0) { 225 if (--rescan == 0) {
209 rescan = 60; 226 rescan = 60;
210 SynchronizeDir(); 227 SynchronizeDir();
211 } 228 }
212 CheckUpdates(); 229 CheckUpdates();
213#if ENABLE_DEBUG_CROND_OPTION
214 if (DebugOpt) 230 if (DebugOpt)
215 crondlog("\005Wakeup dt=%d\n", dt); 231 crondlog(LVL5 "wakeup dt=%ld", dt);
216#endif
217 if (dt < -60 * 60 || dt > 60 * 60) { 232 if (dt < -60 * 60 || dt > 60 * 60) {
218 t1 = t2; 233 crondlog(WARN9 "time disparity of %d minutes detected", dt / 60);
219 crondlog("\111time disparity of %d minutes detected\n", dt / 60);
220 } else if (dt > 0) { 234 } else if (dt > 0) {
221 TestJobs(t1, t2); 235 TestJobs(t1, t2);
222 RunJobs(); 236 RunJobs();
@@ -226,66 +240,59 @@ int crond_main(int ac, char **av)
226 } else { 240 } else {
227 sleep_time = 60; 241 sleep_time = 60;
228 } 242 }
229 t1 = t2;
230 } 243 }
244 t1 = t2;
231 } 245 }
232 } 246 }
233 return 0; /* not reached */ 247 return 0; /* not reached */
234} 248}
235 249
236static int ChangeUser(const char *user) 250#if SETENV_LEAKS
251/* We set environment *before* vfork (because we want to use vfork),
252 * so we cannot use setenv() - repeated calls to setenv() may leak memory!
253 * Using putenv(), and freeing memory after unsetenv() won't leak */
254static void safe_setenv4(char **pvar_val, const char *var, const char *val /*, int len*/)
237{ 255{
238 struct passwd *pas; 256 const int len = 4; /* both var names are 4 char long */
239 const char *err_msg; 257 char *var_val = *pvar_val;
240 258
241 /* 259 if (var_val) {
242 * Obtain password entry and change privileges 260 var_val[len] = '\0'; /* nuke '=' */
243 */ 261 unsetenv(var_val);
244 pas = getpwnam(user); 262 free(var_val);
245 if (pas == 0) {
246 crondlog("\011failed to get uid for %s", user);
247 return -1;
248 } 263 }
264 *pvar_val = xasprintf("%s=%s", var, val);
265 putenv(*pvar_val);
266}
267#endif
268
269static void SetEnv(struct passwd *pas)
270{
271#if SETENV_LEAKS
272 safe_setenv4(&env_var_user, "USER", pas->pw_name);
273 safe_setenv4(&env_var_home, "HOME", pas->pw_dir);
274 /* if we want to set user's shell instead: */
275 /*safe_setenv(env_var_user, "SHELL", pas->pw_shell, 5);*/
276#else
249 setenv("USER", pas->pw_name, 1); 277 setenv("USER", pas->pw_name, 1);
250 setenv("HOME", pas->pw_dir, 1); 278 setenv("HOME", pas->pw_dir, 1);
251 setenv("SHELL", DEFAULT_SHELL, 1); 279#endif
252 280 /* currently, we use constant one: */
253 /* 281 /*setenv("SHELL", DEFAULT_SHELL, 1); - done earlier */
254 * Change running state to the user in question
255 */
256 err_msg = change_identity_e2str(pas);
257 if (err_msg) {
258 crondlog("\011%s for user %s", err_msg, user);
259 return -1;
260 }
261 if (chdir(pas->pw_dir) < 0) {
262 crondlog("\011chdir failed: %s: %m", pas->pw_dir);
263 if (chdir(TMPDIR) < 0) {
264 crondlog("\011chdir failed: %s: %m", TMPDIR);
265 return -1;
266 }
267 }
268 return pas->pw_uid;
269} 282}
270 283
271static void startlogger(void) 284static void ChangeUser(struct passwd *pas)
272{ 285{
273 if (LogFile == NULL) { 286 /* careful: we're after vfork! */
274 openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON); 287 change_identity(pas); /* - initgroups, setgid, setuid */
275 } 288 if (chdir(pas->pw_dir) < 0) {
276#if ENABLE_DEBUG_CROND_OPTION 289 crondlog(LVL9 "can't chdir(%s)", pas->pw_dir);
277 else { /* test logfile */ 290 if (chdir(TMPDIR) < 0) {
278 int logfd; 291 crondlog(DIE9 "can't chdir(%s)", TMPDIR); /* exits */
279
280 logfd = open3_or_warn(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600);
281 if (logfd >= 0) {
282 close(logfd);
283 } 292 }
284 } 293 }
285#endif
286} 294}
287 295
288
289static const char DowAry[] ALIGN1 = 296static const char DowAry[] ALIGN1 =
290 "sun""mon""tue""wed""thu""fri""sat" 297 "sun""mon""tue""wed""thu""fri""sat"
291 /* "Sun""Mon""Tue""Wed""Thu""Fri""Sat" */ 298 /* "Sun""Mon""Tue""Wed""Thu""Fri""Sat" */
@@ -308,17 +315,16 @@ static char *ParseField(char *user, char *ary, int modvalue, int off,
308 return NULL; 315 return NULL;
309 } 316 }
310 317
311 while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') { 318 while (!isspace(*ptr)) {
312 int skip = 0; 319 int skip = 0;
313 320
314 /* Handle numeric digit or symbol or '*' */ 321 /* Handle numeric digit or symbol or '*' */
315
316 if (*ptr == '*') { 322 if (*ptr == '*') {
317 n1 = 0; /* everything will be filled */ 323 n1 = 0; /* everything will be filled */
318 n2 = modvalue - 1; 324 n2 = modvalue - 1;
319 skip = 1; 325 skip = 1;
320 ++ptr; 326 ++ptr;
321 } else if (*ptr >= '0' && *ptr <= '9') { 327 } else if (isdigit(*ptr)) {
322 if (n1 < 0) { 328 if (n1 < 0) {
323 n1 = strtol(ptr, &ptr, 10) + off; 329 n1 = strtol(ptr, &ptr, 10) + off;
324 } else { 330 } else {
@@ -344,9 +350,8 @@ static char *ParseField(char *user, char *ary, int modvalue, int off,
344 } 350 }
345 351
346 /* handle optional range '-' */ 352 /* handle optional range '-' */
347
348 if (skip == 0) { 353 if (skip == 0) {
349 crondlog("\111failed user %s parsing %s\n", user, base); 354 crondlog(WARN9 "user %s: parse error at %s", user, base);
350 return NULL; 355 return NULL;
351 } 356 }
352 if (*ptr == '-' && n2 < 0) { 357 if (*ptr == '-' && n2 < 0) {
@@ -358,18 +363,17 @@ static char *ParseField(char *user, char *ary, int modvalue, int off,
358 * collapse single-value ranges, handle skipmark, and fill 363 * collapse single-value ranges, handle skipmark, and fill
359 * in the character array appropriately. 364 * in the character array appropriately.
360 */ 365 */
361
362 if (n2 < 0) { 366 if (n2 < 0) {
363 n2 = n1; 367 n2 = n1;
364 } 368 }
365 if (*ptr == '/') { 369 if (*ptr == '/') {
366 skip = strtol(ptr + 1, &ptr, 10); 370 skip = strtol(ptr + 1, &ptr, 10);
367 } 371 }
372
368 /* 373 /*
369 * fill array, using a failsafe is the easiest way to prevent 374 * fill array, using a failsafe is the easiest way to prevent
370 * an endless loop 375 * an endless loop
371 */ 376 */
372
373 { 377 {
374 int s0 = 1; 378 int s0 = 1;
375 int failsafe = 1024; 379 int failsafe = 1024;
@@ -382,13 +386,12 @@ static char *ParseField(char *user, char *ary, int modvalue, int off,
382 ary[n1 % modvalue] = 1; 386 ary[n1 % modvalue] = 1;
383 s0 = skip; 387 s0 = skip;
384 } 388 }
385 } 389 if (--failsafe == 0) {
386 while (n1 != n2 && --failsafe); 390 crondlog(WARN9 "user %s: parse error at %s", user, base);
391 return NULL;
392 }
393 } while (n1 != n2);
387 394
388 if (failsafe == 0) {
389 crondlog("\111failed user %s parsing %s\n", user, base);
390 return NULL;
391 }
392 } 395 }
393 if (*ptr != ',') { 396 if (*ptr != ',') {
394 break; 397 break;
@@ -398,147 +401,124 @@ static char *ParseField(char *user, char *ary, int modvalue, int off,
398 n2 = -1; 401 n2 = -1;
399 } 402 }
400 403
401 if (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') { 404 if (!isspace(*ptr)) {
402 crondlog("\111failed user %s parsing %s\n", user, base); 405 crondlog(WARN9 "user %s: parse error at %s", user, base);
403 return NULL; 406 return NULL;
404 } 407 }
405 408
406 while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') { 409 if (DebugOpt && (LogLevel <= 5)) { /* like LVL5 */
407 ++ptr; 410 /* can't use crondlog, it inserts '\n' */
408 }
409#if ENABLE_DEBUG_CROND_OPTION
410 if (DebugOpt) {
411 int i; 411 int i;
412 412 for (i = 0; i < modvalue; ++i)
413 for (i = 0; i < modvalue; ++i) { 413 fprintf(stderr, "%d", (unsigned char)ary[i]);
414 crondlog("\005%d", ary[i]); 414 fputc('\n', stderr);
415 }
416 crondlog("\005\n");
417 } 415 }
418#endif 416 return skip_whitespace(ptr);
419
420 return ptr;
421} 417}
422 418
423static void FixDayDow(CronLine * line) 419static void FixDayDow(CronLine *line)
424{ 420{
425 int i; 421 int i;
426 int weekUsed = 0; 422 int weekUsed = 0;
427 int daysUsed = 0; 423 int daysUsed = 0;
428 424
429 for (i = 0; i < (int)(ARRAY_SIZE(line->cl_Dow)); ++i) { 425 for (i = 0; i < ARRAY_SIZE(line->cl_Dow); ++i) {
430 if (line->cl_Dow[i] == 0) { 426 if (line->cl_Dow[i] == 0) {
431 weekUsed = 1; 427 weekUsed = 1;
432 break; 428 break;
433 } 429 }
434 } 430 }
435 for (i = 0; i < (int)(ARRAY_SIZE(line->cl_Days)); ++i) { 431 for (i = 0; i < ARRAY_SIZE(line->cl_Days); ++i) {
436 if (line->cl_Days[i] == 0) { 432 if (line->cl_Days[i] == 0) {
437 daysUsed = 1; 433 daysUsed = 1;
438 break; 434 break;
439 } 435 }
440 } 436 }
441 if (weekUsed && !daysUsed) { 437 if (weekUsed != daysUsed) {
442 memset(line->cl_Days, 0, sizeof(line->cl_Days)); 438 if (weekUsed)
443 } 439 memset(line->cl_Days, 0, sizeof(line->cl_Days));
444 if (daysUsed && !weekUsed) { 440 else /* daysUsed */
445 memset(line->cl_Dow, 0, sizeof(line->cl_Dow)); 441 memset(line->cl_Dow, 0, sizeof(line->cl_Dow));
446 } 442 }
447} 443}
448 444
449
450
451static void SynchronizeFile(const char *fileName) 445static void SynchronizeFile(const char *fileName)
452{ 446{
453 int maxEntries = MAXLINES; 447 FILE *fi;
448 struct stat sbuf;
449 int maxEntries;
454 int maxLines; 450 int maxLines;
455 char buf[1024]; 451 char buf[1024];
456 452
453 if (!fileName)
454 return;
455
456 DeleteFile(fileName);
457 fi = fopen(fileName, "r");
458 if (!fi)
459 return;
460
461 maxEntries = MAXLINES;
457 if (strcmp(fileName, "root") == 0) { 462 if (strcmp(fileName, "root") == 0) {
458 maxEntries = 65535; 463 maxEntries = 65535;
459 } 464 }
460 maxLines = maxEntries * 10; 465 maxLines = maxEntries * 10;
461 466
462 if (fileName) { 467 if (fstat(fileno(fi), &sbuf) == 0 && sbuf.st_uid == DaemonUid) {
463 FILE *fi; 468 CronFile *file = xzalloc(sizeof(CronFile));
464 469 CronLine **pline;
465 DeleteFile(fileName);
466
467 fi = fopen(fileName, "r");
468 if (fi != NULL) {
469 struct stat sbuf;
470
471 if (fstat(fileno(fi), &sbuf) == 0 && sbuf.st_uid == DaemonUid) {
472 CronFile *file = xzalloc(sizeof(CronFile));
473 CronLine **pline;
474
475 file->cf_User = strdup(fileName);
476 pline = &file->cf_LineBase;
477
478 while (fgets(buf, sizeof(buf), fi) != NULL && --maxLines) {
479 CronLine line;
480 char *ptr;
481
482 trim(buf);
483 if (buf[0] == 0 || buf[0] == '#') {
484 continue;
485 }
486 if (--maxEntries == 0) {
487 break;
488 }
489 memset(&line, 0, sizeof(line));
490
491#if ENABLE_DEBUG_CROND_OPTION
492 if (DebugOpt) {
493 crondlog("\111User %s Entry %s\n", fileName, buf);
494 }
495#endif
496
497 /* parse date ranges */
498 ptr = ParseField(file->cf_User, line.cl_Mins, 60, 0, NULL, buf);
499 ptr = ParseField(file->cf_User, line.cl_Hrs, 24, 0, NULL, ptr);
500 ptr = ParseField(file->cf_User, line.cl_Days, 32, 0, NULL, ptr);
501 ptr = ParseField(file->cf_User, line.cl_Mons, 12, -1, MonAry, ptr);
502 ptr = ParseField(file->cf_User, line.cl_Dow, 7, 0, DowAry, ptr);
503 470
504 /* check failure */ 471 file->cf_User = xstrdup(fileName);
505 if (ptr == NULL) { 472 pline = &file->cf_LineBase;
506 continue;
507 }
508
509 /*
510 * fix days and dow - if one is not * and the other
511 * is *, the other is set to 0, and vise-versa
512 */
513
514 FixDayDow(&line);
515
516 *pline = xzalloc(sizeof(CronLine));
517 **pline = line;
518 473
519 /* copy command */ 474 while (fgets(buf, sizeof(buf), fi) != NULL && --maxLines) {
520 (*pline)->cl_Shell = strdup(ptr); 475 CronLine *line;
521 476 char *ptr;
522#if ENABLE_DEBUG_CROND_OPTION
523 if (DebugOpt) {
524 crondlog("\111 Command %s\n", ptr);
525 }
526#endif
527 477
528 pline = &((*pline)->cl_Next); 478 trim(buf);
529 } 479 if (buf[0] == '\0' || buf[0] == '#') {
530 *pline = NULL; 480 continue;
481 }
482 if (--maxEntries == 0) {
483 break;
484 }
485 if (DebugOpt) {
486 crondlog(LVL5 "user:%s entry:%s", fileName, buf);
487 }
488 *pline = line = xzalloc(sizeof(CronLine));
489 /* parse date ranges */
490 ptr = ParseField(file->cf_User, line->cl_Mins, 60, 0, NULL, buf);
491 ptr = ParseField(file->cf_User, line->cl_Hrs, 24, 0, NULL, ptr);
492 ptr = ParseField(file->cf_User, line->cl_Days, 32, 0, NULL, ptr);
493 ptr = ParseField(file->cf_User, line->cl_Mons, 12, -1, MonAry, ptr);
494 ptr = ParseField(file->cf_User, line->cl_Dow, 7, 0, DowAry, ptr);
495 /* check failure */
496 if (ptr == NULL) {
497 free(line);
498 continue;
499 }
500 /*
501 * fix days and dow - if one is not * and the other
502 * is *, the other is set to 0, and vise-versa
503 */
504 FixDayDow(line);
505 /* copy command */
506 line->cl_Shell = xstrdup(ptr);
507 if (DebugOpt) {
508 crondlog(LVL5 " command:%s", ptr);
509 }
510 pline = &line->cl_Next;
511 }
512 *pline = NULL;
531 513
532 file->cf_Next = FileBase; 514 file->cf_Next = FileBase;
533 FileBase = file; 515 FileBase = file;
534 516
535 if (maxLines == 0 || maxEntries == 0) { 517 if (maxLines == 0 || maxEntries == 0) {
536 crondlog("\111Maximum number of lines reached for user %s\n", fileName); 518 crondlog(WARN9 "maximum number of lines reached for user %s", fileName);
537 }
538 }
539 fclose(fi);
540 } 519 }
541 } 520 }
521 fclose(fi);
542} 522}
543 523
544static void CheckUpdates(void) 524static void CheckUpdates(void)
@@ -550,6 +530,7 @@ static void CheckUpdates(void)
550 if (fi != NULL) { 530 if (fi != NULL) {
551 unlink(CRONUPDATE); 531 unlink(CRONUPDATE);
552 while (fgets(buf, sizeof(buf), fi) != NULL) { 532 while (fgets(buf, sizeof(buf), fi) != NULL) {
533 /* use first word only */
553 SynchronizeFile(strtok(buf, " \t\r\n")); 534 SynchronizeFile(strtok(buf, " \t\r\n"));
554 } 535 }
555 fclose(fi); 536 fclose(fi);
@@ -558,16 +539,14 @@ static void CheckUpdates(void)
558 539
559static void SynchronizeDir(void) 540static void SynchronizeDir(void)
560{ 541{
542 CronFile *file;
561 /* Attempt to delete the database. */ 543 /* Attempt to delete the database. */
562 544 again:
563 for (;;) { 545 for (file = FileBase; file; file = file->cf_Next) {
564 CronFile *file; 546 if (!file->cf_Deleted) {
565 547 DeleteFile(file->cf_User);
566 for (file = FileBase; file && file->cf_Deleted; file = file->cf_Next); 548 goto again;
567 if (file == NULL) {
568 break;
569 } 549 }
570 DeleteFile(file->cf_User);
571 } 550 }
572 551
573 /* 552 /*
@@ -578,41 +557,36 @@ static void SynchronizeDir(void)
578 * 557 *
579 * scan directory and add associated users 558 * scan directory and add associated users
580 */ 559 */
581
582 unlink(CRONUPDATE); 560 unlink(CRONUPDATE);
583 if (chdir(CDir) < 0) { 561 if (chdir(CDir) < 0) {
584 crondlog("\311cannot find %s\n", CDir); 562 crondlog(DIE9 "can't chdir(%s)", CDir);
585 } 563 }
586 { 564 {
587 DIR *dir = opendir("."); 565 DIR *dir = opendir(".");
588 struct dirent *den; 566 struct dirent *den;
589 567
590 if (dir) { 568 if (!dir)
591 while ((den = readdir(dir))) { 569 crondlog(DIE9 "can't chdir(%s)", "."); /* exits */
592 if (strchr(den->d_name, '.') != NULL) { 570 while ((den = readdir(dir))) {
593 continue; 571 if (strchr(den->d_name, '.') != NULL) {
594 } 572 continue;
595 if (getpwnam(den->d_name)) { 573 }
596 SynchronizeFile(den->d_name); 574 if (getpwnam(den->d_name)) {
597 } else { 575 SynchronizeFile(den->d_name);
598 crondlog("\007ignoring %s\n", den->d_name); 576 } else {
599 } 577 crondlog(LVL7 "ignoring %s", den->d_name);
600 } 578 }
601 closedir(dir);
602 } else {
603 crondlog("\311cannot open current dir!\n");
604 } 579 }
580 closedir(dir);
605 } 581 }
606} 582}
607 583
608
609/* 584/*
610 * DeleteFile() - delete user database 585 * DeleteFile() - delete user database
611 * 586 *
612 * Note: multiple entries for same user may exist if we were unable to 587 * Note: multiple entries for same user may exist if we were unable to
613 * completely delete a database due to running processes. 588 * completely delete a database due to running processes.
614 */ 589 */
615
616static void DeleteFile(const char *userName) 590static void DeleteFile(const char *userName)
617{ 591{
618 CronFile **pfile = &FileBase; 592 CronFile **pfile = &FileBase;
@@ -656,7 +630,6 @@ static void DeleteFile(const char *userName)
656 * period is about a minute (one scan). Worst case it will be one 630 * period is about a minute (one scan). Worst case it will be one
657 * hour (60 scans). 631 * hour (60 scans).
658 */ 632 */
659
660static int TestJobs(time_t t1, time_t t2) 633static int TestJobs(time_t t1, time_t t2)
661{ 634{
662 int nJobs = 0; 635 int nJobs = 0;
@@ -665,40 +638,37 @@ static int TestJobs(time_t t1, time_t t2)
665 /* Find jobs > t1 and <= t2 */ 638 /* Find jobs > t1 and <= t2 */
666 639
667 for (t = t1 - t1 % 60; t <= t2; t += 60) { 640 for (t = t1 - t1 % 60; t <= t2; t += 60) {
668 if (t > t1) { 641 struct tm *tp;
669 struct tm *tp = localtime(&t); 642 CronFile *file;
670 CronFile *file; 643 CronLine *line;
671 CronLine *line;
672 644
673 for (file = FileBase; file; file = file->cf_Next) { 645 if (t <= t1)
674#if ENABLE_DEBUG_CROND_OPTION 646 continue;
647
648 tp = localtime(&t);
649 for (file = FileBase; file; file = file->cf_Next) {
650 if (DebugOpt)
651 crondlog(LVL5 "file %s:", file->cf_User);
652 if (file->cf_Deleted)
653 continue;
654 for (line = file->cf_LineBase; line; line = line->cl_Next) {
675 if (DebugOpt) 655 if (DebugOpt)
676 crondlog("\005FILE %s:\n", file->cf_User); 656 crondlog(LVL5 " line %s", line->cl_Shell);
677#endif 657 if (line->cl_Mins[tp->tm_min] && line->cl_Hrs[tp->tm_hour]
678 if (file->cf_Deleted) 658 && (line->cl_Days[tp->tm_mday] || line->cl_Dow[tp->tm_wday])
679 continue; 659 && line->cl_Mons[tp->tm_mon]
680 for (line = file->cf_LineBase; line; line = line->cl_Next) { 660 ) {
681#if ENABLE_DEBUG_CROND_OPTION 661 if (DebugOpt) {
682 if (DebugOpt) 662 crondlog(LVL5 " job: %d %s",
683 crondlog("\005 LINE %s\n", line->cl_Shell); 663 (int)line->cl_Pid, line->cl_Shell);
684#endif 664 }
685 if (line->cl_Mins[tp->tm_min] && line->cl_Hrs[tp->tm_hour] && 665 if (line->cl_Pid > 0) {
686 (line->cl_Days[tp->tm_mday] || line->cl_Dow[tp->tm_wday]) 666 crondlog(LVL8 "user %s: process already running: %s",
687 && line->cl_Mons[tp->tm_mon]) { 667 file->cf_User, line->cl_Shell);
688#if ENABLE_DEBUG_CROND_OPTION 668 } else if (line->cl_Pid == 0) {
689 if (DebugOpt) { 669 line->cl_Pid = -1;
690 crondlog("\005 JobToDo: %d %s\n", 670 file->cf_Ready = 1;
691 line->cl_Pid, line->cl_Shell); 671 ++nJobs;
692 }
693#endif
694 if (line->cl_Pid > 0) {
695 crondlog("\010 process already running: %s %s\n",
696 file->cf_User, line->cl_Shell);
697 } else if (line->cl_Pid == 0) {
698 line->cl_Pid = -1;
699 file->cf_Ready = 1;
700 ++nJobs;
701 }
702 } 672 }
703 } 673 }
704 } 674 }
@@ -713,23 +683,21 @@ static void RunJobs(void)
713 CronLine *line; 683 CronLine *line;
714 684
715 for (file = FileBase; file; file = file->cf_Next) { 685 for (file = FileBase; file; file = file->cf_Next) {
716 if (file->cf_Ready) { 686 if (!file->cf_Ready)
717 file->cf_Ready = 0; 687 continue;
718
719 for (line = file->cf_LineBase; line; line = line->cl_Next) {
720 if (line->cl_Pid < 0) {
721
722 RunJob(file->cf_User, line);
723 688
724 crondlog("\010USER %s pid %3d cmd %s\n", 689 file->cf_Ready = 0;
725 file->cf_User, line->cl_Pid, line->cl_Shell); 690 for (line = file->cf_LineBase; line; line = line->cl_Next) {
726 if (line->cl_Pid < 0) { 691 if (line->cl_Pid >= 0)
727 file->cf_Ready = 1; 692 continue;
728 } 693
729 else if (line->cl_Pid > 0) { 694 RunJob(file->cf_User, line);
730 file->cf_Running = 1; 695 crondlog(LVL8 "USER %s pid %3d cmd %s",
731 } 696 file->cf_User, (int)line->cl_Pid, line->cl_Shell);
732 } 697 if (line->cl_Pid < 0) {
698 file->cf_Ready = 1;
699 } else if (line->cl_Pid > 0) {
700 file->cf_Running = 1;
733 } 701 }
734 } 702 }
735 } 703 }
@@ -741,7 +709,6 @@ static void RunJobs(void)
741 * Check for job completion, return number of jobs still running after 709 * Check for job completion, return number of jobs still running after
742 * all done. 710 * all done.
743 */ 711 */
744
745static int CheckJobs(void) 712static int CheckJobs(void)
746{ 713{
747 CronFile *file; 714 CronFile *file;
@@ -753,18 +720,18 @@ static int CheckJobs(void)
753 file->cf_Running = 0; 720 file->cf_Running = 0;
754 721
755 for (line = file->cf_LineBase; line; line = line->cl_Next) { 722 for (line = file->cf_LineBase; line; line = line->cl_Next) {
756 if (line->cl_Pid > 0) { 723 int status, r;
757 int status; 724 if (line->cl_Pid <= 0)
758 int r = waitpid(line->cl_Pid, &status, WNOHANG); 725 continue;
759 726
760 if (r < 0 || r == line->cl_Pid) { 727 r = waitpid(line->cl_Pid, &status, WNOHANG);
761 EndJob(file->cf_User, line); 728 if (r < 0 || r == line->cl_Pid) {
762 if (line->cl_Pid) { 729 EndJob(file->cf_User, line);
763 file->cf_Running = 1; 730 if (line->cl_Pid) {
764 }
765 } else if (r == 0) {
766 file->cf_Running = 1; 731 file->cf_Running = 1;
767 } 732 }
733 } else if (r == 0) {
734 file->cf_Running = 1;
768 } 735 }
769 } 736 }
770 } 737 }
@@ -773,45 +740,51 @@ static int CheckJobs(void)
773 return nStillRunning; 740 return nStillRunning;
774} 741}
775 742
776
777#if ENABLE_FEATURE_CROND_CALL_SENDMAIL 743#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
744
745// TODO: sendmail should be _run-time_ option, not compile-time!
746
778static void 747static void
779ForkJob(const char *user, CronLine * line, int mailFd, 748ForkJob(const char *user, CronLine *line, int mailFd,
780 const char *prog, const char *cmd, const char *arg, 749 const char *prog, const char *cmd, const char *arg,
781 const char *mail_filename) 750 const char *mail_filename)
782{ 751{
783 /* Fork as the user in question and run program */ 752 struct passwd *pas;
784 pid_t pid = fork(); 753 pid_t pid;
785 754
786 line->cl_Pid = pid; 755 /* prepare things before vfork */
756 pas = getpwnam(user);
757 if (!pas) {
758 crondlog(LVL9 "can't get uid for %s", user);
759 goto err;
760 }
761 SetEnv(pas);
762
763 pid = vfork();
787 if (pid == 0) { 764 if (pid == 0) {
788 /* CHILD */ 765 /* CHILD */
789 /* Change running state to the user in question */ 766 /* change running state to the user in question */
790 767 ChangeUser(pas);
791 if (ChangeUser(user) < 0) {
792 exit(0);
793 }
794#if ENABLE_DEBUG_CROND_OPTION
795 if (DebugOpt) { 768 if (DebugOpt) {
796 crondlog("\005Child Running %s\n", prog); 769 crondlog(LVL5 "child running %s", prog);
797 } 770 }
798#endif
799
800 if (mailFd >= 0) { 771 if (mailFd >= 0) {
801 xmove_fd(mailFd, mail_filename ? 1 : 0); 772 xmove_fd(mailFd, mail_filename ? 1 : 0);
802 dup2(1, 2); 773 dup2(1, 2);
803 } 774 }
804 execl(prog, prog, cmd, arg, NULL); 775 execl(prog, prog, cmd, arg, NULL);
805 crondlog("\024cannot exec, user %s cmd %s %s %s\n", user, prog, cmd, arg); 776 crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user, prog, cmd, arg);
806 if (mail_filename) { 777 if (mail_filename) {
807 fdprintf(1, "Exec failed: %s -c %s\n", prog, arg); 778 fdprintf(1, "Exec failed: %s -c %s\n", prog, arg);
808 } 779 }
809 exit(0); 780 _exit(0);
810 } 781 }
811 782
783 line->cl_Pid = pid;
812 if (pid < 0) { 784 if (pid < 0) {
813 /* FORK FAILED */ 785 /* FORK FAILED */
814 crondlog("\024cannot fork\n"); 786 crondlog(ERR20 "can't vfork");
787 err:
815 line->cl_Pid = 0; 788 line->cl_Pid = 0;
816 if (mail_filename) { 789 if (mail_filename) {
817 unlink(mail_filename); 790 unlink(mail_filename);
@@ -822,20 +795,20 @@ ForkJob(const char *user, CronLine * line, int mailFd,
822 */ 795 */
823 char mailFile2[128]; 796 char mailFile2[128];
824 797
825 snprintf(mailFile2, sizeof(mailFile2), TMPDIR "/cron.%s.%d", user, pid); 798 snprintf(mailFile2, sizeof(mailFile2), "%s/cron.%s.%d", TMPDIR, user, pid);
826 rename(mail_filename, mailFile2); // TODO: xrename? 799 rename(mail_filename, mailFile2); // TODO: xrename?
827 } 800 }
801
828 /* 802 /*
829 * Close the mail file descriptor.. we can't just leave it open in 803 * Close the mail file descriptor.. we can't just leave it open in
830 * a structure, closing it later, because we might run out of descriptors 804 * a structure, closing it later, because we might run out of descriptors
831 */ 805 */
832
833 if (mailFd >= 0) { 806 if (mailFd >= 0) {
834 close(mailFd); 807 close(mailFd);
835 } 808 }
836} 809}
837 810
838static void RunJob(const char *user, CronLine * line) 811static void RunJob(const char *user, CronLine *line)
839{ 812{
840 char mailFile[128]; 813 char mailFile[128];
841 int mailFd; 814 int mailFd;
@@ -844,8 +817,7 @@ static void RunJob(const char *user, CronLine * line)
844 line->cl_MailFlag = 0; 817 line->cl_MailFlag = 0;
845 818
846 /* open mail file - owner root so nobody can screw with it. */ 819 /* open mail file - owner root so nobody can screw with it. */
847 820 snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, getpid());
848 snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, getpid());
849 mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600); 821 mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600);
850 822
851 if (mailFd >= 0) { 823 if (mailFd >= 0) {
@@ -854,7 +826,8 @@ static void RunJob(const char *user, CronLine * line)
854 line->cl_Shell); 826 line->cl_Shell);
855 line->cl_MailPos = lseek(mailFd, 0, SEEK_CUR); 827 line->cl_MailPos = lseek(mailFd, 0, SEEK_CUR);
856 } else { 828 } else {
857 crondlog("\024cannot create mail file user %s file %s, output to /dev/null\n", user, mailFile); 829 crondlog(ERR20 "cannot create mail file %s for user %s, "
830 "discarding output", mailFile, user);
858 } 831 }
859 832
860 ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile); 833 ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile);
@@ -863,15 +836,13 @@ static void RunJob(const char *user, CronLine * line)
863/* 836/*
864 * EndJob - called when job terminates and when mail terminates 837 * EndJob - called when job terminates and when mail terminates
865 */ 838 */
866 839static void EndJob(const char *user, CronLine *line)
867static void EndJob(const char *user, CronLine * line)
868{ 840{
869 int mailFd; 841 int mailFd;
870 char mailFile[128]; 842 char mailFile[128];
871 struct stat sbuf; 843 struct stat sbuf;
872 844
873 /* No job */ 845 /* No job */
874
875 if (line->cl_Pid <= 0) { 846 if (line->cl_Pid <= 0) {
876 line->cl_Pid = 0; 847 line->cl_Pid = 0;
877 return; 848 return;
@@ -881,11 +852,10 @@ static void EndJob(const char *user, CronLine * line)
881 * End of job and no mail file 852 * End of job and no mail file
882 * End of sendmail job 853 * End of sendmail job
883 */ 854 */
884 855 snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, line->cl_Pid);
885 snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, line->cl_Pid);
886 line->cl_Pid = 0; 856 line->cl_Pid = 0;
887 857
888 if (line->cl_MailFlag != 1) { 858 if (line->cl_MailFlag == 0) {
889 return; 859 return;
890 } 860 }
891 line->cl_MailFlag = 0; 861 line->cl_MailFlag = 0;
@@ -894,7 +864,6 @@ static void EndJob(const char *user, CronLine * line)
894 * End of primary job - check for mail file. If size has increased and 864 * End of primary job - check for mail file. If size has increased and
895 * the file is still valid, we sendmail it. 865 * the file is still valid, we sendmail it.
896 */ 866 */
897
898 mailFd = open(mailFile, O_RDONLY); 867 mailFd = open(mailFile, O_RDONLY);
899 unlink(mailFile); 868 unlink(mailFile);
900 if (mailFd < 0) { 869 if (mailFd < 0) {
@@ -910,37 +879,43 @@ static void EndJob(const char *user, CronLine * line)
910 } 879 }
911 ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL); 880 ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL);
912} 881}
913#else
914/* crond without sendmail */
915 882
916static void RunJob(const char *user, CronLine * line) 883#else /* crond without sendmail */
884
885static void RunJob(const char *user, CronLine *line)
917{ 886{
918 /* Fork as the user in question and run program */ 887 struct passwd *pas;
919 pid_t pid = fork(); 888 pid_t pid;
889
890 /* prepare things before vfork */
891 pas = getpwnam(user);
892 if (!pas) {
893 crondlog(LVL9 "can't get uid for %s", user);
894 goto err;
895 }
896 SetEnv(pas);
920 897
898 /* fork as the user in question and run program */
899 pid = vfork();
921 if (pid == 0) { 900 if (pid == 0) {
922 /* CHILD */ 901 /* CHILD */
923 902 /* change running state to the user in question */
924 /* Change running state to the user in question */ 903 ChangeUser(pas);
925
926 if (ChangeUser(user) < 0) {
927 exit(0);
928 }
929#if ENABLE_DEBUG_CROND_OPTION
930 if (DebugOpt) { 904 if (DebugOpt) {
931 crondlog("\005Child Running %s\n", DEFAULT_SHELL); 905 crondlog(LVL5 "child running %s", DEFAULT_SHELL);
932 } 906 }
933#endif
934
935 execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, NULL); 907 execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, NULL);
936 crondlog("\024cannot exec, user %s cmd %s -c %s\n", user, 908 crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user,
937 DEFAULT_SHELL, line->cl_Shell); 909 DEFAULT_SHELL, "-c", line->cl_Shell);
938 exit(0); 910 _exit(0);
939 } else if (pid < 0) { 911 }
912 if (pid < 0) {
940 /* FORK FAILED */ 913 /* FORK FAILED */
941 crondlog("\024cannot, user %s\n", user); 914 crondlog(ERR20 "can't vfork");
915 err:
942 pid = 0; 916 pid = 0;
943 } 917 }
944 line->cl_Pid = pid; 918 line->cl_Pid = pid;
945} 919}
920
946#endif /* ENABLE_FEATURE_CROND_CALL_SENDMAIL */ 921#endif /* ENABLE_FEATURE_CROND_CALL_SENDMAIL */