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

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 12 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod? silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian? keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René? Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

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