aboutsummaryrefslogtreecommitdiff
path: root/miscutils/crond.c
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/crond.c
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/crond.c')
-rw-r--r--miscutils/crond.c745
1 files changed, 360 insertions, 385 deletions
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 */