Magellan Linux

Contents of /trunk/mkinitrd-magellan/busybox/miscutils/crond.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 532 - (show annotations) (download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 20874 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd

1 /* vi: set sw=4 ts=4: */
2 /*
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)
8 * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002
9 *
10 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
11 */
12
13 #define VERSION "2.3.2"
14
15 #include "busybox.h"
16 #include <sys/syslog.h>
17
18 #define arysize(ary) (sizeof(ary)/sizeof((ary)[0]))
19
20 #ifndef CRONTABS
21 #define CRONTABS "/var/spool/cron/crontabs"
22 #endif
23 #ifndef TMPDIR
24 #define TMPDIR "/var/spool/cron"
25 #endif
26 #ifndef SENDMAIL
27 #define SENDMAIL "/usr/sbin/sendmail"
28 #endif
29 #ifndef SENDMAIL_ARGS
30 #define SENDMAIL_ARGS "-ti", "oem"
31 #endif
32 #ifndef CRONUPDATE
33 #define CRONUPDATE "cron.update"
34 #endif
35 #ifndef MAXLINES
36 #define MAXLINES 256 /* max lines in non-root crontabs */
37 #endif
38
39 typedef struct CronFile {
40 struct CronFile *cf_Next;
41 struct CronLine *cf_LineBase;
42 char *cf_User; /* username */
43 int cf_Ready; /* bool: one or more jobs ready */
44 int cf_Running; /* bool: one or more jobs running */
45 int cf_Deleted; /* marked for deletion, ignore */
46 } CronFile;
47
48 typedef struct CronLine {
49 struct CronLine *cl_Next;
50 char *cl_Shell; /* shell command */
51 pid_t cl_Pid; /* running pid, 0, or armed (-1) */
52 int cl_MailFlag; /* running pid is for mail */
53 int cl_MailPos; /* 'empty file' size */
54 char cl_Mins[60]; /* 0-59 */
55 char cl_Hrs[24]; /* 0-23 */
56 char cl_Days[32]; /* 1-31 */
57 char cl_Mons[12]; /* 0-11 */
58 char cl_Dow[7]; /* 0-6, beginning sunday */
59 } CronLine;
60
61 #define RUN_RANOUT 1
62 #define RUN_RUNNING 2
63 #define RUN_FAILED 3
64
65 #define DaemonUid 0
66
67 #if ENABLE_DEBUG_CROND_OPTION
68 static unsigned DebugOpt;
69 #endif
70
71 static unsigned LogLevel = 8;
72 static const char *LogFile;
73 static const char *CDir = CRONTABS;
74
75 static void startlogger(void);
76
77 static void CheckUpdates(void);
78 static void SynchronizeDir(void);
79 static int TestJobs(time_t t1, time_t t2);
80 static void RunJobs(void);
81 static int CheckJobs(void);
82
83 static void RunJob(const char *user, CronLine * line);
84
85 #if ENABLE_FEATURE_CROND_CALL_SENDMAIL
86 static void EndJob(const char *user, CronLine * line);
87 #else
88 #define EndJob(user, line) line->cl_Pid = 0
89 #endif
90
91 static void DeleteFile(const char *userName);
92
93 static CronFile *FileBase;
94
95
96 static void crondlog(const char *ctl, ...)
97 {
98 va_list va;
99 const char *fmt;
100 int level = (int) (ctl[0] & 0xf);
101 int type = level == 20 ?
102 LOG_ERR : ((ctl[0] & 0100) ? LOG_WARNING : LOG_NOTICE);
103
104
105 va_start(va, ctl);
106 fmt = ctl + 1;
107 if (level >= LogLevel) {
108
109 #if ENABLE_DEBUG_CROND_OPTION
110 if (DebugOpt) {
111 vfprintf(stderr, fmt, va);
112 } else
113 #endif
114 if (LogFile == 0) {
115 vsyslog(type, fmt, va);
116 } else {
117 int logfd = open(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600);
118 if (logfd >= 0) {
119 vdprintf(logfd, fmt, va);
120 close(logfd);
121 #if ENABLE_DEBUG_CROND_OPTION
122 } else {
123 bb_perror_msg("can't open log file");
124 #endif
125 }
126 }
127 }
128 va_end(va);
129 if (ctl[0] & 0200) {
130 exit(20);
131 }
132 }
133
134 int crond_main(int ac, char **av)
135 {
136 unsigned opt;
137 char *lopt, *Lopt, *copt;
138
139 #if ENABLE_DEBUG_CROND_OPTION
140 char *dopt;
141
142 opt_complementary = "f-b:b-f:S-L:L-S:d-l";
143 #else
144 opt_complementary = "f-b:b-f:S-L:L-S";
145 #endif
146
147 opterr = 0; /* disable getopt 'errors' message. */
148 opt = getopt32(ac, av, "l:L:fbSc:"
149 #if ENABLE_DEBUG_CROND_OPTION
150 "d:"
151 #endif
152 , &lopt, &Lopt, &copt
153 #if ENABLE_DEBUG_CROND_OPTION
154 , &dopt
155 #endif
156 );
157 if (opt & 1) {
158 LogLevel = xatou(lopt);
159 }
160 if (opt & 2) {
161 if (*Lopt != 0) {
162 LogFile = Lopt;
163 }
164 }
165 if (opt & 32) {
166 if (*copt != 0) {
167 CDir = copt;
168 }
169 }
170 #if ENABLE_DEBUG_CROND_OPTION
171 if (opt & 64) {
172 DebugOpt = xatou(dopt);
173 LogLevel = 0;
174 }
175 #endif
176
177 /*
178 * change directory
179 */
180
181 xchdir(CDir);
182 signal(SIGHUP, SIG_IGN); /* hmm.. but, if kill -HUP original
183 * version - his died. ;(
184 */
185 /*
186 * close stdin and stdout, stderr.
187 * close unused descriptors - don't need.
188 * optional detach from controlling terminal
189 */
190
191 if (!(opt & 4)) {
192 #ifdef BB_NOMMU
193 /* reexec for vfork() do continue parent */
194 vfork_daemon_rexec(1, 0, ac, av, "-f");
195 #else
196 xdaemon(1, 0);
197 #endif
198 }
199
200 (void) startlogger(); /* need if syslog mode selected */
201
202 /*
203 * main loop - synchronize to 1 second after the minute, minimum sleep
204 * of 1 second.
205 */
206
207 crondlog("\011%s " VERSION " dillon, started, log level %d\n",
208 applet_name, LogLevel);
209
210 SynchronizeDir();
211
212 {
213 time_t t1 = time(NULL);
214 time_t t2;
215 long dt;
216 int rescan = 60;
217 short sleep_time = 60;
218
219 for (;;) {
220 sleep((sleep_time + 1) - (short) (time(NULL) % sleep_time));
221
222 t2 = time(NULL);
223 dt = t2 - t1;
224
225 /*
226 * The file 'cron.update' is checked to determine new cron
227 * jobs. The directory is rescanned once an hour to deal
228 * with any screwups.
229 *
230 * check for disparity. Disparities over an hour either way
231 * result in resynchronization. A reverse-indexed disparity
232 * less then an hour causes us to effectively sleep until we
233 * match the original time (i.e. no re-execution of jobs that
234 * have just been run). A forward-indexed disparity less then
235 * an hour causes intermediate jobs to be run, but only once
236 * in the worst case.
237 *
238 * when running jobs, the inequality used is greater but not
239 * equal to t1, and less then or equal to t2.
240 */
241
242 if (--rescan == 0) {
243 rescan = 60;
244 SynchronizeDir();
245 }
246 CheckUpdates();
247 #if ENABLE_DEBUG_CROND_OPTION
248 if (DebugOpt)
249 crondlog("\005Wakeup dt=%d\n", dt);
250 #endif
251 if (dt < -60 * 60 || dt > 60 * 60) {
252 t1 = t2;
253 crondlog("\111time disparity of %d minutes detected\n", dt / 60);
254 } else if (dt > 0) {
255 TestJobs(t1, t2);
256 RunJobs();
257 sleep(5);
258 if (CheckJobs() > 0) {
259 sleep_time = 10;
260 } else {
261 sleep_time = 60;
262 }
263 t1 = t2;
264 }
265 }
266 }
267 return 0; /* not reached */
268 }
269
270 static int ChangeUser(const char *user)
271 {
272 struct passwd *pas;
273 const char *err_msg;
274
275 /*
276 * Obtain password entry and change privileges
277 */
278 pas = getpwnam(user);
279 if (pas == 0) {
280 crondlog("\011failed to get uid for %s", user);
281 return -1;
282 }
283 setenv("USER", pas->pw_name, 1);
284 setenv("HOME", pas->pw_dir, 1);
285 setenv("SHELL", DEFAULT_SHELL, 1);
286
287 /*
288 * Change running state to the user in question
289 */
290 err_msg = change_identity_e2str(pas);
291 if (err_msg) {
292 crondlog("\011%s for user %s", err_msg, user);
293 return -1;
294 }
295 if (chdir(pas->pw_dir) < 0) {
296 crondlog("\011chdir failed: %s: %m", pas->pw_dir);
297 if (chdir(TMPDIR) < 0) {
298 crondlog("\011chdir failed: %s: %m", TMPDIR);
299 return -1;
300 }
301 }
302 return pas->pw_uid;
303 }
304
305 static void startlogger(void)
306 {
307 if (LogFile == 0) {
308 openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON);
309 }
310 #if ENABLE_DEBUG_CROND_OPTION
311 else { /* test logfile */
312 int logfd;
313
314 if ((logfd = open(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600)) >= 0) {
315 close(logfd);
316 } else {
317 bb_perror_msg("failed to open log file '%s': ", LogFile);
318 }
319 }
320 #endif
321 }
322
323
324 static const char *const DowAry[] = {
325 "sun",
326 "mon",
327 "tue",
328 "wed",
329 "thu",
330 "fri",
331 "sat",
332
333 "Sun",
334 "Mon",
335 "Tue",
336 "Wed",
337 "Thu",
338 "Fri",
339 "Sat",
340 NULL
341 };
342
343 static const char *const MonAry[] = {
344 "jan",
345 "feb",
346 "mar",
347 "apr",
348 "may",
349 "jun",
350 "jul",
351 "aug",
352 "sep",
353 "oct",
354 "nov",
355 "dec",
356
357 "Jan",
358 "Feb",
359 "Mar",
360 "Apr",
361 "May",
362 "Jun",
363 "Jul",
364 "Aug",
365 "Sep",
366 "Oct",
367 "Nov",
368 "Dec",
369 NULL
370 };
371
372 static char *ParseField(char *user, char *ary, int modvalue, int off,
373 const char *const *names, char *ptr)
374 {
375 char *base = ptr;
376 int n1 = -1;
377 int n2 = -1;
378
379 if (base == NULL) {
380 return NULL;
381 }
382
383 while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
384 int skip = 0;
385
386 /* Handle numeric digit or symbol or '*' */
387
388 if (*ptr == '*') {
389 n1 = 0; /* everything will be filled */
390 n2 = modvalue - 1;
391 skip = 1;
392 ++ptr;
393 } else if (*ptr >= '0' && *ptr <= '9') {
394 if (n1 < 0) {
395 n1 = strtol(ptr, &ptr, 10) + off;
396 } else {
397 n2 = strtol(ptr, &ptr, 10) + off;
398 }
399 skip = 1;
400 } else if (names) {
401 int i;
402
403 for (i = 0; names[i]; ++i) {
404 if (strncmp(ptr, names[i], strlen(names[i])) == 0) {
405 break;
406 }
407 }
408 if (names[i]) {
409 ptr += strlen(names[i]);
410 if (n1 < 0) {
411 n1 = i;
412 } else {
413 n2 = i;
414 }
415 skip = 1;
416 }
417 }
418
419 /* handle optional range '-' */
420
421 if (skip == 0) {
422 crondlog("\111failed user %s parsing %s\n", user, base);
423 return NULL;
424 }
425 if (*ptr == '-' && n2 < 0) {
426 ++ptr;
427 continue;
428 }
429
430 /*
431 * collapse single-value ranges, handle skipmark, and fill
432 * in the character array appropriately.
433 */
434
435 if (n2 < 0) {
436 n2 = n1;
437 }
438 if (*ptr == '/') {
439 skip = strtol(ptr + 1, &ptr, 10);
440 }
441 /*
442 * fill array, using a failsafe is the easiest way to prevent
443 * an endless loop
444 */
445
446 {
447 int s0 = 1;
448 int failsafe = 1024;
449
450 --n1;
451 do {
452 n1 = (n1 + 1) % modvalue;
453
454 if (--s0 == 0) {
455 ary[n1 % modvalue] = 1;
456 s0 = skip;
457 }
458 }
459 while (n1 != n2 && --failsafe);
460
461 if (failsafe == 0) {
462 crondlog("\111failed user %s parsing %s\n", user, base);
463 return NULL;
464 }
465 }
466 if (*ptr != ',') {
467 break;
468 }
469 ++ptr;
470 n1 = -1;
471 n2 = -1;
472 }
473
474 if (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
475 crondlog("\111failed user %s parsing %s\n", user, base);
476 return NULL;
477 }
478
479 while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') {
480 ++ptr;
481 }
482 #if ENABLE_DEBUG_CROND_OPTION
483 if (DebugOpt) {
484 int i;
485
486 for (i = 0; i < modvalue; ++i) {
487 crondlog("\005%d", ary[i]);
488 }
489 crondlog("\005\n");
490 }
491 #endif
492
493 return ptr;
494 }
495
496 static void FixDayDow(CronLine * line)
497 {
498 int i;
499 int weekUsed = 0;
500 int daysUsed = 0;
501
502 for (i = 0; i < (int)(arysize(line->cl_Dow)); ++i) {
503 if (line->cl_Dow[i] == 0) {
504 weekUsed = 1;
505 break;
506 }
507 }
508 for (i = 0; i < (int)(arysize(line->cl_Days)); ++i) {
509 if (line->cl_Days[i] == 0) {
510 daysUsed = 1;
511 break;
512 }
513 }
514 if (weekUsed && !daysUsed) {
515 memset(line->cl_Days, 0, sizeof(line->cl_Days));
516 }
517 if (daysUsed && !weekUsed) {
518 memset(line->cl_Dow, 0, sizeof(line->cl_Dow));
519 }
520 }
521
522
523
524 static void SynchronizeFile(const char *fileName)
525 {
526 int maxEntries = MAXLINES;
527 int maxLines;
528 char buf[1024];
529
530 if (strcmp(fileName, "root") == 0) {
531 maxEntries = 65535;
532 }
533 maxLines = maxEntries * 10;
534
535 if (fileName) {
536 FILE *fi;
537
538 DeleteFile(fileName);
539
540 fi = fopen(fileName, "r");
541 if (fi != NULL) {
542 struct stat sbuf;
543
544 if (fstat(fileno(fi), &sbuf) == 0 && sbuf.st_uid == DaemonUid) {
545 CronFile *file = xzalloc(sizeof(CronFile));
546 CronLine **pline;
547
548 file->cf_User = strdup(fileName);
549 pline = &file->cf_LineBase;
550
551 while (fgets(buf, sizeof(buf), fi) != NULL && --maxLines) {
552 CronLine line;
553 char *ptr;
554
555 trim(buf);
556 if (buf[0] == 0 || buf[0] == '#') {
557 continue;
558 }
559 if (--maxEntries == 0) {
560 break;
561 }
562 memset(&line, 0, sizeof(line));
563
564 #if ENABLE_DEBUG_CROND_OPTION
565 if (DebugOpt) {
566 crondlog("\111User %s Entry %s\n", fileName, buf);
567 }
568 #endif
569
570 /* parse date ranges */
571 ptr = ParseField(file->cf_User, line.cl_Mins, 60, 0, NULL, buf);
572 ptr = ParseField(file->cf_User, line.cl_Hrs, 24, 0, NULL, ptr);
573 ptr = ParseField(file->cf_User, line.cl_Days, 32, 0, NULL, ptr);
574 ptr = ParseField(file->cf_User, line.cl_Mons, 12, -1, MonAry, ptr);
575 ptr = ParseField(file->cf_User, line.cl_Dow, 7, 0, DowAry, ptr);
576
577 /* check failure */
578 if (ptr == NULL) {
579 continue;
580 }
581
582 /*
583 * fix days and dow - if one is not * and the other
584 * is *, the other is set to 0, and vise-versa
585 */
586
587 FixDayDow(&line);
588
589 *pline = xzalloc(sizeof(CronLine));
590 **pline = line;
591
592 /* copy command */
593 (*pline)->cl_Shell = strdup(ptr);
594
595 #if ENABLE_DEBUG_CROND_OPTION
596 if (DebugOpt) {
597 crondlog("\111 Command %s\n", ptr);
598 }
599 #endif
600
601 pline = &((*pline)->cl_Next);
602 }
603 *pline = NULL;
604
605 file->cf_Next = FileBase;
606 FileBase = file;
607
608 if (maxLines == 0 || maxEntries == 0) {
609 crondlog("\111Maximum number of lines reached for user %s\n", fileName);
610 }
611 }
612 fclose(fi);
613 }
614 }
615 }
616
617 static void CheckUpdates(void)
618 {
619 FILE *fi;
620 char buf[256];
621
622 fi = fopen(CRONUPDATE, "r");
623 if (fi != NULL) {
624 remove(CRONUPDATE);
625 while (fgets(buf, sizeof(buf), fi) != NULL) {
626 SynchronizeFile(strtok(buf, " \t\r\n"));
627 }
628 fclose(fi);
629 }
630 }
631
632 static void SynchronizeDir(void)
633 {
634 /* Attempt to delete the database. */
635
636 for (;;) {
637 CronFile *file;
638
639 for (file = FileBase; file && file->cf_Deleted; file = file->cf_Next);
640 if (file == NULL) {
641 break;
642 }
643 DeleteFile(file->cf_User);
644 }
645
646 /*
647 * Remove cron update file
648 *
649 * Re-chdir, in case directory was renamed & deleted, or otherwise
650 * screwed up.
651 *
652 * scan directory and add associated users
653 */
654
655 remove(CRONUPDATE);
656 if (chdir(CDir) < 0) {
657 crondlog("\311cannot find %s\n", CDir);
658 }
659 {
660 DIR *dir = opendir(".");
661 struct dirent *den;
662
663 if (dir) {
664 while ((den = readdir(dir))) {
665 if (strchr(den->d_name, '.') != NULL) {
666 continue;
667 }
668 if (getpwnam(den->d_name)) {
669 SynchronizeFile(den->d_name);
670 } else {
671 crondlog("\007ignoring %s\n", den->d_name);
672 }
673 }
674 closedir(dir);
675 } else {
676 crondlog("\311cannot open current dir!\n");
677 }
678 }
679 }
680
681
682 /*
683 * DeleteFile() - delete user database
684 *
685 * Note: multiple entries for same user may exist if we were unable to
686 * completely delete a database due to running processes.
687 */
688
689 static void DeleteFile(const char *userName)
690 {
691 CronFile **pfile = &FileBase;
692 CronFile *file;
693
694 while ((file = *pfile) != NULL) {
695 if (strcmp(userName, file->cf_User) == 0) {
696 CronLine **pline = &file->cf_LineBase;
697 CronLine *line;
698
699 file->cf_Running = 0;
700 file->cf_Deleted = 1;
701
702 while ((line = *pline) != NULL) {
703 if (line->cl_Pid > 0) {
704 file->cf_Running = 1;
705 pline = &line->cl_Next;
706 } else {
707 *pline = line->cl_Next;
708 free(line->cl_Shell);
709 free(line);
710 }
711 }
712 if (file->cf_Running == 0) {
713 *pfile = file->cf_Next;
714 free(file->cf_User);
715 free(file);
716 } else {
717 pfile = &file->cf_Next;
718 }
719 } else {
720 pfile = &file->cf_Next;
721 }
722 }
723 }
724
725 /*
726 * TestJobs()
727 *
728 * determine which jobs need to be run. Under normal conditions, the
729 * period is about a minute (one scan). Worst case it will be one
730 * hour (60 scans).
731 */
732
733 static int TestJobs(time_t t1, time_t t2)
734 {
735 int nJobs = 0;
736 time_t t;
737
738 /* Find jobs > t1 and <= t2 */
739
740 for (t = t1 - t1 % 60; t <= t2; t += 60) {
741 if (t > t1) {
742 struct tm *tp = localtime(&t);
743 CronFile *file;
744 CronLine *line;
745
746 for (file = FileBase; file; file = file->cf_Next) {
747 #if ENABLE_DEBUG_CROND_OPTION
748 if (DebugOpt)
749 crondlog("\005FILE %s:\n", file->cf_User);
750 #endif
751 if (file->cf_Deleted)
752 continue;
753 for (line = file->cf_LineBase; line; line = line->cl_Next) {
754 #if ENABLE_DEBUG_CROND_OPTION
755 if (DebugOpt)
756 crondlog("\005 LINE %s\n", line->cl_Shell);
757 #endif
758 if (line->cl_Mins[tp->tm_min] && line->cl_Hrs[tp->tm_hour] &&
759 (line->cl_Days[tp->tm_mday] || line->cl_Dow[tp->tm_wday])
760 && line->cl_Mons[tp->tm_mon]) {
761 #if ENABLE_DEBUG_CROND_OPTION
762 if (DebugOpt) {
763 crondlog("\005 JobToDo: %d %s\n",
764 line->cl_Pid, line->cl_Shell);
765 }
766 #endif
767 if (line->cl_Pid > 0) {
768 crondlog("\010 process already running: %s %s\n",
769 file->cf_User, line->cl_Shell);
770 } else if (line->cl_Pid == 0) {
771 line->cl_Pid = -1;
772 file->cf_Ready = 1;
773 ++nJobs;
774 }
775 }
776 }
777 }
778 }
779 }
780 return nJobs;
781 }
782
783 static void RunJobs(void)
784 {
785 CronFile *file;
786 CronLine *line;
787
788 for (file = FileBase; file; file = file->cf_Next) {
789 if (file->cf_Ready) {
790 file->cf_Ready = 0;
791
792 for (line = file->cf_LineBase; line; line = line->cl_Next) {
793 if (line->cl_Pid < 0) {
794
795 RunJob(file->cf_User, line);
796
797 crondlog("\010USER %s pid %3d cmd %s\n",
798 file->cf_User, line->cl_Pid, line->cl_Shell);
799 if (line->cl_Pid < 0) {
800 file->cf_Ready = 1;
801 }
802 else if (line->cl_Pid > 0) {
803 file->cf_Running = 1;
804 }
805 }
806 }
807 }
808 }
809 }
810
811 /*
812 * CheckJobs() - check for job completion
813 *
814 * Check for job completion, return number of jobs still running after
815 * all done.
816 */
817
818 static int CheckJobs(void)
819 {
820 CronFile *file;
821 CronLine *line;
822 int nStillRunning = 0;
823
824 for (file = FileBase; file; file = file->cf_Next) {
825 if (file->cf_Running) {
826 file->cf_Running = 0;
827
828 for (line = file->cf_LineBase; line; line = line->cl_Next) {
829 if (line->cl_Pid > 0) {
830 int status;
831 int r = wait4(line->cl_Pid, &status, WNOHANG, NULL);
832
833 if (r < 0 || r == line->cl_Pid) {
834 EndJob(file->cf_User, line);
835 if (line->cl_Pid) {
836 file->cf_Running = 1;
837 }
838 } else if (r == 0) {
839 file->cf_Running = 1;
840 }
841 }
842 }
843 }
844 nStillRunning += file->cf_Running;
845 }
846 return nStillRunning;
847 }
848
849
850 #if ENABLE_FEATURE_CROND_CALL_SENDMAIL
851 static void
852 ForkJob(const char *user, CronLine * line, int mailFd,
853 const char *prog, const char *cmd, const char *arg, const char *mailf)
854 {
855 /* Fork as the user in question and run program */
856 pid_t pid = fork();
857
858 line->cl_Pid = pid;
859 if (pid == 0) {
860 /* CHILD */
861
862 /* Change running state to the user in question */
863
864 if (ChangeUser(user) < 0) {
865 exit(0);
866 }
867 #if ENABLE_DEBUG_CROND_OPTION
868 if (DebugOpt) {
869 crondlog("\005Child Running %s\n", prog);
870 }
871 #endif
872
873 if (mailFd >= 0) {
874 dup2(mailFd, mailf != NULL);
875 dup2((mailf ? mailFd : 1), 2);
876 close(mailFd);
877 }
878 execl(prog, prog, cmd, arg, NULL);
879 crondlog("\024cannot exec, user %s cmd %s %s %s\n", user, prog, cmd, arg);
880 if (mailf) {
881 fdprintf(1, "Exec failed: %s -c %s\n", prog, arg);
882 }
883 exit(0);
884 } else if (pid < 0) {
885 /* FORK FAILED */
886 crondlog("\024cannot fork, user %s\n", user);
887 line->cl_Pid = 0;
888 if (mailf) {
889 remove(mailf);
890 }
891 } else if (mailf) {
892 /* PARENT, FORK SUCCESS
893 * rename mail-file based on pid of process
894 */
895 char mailFile2[128];
896
897 snprintf(mailFile2, sizeof(mailFile2), TMPDIR "/cron.%s.%d", user, pid);
898 rename(mailf, mailFile2);
899 }
900 /*
901 * Close the mail file descriptor.. we can't just leave it open in
902 * a structure, closing it later, because we might run out of descriptors
903 */
904
905 if (mailFd >= 0) {
906 close(mailFd);
907 }
908 }
909
910 static void RunJob(const char *user, CronLine * line)
911 {
912 char mailFile[128];
913 int mailFd;
914
915 line->cl_Pid = 0;
916 line->cl_MailFlag = 0;
917
918 /* open mail file - owner root so nobody can screw with it. */
919
920 snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, getpid());
921 mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600);
922
923 if (mailFd >= 0) {
924 line->cl_MailFlag = 1;
925 fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", user,
926 line->cl_Shell);
927 line->cl_MailPos = lseek(mailFd, 0, SEEK_CUR);
928 } else {
929 crondlog("\024cannot create mail file user %s file %s, output to /dev/null\n", user, mailFile);
930 }
931
932 ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile);
933 }
934
935 /*
936 * EndJob - called when job terminates and when mail terminates
937 */
938
939 static void EndJob(const char *user, CronLine * line)
940 {
941 int mailFd;
942 char mailFile[128];
943 struct stat sbuf;
944
945 /* No job */
946
947 if (line->cl_Pid <= 0) {
948 line->cl_Pid = 0;
949 return;
950 }
951
952 /*
953 * End of job and no mail file
954 * End of sendmail job
955 */
956
957 snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, line->cl_Pid);
958 line->cl_Pid = 0;
959
960 if (line->cl_MailFlag != 1) {
961 return;
962 }
963 line->cl_MailFlag = 0;
964
965 /*
966 * End of primary job - check for mail file. If size has increased and
967 * the file is still valid, we sendmail it.
968 */
969
970 mailFd = open(mailFile, O_RDONLY);
971 remove(mailFile);
972 if (mailFd < 0) {
973 return;
974 }
975
976 if (fstat(mailFd, &sbuf) < 0 || sbuf.st_uid != DaemonUid || sbuf.st_nlink != 0 ||
977 sbuf.st_size == line->cl_MailPos || !S_ISREG(sbuf.st_mode)) {
978 close(mailFd);
979 return;
980 }
981 ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL);
982 }
983 #else
984 /* crond without sendmail */
985
986 static void RunJob(const char *user, CronLine * line)
987 {
988 /* Fork as the user in question and run program */
989 pid_t pid = fork();
990
991 if (pid == 0) {
992 /* CHILD */
993
994 /* Change running state to the user in question */
995
996 if (ChangeUser(user) < 0) {
997 exit(0);
998 }
999 #if ENABLE_DEBUG_CROND_OPTION
1000 if (DebugOpt) {
1001 crondlog("\005Child Running %s\n", DEFAULT_SHELL);
1002 }
1003 #endif
1004
1005 execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, NULL);
1006 crondlog("\024cannot exec, user %s cmd %s -c %s\n", user,
1007 DEFAULT_SHELL, line->cl_Shell);
1008 exit(0);
1009 } else if (pid < 0) {
1010 /* FORK FAILED */
1011 crondlog("\024cannot, user %s\n", user);
1012 pid = 0;
1013 }
1014 line->cl_Pid = pid;
1015 }
1016 #endif /* ENABLE_FEATURE_CROND_CALL_SENDMAIL */