1*9c847fa8Smillert /* $OpenBSD: cron.c,v 1.82 2022/07/08 20:47:24 millert Exp $ */
2e134e629Smillert
3df930be7Sderaadt /* Copyright 1988,1990,1993,1994 by Paul Vixie
4a5198fa1Smillert * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
5f454ebdeSmillert * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
6df930be7Sderaadt *
7f454ebdeSmillert * Permission to use, copy, modify, and distribute this software for any
8f454ebdeSmillert * purpose with or without fee is hereby granted, provided that the above
9f454ebdeSmillert * copyright notice and this permission notice appear in all copies.
10df930be7Sderaadt *
11a5198fa1Smillert * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
12a5198fa1Smillert * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13a5198fa1Smillert * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
14a5198fa1Smillert * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15a5198fa1Smillert * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16a5198fa1Smillert * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17a5198fa1Smillert * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18df930be7Sderaadt */
19df930be7Sderaadt
20fa575ea2Smillert #include <sys/types.h>
21fa575ea2Smillert #include <sys/socket.h>
22fa575ea2Smillert #include <sys/stat.h>
2395f2b4e2Smillert #include <sys/time.h>
24fa575ea2Smillert #include <sys/un.h>
25fa575ea2Smillert #include <sys/wait.h>
26fa575ea2Smillert
27fa575ea2Smillert #include <bitstring.h>
28b8c5c73dSmillert #include <err.h>
29fa575ea2Smillert #include <errno.h>
3088959323Smillert #include <grp.h>
31fa575ea2Smillert #include <poll.h>
32fa575ea2Smillert #include <signal.h>
33fa575ea2Smillert #include <stdio.h>
34fa575ea2Smillert #include <stdlib.h>
35fa575ea2Smillert #include <string.h>
36a38a3395Smillert #include <syslog.h>
37fa575ea2Smillert #include <time.h>
38fa575ea2Smillert #include <unistd.h>
39fa575ea2Smillert
40fa575ea2Smillert #include "config.h"
41fa575ea2Smillert #include "pathnames.h"
42fa575ea2Smillert #include "macros.h"
43fa575ea2Smillert #include "structs.h"
44fa575ea2Smillert #include "funcs.h"
45fa575ea2Smillert #include "globals.h"
46df930be7Sderaadt
47dc59ecf4Smillert enum timejump { negative, small, medium, large };
48dc59ecf4Smillert
49f454ebdeSmillert static void usage(void),
50f454ebdeSmillert run_reboot_jobs(cron_db *),
511e5c894eSderaadt find_jobs(time_t, cron_db *, int, int),
52c72b5b24Smillert set_time(int),
53f3ce7c4eSmillert cron_sleep(time_t, sigset_t *),
54f454ebdeSmillert sigchld_handler(int),
55f454ebdeSmillert sigchld_reaper(void),
56f454ebdeSmillert parse_args(int c, char *v[]);
57df930be7Sderaadt
5858a24ea1Smillert static int open_socket(void);
5958a24ea1Smillert
60a38a3395Smillert static volatile sig_atomic_t got_sigchld;
611e5c894eSderaadt static time_t timeRunning, virtualTime, clockTime;
6240616ceaSmillert static long GMToff;
638a6f8ff3Smillert static cron_db *database;
648a6f8ff3Smillert static at_db *at_database;
652b139a93Smillert static double batch_maxload = BATCH_MAXLOAD;
669d64f211Smillert static int NoFork;
679d64f211Smillert static time_t StartTime;
68a9e807deSmillert gid_t cron_gid;
690a66a1feSjca int cronSock;
70df930be7Sderaadt
71df930be7Sderaadt static void
usage(void)72785ecd14Stedu usage(void)
73785ecd14Stedu {
743d61ea5dSderaadt
754a3a0dd4Smillert fprintf(stderr, "usage: %s [-n] [-l load_avg]\n", __progname);
7610ee6820Smillert exit(EXIT_FAILURE);
77df930be7Sderaadt }
78df930be7Sderaadt
79df930be7Sderaadt int
main(int argc,char * argv[])80785ecd14Stedu main(int argc, char *argv[])
81785ecd14Stedu {
82f454ebdeSmillert struct sigaction sact;
83f3ce7c4eSmillert sigset_t blocked, omask;
84a9e807deSmillert struct group *grp;
85df930be7Sderaadt
86b9a887b6Smillert setvbuf(stdout, NULL, _IOLBF, 0);
87b9a887b6Smillert setvbuf(stderr, NULL, _IOLBF, 0);
88df930be7Sderaadt
89df930be7Sderaadt parse_args(argc, argv);
90df930be7Sderaadt
91f454ebdeSmillert bzero((char *)&sact, sizeof sact);
92f454ebdeSmillert sigemptyset(&sact.sa_mask);
9349ff01e1Smillert sact.sa_flags = SA_RESTART;
94f454ebdeSmillert sact.sa_handler = sigchld_handler;
95f454ebdeSmillert (void) sigaction(SIGCHLD, &sact, NULL);
96e134e629Smillert sact.sa_handler = SIG_IGN;
97a38a3395Smillert (void) sigaction(SIGHUP, &sact, NULL);
98e134e629Smillert (void) sigaction(SIGPIPE, &sact, NULL);
99df930be7Sderaadt
100a38a3395Smillert openlog(__progname, LOG_PID, LOG_CRON);
101a38a3395Smillert
102d31b1261Smillert if (pledge("stdio rpath wpath cpath fattr getpw unix id dns proc exec",
103e8d6cae4Sderaadt NULL) == -1) {
104b8c5c73dSmillert warn("pledge");
105a38a3395Smillert syslog(LOG_ERR, "(CRON) PLEDGE (%m)");
106e8d6cae4Sderaadt exit(EXIT_FAILURE);
107e8d6cae4Sderaadt }
1082da949c6Sderaadt
109a9e807deSmillert if ((grp = getgrnam(CRON_GROUP)) == NULL) {
110a9e807deSmillert warnx("can't find cron group %s", CRON_GROUP);
111a9e807deSmillert syslog(LOG_ERR, "(CRON) DEATH (can't find cron group)");
112a9e807deSmillert exit(EXIT_FAILURE);
113a9e807deSmillert }
114a9e807deSmillert cron_gid = grp->gr_gid;
115a9e807deSmillert
11652c15c8aSmillert cronSock = open_socket();
11752c15c8aSmillert
118e134e629Smillert if (putenv("PATH="_PATH_DEFPATH) < 0) {
119b8c5c73dSmillert warn("putenv");
120a38a3395Smillert syslog(LOG_ERR, "(CRON) DEATH (%m)");
12110ee6820Smillert exit(EXIT_FAILURE);
1222c20747dSderaadt }
123df930be7Sderaadt
124585d9be6Stedu if (NoFork == 0) {
12588959323Smillert if (daemon(0, 0) == -1) {
126a38a3395Smillert syslog(LOG_ERR, "(CRON) DEATH (%m)");
12710ee6820Smillert exit(EXIT_FAILURE);
12814852038Smillert }
129a38a3395Smillert syslog(LOG_INFO, "(CRON) STARTUP (%s)", CRON_VERSION);
130df930be7Sderaadt }
131df930be7Sderaadt
132df930be7Sderaadt load_database(&database);
1332b139a93Smillert scan_atjobs(&at_database, NULL);
134e134e629Smillert set_time(TRUE);
1358a6f8ff3Smillert run_reboot_jobs(database);
136f326c411Sderaadt timeRunning = virtualTime = clockTime;
137f326c411Sderaadt
138f326c411Sderaadt /*
139f3ce7c4eSmillert * We block SIGHUP and SIGCHLD while running jobs and receive them
140f3ce7c4eSmillert * only while sleeping in ppoll(). This ensures no signal is lost.
141f3ce7c4eSmillert */
142f3ce7c4eSmillert sigemptyset(&blocked);
143f3ce7c4eSmillert sigaddset(&blocked, SIGCHLD);
144f3ce7c4eSmillert sigaddset(&blocked, SIGHUP);
145f3ce7c4eSmillert sigprocmask(SIG_BLOCK, &blocked, &omask);
146f3ce7c4eSmillert
147f3ce7c4eSmillert /*
148f454ebdeSmillert * Too many clocks, not enough time (Al. Einstein)
14994db05edSmillert * These clocks are in minutes since the epoch, adjusted for timezone.
150f326c411Sderaadt * virtualTime: is the time it *would* be if we woke up
151f326c411Sderaadt * promptly and nobody ever changed the clock. It is
152f326c411Sderaadt * monotonically increasing... unless a timejump happens.
153f326c411Sderaadt * At the top of the loop, all jobs for 'virtualTime' have run.
154f326c411Sderaadt * timeRunning: is the time we last awakened.
155f326c411Sderaadt * clockTime: is the time when set_time was last called.
156f326c411Sderaadt */
157df930be7Sderaadt while (TRUE) {
15894db05edSmillert int timeDiff;
159dc59ecf4Smillert enum timejump wakeupKind;
160df930be7Sderaadt
161f326c411Sderaadt /* ... wait for the time (in minutes) to change ... */
162f326c411Sderaadt do {
163f3ce7c4eSmillert cron_sleep(timeRunning + 1, &omask);
164e134e629Smillert set_time(FALSE);
165f326c411Sderaadt } while (clockTime == timeRunning);
166f326c411Sderaadt timeRunning = clockTime;
167df930be7Sderaadt
168f326c411Sderaadt /*
169f454ebdeSmillert * Calculate how the current time differs from our virtual
170f454ebdeSmillert * clock. Classify the change into one of 4 cases.
171df930be7Sderaadt */
172f326c411Sderaadt timeDiff = timeRunning - virtualTime;
173f326c411Sderaadt
174f326c411Sderaadt /* shortcut for the most common case */
175f326c411Sderaadt if (timeDiff == 1) {
176f326c411Sderaadt virtualTime = timeRunning;
1778a6f8ff3Smillert find_jobs(virtualTime, database, TRUE, TRUE);
178f326c411Sderaadt } else {
179dc59ecf4Smillert if (timeDiff > (3*MINUTE_COUNT) ||
180dc59ecf4Smillert timeDiff < -(3*MINUTE_COUNT))
181dc59ecf4Smillert wakeupKind = large;
182dc59ecf4Smillert else if (timeDiff > 5)
183dc59ecf4Smillert wakeupKind = medium;
184dc59ecf4Smillert else if (timeDiff > 0)
185dc59ecf4Smillert wakeupKind = small;
186dc59ecf4Smillert else
187dc59ecf4Smillert wakeupKind = negative;
188f326c411Sderaadt
189f326c411Sderaadt switch (wakeupKind) {
190dc59ecf4Smillert case small:
191f326c411Sderaadt /*
192f326c411Sderaadt * case 1: timeDiff is a small positive number
193f454ebdeSmillert * (wokeup late) run jobs for each virtual
194f454ebdeSmillert * minute until caught up.
195f326c411Sderaadt */
196f326c411Sderaadt do {
197f326c411Sderaadt if (job_runqueue())
198f326c411Sderaadt sleep(10);
199f326c411Sderaadt virtualTime++;
2008a6f8ff3Smillert find_jobs(virtualTime, database,
201f454ebdeSmillert TRUE, TRUE);
202f326c411Sderaadt } while (virtualTime < timeRunning);
203f326c411Sderaadt break;
204f326c411Sderaadt
205dc59ecf4Smillert case medium:
206f326c411Sderaadt /*
207f454ebdeSmillert * case 2: timeDiff is a medium-sized positive
208f454ebdeSmillert * number, for example because we went to DST
209f454ebdeSmillert * run wildcard jobs once, then run any
210f454ebdeSmillert * fixed-time jobs that would otherwise be
211f454ebdeSmillert * skipped if we use up our minute (possible,
212f454ebdeSmillert * if there are a lot of jobs to run) go
213f454ebdeSmillert * around the loop again so that wildcard jobs
214f454ebdeSmillert * have a chance to run, and we do our
215f454ebdeSmillert * housekeeping.
216f326c411Sderaadt */
217f326c411Sderaadt /* run wildcard jobs for current minute */
2188a6f8ff3Smillert find_jobs(timeRunning, database, TRUE, FALSE);
219f326c411Sderaadt
220f326c411Sderaadt /* run fixed-time jobs for each minute missed */
221f326c411Sderaadt do {
222f326c411Sderaadt if (job_runqueue())
223f326c411Sderaadt sleep(10);
224f326c411Sderaadt virtualTime++;
2258a6f8ff3Smillert find_jobs(virtualTime, database,
226f454ebdeSmillert FALSE, TRUE);
227e134e629Smillert set_time(FALSE);
228f326c411Sderaadt } while (virtualTime< timeRunning &&
229f326c411Sderaadt clockTime == timeRunning);
230f326c411Sderaadt break;
231f326c411Sderaadt
232dc59ecf4Smillert case negative:
233f326c411Sderaadt /*
234f326c411Sderaadt * case 3: timeDiff is a small or medium-sized
23594db05edSmillert * negative num, eg. because of DST ending.
23694db05edSmillert * Just run the wildcard jobs. The fixed-time
23794db05edSmillert * jobs probably have already run, and should
23894db05edSmillert * not be repeated. Virtual time does not
23994db05edSmillert * change until we are caught up.
240f326c411Sderaadt */
2418a6f8ff3Smillert find_jobs(timeRunning, database, TRUE, FALSE);
242f326c411Sderaadt break;
243f326c411Sderaadt default:
244f326c411Sderaadt /*
245f326c411Sderaadt * other: time has changed a *lot*,
246f326c411Sderaadt * jump virtual time, and run everything
247f326c411Sderaadt */
248f326c411Sderaadt virtualTime = timeRunning;
2498a6f8ff3Smillert find_jobs(timeRunning, database, TRUE, TRUE);
250f326c411Sderaadt }
251f326c411Sderaadt }
252f454ebdeSmillert
253f454ebdeSmillert /* Jobs to be run (if any) are loaded; clear the queue. */
254f326c411Sderaadt job_runqueue();
2552b139a93Smillert
2562b139a93Smillert /* Run any jobs in the at queue. */
2578a6f8ff3Smillert atrun(at_database, batch_maxload,
2582b139a93Smillert timeRunning * SECONDS_PER_MINUTE - GMToff);
2592b139a93Smillert
260f3ce7c4eSmillert /* Reload jobs as needed. */
2612b139a93Smillert load_database(&database);
2622b139a93Smillert scan_atjobs(&at_database, NULL);
263df930be7Sderaadt }
264df930be7Sderaadt }
265df930be7Sderaadt
266df930be7Sderaadt static void
run_reboot_jobs(cron_db * db)267785ecd14Stedu run_reboot_jobs(cron_db *db)
268785ecd14Stedu {
269f454ebdeSmillert user *u;
270f454ebdeSmillert entry *e;
271df930be7Sderaadt
2728a6f8ff3Smillert TAILQ_FOREACH(u, &db->users, entries) {
2738a6f8ff3Smillert SLIST_FOREACH(e, &u->crontab, entries) {
274f454ebdeSmillert if (e->flags & WHEN_REBOOT)
275df930be7Sderaadt job_add(e, u);
276df930be7Sderaadt }
277df930be7Sderaadt }
278df930be7Sderaadt (void) job_runqueue();
279df930be7Sderaadt }
280df930be7Sderaadt
281df930be7Sderaadt static void
find_jobs(time_t vtime,cron_db * db,int doWild,int doNonWild)282785ecd14Stedu find_jobs(time_t vtime, cron_db *db, int doWild, int doNonWild)
283785ecd14Stedu {
284f326c411Sderaadt time_t virtualSecond = vtime * SECONDS_PER_MINUTE;
28594db05edSmillert struct tm *tm = gmtime(&virtualSecond);
286f454ebdeSmillert int minute, hour, dom, month, dow;
287f454ebdeSmillert user *u;
288f454ebdeSmillert entry *e;
289df930be7Sderaadt
290cb354146Smiod /* make 0-based values out of these so we can use them as indices
291df930be7Sderaadt */
292df930be7Sderaadt minute = tm->tm_min -FIRST_MINUTE;
293df930be7Sderaadt hour = tm->tm_hour -FIRST_HOUR;
294df930be7Sderaadt dom = tm->tm_mday -FIRST_DOM;
295df930be7Sderaadt month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
296df930be7Sderaadt dow = tm->tm_wday -FIRST_DOW;
297df930be7Sderaadt
298df930be7Sderaadt /* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the
299df930be7Sderaadt * first and fifteenth AND every Sunday; '* * * * Sun' will run *only*
300df930be7Sderaadt * on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this
301df930be7Sderaadt * is why we keep 'e->dow_star' and 'e->dom_star'. yes, it's bizarre.
302df930be7Sderaadt * like many bizarre things, it's the standard.
303df930be7Sderaadt */
3048a6f8ff3Smillert TAILQ_FOREACH(u, &db->users, entries) {
3058a6f8ff3Smillert SLIST_FOREACH(e, &u->crontab, entries) {
306f326c411Sderaadt if (bit_test(e->minute, minute) &&
307f326c411Sderaadt bit_test(e->hour, hour) &&
308f326c411Sderaadt bit_test(e->month, month) &&
309f326c411Sderaadt ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
310df930be7Sderaadt ? (bit_test(e->dow,dow) && bit_test(e->dom,dom))
311f454ebdeSmillert : (bit_test(e->dow,dow) || bit_test(e->dom,dom))
312f454ebdeSmillert )
313bb0cdb18Smillert ) {
314bb0cdb18Smillert if ((doNonWild &&
315bb0cdb18Smillert !(e->flags & (MIN_STAR|HR_STAR))) ||
316bb0cdb18Smillert (doWild && (e->flags & (MIN_STAR|HR_STAR))))
317df930be7Sderaadt job_add(e, u);
318df930be7Sderaadt }
319df930be7Sderaadt }
320df930be7Sderaadt }
321bb0cdb18Smillert }
322df930be7Sderaadt
323f326c411Sderaadt /*
324f454ebdeSmillert * Set StartTime and clockTime to the current time.
325f454ebdeSmillert * These are used for computing what time it really is right now.
326f454ebdeSmillert * Note that clockTime is a unix wallclock time converted to minutes.
327df930be7Sderaadt */
328df930be7Sderaadt static void
set_time(int initialize)329785ecd14Stedu set_time(int initialize)
330785ecd14Stedu {
331e134e629Smillert struct tm tm;
33294db05edSmillert static int isdst;
333f454ebdeSmillert
33494db05edSmillert StartTime = time(NULL);
33594db05edSmillert
33694db05edSmillert /* We adjust the time to GMT so we can catch DST changes. */
337e134e629Smillert tm = *localtime(&StartTime);
338e134e629Smillert if (initialize || tm.tm_isdst != isdst) {
339e134e629Smillert isdst = tm.tm_isdst;
340e134e629Smillert GMToff = get_gmtoff(&StartTime, &tm);
34194db05edSmillert }
34240616ceaSmillert clockTime = (StartTime + GMToff) / (time_t)SECONDS_PER_MINUTE;
343f326c411Sderaadt }
344f326c411Sderaadt
345f326c411Sderaadt /*
346f454ebdeSmillert * Try to just hit the next minute.
347f326c411Sderaadt */
348f326c411Sderaadt static void
cron_sleep(time_t target,sigset_t * mask)349f3ce7c4eSmillert cron_sleep(time_t target, sigset_t *mask)
350785ecd14Stedu {
35115237702Smillert int fd, nfds;
3522b139a93Smillert unsigned char poke;
353f3ce7c4eSmillert struct timespec t1, t2, timeout;
354e134e629Smillert struct sockaddr_un s_un;
35515237702Smillert socklen_t sunlen;
35612c65670Sderaadt static struct pollfd pfd[1];
357b636719cSmillert
358f3ce7c4eSmillert clock_gettime(CLOCK_REALTIME, &t1);
35915237702Smillert t1.tv_sec += GMToff;
360f3ce7c4eSmillert timeout.tv_sec = (target * SECONDS_PER_MINUTE - t1.tv_sec) + 1;
361*9c847fa8Smillert if (timeout.tv_sec < 0)
362*9c847fa8Smillert timeout.tv_sec = 0;
363f3ce7c4eSmillert timeout.tv_nsec = 0;
364df930be7Sderaadt
36512c65670Sderaadt pfd[0].fd = cronSock;
36612c65670Sderaadt pfd[0].events = POLLIN;
367b636719cSmillert
368f3ce7c4eSmillert while (timespecisset(&timeout) && timeout.tv_sec < 65) {
3691a16ac32Smillert poke = RELOAD_CRON | RELOAD_AT;
37012c65670Sderaadt
3712b139a93Smillert /* Sleep until we time out, get a poke, or get a signal. */
372f3ce7c4eSmillert nfds = ppoll(pfd, 1, &timeout, mask);
373be425821Smillert switch (nfds) {
374be425821Smillert case -1:
375139e0acfSjca if (errno != EINTR && errno != EAGAIN) {
376139e0acfSjca syslog(LOG_ERR, "(CRON) DEATH (ppoll failure: %m)");
377139e0acfSjca exit(EXIT_FAILURE);
378139e0acfSjca }
379be425821Smillert if (errno == EINTR) {
380be425821Smillert if (got_sigchld) {
381be425821Smillert got_sigchld = 0;
382be425821Smillert sigchld_reaper();
383be425821Smillert }
384be425821Smillert }
385be425821Smillert break;
386be425821Smillert case 0:
387be425821Smillert /* done sleeping */
388be425821Smillert return;
389be425821Smillert default:
39070805688Smillert sunlen = sizeof(s_un);
3915ce06057Smillert fd = accept4(cronSock, (struct sockaddr *)&s_un,
3925ce06057Smillert &sunlen, SOCK_NONBLOCK);
3935ce06057Smillert if (fd >= 0) {
3942b139a93Smillert (void) read(fd, &poke, 1);
39515237702Smillert close(fd);
39669386091Smillert if (poke & RELOAD_CRON) {
39795f2b4e2Smillert timespecclear(&database->mtime);
3982b139a93Smillert load_database(&database);
39969386091Smillert }
4002b139a93Smillert if (poke & RELOAD_AT) {
4012b139a93Smillert /*
4022b139a93Smillert * We run any pending at jobs right
4032b139a93Smillert * away so that "at now" really runs
4042b139a93Smillert * jobs immediately.
4052b139a93Smillert */
406f3ce7c4eSmillert clock_gettime(CLOCK_REALTIME, &t2);
40795f2b4e2Smillert timespecclear(&at_database->mtime);
4082b139a93Smillert if (scan_atjobs(&at_database, &t2))
4098a6f8ff3Smillert atrun(at_database,
4102b139a93Smillert batch_maxload, t2.tv_sec);
4112b139a93Smillert }
4122b139a93Smillert }
41315237702Smillert }
41415237702Smillert
4152b139a93Smillert /* Adjust tv and continue where we left off. */
416f3ce7c4eSmillert clock_gettime(CLOCK_REALTIME, &t2);
41715237702Smillert t2.tv_sec += GMToff;
418f3ce7c4eSmillert timespecsub(&t2, &t1, &t1);
419f3ce7c4eSmillert timespecsub(&timeout, &t1, &timeout);
42015237702Smillert memcpy(&t1, &t2, sizeof(t1));
421f3ce7c4eSmillert if (timeout.tv_sec < 0)
422*9c847fa8Smillert timespecclear(&timeout);
42315237702Smillert }
424df930be7Sderaadt }
425df930be7Sderaadt
42658a24ea1Smillert /* int open_socket(void)
42758a24ea1Smillert * opens a UNIX domain socket that crontab uses to poke cron.
42858a24ea1Smillert * If the socket is already in use, return an error.
42958a24ea1Smillert */
43058a24ea1Smillert static int
open_socket(void)43158a24ea1Smillert open_socket(void)
43258a24ea1Smillert {
43358a24ea1Smillert int sock, rc;
43458a24ea1Smillert mode_t omask;
43558a24ea1Smillert struct sockaddr_un s_un;
43658a24ea1Smillert
43758a24ea1Smillert sock = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
43858a24ea1Smillert if (sock == -1) {
439b8c5c73dSmillert warn("socket");
440a38a3395Smillert syslog(LOG_ERR, "(CRON) DEATH (can't create socket)");
44158a24ea1Smillert exit(EXIT_FAILURE);
44258a24ea1Smillert }
44358a24ea1Smillert bzero(&s_un, sizeof(s_un));
44488959323Smillert if (strlcpy(s_un.sun_path, _PATH_CRON_SOCK, sizeof(s_un.sun_path))
4456b36bf07Smillert >= sizeof(s_un.sun_path)) {
446b8c5c73dSmillert warnc(ENAMETOOLONG, _PATH_CRON_SOCK);
447a38a3395Smillert syslog(LOG_ERR, "(CRON) DEATH (socket path too long)");
44858a24ea1Smillert exit(EXIT_FAILURE);
44958a24ea1Smillert }
45058a24ea1Smillert s_un.sun_family = AF_UNIX;
45158a24ea1Smillert
45258a24ea1Smillert if (connect(sock, (struct sockaddr *)&s_un, sizeof(s_un)) == 0) {
453b8c5c73dSmillert warnx("already running");
454a38a3395Smillert syslog(LOG_ERR, "(CRON) DEATH (already running)");
45558a24ea1Smillert exit(EXIT_FAILURE);
45658a24ea1Smillert }
45758a24ea1Smillert if (errno != ENOENT)
45858a24ea1Smillert unlink(s_un.sun_path);
45958a24ea1Smillert
46058a24ea1Smillert omask = umask(007);
46158a24ea1Smillert rc = bind(sock, (struct sockaddr *)&s_un, sizeof(s_un));
46258a24ea1Smillert umask(omask);
46358a24ea1Smillert if (rc != 0) {
464b8c5c73dSmillert warn("bind");
465a38a3395Smillert syslog(LOG_ERR, "(CRON) DEATH (can't bind socket)");
46658a24ea1Smillert exit(EXIT_FAILURE);
46758a24ea1Smillert }
46858a24ea1Smillert if (listen(sock, SOMAXCONN)) {
469b8c5c73dSmillert warn("listen");
470a38a3395Smillert syslog(LOG_ERR, "(CRON) DEATH (can't listen on socket)");
47158a24ea1Smillert exit(EXIT_FAILURE);
47258a24ea1Smillert }
473a9e807deSmillert
47488959323Smillert /* pledge won't let us change files to a foreign group. */
475a9e807deSmillert if (setegid(cron_gid) == 0) {
476a9e807deSmillert chown(s_un.sun_path, -1, cron_gid);
47788959323Smillert (void)setegid(getgid());
47888959323Smillert }
479a100c690Smillert chmod(s_un.sun_path, 0660);
48058a24ea1Smillert
48158a24ea1Smillert return(sock);
48258a24ea1Smillert }
48358a24ea1Smillert
484df930be7Sderaadt static void
sigchld_handler(int x)485785ecd14Stedu sigchld_handler(int x)
486785ecd14Stedu {
487f454ebdeSmillert got_sigchld = 1;
488f454ebdeSmillert }
489f454ebdeSmillert
490f454ebdeSmillert static void
sigchld_reaper(void)491785ecd14Stedu sigchld_reaper(void)
492785ecd14Stedu {
493afc409ecSmillert int waiter;
494afc409ecSmillert pid_t pid;
495df930be7Sderaadt
496f454ebdeSmillert do {
497df930be7Sderaadt pid = waitpid(-1, &waiter, WNOHANG);
498df930be7Sderaadt switch (pid) {
499df930be7Sderaadt case -1:
5007b12bc57Smillert if (errno == EINTR)
5017b12bc57Smillert continue;
502f454ebdeSmillert break;
503df930be7Sderaadt case 0:
504f454ebdeSmillert break;
505df930be7Sderaadt default:
5062e6cac80Smillert job_exit(pid);
507fd579382Smillert break;
508df930be7Sderaadt }
509f454ebdeSmillert } while (pid > 0);
510df930be7Sderaadt }
511df930be7Sderaadt
512df930be7Sderaadt static void
parse_args(int argc,char * argv[])513785ecd14Stedu parse_args(int argc, char *argv[])
514785ecd14Stedu {
515df930be7Sderaadt int argch;
5162b139a93Smillert char *ep;
517df930be7Sderaadt
518585d9be6Stedu while (-1 != (argch = getopt(argc, argv, "l:n"))) {
519df930be7Sderaadt switch (argch) {
5202b139a93Smillert case 'l':
5212b139a93Smillert errno = 0;
5222b139a93Smillert batch_maxload = strtod(optarg, &ep);
5232b139a93Smillert if (*ep != '\0' || ep == optarg || errno == ERANGE ||
5242b139a93Smillert batch_maxload < 0) {
525b8c5c73dSmillert warnx("illegal load average: %s", optarg);
526df930be7Sderaadt usage();
5272b139a93Smillert }
5282b139a93Smillert break;
52913ae079dSmillert case 'n':
53013ae079dSmillert NoFork = 1;
53113ae079dSmillert break;
5322b139a93Smillert default:
5332b139a93Smillert usage();
534df930be7Sderaadt }
535df930be7Sderaadt }
536df930be7Sderaadt }
537