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