xref: /openbsd-src/usr.sbin/cron/misc.c (revision 7bbe964f6b7d22ad07ca46292495604f942eba4e)
1 /*	$OpenBSD: misc.c,v 1.40 2009/10/27 23:59:51 deraadt 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(ERROR_EXIT);
132 	}
133 #else
134 	if (setuid(ROOT_UID) < OK) {
135 		perror("setuid");
136 		exit(ERROR_EXIT);
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(ERROR_EXIT);
160 		}
161 	}
162 	if (!S_ISDIR(sb.st_mode)) {
163 		fprintf(stderr, "'%s' is not a directory, bailing out.\n",
164 			CRONDIR);
165 		exit(ERROR_EXIT);
166 	}
167 	if (chdir(CRONDIR) < OK) {
168 		fprintf(stderr, "cannot chdir(%s), bailing out.\n", CRONDIR);
169 		perror(CRONDIR);
170 		exit(ERROR_EXIT);
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(ERROR_EXIT);
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(ERROR_EXIT);
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(ERROR_EXIT);
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(ERROR_EXIT);
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 /etc/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(ERROR_EXIT);
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(ERROR_EXIT);
281 		}
282 		/* fd must be > STDERR since we dup fd 0-2 to /dev/null */
283 		if (fd <= STDERR) {
284 			if (dup2(fd, STDERR + 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(ERROR_EXIT);
290 			}
291 			close(fd);
292 			fd = STDERR + 1;
293 		}
294 		(void) fcntl(fd, F_SETFD, 1);
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 
450 #if defined(LOG_FILE)
451 	/* we assume that MAX_TEMPSTR will hold the date, time, &punctuation.
452 	 */
453 	msglen = strlen(username) + strlen(event) + strlen(detail) +
454 	    MAX_TEMPSTR;
455 	if ((msg = malloc(msglen)) == NULL)
456 		return;
457 
458 	if (LogFD < OK) {
459 		LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600);
460 		if (LogFD < OK) {
461 			fprintf(stderr, "%s: can't open log file\n",
462 				ProgramName);
463 			perror(LOG_FILE);
464 		} else {
465 			(void) fcntl(LogFD, F_SETFD, 1);
466 		}
467 	}
468 
469 	/* we have to snprintf() it because fprintf() doesn't always write
470 	 * everything out in one chunk and this has to be atomically appended
471 	 * to the log file.
472 	 */
473 	snprintf(msg, msglen, "%s (%02d/%02d-%02d:%02d:%02d-%ld) %s (%s)\n",
474 		username,
475 		t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec,
476 		(long)pid, event, detail);
477 
478 	if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) {
479 		if (LogFD >= OK)
480 			perror(LOG_FILE);
481 		fprintf(stderr, "%s: can't write to log file\n", ProgramName);
482 		write(STDERR, msg, strlen(msg));
483 	}
484 
485 	free(msg);
486 #endif /*LOG_FILE*/
487 
488 #if defined(SYSLOG)
489 	if (!syslog_open) {
490 # ifdef LOG_DAEMON
491 		openlog(ProgramName, LOG_PID, FACILITY);
492 # else
493 		openlog(ProgramName, LOG_PID);
494 # endif
495 		syslog_open = TRUE;		/* assume openlog success */
496 	}
497 
498 	syslog(LOG_INFO, "(%s) %s (%s)", username, event, detail);
499 
500 #endif /*SYSLOG*/
501 
502 #if DEBUGGING
503 	if (DebugFlags) {
504 		fprintf(stderr, "log_it: (%s %ld) %s (%s)\n",
505 			username, (long)pid, event, detail);
506 	}
507 #endif
508 }
509 
510 void
511 log_close(void) {
512 	if (LogFD != ERR) {
513 		close(LogFD);
514 		LogFD = ERR;
515 	}
516 #if defined(SYSLOG)
517 	closelog();
518 	syslog_open = FALSE;
519 #endif /*SYSLOG*/
520 }
521 
522 /* char *first_word(char *s, char *t)
523  *	return pointer to first word
524  * parameters:
525  *	s - string we want the first word of
526  *	t - terminators, implicitly including \0
527  * warnings:
528  *	(1) this routine is fairly slow
529  *	(2) it returns a pointer to static storage
530  */
531 char *
532 first_word(char *s, char *t) {
533 	static char retbuf[2][MAX_TEMPSTR + 1];	/* sure wish C had GC */
534 	static int retsel = 0;
535 	char *rb, *rp;
536 
537 	/* select a return buffer */
538 	retsel = 1-retsel;
539 	rb = &retbuf[retsel][0];
540 	rp = rb;
541 
542 	/* skip any leading terminators */
543 	while (*s && (NULL != strchr(t, *s))) {
544 		s++;
545 	}
546 
547 	/* copy until next terminator or full buffer */
548 	while (*s && (NULL == strchr(t, *s)) && (rp < &rb[MAX_TEMPSTR])) {
549 		*rp++ = *s++;
550 	}
551 
552 	/* finish the return-string and return it */
553 	*rp = '\0';
554 	return (rb);
555 }
556 
557 /* warning:
558  *	heavily ascii-dependent.
559  */
560 void
561 mkprint(dst, src, len)
562 	char *dst;
563 	unsigned char *src;
564 	int len;
565 {
566 	/*
567 	 * XXX
568 	 * We know this routine can't overflow the dst buffer because mkprints()
569 	 * allocated enough space for the worst case.
570 	 */
571 	while (len-- > 0)
572 	{
573 		unsigned char ch = *src++;
574 
575 		if (ch < ' ') {			/* control character */
576 			*dst++ = '^';
577 			*dst++ = ch + '@';
578 		} else if (ch < 0177) {		/* printable */
579 			*dst++ = ch;
580 		} else if (ch == 0177) {	/* delete/rubout */
581 			*dst++ = '^';
582 			*dst++ = '?';
583 		} else {			/* parity character */
584 			snprintf(dst, 5, "\\%03o", ch);
585 			dst += strlen(dst);
586 		}
587 	}
588 	*dst = '\0';
589 }
590 
591 /* warning:
592  *	returns a pointer to malloc'd storage, you must call free yourself.
593  */
594 char *
595 mkprints(src, len)
596 	unsigned char *src;
597 	unsigned int len;
598 {
599 	char *dst = malloc(len*4 + 1);
600 
601 	if (dst)
602 		mkprint(dst, src, len);
603 
604 	return (dst);
605 }
606 
607 #ifdef MAIL_DATE
608 /* Sat, 27 Feb 1993 11:44:51 -0800 (CST)
609  * 1234567890123456789012345678901234567
610  */
611 char *
612 arpadate(clock)
613 	time_t *clock;
614 {
615 	time_t t = clock ? *clock : time(NULL);
616 	struct tm *tm = localtime(&t);
617 	static char ret[64];	/* zone name might be >3 chars */
618 	char *qmark;
619 	size_t len;
620 	long gmtoff = get_gmtoff(&t, tm);
621 	int hours = gmtoff / 3600;
622 	int minutes = (gmtoff - (hours * 3600)) / 60;
623 
624 	if (minutes < 0)
625 		minutes = -minutes;
626 
627 	/* Defensive coding (almost) never hurts... */
628 	len = strftime(ret, sizeof(ret), "%a, %e %b %Y %T ????? (%Z)", tm);
629 	if (len == 0) {
630 		ret[0] = '?';
631 		ret[1] = '\0';
632 		return (ret);
633 	}
634 	qmark = strchr(ret, '?');
635 	if (qmark && len - (qmark - ret) >= 6) {
636 		snprintf(qmark, 6, "% .2d%.2d", hours, minutes);
637 		qmark[5] = ' ';
638 	}
639 	return (ret);
640 }
641 #endif /*MAIL_DATE*/
642 
643 #ifdef HAVE_SAVED_UIDS
644 static gid_t save_egid;
645 int swap_gids() { save_egid = getegid(); return (setegid(getgid())); }
646 int swap_gids_back() { return (setegid(save_egid)); }
647 #else /*HAVE_SAVED_UIDS*/
648 int swap_gids() { return (setregid(getegid(), getgid())); }
649 int swap_gids_back() { return (swap_gids()); }
650 #endif /*HAVE_SAVED_UIDS*/
651 
652 /* Return the offset from GMT in seconds (algorithm taken from sendmail).
653  *
654  * warning:
655  *	clobbers the static storage space used by localtime() and gmtime().
656  *	If the local pointer is non-NULL it *must* point to a local copy.
657  */
658 #ifndef HAVE_TM_GMTOFF
659 long get_gmtoff(time_t *clock, struct tm *local)
660 {
661 	struct tm gmt;
662 	long offset;
663 
664 	gmt = *gmtime(clock);
665 	if (local == NULL)
666 		local = localtime(clock);
667 
668 	offset = (local->tm_sec - gmt.tm_sec) +
669 	    ((local->tm_min - gmt.tm_min) * 60) +
670 	    ((local->tm_hour - gmt.tm_hour) * 3600);
671 
672 	/* Timezone may cause year rollover to happen on a different day. */
673 	if (local->tm_year < gmt.tm_year)
674 		offset -= 24 * 3600;
675 	else if (local->tm_year > gmt.tm_year)
676 		offset -= 24 * 3600;
677 	else if (local->tm_yday < gmt.tm_yday)
678 		offset -= 24 * 3600;
679 	else if (local->tm_yday > gmt.tm_yday)
680 		offset += 24 * 3600;
681 
682 	return (offset);
683 }
684 #endif /* HAVE_TM_GMTOFF */
685 
686 /* void open_socket(void)
687  *	opens a UNIX domain socket that crontab uses to poke cron.
688  */
689 int
690 open_socket(void)
691 {
692 	int		   sock;
693 	mode_t		   omask;
694 	struct sockaddr_un s_un;
695 
696 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
697 	if (sock == -1) {
698 		fprintf(stderr, "%s: can't create socket: %s\n",
699 		    ProgramName, strerror(errno));
700 		log_it("CRON", getpid(), "DEATH", "can't create socket");
701 		exit(ERROR_EXIT);
702 	}
703 	if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
704 		fprintf(stderr, "%s: can't make socket close on exec: %s\n",
705 		    ProgramName, strerror(errno));
706 		log_it("CRON", getpid(), "DEATH",
707 		    "can't make socket close on exec");
708 		exit(ERROR_EXIT);
709 	}
710 	if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
711 		fprintf(stderr, "%s: can't make socket non-blocking: %s\n",
712 		    ProgramName, strerror(errno));
713 		log_it("CRON", getpid(), "DEATH",
714 		    "can't make socket non-blocking");
715 		exit(ERROR_EXIT);
716 	}
717 	bzero(&s_un, sizeof(s_un));
718 	if (snprintf(s_un.sun_path, sizeof s_un.sun_path, "%s/%s",
719 	      SPOOL_DIR, CRONSOCK) >= sizeof(s_un.sun_path)) {
720 		fprintf(stderr, "%s/%s: path too long\n", SPOOL_DIR, CRONSOCK);
721 		log_it("CRON", getpid(), "DEATH", "path too long");
722 		exit(ERROR_EXIT);
723 	}
724 	unlink(s_un.sun_path);
725 	s_un.sun_family = AF_UNIX;
726 #ifdef SUN_LEN
727 	s_un.sun_len = SUN_LEN(&s_un);
728 #endif
729 
730 	omask = umask(007);
731 	if (bind(sock, (struct sockaddr *)&s_un, sizeof(s_un))) {
732 		fprintf(stderr, "%s: can't bind socket: %s\n",
733 		    ProgramName, strerror(errno));
734 		log_it("CRON", getpid(), "DEATH", "can't bind socket");
735 		umask(omask);
736 		exit(ERROR_EXIT);
737 	}
738 	umask(omask);
739 	if (listen(sock, SOMAXCONN)) {
740 		fprintf(stderr, "%s: can't listen on socket: %s\n",
741 		    ProgramName, strerror(errno));
742 		log_it("CRON", getpid(), "DEATH", "can't listen on socket");
743 		exit(ERROR_EXIT);
744 	}
745 	chmod(s_un.sun_path, 0660);
746 
747 	return(sock);
748 }
749 
750 void
751 poke_daemon(const char *spool_dir, unsigned char cookie) {
752 	int sock = -1;
753 	struct sockaddr_un s_un;
754 
755 	(void) utime(spool_dir, NULL);		/* old poke method */
756 
757 	bzero(&s_un, sizeof(s_un));
758 	if (snprintf(s_un.sun_path, sizeof s_un.sun_path, "%s/%s",
759 	      SPOOL_DIR, CRONSOCK) >= sizeof(s_un.sun_path)) {
760 		fprintf(stderr, "%s: %s/%s: path too long\n",
761 		    ProgramName, SPOOL_DIR, CRONSOCK);
762 		return;
763 	}
764 	s_un.sun_family = AF_UNIX;
765 #ifdef SUN_LEN
766 	s_un.sun_len = SUN_LEN(&s_un);
767 #endif
768 	(void) signal(SIGPIPE, SIG_IGN);
769 	if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0 &&
770 	    connect(sock, (struct sockaddr *)&s_un, sizeof(s_un)) == 0)
771 		write(sock, &cookie, 1);
772 	else
773 		fprintf(stderr, "%s: warning, cron does not appear to be "
774 		    "running.\n", ProgramName);
775 	if (sock >= 0)
776 		close(sock);
777 	(void) signal(SIGPIPE, SIG_DFL);
778 }
779