os.c revision 193149
1/*
2 * Copyright (C) 2004-2009  Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2002  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* $Id: os.c,v 1.89.12.5 2009/03/02 03:03:54 marka Exp $ */
19
20/*! \file */
21
22#include <config.h>
23#include <stdarg.h>
24
25#include <sys/types.h>	/* dev_t FreeBSD 2.1 */
26#include <sys/stat.h>
27
28#include <ctype.h>
29#include <errno.h>
30#include <fcntl.h>
31#include <grp.h>		/* Required for initgroups() on IRIX. */
32#include <pwd.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <signal.h>
36#include <syslog.h>
37#ifdef HAVE_TZSET
38#include <time.h>
39#endif
40#include <unistd.h>
41
42#include <isc/buffer.h>
43#include <isc/file.h>
44#include <isc/print.h>
45#include <isc/resource.h>
46#include <isc/result.h>
47#include <isc/strerror.h>
48#include <isc/string.h>
49
50#include <named/main.h>
51#include <named/os.h>
52#ifdef HAVE_LIBSCF
53#include <named/ns_smf_globals.h>
54#endif
55
56static char *pidfile = NULL;
57static int devnullfd = -1;
58
59#ifndef ISC_FACILITY
60#define ISC_FACILITY LOG_DAEMON
61#endif
62
63/*
64 * If there's no <linux/capability.h>, we don't care about <sys/prctl.h>
65 */
66#ifndef HAVE_LINUX_CAPABILITY_H
67#undef HAVE_SYS_PRCTL_H
68#endif
69
70/*
71 * Linux defines:
72 *	(T) HAVE_LINUXTHREADS
73 *	(C) HAVE_SYS_CAPABILITY_H (or HAVE_LINUX_CAPABILITY_H)
74 *	(P) HAVE_SYS_PRCTL_H
75 * The possible cases are:
76 *	none:	setuid() normally
77 *	T:	no setuid()
78 *	C:	setuid() normally, drop caps (keep CAP_SETUID)
79 *	T+C:	no setuid(), drop caps (don't keep CAP_SETUID)
80 *	T+C+P:	setuid() early, drop caps (keep CAP_SETUID)
81 *	C+P:	setuid() normally, drop caps (keep CAP_SETUID)
82 *	P:	not possible
83 *	T+P:	not possible
84 *
85 * if (C)
86 *	caps = BIND_SERVICE + CHROOT + SETGID
87 *	if ((T && C && P) || !T)
88 *		caps += SETUID
89 *	endif
90 *	capset(caps)
91 * endif
92 * if (T && C && P && -u)
93 *	setuid()
94 * else if (T && -u)
95 *	fail
96 * --> start threads
97 * if (!T && -u)
98 *	setuid()
99 * if (C && (P || !-u))
100 *	caps = BIND_SERVICE
101 *	capset(caps)
102 * endif
103 *
104 * It will be nice when Linux threads work properly with setuid().
105 */
106
107#ifdef HAVE_LINUXTHREADS
108static pid_t mainpid = 0;
109#endif
110
111static struct passwd *runas_pw = NULL;
112static isc_boolean_t done_setuid = ISC_FALSE;
113static int dfd[2] = { -1, -1 };
114
115#ifdef HAVE_LINUX_CAPABILITY_H
116
117static isc_boolean_t non_root = ISC_FALSE;
118static isc_boolean_t non_root_caps = ISC_FALSE;
119
120#ifdef HAVE_SYS_CAPABILITY_H
121#include <sys/capability.h>
122#else
123/*%
124 * We define _LINUX_FS_H to prevent it from being included.  We don't need
125 * anything from it, and the files it includes cause warnings with 2.2
126 * kernels, and compilation failures (due to conflicts between <linux/string.h>
127 * and <string.h>) on 2.3 kernels.
128 */
129#define _LINUX_FS_H
130#include <linux/capability.h>
131#include <syscall.h>
132#ifndef SYS_capset
133#ifndef __NR_capset
134#include <asm/unistd.h> /* Slackware 4.0 needs this. */
135#endif /* __NR_capset */
136#define SYS_capset __NR_capset
137#endif /* SYS_capset */
138#endif /* HAVE_SYS_CAPABILITY_H */
139
140#ifdef HAVE_SYS_PRCTL_H
141#include <sys/prctl.h>		/* Required for prctl(). */
142
143/*
144 * If the value of PR_SET_KEEPCAPS is not in <sys/prctl.h>, define it
145 * here.  This allows setuid() to work on systems running a new enough
146 * kernel but with /usr/include/linux pointing to "standard" kernel
147 * headers.
148 */
149#ifndef PR_SET_KEEPCAPS
150#define PR_SET_KEEPCAPS 8
151#endif
152
153#endif /* HAVE_SYS_PRCTL_H */
154
155#ifdef HAVE_LIBCAP
156#define SETCAPS_FUNC "cap_set_proc "
157#else
158typedef unsigned int cap_t;
159#define SETCAPS_FUNC "syscall(capset) "
160#endif /* HAVE_LIBCAP */
161
162static void
163linux_setcaps(cap_t caps) {
164#ifndef HAVE_LIBCAP
165	struct __user_cap_header_struct caphead;
166	struct __user_cap_data_struct cap;
167#endif
168	char strbuf[ISC_STRERRORSIZE];
169
170	if ((getuid() != 0 && !non_root_caps) || non_root)
171		return;
172#ifndef HAVE_LIBCAP
173	memset(&caphead, 0, sizeof(caphead));
174	caphead.version = _LINUX_CAPABILITY_VERSION;
175	caphead.pid = 0;
176	memset(&cap, 0, sizeof(cap));
177	cap.effective = caps;
178	cap.permitted = caps;
179	cap.inheritable = 0;
180#endif
181#ifdef HAVE_LIBCAP
182	if (cap_set_proc(caps) < 0) {
183#else
184	if (syscall(SYS_capset, &caphead, &cap) < 0) {
185#endif
186		isc__strerror(errno, strbuf, sizeof(strbuf));
187		ns_main_earlyfatal(SETCAPS_FUNC "failed: %s:"
188				   " please ensure that the capset kernel"
189				   " module is loaded.  see insmod(8)",
190				   strbuf);
191	}
192}
193
194#ifdef HAVE_LIBCAP
195#define SET_CAP(flag) \
196	do { \
197		capval = (flag); \
198		cap_flag_value_t curval; \
199		err = cap_get_flag(curcaps, capval, CAP_PERMITTED, &curval); \
200		if (err != -1 && curval) { \
201			err = cap_set_flag(caps, CAP_EFFECTIVE, 1, &capval, CAP_SET); \
202			if (err == -1) { \
203				isc__strerror(errno, strbuf, sizeof(strbuf)); \
204				ns_main_earlyfatal("cap_set_proc failed: %s", strbuf); \
205			} \
206			\
207			err = cap_set_flag(caps, CAP_PERMITTED, 1, &capval, CAP_SET); \
208			if (err == -1) { \
209				isc__strerror(errno, strbuf, sizeof(strbuf)); \
210				ns_main_earlyfatal("cap_set_proc failed: %s", strbuf); \
211			} \
212		} \
213	} while (0)
214#define INIT_CAP \
215	do { \
216		caps = cap_init(); \
217		if (caps == NULL) { \
218			isc__strerror(errno, strbuf, sizeof(strbuf)); \
219			ns_main_earlyfatal("cap_init failed: %s", strbuf); \
220		} \
221		curcaps = cap_get_proc(); \
222		if (curcaps == NULL) { \
223			isc__strerror(errno, strbuf, sizeof(strbuf)); \
224			ns_main_earlyfatal("cap_get_proc failed: %s", strbuf); \
225		} \
226	} while (0)
227#define FREE_CAP \
228	{ \
229		cap_free(caps); \
230		cap_free(curcaps); \
231	} while (0)
232#else
233#define SET_CAP(flag) do { caps |= (1 << (flag)); } while (0)
234#define INIT_CAP do { caps = 0; } while (0)
235#endif /* HAVE_LIBCAP */
236
237static void
238linux_initialprivs(void) {
239	cap_t caps;
240#ifdef HAVE_LIBCAP
241	cap_t curcaps;
242	cap_value_t capval;
243	char strbuf[ISC_STRERRORSIZE];
244	int err;
245#endif
246
247	/*%
248	 * We don't need most privileges, so we drop them right away.
249	 * Later on linux_minprivs() will be called, which will drop our
250	 * capabilities to the minimum needed to run the server.
251	 */
252	INIT_CAP;
253
254	/*
255	 * We need to be able to bind() to privileged ports, notably port 53!
256	 */
257	SET_CAP(CAP_NET_BIND_SERVICE);
258
259	/*
260	 * We need chroot() initially too.
261	 */
262	SET_CAP(CAP_SYS_CHROOT);
263
264#if defined(HAVE_SYS_PRCTL_H) || !defined(HAVE_LINUXTHREADS)
265	/*
266	 * We can setuid() only if either the kernel supports keeping
267	 * capabilities after setuid() (which we don't know until we've
268	 * tried) or we're not using threads.  If either of these is
269	 * true, we want the setuid capability.
270	 */
271	SET_CAP(CAP_SETUID);
272#endif
273
274	/*
275	 * Since we call initgroups, we need this.
276	 */
277	SET_CAP(CAP_SETGID);
278
279	/*
280	 * Without this, we run into problems reading a configuration file
281	 * owned by a non-root user and non-world-readable on startup.
282	 */
283	SET_CAP(CAP_DAC_READ_SEARCH);
284
285	/*
286	 * XXX  We might want to add CAP_SYS_RESOURCE, though it's not
287	 *      clear it would work right given the way linuxthreads work.
288	 * XXXDCL But since we need to be able to set the maximum number
289	 * of files, the stack size, data size, and core dump size to
290	 * support named.conf options, this is now being added to test.
291	 */
292	SET_CAP(CAP_SYS_RESOURCE);
293
294	linux_setcaps(caps);
295
296#ifdef HAVE_LIBCAP
297	FREE_CAP;
298#endif
299}
300
301static void
302linux_minprivs(void) {
303	cap_t caps;
304#ifdef HAVE_LIBCAP
305	cap_t curcaps;
306	cap_value_t capval;
307	char strbuf[ISC_STRERRORSIZE];
308	int err;
309#endif
310
311	INIT_CAP;
312	/*%
313	 * Drop all privileges except the ability to bind() to privileged
314	 * ports.
315	 *
316	 * It's important that we drop CAP_SYS_CHROOT.  If we didn't, it
317	 * chroot() could be used to escape from the chrooted area.
318	 */
319
320	SET_CAP(CAP_NET_BIND_SERVICE);
321
322	/*
323	 * XXX  We might want to add CAP_SYS_RESOURCE, though it's not
324	 *      clear it would work right given the way linuxthreads work.
325	 * XXXDCL But since we need to be able to set the maximum number
326	 * of files, the stack size, data size, and core dump size to
327	 * support named.conf options, this is now being added to test.
328	 */
329	SET_CAP(CAP_SYS_RESOURCE);
330
331	linux_setcaps(caps);
332
333#ifdef HAVE_LIBCAP
334	FREE_CAP;
335#endif
336}
337
338#ifdef HAVE_SYS_PRCTL_H
339static void
340linux_keepcaps(void) {
341	char strbuf[ISC_STRERRORSIZE];
342	/*%
343	 * Ask the kernel to allow us to keep our capabilities after we
344	 * setuid().
345	 */
346
347	if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
348		if (errno != EINVAL) {
349			isc__strerror(errno, strbuf, sizeof(strbuf));
350			ns_main_earlyfatal("prctl() failed: %s", strbuf);
351		}
352	} else {
353		non_root_caps = ISC_TRUE;
354		if (getuid() != 0)
355			non_root = ISC_TRUE;
356	}
357}
358#endif
359
360#endif	/* HAVE_LINUX_CAPABILITY_H */
361
362
363static void
364setup_syslog(const char *progname) {
365	int options;
366
367	options = LOG_PID;
368#ifdef LOG_NDELAY
369	options |= LOG_NDELAY;
370#endif
371	openlog(isc_file_basename(progname), options, ISC_FACILITY);
372}
373
374void
375ns_os_init(const char *progname) {
376	setup_syslog(progname);
377#ifdef HAVE_LINUX_CAPABILITY_H
378	linux_initialprivs();
379#endif
380#ifdef HAVE_LINUXTHREADS
381	mainpid = getpid();
382#endif
383#ifdef SIGXFSZ
384	signal(SIGXFSZ, SIG_IGN);
385#endif
386}
387
388void
389ns_os_daemonize(void) {
390	pid_t pid;
391	char strbuf[ISC_STRERRORSIZE];
392
393	if (pipe(dfd) == -1) {
394		isc__strerror(errno, strbuf, sizeof(strbuf));
395		ns_main_earlyfatal("pipe(): %s", strbuf);
396	}
397
398	pid = fork();
399	if (pid == -1) {
400		isc__strerror(errno, strbuf, sizeof(strbuf));
401		ns_main_earlyfatal("fork(): %s", strbuf);
402	}
403	if (pid != 0) {
404		int n;
405		/*
406		 * Wait for the child to finish loading for the first time.
407		 * This would be so much simpler if fork() worked once we
408		 * were multi-threaded.
409		 */
410		(void)close(dfd[1]);
411		do {
412			char buf;
413			n = read(dfd[0], &buf, 1);
414			if (n == 1)
415				_exit(0);
416		} while (n == -1 && errno == EINTR);
417		_exit(1);
418	}
419	(void)close(dfd[0]);
420
421	/*
422	 * We're the child.
423	 */
424
425#ifdef HAVE_LINUXTHREADS
426	mainpid = getpid();
427#endif
428
429	if (setsid() == -1) {
430		isc__strerror(errno, strbuf, sizeof(strbuf));
431		ns_main_earlyfatal("setsid(): %s", strbuf);
432	}
433
434	/*
435	 * Try to set stdin, stdout, and stderr to /dev/null, but press
436	 * on even if it fails.
437	 *
438	 * XXXMLG The close() calls here are unneeded on all but NetBSD, but
439	 * are harmless to include everywhere.  dup2() is supposed to close
440	 * the FD if it is in use, but unproven-pthreads-0.16 is broken
441	 * and will end up closing the wrong FD.  This will be fixed eventually,
442	 * and these calls will be removed.
443	 */
444	if (devnullfd != -1) {
445		if (devnullfd != STDIN_FILENO) {
446			(void)close(STDIN_FILENO);
447			(void)dup2(devnullfd, STDIN_FILENO);
448		}
449		if (devnullfd != STDOUT_FILENO) {
450			(void)close(STDOUT_FILENO);
451			(void)dup2(devnullfd, STDOUT_FILENO);
452		}
453		if (devnullfd != STDERR_FILENO) {
454			(void)close(STDERR_FILENO);
455			(void)dup2(devnullfd, STDERR_FILENO);
456		}
457	}
458}
459
460void
461ns_os_started(void) {
462	char buf = 0;
463
464	/*
465	 * Signal to the parent that we started successfully.
466	 */
467	if (dfd[0] != -1 && dfd[1] != -1) {
468		if (write(dfd[1], &buf, 1) != 1)
469			ns_main_earlyfatal("unable to signal parent that we "
470					   "otherwise started successfully.");
471		close(dfd[1]);
472		dfd[0] = dfd[1] = -1;
473	}
474}
475
476void
477ns_os_opendevnull(void) {
478	devnullfd = open("/dev/null", O_RDWR, 0);
479}
480
481void
482ns_os_closedevnull(void) {
483	if (devnullfd != STDIN_FILENO &&
484	    devnullfd != STDOUT_FILENO &&
485	    devnullfd != STDERR_FILENO) {
486		close(devnullfd);
487		devnullfd = -1;
488	}
489}
490
491static isc_boolean_t
492all_digits(const char *s) {
493	if (*s == '\0')
494		return (ISC_FALSE);
495	while (*s != '\0') {
496		if (!isdigit((*s)&0xff))
497			return (ISC_FALSE);
498		s++;
499	}
500	return (ISC_TRUE);
501}
502
503void
504ns_os_chroot(const char *root) {
505	char strbuf[ISC_STRERRORSIZE];
506#ifdef HAVE_LIBSCF
507	ns_smf_chroot = 0;
508#endif
509	if (root != NULL) {
510#ifdef HAVE_CHROOT
511		if (chroot(root) < 0) {
512			isc__strerror(errno, strbuf, sizeof(strbuf));
513			ns_main_earlyfatal("chroot(): %s", strbuf);
514		}
515#else
516		ns_main_earlyfatal("chroot(): disabled");
517#endif
518		if (chdir("/") < 0) {
519			isc__strerror(errno, strbuf, sizeof(strbuf));
520			ns_main_earlyfatal("chdir(/): %s", strbuf);
521		}
522#ifdef HAVE_LIBSCF
523		/* Set ns_smf_chroot flag on successful chroot. */
524		ns_smf_chroot = 1;
525#endif
526	}
527}
528
529void
530ns_os_inituserinfo(const char *username) {
531	char strbuf[ISC_STRERRORSIZE];
532	if (username == NULL)
533		return;
534
535	if (all_digits(username))
536		runas_pw = getpwuid((uid_t)atoi(username));
537	else
538		runas_pw = getpwnam(username);
539	endpwent();
540
541	if (runas_pw == NULL)
542		ns_main_earlyfatal("user '%s' unknown", username);
543
544	if (getuid() == 0) {
545		if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) {
546			isc__strerror(errno, strbuf, sizeof(strbuf));
547			ns_main_earlyfatal("initgroups(): %s", strbuf);
548		}
549	}
550
551}
552
553void
554ns_os_changeuser(void) {
555	char strbuf[ISC_STRERRORSIZE];
556	if (runas_pw == NULL || done_setuid)
557		return;
558
559	done_setuid = ISC_TRUE;
560
561#ifdef HAVE_LINUXTHREADS
562#ifdef HAVE_LINUX_CAPABILITY_H
563	if (!non_root_caps)
564		ns_main_earlyfatal("-u with Linux threads not supported: "
565				   "requires kernel support for "
566				   "prctl(PR_SET_KEEPCAPS)");
567#else
568	ns_main_earlyfatal("-u with Linux threads not supported: "
569			   "no capabilities support or capabilities "
570			   "disabled at build time");
571#endif
572#endif
573
574	if (setgid(runas_pw->pw_gid) < 0) {
575		isc__strerror(errno, strbuf, sizeof(strbuf));
576		ns_main_earlyfatal("setgid(): %s", strbuf);
577	}
578
579	if (setuid(runas_pw->pw_uid) < 0) {
580		isc__strerror(errno, strbuf, sizeof(strbuf));
581		ns_main_earlyfatal("setuid(): %s", strbuf);
582	}
583
584#if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE)
585	/*
586	 * Restore the ability of named to drop core after the setuid()
587	 * call has disabled it.
588	 */
589	if (prctl(PR_SET_DUMPABLE,1,0,0,0) < 0) {
590		isc__strerror(errno, strbuf, sizeof(strbuf));
591		ns_main_earlywarning("prctl(PR_SET_DUMPABLE) failed: %s",
592				     strbuf);
593	}
594#endif
595#if defined(HAVE_LINUX_CAPABILITY_H) && !defined(HAVE_LINUXTHREADS)
596	linux_minprivs();
597#endif
598}
599
600void
601ns_os_adjustnofile() {
602#ifdef HAVE_LINUXTHREADS
603	isc_result_t result;
604	isc_resourcevalue_t newvalue;
605
606	/*
607	 * Linux: max number of open files specified by one thread doesn't seem
608	 * to apply to other threads on Linux.
609	 */
610	newvalue = ISC_RESOURCE_UNLIMITED;
611
612	result = isc_resource_setlimit(isc_resource_openfiles, newvalue);
613	if (result != ISC_R_SUCCESS)
614		ns_main_earlywarning("couldn't adjust limit on open files");
615#endif
616}
617
618void
619ns_os_minprivs(void) {
620#ifdef HAVE_SYS_PRCTL_H
621	linux_keepcaps();
622#endif
623
624#ifdef HAVE_LINUXTHREADS
625	ns_os_changeuser(); /* Call setuid() before threads are started */
626#endif
627
628#if defined(HAVE_LINUX_CAPABILITY_H) && defined(HAVE_LINUXTHREADS)
629	linux_minprivs();
630#endif
631}
632
633static int
634safe_open(const char *filename, isc_boolean_t append) {
635	int fd;
636	struct stat sb;
637
638	if (stat(filename, &sb) == -1) {
639		if (errno != ENOENT)
640			return (-1);
641	} else if ((sb.st_mode & S_IFREG) == 0) {
642		errno = EOPNOTSUPP;
643		return (-1);
644	}
645
646	if (append)
647		fd = open(filename, O_WRONLY|O_CREAT|O_APPEND,
648			  S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
649	else {
650		if (unlink(filename) < 0 && errno != ENOENT)
651			return (-1);
652		fd = open(filename, O_WRONLY|O_CREAT|O_EXCL,
653			  S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
654	}
655	return (fd);
656}
657
658static void
659cleanup_pidfile(void) {
660	int n;
661	if (pidfile != NULL) {
662		n = unlink(pidfile);
663		if (n == -1 && errno != ENOENT)
664			ns_main_earlywarning("unlink '%s': failed", pidfile);
665		free(pidfile);
666	}
667	pidfile = NULL;
668}
669
670static int
671mkdirpath(char *filename, void (*report)(const char *, ...)) {
672	char *slash = strrchr(filename, '/');
673	char strbuf[ISC_STRERRORSIZE];
674	unsigned int mode;
675
676	if (slash != NULL && slash != filename) {
677		struct stat sb;
678		*slash = '\0';
679
680		if (stat(filename, &sb) == -1) {
681			if (errno != ENOENT) {
682				isc__strerror(errno, strbuf, sizeof(strbuf));
683				(*report)("couldn't stat '%s': %s", filename,
684					  strbuf);
685				goto error;
686			}
687			if (mkdirpath(filename, report) == -1)
688				goto error;
689			mode = S_IRUSR | S_IWUSR | S_IXUSR;	/* u=rwx */
690			mode |= S_IRGRP | S_IXGRP;		/* g=rx */
691			mode |= S_IROTH | S_IXOTH;		/* o=rx */
692			if (mkdir(filename, mode) == -1) {
693				isc__strerror(errno, strbuf, sizeof(strbuf));
694				(*report)("couldn't mkdir '%s': %s", filename,
695					  strbuf);
696				goto error;
697			}
698		}
699		*slash = '/';
700	}
701	return (0);
702
703 error:
704	*slash = '/';
705	return (-1);
706}
707
708void
709ns_os_writepidfile(const char *filename, isc_boolean_t first_time) {
710	int fd;
711	FILE *lockfile;
712	size_t len;
713	pid_t pid;
714	char strbuf[ISC_STRERRORSIZE];
715	void (*report)(const char *, ...);
716
717	/*
718	 * The caller must ensure any required synchronization.
719	 */
720
721	report = first_time ? ns_main_earlyfatal : ns_main_earlywarning;
722
723	cleanup_pidfile();
724
725	if (filename == NULL)
726		return;
727
728	len = strlen(filename);
729	pidfile = malloc(len + 1);
730	if (pidfile == NULL) {
731		isc__strerror(errno, strbuf, sizeof(strbuf));
732		(*report)("couldn't malloc '%s': %s", filename, strbuf);
733		return;
734	}
735
736	/* This is safe. */
737	strcpy(pidfile, filename);
738
739	/*
740	 * Make the containing directory if it doesn't exist.
741	 */
742	if (mkdirpath(pidfile, report) == -1) {
743		free(pidfile);
744		pidfile = NULL;
745		return;
746	}
747
748	fd = safe_open(filename, ISC_FALSE);
749	if (fd < 0) {
750		isc__strerror(errno, strbuf, sizeof(strbuf));
751		(*report)("couldn't open pid file '%s': %s", filename, strbuf);
752		free(pidfile);
753		pidfile = NULL;
754		return;
755	}
756	lockfile = fdopen(fd, "w");
757	if (lockfile == NULL) {
758		isc__strerror(errno, strbuf, sizeof(strbuf));
759		(*report)("could not fdopen() pid file '%s': %s",
760			  filename, strbuf);
761		(void)close(fd);
762		cleanup_pidfile();
763		return;
764	}
765#ifdef HAVE_LINUXTHREADS
766	pid = mainpid;
767#else
768	pid = getpid();
769#endif
770	if (fprintf(lockfile, "%ld\n", (long)pid) < 0) {
771		(*report)("fprintf() to pid file '%s' failed", filename);
772		(void)fclose(lockfile);
773		cleanup_pidfile();
774		return;
775	}
776	if (fflush(lockfile) == EOF) {
777		(*report)("fflush() to pid file '%s' failed", filename);
778		(void)fclose(lockfile);
779		cleanup_pidfile();
780		return;
781	}
782	(void)fclose(lockfile);
783}
784
785void
786ns_os_shutdown(void) {
787	closelog();
788	cleanup_pidfile();
789}
790
791isc_result_t
792ns_os_gethostname(char *buf, size_t len) {
793	int n;
794
795	n = gethostname(buf, len);
796	return ((n == 0) ? ISC_R_SUCCESS : ISC_R_FAILURE);
797}
798
799static char *
800next_token(char **stringp, const char *delim) {
801	char *res;
802
803	do {
804		res = strsep(stringp, delim);
805		if (res == NULL)
806			break;
807	} while (*res == '\0');
808	return (res);
809}
810
811void
812ns_os_shutdownmsg(char *command, isc_buffer_t *text) {
813	char *input, *ptr;
814	unsigned int n;
815	pid_t pid;
816
817	input = command;
818
819	/* Skip the command name. */
820	ptr = next_token(&input, " \t");
821	if (ptr == NULL)
822		return;
823
824	ptr = next_token(&input, " \t");
825	if (ptr == NULL)
826		return;
827
828	if (strcmp(ptr, "-p") != 0)
829		return;
830
831#ifdef HAVE_LINUXTHREADS
832	pid = mainpid;
833#else
834	pid = getpid();
835#endif
836
837	n = snprintf((char *)isc_buffer_used(text),
838		     isc_buffer_availablelength(text),
839		     "pid: %ld", (long)pid);
840	/* Only send a message if it is complete. */
841	if (n < isc_buffer_availablelength(text))
842		isc_buffer_add(text, n);
843}
844
845void
846ns_os_tzset(void) {
847#ifdef HAVE_TZSET
848	tzset();
849#endif
850}
851