12311Sjkh/* Copyright 1988,1990,1993,1994 by Paul Vixie 22311Sjkh * All rights reserved 32311Sjkh * 42311Sjkh * Distribute freely, except: don't remove my name from the source or 52311Sjkh * documentation (don't take credit for my work), mark your changes (don't 62311Sjkh * get me blamed for your possible bugs), don't alter or remove this 72311Sjkh * notice. May be sold if buildable source is provided to buyer. No 82311Sjkh * warrantee of any kind, express or implied, is included with this 92311Sjkh * software; use at your own risk, responsibility for damages (if any) to 102311Sjkh * anyone resulting from the use of this software rests entirely with the 112311Sjkh * user. 122311Sjkh * 132311Sjkh * Send bug reports, bug fixes, enhancements, requests, flames, etc., and 142311Sjkh * I'll try to keep a version up to date. I can be reached as follows: 152311Sjkh * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul 162311Sjkh */ 172311Sjkh 182311Sjkh#if !defined(lint) && !defined(LINT) 1929452Scharnierstatic const char rcsid[] = 2050479Speter "$FreeBSD$"; 212311Sjkh#endif 222311Sjkh 232311Sjkh#define MAIN_PROGRAM 242311Sjkh 252311Sjkh 262311Sjkh#include "cron.h" 27199804Sattilio#include <sys/mman.h> 282311Sjkh#include <sys/signal.h> 292311Sjkh#if SYS_TIME_H 302311Sjkh# include <sys/time.h> 312311Sjkh#else 322311Sjkh# include <time.h> 332311Sjkh#endif 342311Sjkh 352311Sjkh 36173412Skevlostatic void usage(void), 37173412Skevlo run_reboot_jobs(cron_db *), 38242101Ssobomax cron_tick(cron_db *, int), 39242101Ssobomax cron_sync(int), 40242101Ssobomax cron_sleep(cron_db *, int), 41173412Skevlo cron_clean(cron_db *), 422311Sjkh#ifdef USE_SIGCHLD 43173412Skevlo sigchld_handler(int), 442311Sjkh#endif 45173412Skevlo sighup_handler(int), 46173412Skevlo parse_args(int c, char *v[]); 472311Sjkh 48242101Ssobomaxstatic int run_at_secres(cron_db *); 49242101Ssobomax 5074010Sbabkinstatic time_t last_time = 0; 5174010Sbabkinstatic int dst_enabled = 0; 52149430Spjdstruct pidfh *pfh; 532311Sjkh 542311Sjkhstatic void 552311Sjkhusage() { 56241124Spluknet#if DEBUGGING 5716859Swosch char **dflags; 58241124Spluknet#endif 5916859Swosch 60129280Syar fprintf(stderr, "usage: cron [-j jitter] [-J rootjitter] " 61180096Smarck "[-m mailto] [-s] [-o] [-x debugflag[,...]]\n"); 62241124Spluknet#if DEBUGGING 6316859Swosch fprintf(stderr, "\ndebugflags: "); 6416859Swosch 6516859Swosch for(dflags = DebugFlagNames; *dflags; dflags++) { 6616859Swosch fprintf(stderr, "%s ", *dflags); 6716859Swosch } 6816859Swosch fprintf(stderr, "\n"); 69241124Spluknet#endif 7016859Swosch 712311Sjkh exit(ERROR_EXIT); 722311Sjkh} 732311Sjkh 74149430Spjdstatic void 75149430Spjdopen_pidfile(void) 76149430Spjd{ 77149430Spjd char pidfile[MAX_FNAME]; 78149430Spjd char buf[MAX_TEMPSTR]; 79149430Spjd int otherpid; 802311Sjkh 81149430Spjd (void) snprintf(pidfile, sizeof(pidfile), PIDFILE, PIDDIR); 82150214Spjd pfh = pidfile_open(pidfile, 0600, &otherpid); 83149430Spjd if (pfh == NULL) { 84149430Spjd if (errno == EEXIST) { 85149430Spjd snprintf(buf, sizeof(buf), 86149430Spjd "cron already running, pid: %d", otherpid); 87149430Spjd } else { 88149430Spjd snprintf(buf, sizeof(buf), 89149430Spjd "can't open or create %s: %s", pidfile, 90149430Spjd strerror(errno)); 91149430Spjd } 92149430Spjd log_it("CRON", getpid(), "DEATH", buf); 93149430Spjd errx(ERROR_EXIT, "%s", buf); 94149430Spjd } 95149430Spjd} 96149430Spjd 972311Sjkhint 982311Sjkhmain(argc, argv) 992311Sjkh int argc; 1002311Sjkh char *argv[]; 1012311Sjkh{ 1022311Sjkh cron_db database; 103242101Ssobomax int runnum; 104242101Ssobomax int secres1, secres2; 105242101Ssobomax struct tm *tm; 10671407Sbabkin 1072311Sjkh ProgramName = argv[0]; 1082311Sjkh 1092311Sjkh#if defined(BSD) 1102311Sjkh setlinebuf(stdout); 1112311Sjkh setlinebuf(stderr); 1122311Sjkh#endif 1132311Sjkh 1142311Sjkh parse_args(argc, argv); 1152311Sjkh 1162311Sjkh#ifdef USE_SIGCHLD 1172311Sjkh (void) signal(SIGCHLD, sigchld_handler); 1182311Sjkh#else 1192311Sjkh (void) signal(SIGCLD, SIG_IGN); 1202311Sjkh#endif 1212311Sjkh (void) signal(SIGHUP, sighup_handler); 1222311Sjkh 123149430Spjd open_pidfile(); 1242311Sjkh set_cron_uid(); 1252311Sjkh set_cron_cwd(); 1262311Sjkh 1272311Sjkh#if defined(POSIX) 1282311Sjkh setenv("PATH", _PATH_DEFPATH, 1); 1292311Sjkh#endif 1302311Sjkh 1312311Sjkh /* if there are no debug flags turned on, fork as a daemon should. 1322311Sjkh */ 1332311Sjkh# if DEBUGGING 1342311Sjkh if (DebugFlags) { 1352311Sjkh# else 1362311Sjkh if (0) { 1372311Sjkh# endif 1382311Sjkh (void) fprintf(stderr, "[%d] cron started\n", getpid()); 1392311Sjkh } else { 14073955Speter if (daemon(1, 0) == -1) { 141149430Spjd pidfile_remove(pfh); 14273955Speter log_it("CRON",getpid(),"DEATH","can't become daemon"); 1432311Sjkh exit(0); 1442311Sjkh } 1452311Sjkh } 1462311Sjkh 147199804Sattilio if (madvise(NULL, 0, MADV_PROTECT) != 0) 148199804Sattilio log_it("CRON", getpid(), "WARNING", "madvise() failed"); 149199804Sattilio 150149430Spjd pidfile_write(pfh); 1512311Sjkh database.head = NULL; 1522311Sjkh database.tail = NULL; 1532311Sjkh database.mtime = (time_t) 0; 1542311Sjkh load_database(&database); 155242101Ssobomax secres1 = secres2 = run_at_secres(&database); 1562311Sjkh run_reboot_jobs(&database); 157242101Ssobomax cron_sync(secres1); 158242101Ssobomax runnum = 0; 1592311Sjkh while (TRUE) { 1602311Sjkh# if DEBUGGING 16116859Swosch /* if (!(DebugFlags & DTEST)) */ 1622311Sjkh# endif /*DEBUGGING*/ 163242101Ssobomax cron_sleep(&database, secres1); 1642311Sjkh 165242101Ssobomax if (secres1 == 0 || runnum % 60 == 0) { 166242101Ssobomax load_database(&database); 167242101Ssobomax secres2 = run_at_secres(&database); 168242101Ssobomax if (secres2 != secres1) { 169242101Ssobomax secres1 = secres2; 170242101Ssobomax if (secres1 != 0) { 171242101Ssobomax runnum = 0; 172242101Ssobomax } else { 173242101Ssobomax /* 174242101Ssobomax * Going from 1 sec to 60 sec res. If we 175242101Ssobomax * are already at minute's boundary, so 176242101Ssobomax * let it run, otherwise schedule for the 177242101Ssobomax * next minute. 178242101Ssobomax */ 179242101Ssobomax tm = localtime(&TargetTime); 180242101Ssobomax if (tm->tm_sec > 0) { 181242101Ssobomax cron_sync(secres2); 182242101Ssobomax continue; 183242101Ssobomax } 184242101Ssobomax } 185242101Ssobomax } 186242101Ssobomax } 1872311Sjkh 1882311Sjkh /* do this iteration 1892311Sjkh */ 190242101Ssobomax cron_tick(&database, secres1); 1912311Sjkh 192242101Ssobomax /* sleep 1 or 60 seconds 1932311Sjkh */ 194242101Ssobomax TargetTime += (secres1 != 0) ? 1 : 60; 195242101Ssobomax runnum += 1; 1962311Sjkh } 1972311Sjkh} 1982311Sjkh 1992311Sjkh 2002311Sjkhstatic void 2012311Sjkhrun_reboot_jobs(db) 2022311Sjkh cron_db *db; 2032311Sjkh{ 2042311Sjkh register user *u; 2052311Sjkh register entry *e; 2062311Sjkh 2072311Sjkh for (u = db->head; u != NULL; u = u->next) { 2082311Sjkh for (e = u->crontab; e != NULL; e = e->next) { 2092311Sjkh if (e->flags & WHEN_REBOOT) { 2102311Sjkh job_add(e, u); 2112311Sjkh } 2122311Sjkh } 2132311Sjkh } 2142311Sjkh (void) job_runqueue(); 2152311Sjkh} 2162311Sjkh 2172311Sjkh 2182311Sjkhstatic void 219242101Ssobomaxcron_tick(cron_db *db, int secres) 2202311Sjkh{ 22174010Sbabkin static struct tm lasttm; 22274010Sbabkin static time_t diff = 0, /* time difference in seconds from the last offset change */ 22374010Sbabkin difflimit = 0; /* end point for the time zone correction */ 22474010Sbabkin struct tm otztm; /* time in the old time zone */ 225242101Ssobomax int otzsecond, otzminute, otzhour, otzdom, otzmonth, otzdow; 2262311Sjkh register struct tm *tm = localtime(&TargetTime); 227242101Ssobomax register int second, minute, hour, dom, month, dow; 2282311Sjkh register user *u; 2292311Sjkh register entry *e; 2302311Sjkh 2312311Sjkh /* make 0-based values out of these so we can use them as indicies 2322311Sjkh */ 233242101Ssobomax second = (secres == 0) ? 0 : tm->tm_sec -FIRST_SECOND; 2342311Sjkh minute = tm->tm_min -FIRST_MINUTE; 2352311Sjkh hour = tm->tm_hour -FIRST_HOUR; 2362311Sjkh dom = tm->tm_mday -FIRST_DOM; 2372311Sjkh month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH; 2382311Sjkh dow = tm->tm_wday -FIRST_DOW; 2392311Sjkh 240242101Ssobomax Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d,%d)\n", 241242101Ssobomax getpid(), second, minute, hour, dom, month, dow)) 2422311Sjkh 24374010Sbabkin if (dst_enabled && last_time != 0 24474010Sbabkin && TargetTime > last_time /* exclude stepping back */ 24574010Sbabkin && tm->tm_gmtoff != lasttm.tm_gmtoff ) { 24674010Sbabkin 24774010Sbabkin diff = tm->tm_gmtoff - lasttm.tm_gmtoff; 24874010Sbabkin 24974010Sbabkin if ( diff > 0 ) { /* ST->DST */ 25074010Sbabkin /* mark jobs for an earlier run */ 25174010Sbabkin difflimit = TargetTime + diff; 25274010Sbabkin for (u = db->head; u != NULL; u = u->next) { 25374010Sbabkin for (e = u->crontab; e != NULL; e = e->next) { 25474010Sbabkin e->flags &= ~NOT_UNTIL; 25574010Sbabkin if ( e->lastrun >= TargetTime ) 25674010Sbabkin e->lastrun = 0; 25774010Sbabkin /* not include the ends of hourly ranges */ 25874010Sbabkin if ( e->lastrun < TargetTime - 3600 ) 25974010Sbabkin e->flags |= RUN_AT; 26074010Sbabkin else 26174010Sbabkin e->flags &= ~RUN_AT; 26274010Sbabkin } 26374010Sbabkin } 26474010Sbabkin } else { /* diff < 0 : DST->ST */ 26574010Sbabkin /* mark jobs for skipping */ 26674010Sbabkin difflimit = TargetTime - diff; 26774010Sbabkin for (u = db->head; u != NULL; u = u->next) { 26874010Sbabkin for (e = u->crontab; e != NULL; e = e->next) { 26974010Sbabkin e->flags |= NOT_UNTIL; 27074010Sbabkin e->flags &= ~RUN_AT; 27174010Sbabkin } 27274010Sbabkin } 27374010Sbabkin } 27474010Sbabkin } 27574010Sbabkin 27674010Sbabkin if (diff != 0) { 27774010Sbabkin /* if the time was reset of the end of special zone is reached */ 27874010Sbabkin if (last_time == 0 || TargetTime >= difflimit) { 27974010Sbabkin /* disable the TZ switch checks */ 28074010Sbabkin diff = 0; 28174010Sbabkin difflimit = 0; 28274010Sbabkin for (u = db->head; u != NULL; u = u->next) { 28374010Sbabkin for (e = u->crontab; e != NULL; e = e->next) { 28474010Sbabkin e->flags &= ~(RUN_AT|NOT_UNTIL); 28574010Sbabkin } 28674010Sbabkin } 28774010Sbabkin } else { 28874010Sbabkin /* get the time in the old time zone */ 28974010Sbabkin time_t difftime = TargetTime + tm->tm_gmtoff - diff; 29074010Sbabkin gmtime_r(&difftime, &otztm); 29174010Sbabkin 29274010Sbabkin /* make 0-based values out of these so we can use them as indicies 29374010Sbabkin */ 294242101Ssobomax otzsecond = (secres == 0) ? 0 : otztm.tm_sec -FIRST_SECOND; 29574010Sbabkin otzminute = otztm.tm_min -FIRST_MINUTE; 29674010Sbabkin otzhour = otztm.tm_hour -FIRST_HOUR; 29774010Sbabkin otzdom = otztm.tm_mday -FIRST_DOM; 29874010Sbabkin otzmonth = otztm.tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH; 29974010Sbabkin otzdow = otztm.tm_wday -FIRST_DOW; 30074010Sbabkin } 30174010Sbabkin } 30274010Sbabkin 3032311Sjkh /* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the 3042311Sjkh * first and fifteenth AND every Sunday; '* * * * Sun' will run *only* 3052311Sjkh * on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this 3062311Sjkh * is why we keep 'e->dow_star' and 'e->dom_star'. yes, it's bizarre. 3072311Sjkh * like many bizarre things, it's the standard. 3082311Sjkh */ 3092311Sjkh for (u = db->head; u != NULL; u = u->next) { 3102311Sjkh for (e = u->crontab; e != NULL; e = e->next) { 3112311Sjkh Debug(DSCH|DEXT, ("user [%s:%d:%d:...] cmd=\"%s\"\n", 3122311Sjkh env_get("LOGNAME", e->envp), 3132311Sjkh e->uid, e->gid, e->cmd)) 31474010Sbabkin 31574010Sbabkin if ( diff != 0 && (e->flags & (RUN_AT|NOT_UNTIL)) ) { 316242101Ssobomax if (bit_test(e->second, otzsecond) 317242101Ssobomax && bit_test(e->minute, otzminute) 31874010Sbabkin && bit_test(e->hour, otzhour) 31974010Sbabkin && bit_test(e->month, otzmonth) 32074010Sbabkin && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) 32174010Sbabkin ? (bit_test(e->dow,otzdow) && bit_test(e->dom,otzdom)) 32274010Sbabkin : (bit_test(e->dow,otzdow) || bit_test(e->dom,otzdom)) 32374010Sbabkin ) 32474010Sbabkin ) { 32574010Sbabkin if ( e->flags & RUN_AT ) { 32674010Sbabkin e->flags &= ~RUN_AT; 32774010Sbabkin e->lastrun = TargetTime; 32874010Sbabkin job_add(e, u); 32974010Sbabkin continue; 33074010Sbabkin } else 33174010Sbabkin e->flags &= ~NOT_UNTIL; 33274010Sbabkin } else if ( e->flags & NOT_UNTIL ) 33374010Sbabkin continue; 33474010Sbabkin } 33574010Sbabkin 336242101Ssobomax if (bit_test(e->second, second) 337242101Ssobomax && bit_test(e->minute, minute) 3382311Sjkh && bit_test(e->hour, hour) 3392311Sjkh && bit_test(e->month, month) 3402311Sjkh && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) 3412311Sjkh ? (bit_test(e->dow,dow) && bit_test(e->dom,dom)) 3422311Sjkh : (bit_test(e->dow,dow) || bit_test(e->dom,dom)) 3432311Sjkh ) 3442311Sjkh ) { 34574010Sbabkin e->flags &= ~RUN_AT; 34674010Sbabkin e->lastrun = TargetTime; 3472311Sjkh job_add(e, u); 3482311Sjkh } 3492311Sjkh } 3502311Sjkh } 35174010Sbabkin 35274010Sbabkin last_time = TargetTime; 35374010Sbabkin lasttm = *tm; 3542311Sjkh} 3552311Sjkh 3562311Sjkh 3572311Sjkh/* the task here is to figure out how long it's going to be until :00 of the 3582311Sjkh * following minute and initialize TargetTime to this value. TargetTime 3592311Sjkh * will subsequently slide 60 seconds at a time, with correction applied 3602311Sjkh * implicitly in cron_sleep(). it would be nice to let cron execute in 3612311Sjkh * the "current minute" before going to sleep, but by restarting cron you 3622311Sjkh * could then get it to execute a given minute's jobs more than once. 3632311Sjkh * instead we have the chance of missing a minute's jobs completely, but 3642311Sjkh * that's something sysadmin's know to expect what with crashing computers.. 3652311Sjkh */ 3662311Sjkhstatic void 367242101Ssobomaxcron_sync(int secres) { 368242101Ssobomax struct tm *tm; 3692311Sjkh 370241672Ssobomax TargetTime = time((time_t*)0); 371242101Ssobomax if (secres != 0) { 372242101Ssobomax TargetTime += 1; 373242101Ssobomax } else { 374242101Ssobomax tm = localtime(&TargetTime); 375242101Ssobomax TargetTime += (60 - tm->tm_sec); 376242101Ssobomax } 3772311Sjkh} 3782311Sjkh 379242101Ssobomaxstatic int 380242101Ssobomaxtimespec_subtract(struct timespec *result, struct timespec *x, 381242101Ssobomax struct timespec *y) 382242101Ssobomax{ 383242101Ssobomax time_t nsec; 3842311Sjkh 385242101Ssobomax /* Perform the carry for the later subtraction by updating y. */ 386242101Ssobomax if (x->tv_nsec < y->tv_nsec) { 387242101Ssobomax nsec = (y->tv_nsec - x->tv_nsec) / 10000000 + 1; 388242101Ssobomax y->tv_nsec -= 1000000000 * nsec; 389242101Ssobomax y->tv_sec += nsec; 390242101Ssobomax } 391242101Ssobomax if (x->tv_nsec - y->tv_nsec > 1000000000) { 392242101Ssobomax nsec = (x->tv_nsec - y->tv_nsec) / 1000000000; 393242101Ssobomax y->tv_nsec += 1000000000 * nsec; 394242101Ssobomax y->tv_sec -= nsec; 395242101Ssobomax } 396242101Ssobomax 397242101Ssobomax /* tv_nsec is certainly positive. */ 398242101Ssobomax result->tv_sec = x->tv_sec - y->tv_sec; 399242101Ssobomax result->tv_nsec = x->tv_nsec - y->tv_nsec; 400242101Ssobomax 401242101Ssobomax /* Return True if result is negative. */ 402242101Ssobomax return (x->tv_sec < y->tv_sec); 403242101Ssobomax} 404242101Ssobomax 4052311Sjkhstatic void 406242101Ssobomaxcron_sleep(cron_db *db, int secres) 40774010Sbabkin{ 408242101Ssobomax int seconds_to_wait; 409242101Ssobomax int rval; 410242101Ssobomax struct timespec ctime, ttime, stime, remtime; 4112311Sjkh 41241723Sdillon /* 41341723Sdillon * Loop until we reach the top of the next minute, sleep when possible. 41441723Sdillon */ 41541723Sdillon 41641723Sdillon for (;;) { 417242101Ssobomax clock_gettime(CLOCK_REALTIME, &ctime); 418242101Ssobomax ttime.tv_sec = TargetTime; 419242101Ssobomax ttime.tv_nsec = 0; 420242101Ssobomax timespec_subtract(&stime, &ttime, &ctime); 42141723Sdillon 42241723Sdillon /* 42341723Sdillon * If the seconds_to_wait value is insane, jump the cron 42441723Sdillon */ 42541723Sdillon 426242101Ssobomax if (stime.tv_sec < -600 || stime.tv_sec > 600) { 42774010Sbabkin cron_clean(db); 428242101Ssobomax cron_sync(secres); 42941723Sdillon continue; 43041723Sdillon } 43141723Sdillon 432242101Ssobomax seconds_to_wait = (stime.tv_nsec > 0) ? stime.tv_sec + 1 : 433242101Ssobomax stime.tv_sec; 434242101Ssobomax 4352311Sjkh Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n", 43637450Sbde getpid(), (long)TargetTime, seconds_to_wait)) 4372311Sjkh 43841723Sdillon /* 43941723Sdillon * If we've run out of wait time or there are no jobs left 44041723Sdillon * to run, break 4412311Sjkh */ 4422311Sjkh 443242101Ssobomax if (stime.tv_sec < 0) 44441723Sdillon break; 44541723Sdillon if (job_runqueue() == 0) { 44641723Sdillon Debug(DSCH, ("[%d] sleeping for %d seconds\n", 44741723Sdillon getpid(), seconds_to_wait)) 44841723Sdillon 449242101Ssobomax for (;;) { 450242101Ssobomax rval = nanosleep(&stime, &remtime); 451242101Ssobomax if (rval == 0 || errno != EINTR) 452242101Ssobomax break; 453242101Ssobomax stime.tv_sec = remtime.tv_sec; 454242101Ssobomax stime.tv_nsec = remtime.tv_nsec; 455242101Ssobomax } 45641723Sdillon } 4572311Sjkh } 4582311Sjkh} 4592311Sjkh 4602311Sjkh 46174010Sbabkin/* if the time was changed abruptly, clear the flags related 46274010Sbabkin * to the daylight time switch handling to avoid strange effects 46374010Sbabkin */ 46474010Sbabkin 46574010Sbabkinstatic void 46674010Sbabkincron_clean(db) 46774010Sbabkin cron_db *db; 46874010Sbabkin{ 46974010Sbabkin user *u; 47074010Sbabkin entry *e; 47174010Sbabkin 47274010Sbabkin last_time = 0; 47374010Sbabkin 47474010Sbabkin for (u = db->head; u != NULL; u = u->next) { 47574010Sbabkin for (e = u->crontab; e != NULL; e = e->next) { 47674010Sbabkin e->flags &= ~(RUN_AT|NOT_UNTIL); 47774010Sbabkin } 47874010Sbabkin } 47974010Sbabkin} 48074010Sbabkin 4812311Sjkh#ifdef USE_SIGCHLD 4822311Sjkhstatic void 483160521Sstefanfsigchld_handler(int x) 484160521Sstefanf{ 4852311Sjkh WAIT_T waiter; 4862311Sjkh PID_T pid; 4872311Sjkh 4882311Sjkh for (;;) { 4892311Sjkh#ifdef POSIX 4902311Sjkh pid = waitpid(-1, &waiter, WNOHANG); 4912311Sjkh#else 4922311Sjkh pid = wait3(&waiter, WNOHANG, (struct rusage *)0); 4932311Sjkh#endif 4942311Sjkh switch (pid) { 4952311Sjkh case -1: 4962311Sjkh Debug(DPROC, 4972311Sjkh ("[%d] sigchld...no children\n", getpid())) 4982311Sjkh return; 4992311Sjkh case 0: 5002311Sjkh Debug(DPROC, 5012311Sjkh ("[%d] sigchld...no dead kids\n", getpid())) 5022311Sjkh return; 5032311Sjkh default: 5042311Sjkh Debug(DPROC, 5052311Sjkh ("[%d] sigchld...pid #%d died, stat=%d\n", 5062311Sjkh getpid(), pid, WEXITSTATUS(waiter))) 5072311Sjkh } 5082311Sjkh } 5092311Sjkh} 5102311Sjkh#endif /*USE_SIGCHLD*/ 5112311Sjkh 5122311Sjkh 5132311Sjkhstatic void 514160521Sstefanfsighup_handler(int x) 515160521Sstefanf{ 5162311Sjkh log_close(); 5172311Sjkh} 5182311Sjkh 5192311Sjkh 5202311Sjkhstatic void 5212311Sjkhparse_args(argc, argv) 5222311Sjkh int argc; 5232311Sjkh char *argv[]; 5242311Sjkh{ 5252311Sjkh int argch; 526129280Syar char *endp; 5272311Sjkh 528180096Smarck while ((argch = getopt(argc, argv, "j:J:m:osx:")) != -1) { 5292311Sjkh switch (argch) { 530129280Syar case 'j': 531129280Syar Jitter = strtoul(optarg, &endp, 10); 532129280Syar if (*optarg == '\0' || *endp != '\0' || Jitter > 60) 533129280Syar errx(ERROR_EXIT, 534129280Syar "bad value for jitter: %s", optarg); 535129280Syar break; 536129280Syar case 'J': 537129280Syar RootJitter = strtoul(optarg, &endp, 10); 538129280Syar if (*optarg == '\0' || *endp != '\0' || RootJitter > 60) 539129280Syar errx(ERROR_EXIT, 540129280Syar "bad value for root jitter: %s", optarg); 541129280Syar break; 542180096Smarck case 'm': 543180096Smarck defmailto = optarg; 544180096Smarck break; 54574010Sbabkin case 'o': 54674010Sbabkin dst_enabled = 0; 54774010Sbabkin break; 54874010Sbabkin case 's': 54974010Sbabkin dst_enabled = 1; 55074010Sbabkin break; 5512311Sjkh case 'x': 5522311Sjkh if (!set_debug_flags(optarg)) 5532311Sjkh usage(); 5542311Sjkh break; 55516859Swosch default: 55616859Swosch usage(); 5572311Sjkh } 5582311Sjkh } 5592311Sjkh} 56074010Sbabkin 561242101Ssobomaxstatic int 562242101Ssobomaxrun_at_secres(cron_db *db) 563242101Ssobomax{ 564242101Ssobomax user *u; 565242101Ssobomax entry *e; 566242101Ssobomax 567242101Ssobomax for (u = db->head; u != NULL; u = u->next) { 568242101Ssobomax for (e = u->crontab; e != NULL; e = e->next) { 569242101Ssobomax if ((e->flags & SEC_RES) != 0) 570242101Ssobomax return 1; 571242101Ssobomax } 572242101Ssobomax } 573242101Ssobomax return 0; 574242101Ssobomax} 575