source: MondoRescue/branches/stable/mindi-busybox/miscutils/crond.c@ 821

Last change on this file since 821 was 821, checked in by Bruno Cornec, 18 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

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