xref: /openbsd/usr.sbin/cron/misc.c (revision cca36db2)
1 /*	$OpenBSD: misc.c,v 1.44 2011/08/22 19:32:42 millert Exp $	*/
2 
3 /* Copyright 1988,1990,1993,1994 by Paul Vixie
4  * All rights reserved
5  */
6 
7 /*
8  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
9  * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
10  *
11  * Permission to use, copy, modify, and distribute this software for any
12  * purpose with or without fee is hereby granted, provided that the above
13  * copyright notice and this permission notice appear in all copies.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
16  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
18  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  */
23 
24 /* vix 26jan87 [RCS has the rest of the log]
25  * vix 30dec86 [written]
26  */
27 
28 #include "cron.h"
29 #include <limits.h>
30 
31 #if defined(SYSLOG) && defined(LOG_FILE)
32 # undef LOG_FILE
33 #endif
34 
35 #if defined(LOG_DAEMON) && !defined(LOG_CRON)
36 # define LOG_CRON LOG_DAEMON
37 #endif
38 
39 #ifndef FACILITY
40 #define FACILITY LOG_CRON
41 #endif
42 
43 static int LogFD = ERR;
44 
45 #if defined(SYSLOG)
46 static int syslog_open = FALSE;
47 #endif
48 
49 int
50 strcmp_until(const char *left, const char *right, char until) {
51 	while (*left && *left != until && *left == *right) {
52 		left++;
53 		right++;
54 	}
55 
56 	if ((*left=='\0' || *left == until) &&
57 	    (*right=='\0' || *right == until)) {
58 		return (0);
59 	}
60 	return (*left - *right);
61 }
62 
63 int
64 set_debug_flags(const char *flags) {
65 	/* debug flags are of the form    flag[,flag ...]
66 	 *
67 	 * if an error occurs, print a message to stdout and return FALSE.
68 	 * otherwise return TRUE after setting ERROR_FLAGS.
69 	 */
70 
71 #if !DEBUGGING
72 
73 	printf("this program was compiled without debugging enabled\n");
74 	return (FALSE);
75 
76 #else /* DEBUGGING */
77 
78 	const char *pc = flags;
79 
80 	DebugFlags = 0;
81 
82 	while (*pc) {
83 		const char	**test;
84 		int		mask;
85 
86 		/* try to find debug flag name in our list.
87 		 */
88 		for (test = DebugFlagNames, mask = 1;
89 		     *test != NULL && strcmp_until(*test, pc, ',');
90 		     test++, mask <<= 1)
91 			continue;
92 
93 		if (!*test) {
94 			fprintf(stderr,
95 				"unrecognized debug flag <%s> <%s>\n",
96 				flags, pc);
97 			return (FALSE);
98 		}
99 
100 		DebugFlags |= mask;
101 
102 		/* skip to the next flag
103 		 */
104 		while (*pc && *pc != ',')
105 			pc++;
106 		if (*pc == ',')
107 			pc++;
108 	}
109 
110 	if (DebugFlags) {
111 		int flag;
112 
113 		fprintf(stderr, "debug flags enabled:");
114 
115 		for (flag = 0;  DebugFlagNames[flag];  flag++)
116 			if (DebugFlags & (1 << flag))
117 				fprintf(stderr, " %s", DebugFlagNames[flag]);
118 		fprintf(stderr, "\n");
119 	}
120 
121 	return (TRUE);
122 
123 #endif /* DEBUGGING */
124 }
125 
126 void
127 set_cron_uid(void) {
128 #if defined(BSD) || defined(POSIX)
129 	if (seteuid(ROOT_UID) < OK) {
130 		perror("seteuid");
131 		exit(EXIT_FAILURE);
132 	}
133 #else
134 	if (setuid(ROOT_UID) < OK) {
135 		perror("setuid");
136 		exit(EXIT_FAILURE);
137 	}
138 #endif
139 }
140 
141 void
142 set_cron_cwd(void) {
143 	struct stat sb;
144 	struct group *grp = NULL;
145 
146 #ifdef CRON_GROUP
147 	grp = getgrnam(CRON_GROUP);
148 #endif
149 	/* first check for CRONDIR ("/var/cron" or some such)
150 	 */
151 	if (stat(CRONDIR, &sb) < OK && errno == ENOENT) {
152 		perror(CRONDIR);
153 		if (OK == mkdir(CRONDIR, 0710)) {
154 			fprintf(stderr, "%s: created\n", CRONDIR);
155 			stat(CRONDIR, &sb);
156 		} else {
157 			fprintf(stderr, "%s: ", CRONDIR);
158 			perror("mkdir");
159 			exit(EXIT_FAILURE);
160 		}
161 	}
162 	if (!S_ISDIR(sb.st_mode)) {
163 		fprintf(stderr, "'%s' is not a directory, bailing out.\n",
164 			CRONDIR);
165 		exit(EXIT_FAILURE);
166 	}
167 	if (chdir(CRONDIR) < OK) {
168 		fprintf(stderr, "cannot chdir(%s), bailing out.\n", CRONDIR);
169 		perror(CRONDIR);
170 		exit(EXIT_FAILURE);
171 	}
172 
173 	/* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such)
174 	 */
175 	if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) {
176 		perror(SPOOL_DIR);
177 		if (OK == mkdir(SPOOL_DIR, 0700)) {
178 			fprintf(stderr, "%s: created\n", SPOOL_DIR);
179 			stat(SPOOL_DIR, &sb);
180 		} else {
181 			fprintf(stderr, "%s: ", SPOOL_DIR);
182 			perror("mkdir");
183 			exit(EXIT_FAILURE);
184 		}
185 	}
186 	if (!S_ISDIR(sb.st_mode)) {
187 		fprintf(stderr, "'%s' is not a directory, bailing out.\n",
188 			SPOOL_DIR);
189 		exit(EXIT_FAILURE);
190 	}
191 	if (grp != NULL) {
192 		if (sb.st_gid != grp->gr_gid)
193 			chown(SPOOL_DIR, -1, grp->gr_gid);
194 		if (sb.st_mode != 01730)
195 			chmod(SPOOL_DIR, 01730);
196 	}
197 
198 	/* finally, look at AT_DIR ("atjobs" or some such)
199 	 */
200 	if (stat(AT_DIR, &sb) < OK && errno == ENOENT) {
201 		perror(AT_DIR);
202 		if (OK == mkdir(AT_DIR, 0700)) {
203 			fprintf(stderr, "%s: created\n", AT_DIR);
204 			stat(AT_DIR, &sb);
205 		} else {
206 			fprintf(stderr, "%s: ", AT_DIR);
207 			perror("mkdir");
208 			exit(EXIT_FAILURE);
209 		}
210 	}
211 	if (!S_ISDIR(sb.st_mode)) {
212 		fprintf(stderr, "'%s' is not a directory, bailing out.\n",
213 			AT_DIR);
214 		exit(EXIT_FAILURE);
215 	}
216 	if (grp != NULL) {
217 		if (sb.st_gid != grp->gr_gid)
218 			chown(AT_DIR, -1, grp->gr_gid);
219 		if (sb.st_mode != 01770)
220 			chmod(AT_DIR, 01770);
221 	}
222 }
223 
224 /* acquire_daemonlock() - write our PID into /var/run/cron.pid, unless
225  *	another daemon is already running, which we detect here.
226  *
227  * note: main() calls us twice; once before forking, once after.
228  *	we maintain static storage of the file pointer so that we
229  *	can rewrite our PID into _PATH_CRON_PID after the fork.
230  */
231 void
232 acquire_daemonlock(int closeflag) {
233 	static int fd = -1;
234 	char buf[3*MAX_FNAME];
235 	const char *pidfile;
236 	char *ep;
237 	long otherpid;
238 	ssize_t num;
239 
240 	if (closeflag) {
241 		/* close stashed fd for child so we don't leak it. */
242 		if (fd != -1) {
243 			close(fd);
244 			fd = -1;
245 		}
246 		return;
247 	}
248 
249 	if (fd == -1) {
250 		pidfile = _PATH_CRON_PID;
251 		if ((fd = open(pidfile, O_RDWR|O_CREAT|O_EXLOCK|O_NONBLOCK,
252 		    0644)) == -1) {
253 			int save_errno = errno;
254 
255 			if (errno != EWOULDBLOCK)  {
256 				snprintf(buf, sizeof buf,
257 				    "can't open or create %s: %s", pidfile,
258 				    strerror(save_errno));
259 				fprintf(stderr, "%s: %s\n", ProgramName, buf);
260 				log_it("CRON", getpid(), "DEATH", buf);
261 				exit(EXIT_FAILURE);
262 			}
263 
264 			/* couldn't lock the pid file, try to read existing. */
265 			bzero(buf, sizeof(buf));
266 			if ((fd = open(pidfile, O_RDONLY, 0)) >= 0 &&
267 			    (num = read(fd, buf, sizeof(buf) - 1)) > 0 &&
268 			    (otherpid = strtol(buf, &ep, 10)) > 0 &&
269 			    ep != buf && *ep == '\n' && otherpid != LONG_MAX) {
270 				snprintf(buf, sizeof buf,
271 				    "can't lock %s, otherpid may be %ld: %s",
272 				    pidfile, otherpid, strerror(save_errno));
273 			} else {
274 				snprintf(buf, sizeof buf,
275 				    "can't lock %s, otherpid unknown: %s",
276 				    pidfile, strerror(save_errno));
277 			}
278 			fprintf(stderr, "%s: %s\n", ProgramName, buf);
279 			log_it("CRON", getpid(), "DEATH", buf);
280 			exit(EXIT_FAILURE);
281 		}
282 		/* fd must be > STDERR_FILENO since we dup fd 0-2 to /dev/null */
283 		if (fd <= STDERR_FILENO) {
284 			if (dup2(fd, STDERR_FILENO + 1) < 0) {
285 				snprintf(buf, sizeof buf,
286 				    "can't dup pid fd: %s", strerror(errno));
287 				fprintf(stderr, "%s: %s\n", ProgramName, buf);
288 				log_it("CRON", getpid(), "DEATH", buf);
289 				exit(EXIT_FAILURE);
290 			}
291 			close(fd);
292 			fd = STDERR_FILENO + 1;
293 		}
294 		(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
295 	}
296 
297 	snprintf(buf, sizeof(buf), "%ld\n", (long)getpid());
298 	(void) lseek(fd, (off_t)0, SEEK_SET);
299 	num = write(fd, buf, strlen(buf));
300 	(void) ftruncate(fd, (off_t)num);
301 
302 	/* abandon fd even though the file is open. we need to keep
303 	 * it open and locked, but we don't need the handles elsewhere.
304 	 */
305 }
306 
307 /* get_char(file) : like getc() but increment LineNumber on newlines
308  */
309 int
310 get_char(FILE *file) {
311 	int ch;
312 
313 	ch = getc(file);
314 	if (ch == '\n')
315 		Set_LineNum(LineNumber + 1)
316 	return (ch);
317 }
318 
319 /* unget_char(ch, file) : like ungetc but do LineNumber processing
320  */
321 void
322 unget_char(int ch, FILE *file) {
323 	ungetc(ch, file);
324 	if (ch == '\n')
325 		Set_LineNum(LineNumber - 1)
326 }
327 
328 /* get_string(str, max, file, termstr) : like fgets() but
329  *		(1) has terminator string which should include \n
330  *		(2) will always leave room for the null
331  *		(3) uses get_char() so LineNumber will be accurate
332  *		(4) returns EOF or terminating character, whichever
333  */
334 int
335 get_string(char *string, int size, FILE *file, char *terms) {
336 	int ch;
337 
338 	while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) {
339 		if (size > 1) {
340 			*string++ = (char) ch;
341 			size--;
342 		}
343 	}
344 
345 	if (size > 0)
346 		*string = '\0';
347 
348 	return (ch);
349 }
350 
351 /* skip_comments(file) : read past comment (if any)
352  */
353 void
354 skip_comments(FILE *file) {
355 	int ch;
356 
357 	while (EOF != (ch = get_char(file))) {
358 		/* ch is now the first character of a line.
359 		 */
360 
361 		while (ch == ' ' || ch == '\t')
362 			ch = get_char(file);
363 
364 		if (ch == EOF)
365 			break;
366 
367 		/* ch is now the first non-blank character of a line.
368 		 */
369 
370 		if (ch != '\n' && ch != '#')
371 			break;
372 
373 		/* ch must be a newline or comment as first non-blank
374 		 * character on a line.
375 		 */
376 
377 		while (ch != '\n' && ch != EOF)
378 			ch = get_char(file);
379 
380 		/* ch is now the newline of a line which we're going to
381 		 * ignore.
382 		 */
383 	}
384 	if (ch != EOF)
385 		unget_char(ch, file);
386 }
387 
388 /* int in_file(const char *string, FILE *file, int error)
389  *	return TRUE if one of the lines in file matches string exactly,
390  *	FALSE if no lines match, and error on error.
391  */
392 static int
393 in_file(const char *string, FILE *file, int error)
394 {
395 	char line[MAX_TEMPSTR];
396 	char *endp;
397 
398 	if (fseek(file, 0L, SEEK_SET))
399 		return (error);
400 	while (fgets(line, MAX_TEMPSTR, file)) {
401 		if (line[0] != '\0') {
402 			endp = &line[strlen(line) - 1];
403 			if (*endp != '\n')
404 				return (error);
405 			*endp = '\0';
406 			if (0 == strcmp(line, string))
407 				return (TRUE);
408 		}
409 	}
410 	if (ferror(file))
411 		return (error);
412 	return (FALSE);
413 }
414 
415 /* int allowed(const char *username, const char *allow_file, const char *deny_file)
416  *	returns TRUE if (allow_file exists and user is listed)
417  *	or (deny_file exists and user is NOT listed).
418  *	root is always allowed.
419  */
420 int
421 allowed(const char *username, const char *allow_file, const char *deny_file) {
422 	FILE	*fp;
423 	int	isallowed;
424 
425 	if (strcmp(username, ROOT_USER) == 0)
426 		return (TRUE);
427 	isallowed = FALSE;
428 	if ((fp = fopen(allow_file, "r")) != NULL) {
429 		isallowed = in_file(username, fp, FALSE);
430 		fclose(fp);
431 	} else if ((fp = fopen(deny_file, "r")) != NULL) {
432 		isallowed = !in_file(username, fp, FALSE);
433 		fclose(fp);
434 	}
435 	return (isallowed);
436 }
437 
438 void
439 log_it(const char *username, PID_T xpid, const char *event, const char *detail) {
440 #if defined(LOG_FILE) || DEBUGGING
441 	PID_T pid = xpid;
442 #endif
443 #if defined(LOG_FILE)
444 	char *msg;
445 	size_t msglen;
446 	TIME_T now = time((TIME_T) 0);
447 	struct tm *t = localtime(&now);
448 #endif /*LOG_FILE*/
449 #if defined(SYSLOG)
450 	char **info, *info_events[] = { "CMD", "ATJOB", "BEGIN EDIT", "DELETE",
451 	    "END EDIT", "LIST", "MAIL", "RELOAD", "REPLACE", "STARTUP", NULL };
452 #endif /*SYSLOG*/
453 
454 #if defined(LOG_FILE)
455 	/* we assume that MAX_TEMPSTR will hold the date, time, &punctuation.
456 	 */
457 	msglen = strlen(username) + strlen(event) + strlen(detail) +
458 	    MAX_TEMPSTR;
459 	if ((msg = malloc(msglen)) == NULL)
460 		return;
461 
462 	if (LogFD < OK) {
463 		LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600);
464 		if (LogFD < OK) {
465 			fprintf(stderr, "%s: can't open log file\n",
466 				ProgramName);
467 			perror(LOG_FILE);
468 		} else {
469 			(void) fcntl(LogFD, F_SETFD, FD_CLOEXEC);
470 		}
471 	}
472 
473 	/* we have to snprintf() it because fprintf() doesn't always write
474 	 * everything out in one chunk and this has to be atomically appended
475 	 * to the log file.
476 	 */
477 	snprintf(msg, msglen, "%s (%02d/%02d-%02d:%02d:%02d-%ld) %s (%s)\n",
478 		username,
479 		t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec,
480 		(long)pid, event, detail);
481 
482 	if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) {
483 		if (LogFD >= OK)
484 			perror(LOG_FILE);
485 		fprintf(stderr, "%s: can't write to log file\n", ProgramName);
486 		write(STDERR_FILENO, msg, strlen(msg));
487 	}
488 
489 	free(msg);
490 #endif /*LOG_FILE*/
491 
492 #if defined(SYSLOG)
493 	if (!syslog_open) {
494 # ifdef LOG_DAEMON
495 		openlog(ProgramName, LOG_PID, FACILITY);
496 # else
497 		openlog(ProgramName, LOG_PID);
498 # endif
499 		syslog_open = TRUE;		/* assume openlog success */
500 	}
501 
502 	for (info = info_events; *info; info++)
503 		if (!strcmp(event, *info))
504 			break;
505 	syslog(*info ? LOG_INFO : LOG_WARNING, "(%s) %s (%s)", username, event,
506 	    detail);
507 
508 #endif /*SYSLOG*/
509 
510 #if DEBUGGING
511 	if (DebugFlags) {
512 		fprintf(stderr, "log_it: (%s %ld) %s (%s)\n",
513 			username, (long)pid, event, detail);
514 	}
515 #endif
516 }
517 
518 void
519 log_close(void) {
520 	if (LogFD != ERR) {
521 		close(LogFD);
522 		LogFD = ERR;
523 	}
524 #if defined(SYSLOG)
525 	closelog();
526 	syslog_open = FALSE;
527 #endif /*SYSLOG*/
528 }
529 
530 /* char *first_word(char *s, char *t)
531  *	return pointer to first word
532  * parameters:
533  *	s - string we want the first word of
534  *	t - terminators, implicitly including \0
535  * warnings:
536  *	(1) this routine is fairly slow
537  *	(2) it returns a pointer to static storage
538  */
539 char *
540 first_word(char *s, char *t) {
541 	static char retbuf[2][MAX_TEMPSTR + 1];	/* sure wish C had GC */
542 	static int retsel = 0;
543 	char *rb, *rp;
544 
545 	/* select a return buffer */
546 	retsel = 1-retsel;
547 	rb = &retbuf[retsel][0];
548 	rp = rb;
549 
550 	/* skip any leading terminators */
551 	while (*s && (NULL != strchr(t, *s))) {
552 		s++;
553 	}
554 
555 	/* copy until next terminator or full buffer */
556 	while (*s && (NULL == strchr(t, *s)) && (rp < &rb[MAX_TEMPSTR])) {
557 		*rp++ = *s++;
558 	}
559 
560 	/* finish the return-string and return it */
561 	*rp = '\0';
562 	return (rb);
563 }
564 
565 /* warning:
566  *	heavily ascii-dependent.
567  */
568 void
569 mkprint(dst, src, len)
570 	char *dst;
571 	unsigned char *src;
572 	int len;
573 {
574 	/*
575 	 * XXX
576 	 * We know this routine can't overflow the dst buffer because mkprints()
577 	 * allocated enough space for the worst case.
578 	 */
579 	while (len-- > 0)
580 	{
581 		unsigned char ch = *src++;
582 
583 		if (ch < ' ') {			/* control character */
584 			*dst++ = '^';
585 			*dst++ = ch + '@';
586 		} else if (ch < 0177) {		/* printable */
587 			*dst++ = ch;
588 		} else if (ch == 0177) {	/* delete/rubout */
589 			*dst++ = '^';
590 			*dst++ = '?';
591 		} else {			/* parity character */
592 			snprintf(dst, 5, "\\%03o", ch);
593 			dst += strlen(dst);
594 		}
595 	}
596 	*dst = '\0';
597 }
598 
599 /* warning:
600  *	returns a pointer to malloc'd storage, you must call free yourself.
601  */
602 char *
603 mkprints(src, len)
604 	unsigned char *src;
605 	unsigned int len;
606 {
607 	char *dst = malloc(len*4 + 1);
608 
609 	if (dst)
610 		mkprint(dst, src, len);
611 
612 	return (dst);
613 }
614 
615 #ifdef MAIL_DATE
616 /* Sat, 27 Feb 1993 11:44:51 -0800 (CST)
617  * 1234567890123456789012345678901234567
618  */
619 char *
620 arpadate(clock)
621 	time_t *clock;
622 {
623 	time_t t = clock ? *clock : time(NULL);
624 	struct tm *tm = localtime(&t);
625 	static char ret[64];	/* zone name might be >3 chars */
626 	char *qmark;
627 	size_t len;
628 	long gmtoff = get_gmtoff(&t, tm);
629 	int hours = gmtoff / 3600;
630 	int minutes = (gmtoff - (hours * 3600)) / 60;
631 
632 	if (minutes < 0)
633 		minutes = -minutes;
634 
635 	/* Defensive coding (almost) never hurts... */
636 	len = strftime(ret, sizeof(ret), "%a, %e %b %Y %T ????? (%Z)", tm);
637 	if (len == 0) {
638 		ret[0] = '?';
639 		ret[1] = '\0';
640 		return (ret);
641 	}
642 	qmark = strchr(ret, '?');
643 	if (qmark && len - (qmark - ret) >= 6) {
644 		snprintf(qmark, 6, "% .2d%.2d", hours, minutes);
645 		qmark[5] = ' ';
646 	}
647 	return (ret);
648 }
649 #endif /*MAIL_DATE*/
650 
651 #ifdef HAVE_SAVED_UIDS
652 static gid_t save_egid;
653 int swap_gids() { save_egid = getegid(); return (setegid(getgid())); }
654 int swap_gids_back() { return (setegid(save_egid)); }
655 #else /*HAVE_SAVED_UIDS*/
656 int swap_gids() { return (setregid(getegid(), getgid())); }
657 int swap_gids_back() { return (swap_gids()); }
658 #endif /*HAVE_SAVED_UIDS*/
659 
660 /* Return the offset from GMT in seconds (algorithm taken from sendmail).
661  *
662  * warning:
663  *	clobbers the static storage space used by localtime() and gmtime().
664  *	If the local pointer is non-NULL it *must* point to a local copy.
665  */
666 #ifndef HAVE_TM_GMTOFF
667 long get_gmtoff(time_t *clock, struct tm *local)
668 {
669 	struct tm gmt;
670 	long offset;
671 
672 	gmt = *gmtime(clock);
673 	if (local == NULL)
674 		local = localtime(clock);
675 
676 	offset = (local->tm_sec - gmt.tm_sec) +
677 	    ((local->tm_min - gmt.tm_min) * 60) +
678 	    ((local->tm_hour - gmt.tm_hour) * 3600);
679 
680 	/* Timezone may cause year rollover to happen on a different day. */
681 	if (local->tm_year < gmt.tm_year)
682 		offset -= 24 * 3600;
683 	else if (local->tm_year > gmt.tm_year)
684 		offset += 24 * 3600;
685 	else if (local->tm_yday < gmt.tm_yday)
686 		offset -= 24 * 3600;
687 	else if (local->tm_yday > gmt.tm_yday)
688 		offset += 24 * 3600;
689 
690 	return (offset);
691 }
692 #endif /* HAVE_TM_GMTOFF */
693 
694 /* void open_socket(void)
695  *	opens a UNIX domain socket that crontab uses to poke cron.
696  */
697 int
698 open_socket(void)
699 {
700 	int		   sock;
701 	mode_t		   omask;
702 	struct sockaddr_un s_un;
703 
704 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
705 	if (sock == -1) {
706 		fprintf(stderr, "%s: can't create socket: %s\n",
707 		    ProgramName, strerror(errno));
708 		log_it("CRON", getpid(), "DEATH", "can't create socket");
709 		exit(EXIT_FAILURE);
710 	}
711 	if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
712 		fprintf(stderr, "%s: can't make socket close on exec: %s\n",
713 		    ProgramName, strerror(errno));
714 		log_it("CRON", getpid(), "DEATH",
715 		    "can't make socket close on exec");
716 		exit(EXIT_FAILURE);
717 	}
718 	if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
719 		fprintf(stderr, "%s: can't make socket non-blocking: %s\n",
720 		    ProgramName, strerror(errno));
721 		log_it("CRON", getpid(), "DEATH",
722 		    "can't make socket non-blocking");
723 		exit(EXIT_FAILURE);
724 	}
725 	bzero(&s_un, sizeof(s_un));
726 	if (snprintf(s_un.sun_path, sizeof s_un.sun_path, "%s/%s",
727 	      SPOOL_DIR, CRONSOCK) >= sizeof(s_un.sun_path)) {
728 		fprintf(stderr, "%s/%s: path too long\n", SPOOL_DIR, CRONSOCK);
729 		log_it("CRON", getpid(), "DEATH", "path too long");
730 		exit(EXIT_FAILURE);
731 	}
732 	unlink(s_un.sun_path);
733 	s_un.sun_family = AF_UNIX;
734 #ifdef SUN_LEN
735 	s_un.sun_len = SUN_LEN(&s_un);
736 #endif
737 
738 	omask = umask(007);
739 	if (bind(sock, (struct sockaddr *)&s_un, sizeof(s_un))) {
740 		fprintf(stderr, "%s: can't bind socket: %s\n",
741 		    ProgramName, strerror(errno));
742 		log_it("CRON", getpid(), "DEATH", "can't bind socket");
743 		umask(omask);
744 		exit(EXIT_FAILURE);
745 	}
746 	umask(omask);
747 	if (listen(sock, SOMAXCONN)) {
748 		fprintf(stderr, "%s: can't listen on socket: %s\n",
749 		    ProgramName, strerror(errno));
750 		log_it("CRON", getpid(), "DEATH", "can't listen on socket");
751 		exit(EXIT_FAILURE);
752 	}
753 	chmod(s_un.sun_path, 0660);
754 
755 	return(sock);
756 }
757 
758 void
759 poke_daemon(const char *spool_dir, unsigned char cookie) {
760 	int sock = -1;
761 	struct sockaddr_un s_un;
762 
763 	(void) utime(spool_dir, NULL);		/* old poke method */
764 
765 	bzero(&s_un, sizeof(s_un));
766 	if (snprintf(s_un.sun_path, sizeof s_un.sun_path, "%s/%s",
767 	      SPOOL_DIR, CRONSOCK) >= sizeof(s_un.sun_path)) {
768 		fprintf(stderr, "%s: %s/%s: path too long\n",
769 		    ProgramName, SPOOL_DIR, CRONSOCK);
770 		return;
771 	}
772 	s_un.sun_family = AF_UNIX;
773 #ifdef SUN_LEN
774 	s_un.sun_len = SUN_LEN(&s_un);
775 #endif
776 	(void) signal(SIGPIPE, SIG_IGN);
777 	if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0 &&
778 	    connect(sock, (struct sockaddr *)&s_un, sizeof(s_un)) == 0)
779 		write(sock, &cookie, 1);
780 	else
781 		fprintf(stderr, "%s: warning, cron does not appear to be "
782 		    "running.\n", ProgramName);
783 	if (sock >= 0)
784 		close(sock);
785 	(void) signal(SIGPIPE, SIG_DFL);
786 }
787