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