xref: /openbsd-src/usr.sbin/cron/misc.c (revision daf88648c0e349d5c02e1504293082072c981640)
1 /*	$OpenBSD: misc.c,v 1.37 2005/06/08 18:34:00 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 #if !defined(lint) && !defined(LINT)
25 static char const rcsid[] = "$OpenBSD: misc.c,v 1.37 2005/06/08 18:34:00 millert 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 /* strdtb(s) - delete trailing blanks in string 's' and return new length
68  */
69 int
70 strdtb(char *s) {
71 	char	*x = s;
72 
73 	/* scan forward to the null
74 	 */
75 	while (*x)
76 		x++;
77 
78 	/* scan backward to either the first character before the string,
79 	 * or the last non-blank in the string, whichever comes first.
80 	 */
81 	do	{x--;}
82 	while (x >= s && isspace((unsigned char)*x));
83 
84 	/* one character beyond where we stopped above is where the null
85 	 * goes.
86 	 */
87 	*++x = '\0';
88 
89 	/* the difference between the position of the null character and
90 	 * the position of the first character of the string is the length.
91 	 */
92 	return (x - s);
93 }
94 
95 int
96 set_debug_flags(const char *flags) {
97 	/* debug flags are of the form    flag[,flag ...]
98 	 *
99 	 * if an error occurs, print a message to stdout and return FALSE.
100 	 * otherwise return TRUE after setting ERROR_FLAGS.
101 	 */
102 
103 #if !DEBUGGING
104 
105 	printf("this program was compiled without debugging enabled\n");
106 	return (FALSE);
107 
108 #else /* DEBUGGING */
109 
110 	const char *pc = flags;
111 
112 	DebugFlags = 0;
113 
114 	while (*pc) {
115 		const char	**test;
116 		int		mask;
117 
118 		/* try to find debug flag name in our list.
119 		 */
120 		for (test = DebugFlagNames, mask = 1;
121 		     *test != NULL && strcmp_until(*test, pc, ',');
122 		     test++, mask <<= 1)
123 			continue;
124 
125 		if (!*test) {
126 			fprintf(stderr,
127 				"unrecognized debug flag <%s> <%s>\n",
128 				flags, pc);
129 			return (FALSE);
130 		}
131 
132 		DebugFlags |= mask;
133 
134 		/* skip to the next flag
135 		 */
136 		while (*pc && *pc != ',')
137 			pc++;
138 		if (*pc == ',')
139 			pc++;
140 	}
141 
142 	if (DebugFlags) {
143 		int flag;
144 
145 		fprintf(stderr, "debug flags enabled:");
146 
147 		for (flag = 0;  DebugFlagNames[flag];  flag++)
148 			if (DebugFlags & (1 << flag))
149 				fprintf(stderr, " %s", DebugFlagNames[flag]);
150 		fprintf(stderr, "\n");
151 	}
152 
153 	return (TRUE);
154 
155 #endif /* DEBUGGING */
156 }
157 
158 void
159 set_cron_uid(void) {
160 #if defined(BSD) || defined(POSIX)
161 	if (seteuid(ROOT_UID) < OK) {
162 		perror("seteuid");
163 		exit(ERROR_EXIT);
164 	}
165 #else
166 	if (setuid(ROOT_UID) < OK) {
167 		perror("setuid");
168 		exit(ERROR_EXIT);
169 	}
170 #endif
171 }
172 
173 void
174 set_cron_cwd(void) {
175 	struct stat sb;
176 	struct group *grp = NULL;
177 
178 #ifdef CRON_GROUP
179 	grp = getgrnam(CRON_GROUP);
180 #endif
181 	/* first check for CRONDIR ("/var/cron" or some such)
182 	 */
183 	if (stat(CRONDIR, &sb) < OK && errno == ENOENT) {
184 		perror(CRONDIR);
185 		if (OK == mkdir(CRONDIR, 0710)) {
186 			fprintf(stderr, "%s: created\n", CRONDIR);
187 			stat(CRONDIR, &sb);
188 		} else {
189 			fprintf(stderr, "%s: ", CRONDIR);
190 			perror("mkdir");
191 			exit(ERROR_EXIT);
192 		}
193 	}
194 	if (!S_ISDIR(sb.st_mode)) {
195 		fprintf(stderr, "'%s' is not a directory, bailing out.\n",
196 			CRONDIR);
197 		exit(ERROR_EXIT);
198 	}
199 	if (chdir(CRONDIR) < OK) {
200 		fprintf(stderr, "cannot chdir(%s), bailing out.\n", CRONDIR);
201 		perror(CRONDIR);
202 		exit(ERROR_EXIT);
203 	}
204 
205 	/* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such)
206 	 */
207 	if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) {
208 		perror(SPOOL_DIR);
209 		if (OK == mkdir(SPOOL_DIR, 0700)) {
210 			fprintf(stderr, "%s: created\n", SPOOL_DIR);
211 			stat(SPOOL_DIR, &sb);
212 		} else {
213 			fprintf(stderr, "%s: ", SPOOL_DIR);
214 			perror("mkdir");
215 			exit(ERROR_EXIT);
216 		}
217 	}
218 	if (!S_ISDIR(sb.st_mode)) {
219 		fprintf(stderr, "'%s' is not a directory, bailing out.\n",
220 			SPOOL_DIR);
221 		exit(ERROR_EXIT);
222 	}
223 	if (grp != NULL) {
224 		if (sb.st_gid != grp->gr_gid)
225 			chown(SPOOL_DIR, -1, grp->gr_gid);
226 		if (sb.st_mode != 01730)
227 			chmod(SPOOL_DIR, 01730);
228 	}
229 
230 	/* finally, look at AT_DIR ("atjobs" or some such)
231 	 */
232 	if (stat(AT_DIR, &sb) < OK && errno == ENOENT) {
233 		perror(AT_DIR);
234 		if (OK == mkdir(AT_DIR, 0700)) {
235 			fprintf(stderr, "%s: created\n", AT_DIR);
236 			stat(AT_DIR, &sb);
237 		} else {
238 			fprintf(stderr, "%s: ", AT_DIR);
239 			perror("mkdir");
240 			exit(ERROR_EXIT);
241 		}
242 	}
243 	if (!S_ISDIR(sb.st_mode)) {
244 		fprintf(stderr, "'%s' is not a directory, bailing out.\n",
245 			AT_DIR);
246 		exit(ERROR_EXIT);
247 	}
248 	if (grp != NULL) {
249 		if (sb.st_gid != grp->gr_gid)
250 			chown(AT_DIR, -1, grp->gr_gid);
251 		if (sb.st_mode != 01770)
252 			chmod(AT_DIR, 01770);
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 		/* fd must be > STDERR since we dup fd 0-2 to /dev/null */
315 		if (fd <= STDERR) {
316 			if (dup2(fd, STDERR + 1) < 0) {
317 				snprintf(buf, sizeof buf,
318 				    "can't dup pid fd: %s", strerror(errno));
319 				fprintf(stderr, "%s: %s\n", ProgramName, buf);
320 				log_it("CRON", getpid(), "DEATH", buf);
321 				exit(ERROR_EXIT);
322 			}
323 			close(fd);
324 			fd = STDERR + 1;
325 		}
326 		(void) fcntl(fd, F_SETFD, 1);
327 	}
328 
329 	snprintf(buf, sizeof(buf), "%ld\n", (long)getpid());
330 	(void) lseek(fd, (off_t)0, SEEK_SET);
331 	num = write(fd, buf, strlen(buf));
332 	(void) ftruncate(fd, (off_t)num);
333 
334 	/* abandon fd even though the file is open. we need to keep
335 	 * it open and locked, but we don't need the handles elsewhere.
336 	 */
337 }
338 
339 /* get_char(file) : like getc() but increment LineNumber on newlines
340  */
341 int
342 get_char(FILE *file) {
343 	int ch;
344 
345 	ch = getc(file);
346 	if (ch == '\n')
347 		Set_LineNum(LineNumber + 1)
348 	return (ch);
349 }
350 
351 /* unget_char(ch, file) : like ungetc but do LineNumber processing
352  */
353 void
354 unget_char(int ch, FILE *file) {
355 	ungetc(ch, file);
356 	if (ch == '\n')
357 		Set_LineNum(LineNumber - 1)
358 }
359 
360 /* get_string(str, max, file, termstr) : like fgets() but
361  *		(1) has terminator string which should include \n
362  *		(2) will always leave room for the null
363  *		(3) uses get_char() so LineNumber will be accurate
364  *		(4) returns EOF or terminating character, whichever
365  */
366 int
367 get_string(char *string, int size, FILE *file, char *terms) {
368 	int ch;
369 
370 	while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) {
371 		if (size > 1) {
372 			*string++ = (char) ch;
373 			size--;
374 		}
375 	}
376 
377 	if (size > 0)
378 		*string = '\0';
379 
380 	return (ch);
381 }
382 
383 /* skip_comments(file) : read past comment (if any)
384  */
385 void
386 skip_comments(FILE *file) {
387 	int ch;
388 
389 	while (EOF != (ch = get_char(file))) {
390 		/* ch is now the first character of a line.
391 		 */
392 
393 		while (ch == ' ' || ch == '\t')
394 			ch = get_char(file);
395 
396 		if (ch == EOF)
397 			break;
398 
399 		/* ch is now the first non-blank character of a line.
400 		 */
401 
402 		if (ch != '\n' && ch != '#')
403 			break;
404 
405 		/* ch must be a newline or comment as first non-blank
406 		 * character on a line.
407 		 */
408 
409 		while (ch != '\n' && ch != EOF)
410 			ch = get_char(file);
411 
412 		/* ch is now the newline of a line which we're going to
413 		 * ignore.
414 		 */
415 	}
416 	if (ch != EOF)
417 		unget_char(ch, file);
418 }
419 
420 /* int in_file(const char *string, FILE *file, int error)
421  *	return TRUE if one of the lines in file matches string exactly,
422  *	FALSE if no lines match, and error on error.
423  */
424 static int
425 in_file(const char *string, FILE *file, int error)
426 {
427 	char line[MAX_TEMPSTR];
428 	char *endp;
429 
430 	if (fseek(file, 0L, SEEK_SET))
431 		return (error);
432 	while (fgets(line, MAX_TEMPSTR, file)) {
433 		if (line[0] != '\0') {
434 			endp = &line[strlen(line) - 1];
435 			if (*endp != '\n')
436 				return (error);
437 			*endp = '\0';
438 			if (0 == strcmp(line, string))
439 				return (TRUE);
440 		}
441 	}
442 	if (ferror(file))
443 		return (error);
444 	return (FALSE);
445 }
446 
447 /* int allowed(const char *username, const char *allow_file, const char *deny_file)
448  *	returns TRUE if (allow_file exists and user is listed)
449  *	or (deny_file exists and user is NOT listed).
450  *	root is always allowed.
451  */
452 int
453 allowed(const char *username, const char *allow_file, const char *deny_file) {
454 	FILE	*fp;
455 	int	isallowed;
456 
457 	if (strcmp(username, ROOT_USER) == 0)
458 		return (TRUE);
459 	isallowed = FALSE;
460 	if ((fp = fopen(allow_file, "r")) != NULL) {
461 		isallowed = in_file(username, fp, FALSE);
462 		fclose(fp);
463 	} else if ((fp = fopen(deny_file, "r")) != NULL) {
464 		isallowed = !in_file(username, fp, FALSE);
465 		fclose(fp);
466 	}
467 	return (isallowed);
468 }
469 
470 void
471 log_it(const char *username, PID_T xpid, const char *event, const char *detail) {
472 #if defined(LOG_FILE) || DEBUGGING
473 	PID_T pid = xpid;
474 #endif
475 #if defined(LOG_FILE)
476 	char *msg;
477 	size_t msglen;
478 	TIME_T now = time((TIME_T) 0);
479 	struct tm *t = localtime(&now);
480 #endif /*LOG_FILE*/
481 
482 #if defined(LOG_FILE)
483 	/* we assume that MAX_TEMPSTR will hold the date, time, &punctuation.
484 	 */
485 	msglen = strlen(username) + strlen(event) + strlen(detail) +
486 	    MAX_TEMPSTR;
487 	if ((msg = malloc(msglen)) == NULL)
488 		return;
489 
490 	if (LogFD < OK) {
491 		LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600);
492 		if (LogFD < OK) {
493 			fprintf(stderr, "%s: can't open log file\n",
494 				ProgramName);
495 			perror(LOG_FILE);
496 		} else {
497 			(void) fcntl(LogFD, F_SETFD, 1);
498 		}
499 	}
500 
501 	/* we have to snprintf() it because fprintf() doesn't always write
502 	 * everything out in one chunk and this has to be atomically appended
503 	 * to the log file.
504 	 */
505 	snprintf(msg, msglen, "%s (%02d/%02d-%02d:%02d:%02d-%ld) %s (%s)\n",
506 		username,
507 		t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec,
508 		(long)pid, event, detail);
509 
510 	if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) {
511 		if (LogFD >= OK)
512 			perror(LOG_FILE);
513 		fprintf(stderr, "%s: can't write to log file\n", ProgramName);
514 		write(STDERR, msg, strlen(msg));
515 	}
516 
517 	free(msg);
518 #endif /*LOG_FILE*/
519 
520 #if defined(SYSLOG)
521 	if (!syslog_open) {
522 # ifdef LOG_DAEMON
523 		openlog(ProgramName, LOG_PID, FACILITY);
524 # else
525 		openlog(ProgramName, LOG_PID);
526 # endif
527 		syslog_open = TRUE;		/* assume openlog success */
528 	}
529 
530 	syslog(LOG_INFO, "(%s) %s (%s)", username, event, detail);
531 
532 #endif /*SYSLOG*/
533 
534 #if DEBUGGING
535 	if (DebugFlags) {
536 		fprintf(stderr, "log_it: (%s %ld) %s (%s)\n",
537 			username, (long)pid, event, detail);
538 	}
539 #endif
540 }
541 
542 void
543 log_close(void) {
544 	if (LogFD != ERR) {
545 		close(LogFD);
546 		LogFD = ERR;
547 	}
548 #if defined(SYSLOG)
549 	closelog();
550 	syslog_open = FALSE;
551 #endif /*SYSLOG*/
552 }
553 
554 /* char *first_word(char *s, char *t)
555  *	return pointer to first word
556  * parameters:
557  *	s - string we want the first word of
558  *	t - terminators, implicitly including \0
559  * warnings:
560  *	(1) this routine is fairly slow
561  *	(2) it returns a pointer to static storage
562  */
563 char *
564 first_word(char *s, char *t) {
565 	static char retbuf[2][MAX_TEMPSTR + 1];	/* sure wish C had GC */
566 	static int retsel = 0;
567 	char *rb, *rp;
568 
569 	/* select a return buffer */
570 	retsel = 1-retsel;
571 	rb = &retbuf[retsel][0];
572 	rp = rb;
573 
574 	/* skip any leading terminators */
575 	while (*s && (NULL != strchr(t, *s))) {
576 		s++;
577 	}
578 
579 	/* copy until next terminator or full buffer */
580 	while (*s && (NULL == strchr(t, *s)) && (rp < &rb[MAX_TEMPSTR])) {
581 		*rp++ = *s++;
582 	}
583 
584 	/* finish the return-string and return it */
585 	*rp = '\0';
586 	return (rb);
587 }
588 
589 /* warning:
590  *	heavily ascii-dependent.
591  */
592 void
593 mkprint(dst, src, len)
594 	char *dst;
595 	unsigned char *src;
596 	int len;
597 {
598 	/*
599 	 * XXX
600 	 * We know this routine can't overflow the dst buffer because mkprints()
601 	 * allocated enough space for the worst case.
602 	 */
603 	while (len-- > 0)
604 	{
605 		unsigned char ch = *src++;
606 
607 		if (ch < ' ') {			/* control character */
608 			*dst++ = '^';
609 			*dst++ = ch + '@';
610 		} else if (ch < 0177) {		/* printable */
611 			*dst++ = ch;
612 		} else if (ch == 0177) {	/* delete/rubout */
613 			*dst++ = '^';
614 			*dst++ = '?';
615 		} else {			/* parity character */
616 			snprintf(dst, 5, "\\%03o", ch);
617 			dst += 4;
618 		}
619 	}
620 	*dst = '\0';
621 }
622 
623 /* warning:
624  *	returns a pointer to malloc'd storage, you must call free yourself.
625  */
626 char *
627 mkprints(src, len)
628 	unsigned char *src;
629 	unsigned int len;
630 {
631 	char *dst = malloc(len*4 + 1);
632 
633 	if (dst)
634 		mkprint(dst, src, len);
635 
636 	return (dst);
637 }
638 
639 #ifdef MAIL_DATE
640 /* Sat, 27 Feb 1993 11:44:51 -0800 (CST)
641  * 1234567890123456789012345678901234567
642  */
643 char *
644 arpadate(clock)
645 	time_t *clock;
646 {
647 	time_t t = clock ? *clock : time(NULL);
648 	struct tm *tm = localtime(&t);
649 	static char ret[64];	/* zone name might be >3 chars */
650 	char *qmark;
651 	size_t len;
652 	long gmtoff = get_gmtoff(&t, tm);
653 	int hours = gmtoff / 3600;
654 	int minutes = (gmtoff - (hours * 3600)) / 60;
655 
656 	if (minutes < 0)
657 		minutes = -minutes;
658 
659 	/* Defensive coding (almost) never hurts... */
660 	len = strftime(ret, sizeof(ret), "%a, %e %b %Y %T ????? (%Z)", tm);
661 	if (len == 0) {
662 		ret[0] = '?';
663 		ret[1] = '\0';
664 		return (ret);
665 	}
666 	qmark = strchr(ret, '?');
667 	if (qmark && len - (qmark - ret) >= 6) {
668 		snprintf(qmark, 6, "% .2d%.2d", hours, minutes);
669 		qmark[5] = ' ';
670 	}
671 	return (ret);
672 }
673 #endif /*MAIL_DATE*/
674 
675 #ifdef HAVE_SAVED_UIDS
676 static gid_t save_egid;
677 int swap_gids() { save_egid = getegid(); return (setegid(getgid())); }
678 int swap_gids_back() { return (setegid(save_egid)); }
679 #else /*HAVE_SAVED_UIDS*/
680 int swap_gids() { return (setregid(getegid(), getgid())); }
681 int swap_gids_back() { return (swap_gids()); }
682 #endif /*HAVE_SAVED_UIDS*/
683 
684 /* Return the offset from GMT in seconds (algorithm taken from sendmail).
685  *
686  * warning:
687  *	clobbers the static storage space used by localtime() and gmtime().
688  *	If the local pointer is non-NULL it *must* point to a local copy.
689  */
690 #ifndef HAVE_TM_GMTOFF
691 long get_gmtoff(time_t *clock, struct tm *local)
692 {
693 	struct tm gmt;
694 	long offset;
695 
696 	gmt = *gmtime(clock);
697 	if (local == NULL)
698 		local = localtime(clock);
699 
700 	offset = (local->tm_sec - gmt.tm_sec) +
701 	    ((local->tm_min - gmt.tm_min) * 60) +
702 	    ((local->tm_hour - gmt.tm_hour) * 3600);
703 
704 	/* Timezone may cause year rollover to happen on a different day. */
705 	if (local->tm_year < gmt.tm_year)
706 		offset -= 24 * 3600;
707 	else if (local->tm_year > gmt.tm_year)
708 		offset -= 24 * 3600;
709 	else if (local->tm_yday < gmt.tm_yday)
710 		offset -= 24 * 3600;
711 	else if (local->tm_yday > gmt.tm_yday)
712 		offset += 24 * 3600;
713 
714 	return (offset);
715 }
716 #endif /* HAVE_TM_GMTOFF */
717 
718 /* void open_socket(void)
719  *	opens a UNIX domain socket that crontab uses to poke cron.
720  */
721 int
722 open_socket(void)
723 {
724 	int		   sock;
725 	mode_t		   omask;
726 	struct sockaddr_un s_un;
727 
728 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
729 	if (sock == -1) {
730 		fprintf(stderr, "%s: can't create socket: %s\n",
731 		    ProgramName, strerror(errno));
732 		log_it("CRON", getpid(), "DEATH", "can't create socket");
733 		exit(ERROR_EXIT);
734 	}
735 	if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
736 		fprintf(stderr, "%s: can't make socket close on exec: %s\n",
737 		    ProgramName, strerror(errno));
738 		log_it("CRON", getpid(), "DEATH",
739 		    "can't make socket close on exec");
740 		exit(ERROR_EXIT);
741 	}
742 	if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
743 		fprintf(stderr, "%s: can't make socket non-blocking: %s\n",
744 		    ProgramName, strerror(errno));
745 		log_it("CRON", getpid(), "DEATH",
746 		    "can't make socket non-blocking");
747 		exit(ERROR_EXIT);
748 	}
749 	bzero(&s_un, sizeof(s_un));
750 	if (snprintf(s_un.sun_path, sizeof s_un.sun_path, "%s/%s",
751 	      SPOOL_DIR, CRONSOCK) >= sizeof(s_un.sun_path)) {
752 		fprintf(stderr, "%s/%s: path too long\n", SPOOL_DIR, CRONSOCK);
753 		log_it("CRON", getpid(), "DEATH", "path too long");
754 		exit(ERROR_EXIT);
755 	}
756 	unlink(s_un.sun_path);
757 	s_un.sun_family = AF_UNIX;
758 #ifdef SUN_LEN
759 	s_un.sun_len = SUN_LEN(&s_un);
760 #endif
761 
762 	omask = umask(007);
763 	if (bind(sock, (struct sockaddr *)&s_un, sizeof(s_un))) {
764 		fprintf(stderr, "%s: can't bind socket: %s\n",
765 		    ProgramName, strerror(errno));
766 		log_it("CRON", getpid(), "DEATH", "can't bind socket");
767 		umask(omask);
768 		exit(ERROR_EXIT);
769 	}
770 	umask(omask);
771 	if (listen(sock, SOMAXCONN)) {
772 		fprintf(stderr, "%s: can't listen on socket: %s\n",
773 		    ProgramName, strerror(errno));
774 		log_it("CRON", getpid(), "DEATH", "can't listen on socket");
775 		exit(ERROR_EXIT);
776 	}
777 	chmod(s_un.sun_path, 0660);
778 
779 	return(sock);
780 }
781 
782 void
783 poke_daemon(const char *spool_dir, unsigned char cookie) {
784 	int sock = -1;
785 	struct sockaddr_un s_un;
786 
787 	(void) utime(spool_dir, NULL);		/* old poke method */
788 
789 	bzero(&s_un, sizeof(s_un));
790 	if (snprintf(s_un.sun_path, sizeof s_un.sun_path, "%s/%s",
791 	      SPOOL_DIR, CRONSOCK) >= sizeof(s_un.sun_path)) {
792 		fprintf(stderr, "%s: %s/%s: path too long\n",
793 		    ProgramName, SPOOL_DIR, CRONSOCK);
794 		return;
795 	}
796 	s_un.sun_family = AF_UNIX;
797 #ifdef SUN_LEN
798 	s_un.sun_len = SUN_LEN(&s_un);
799 #endif
800 	(void) signal(SIGPIPE, SIG_IGN);
801 	if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0 &&
802 	    connect(sock, (struct sockaddr *)&s_un, sizeof(s_un)) == 0)
803 		write(sock, &cookie, 1);
804 	else
805 		fprintf(stderr, "%s: warning, cron does not appear to be "
806 		    "running.\n", ProgramName);
807 	if (sock >= 0)
808 		close(sock);
809 	(void) signal(SIGPIPE, SIG_DFL);
810 }
811