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

Last change on this file since 821 was 821, checked in by Bruno Cornec, 14 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
Line 
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.