xref: /netbsd-src/external/mpl/bind/dist/bin/named/os.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: os.c,v 1.4 2025/01/26 16:24:33 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 /*! \file */
17 #include <stdarg.h>
18 #include <stdbool.h>
19 #include <sys/resource.h>
20 #include <sys/stat.h>
21 #include <sys/types.h> /* dev_t FreeBSD 2.1 */
22 #ifdef HAVE_UNAME
23 #include <sys/utsname.h>
24 #endif /* ifdef HAVE_UNAME */
25 
26 #include <ctype.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <grp.h>
30 #include <pwd.h>
31 #include <signal.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <syslog.h>
35 #ifdef HAVE_TZSET
36 #include <time.h>
37 #endif /* ifdef HAVE_TZSET */
38 #include <unistd.h>
39 
40 #include <isc/buffer.h>
41 #include <isc/file.h>
42 #include <isc/result.h>
43 #include <isc/strerr.h>
44 #include <isc/string.h>
45 #include <isc/util.h>
46 
47 #include <named/globals.h>
48 #include <named/log.h>
49 #include <named/main.h>
50 #include <named/os.h>
51 #ifdef HAVE_LIBSCF
52 #include <named/smf_globals.h>
53 #endif /* ifdef HAVE_LIBSCF */
54 
55 static char *pidfile = NULL;
56 static int devnullfd = -1;
57 
58 #ifndef ISC_FACILITY
59 #define ISC_FACILITY LOG_DAEMON
60 #endif /* ifndef ISC_FACILITY */
61 
62 static struct passwd *runas_pw = NULL;
63 static bool done_setuid = false;
64 static int dfd[2] = { -1, -1 };
65 
66 static uid_t saved_uid = (uid_t)-1;
67 static gid_t saved_gid = (gid_t)-1;
68 
69 #if HAVE_LIBCAP
70 
71 static bool non_root = false;
72 static bool non_root_caps = false;
73 
74 #include <sys/capability.h>
75 #include <sys/prctl.h>
76 
77 static void
78 linux_setcaps(cap_t caps) {
79 	char strbuf[ISC_STRERRORSIZE];
80 
81 	if ((getuid() != 0 && !non_root_caps) || non_root) {
82 		return;
83 	}
84 	if (cap_set_proc(caps) < 0) {
85 		strerror_r(errno, strbuf, sizeof(strbuf));
86 		named_main_earlyfatal("cap_set_proc() failed: %s:"
87 				      " please ensure that the capset kernel"
88 				      " module is loaded.  see insmod(8)",
89 				      strbuf);
90 	}
91 }
92 
93 #define SET_CAP(flag)                                                         \
94 	do {                                                                  \
95 		cap_flag_value_t curval;                                      \
96 		capval = (flag);                                              \
97 		err = cap_get_flag(curcaps, capval, CAP_PERMITTED, &curval);  \
98 		if (err != -1 && curval) {                                    \
99 			err = cap_set_flag(caps, CAP_EFFECTIVE, 1, &capval,   \
100 					   CAP_SET);                          \
101 			if (err == -1) {                                      \
102 				strerror_r(errno, strbuf, sizeof(strbuf));    \
103 				named_main_earlyfatal("cap_set_proc failed: " \
104 						      "%s",                   \
105 						      strbuf);                \
106 			}                                                     \
107                                                                               \
108 			err = cap_set_flag(caps, CAP_PERMITTED, 1, &capval,   \
109 					   CAP_SET);                          \
110 			if (err == -1) {                                      \
111 				strerror_r(errno, strbuf, sizeof(strbuf));    \
112 				named_main_earlyfatal("cap_set_proc failed: " \
113 						      "%s",                   \
114 						      strbuf);                \
115 			}                                                     \
116 		}                                                             \
117 	} while (0)
118 #define INIT_CAP                                                              \
119 	do {                                                                  \
120 		caps = cap_init();                                            \
121 		if (caps == NULL) {                                           \
122 			strerror_r(errno, strbuf, sizeof(strbuf));            \
123 			named_main_earlyfatal("cap_init failed: %s", strbuf); \
124 		}                                                             \
125 		curcaps = cap_get_proc();                                     \
126 		if (curcaps == NULL) {                                        \
127 			strerror_r(errno, strbuf, sizeof(strbuf));            \
128 			named_main_earlyfatal("cap_get_proc failed: %s",      \
129 					      strbuf);                        \
130 		}                                                             \
131 	} while (0)
132 #define FREE_CAP                   \
133 	{                          \
134 		cap_free(caps);    \
135 		cap_free(curcaps); \
136 	}                          \
137 	while (0)
138 
139 static void
140 linux_initialprivs(void) {
141 	cap_t caps;
142 	cap_t curcaps;
143 	cap_value_t capval;
144 	char strbuf[ISC_STRERRORSIZE];
145 	int err;
146 
147 	/*%
148 	 * We don't need most privileges, so we drop them right away.
149 	 * Later on linux_minprivs() will be called, which will drop our
150 	 * capabilities to the minimum needed to run the server.
151 	 */
152 	INIT_CAP;
153 
154 	/*
155 	 * We need to be able to bind() to privileged ports, notably port 53!
156 	 */
157 	SET_CAP(CAP_NET_BIND_SERVICE);
158 
159 	/*
160 	 * We need chroot() initially too.
161 	 */
162 	SET_CAP(CAP_SYS_CHROOT);
163 
164 	/*
165 	 * We need setuid() as the kernel supports keeping capabilities after
166 	 * setuid().
167 	 */
168 	SET_CAP(CAP_SETUID);
169 
170 	/*
171 	 * Since we call initgroups, we need this.
172 	 */
173 	SET_CAP(CAP_SETGID);
174 
175 	/*
176 	 * Without this, we run into problems reading a configuration file
177 	 * owned by a non-root user and non-world-readable on startup.
178 	 */
179 	SET_CAP(CAP_DAC_READ_SEARCH);
180 
181 	/*
182 	 * XXX  We might want to add CAP_SYS_RESOURCE, though it's not
183 	 *      clear it would work right given the way linuxthreads work.
184 	 * XXXDCL But since we need to be able to set the maximum number
185 	 * of files, the stack size, data size, and core dump size to
186 	 * support named.conf options, this is now being added to test.
187 	 */
188 	SET_CAP(CAP_SYS_RESOURCE);
189 
190 	/*
191 	 * We need to be able to set the ownership of the containing
192 	 * directory of the pid file when we create it.
193 	 */
194 	SET_CAP(CAP_CHOWN);
195 
196 	linux_setcaps(caps);
197 
198 	FREE_CAP;
199 }
200 
201 static void
202 linux_minprivs(void) {
203 	cap_t caps;
204 	cap_t curcaps;
205 	cap_value_t capval;
206 	char strbuf[ISC_STRERRORSIZE];
207 	int err;
208 
209 	INIT_CAP;
210 	/*%
211 	 * Drop all privileges except the ability to bind() to privileged
212 	 * ports.
213 	 *
214 	 * It's important that we drop CAP_SYS_CHROOT.  If we didn't, it
215 	 * chroot() could be used to escape from the chrooted area.
216 	 */
217 
218 	SET_CAP(CAP_NET_BIND_SERVICE);
219 
220 	/*
221 	 * XXX  We might want to add CAP_SYS_RESOURCE, though it's not
222 	 *      clear it would work right given the way linuxthreads work.
223 	 * XXXDCL But since we need to be able to set the maximum number
224 	 * of files, the stack size, data size, and core dump size to
225 	 * support named.conf options, this is now being added to test.
226 	 */
227 	SET_CAP(CAP_SYS_RESOURCE);
228 
229 	linux_setcaps(caps);
230 
231 	FREE_CAP;
232 }
233 
234 static void
235 linux_keepcaps(void) {
236 	char strbuf[ISC_STRERRORSIZE];
237 	/*%
238 	 * Ask the kernel to allow us to keep our capabilities after we
239 	 * setuid().
240 	 */
241 
242 	if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
243 		if (errno != EINVAL) {
244 			strerror_r(errno, strbuf, sizeof(strbuf));
245 			named_main_earlyfatal("prctl() failed: %s", strbuf);
246 		}
247 	} else {
248 		non_root_caps = true;
249 		if (getuid() != 0) {
250 			non_root = true;
251 		}
252 	}
253 }
254 
255 #endif /* HAVE_LIBCAP */
256 
257 static void
258 setperms(uid_t uid, gid_t gid) {
259 	char strbuf[ISC_STRERRORSIZE];
260 
261 	/*
262 	 * Drop the gid privilege first, because in some cases the gid privilege
263 	 * cannot be dropped after the uid privilege has been dropped.
264 	 */
265 	if (setegid(gid) == -1) {
266 		strerror_r(errno, strbuf, sizeof(strbuf));
267 		named_main_earlywarning("unable to set effective gid to %d: %s",
268 					gid, strbuf);
269 	}
270 
271 	if (seteuid(uid) == -1) {
272 		strerror_r(errno, strbuf, sizeof(strbuf));
273 		named_main_earlywarning("unable to set effective uid to %d: %s",
274 					uid, strbuf);
275 	}
276 }
277 
278 static void
279 setup_syslog(const char *progname) {
280 	int options;
281 
282 	options = LOG_PID;
283 #ifdef LOG_NDELAY
284 	options |= LOG_NDELAY;
285 #endif /* ifdef LOG_NDELAY */
286 	openlog(isc_file_basename(progname), options, ISC_FACILITY);
287 }
288 
289 void
290 named_os_init(const char *progname) {
291 	setup_syslog(progname);
292 #if HAVE_LIBCAP
293 	linux_initialprivs();
294 #endif /* HAVE_LIBCAP */
295 #ifdef SIGXFSZ
296 	signal(SIGXFSZ, SIG_IGN);
297 #endif /* ifdef SIGXFSZ */
298 }
299 
300 void
301 named_os_daemonize(void) {
302 	pid_t pid;
303 	char strbuf[ISC_STRERRORSIZE];
304 
305 	if (pipe(dfd) == -1) {
306 		strerror_r(errno, strbuf, sizeof(strbuf));
307 		named_main_earlyfatal("pipe(): %s", strbuf);
308 	}
309 
310 	pid = fork();
311 	if (pid == -1) {
312 		strerror_r(errno, strbuf, sizeof(strbuf));
313 		named_main_earlyfatal("fork(): %s", strbuf);
314 	}
315 	if (pid != 0) {
316 		int n;
317 		/*
318 		 * Wait for the child to finish loading for the first time.
319 		 * This would be so much simpler if fork() worked once we
320 		 * were multi-threaded.
321 		 */
322 		(void)close(dfd[1]);
323 		do {
324 			char buf;
325 			n = read(dfd[0], &buf, 1);
326 			if (n == 1) {
327 				_exit(EXIT_SUCCESS);
328 			}
329 		} while (n == -1 && errno == EINTR);
330 		_exit(EXIT_FAILURE);
331 	}
332 	(void)close(dfd[0]);
333 
334 	/*
335 	 * We're the child.
336 	 */
337 
338 	if (setsid() == -1) {
339 		strerror_r(errno, strbuf, sizeof(strbuf));
340 		named_main_earlyfatal("setsid(): %s", strbuf);
341 	}
342 
343 	/*
344 	 * Try to set stdin, stdout, and stderr to /dev/null, but press
345 	 * on even if it fails.
346 	 *
347 	 * XXXMLG The close() calls here are unneeded on all but NetBSD, but
348 	 * are harmless to include everywhere.  dup2() is supposed to close
349 	 * the FD if it is in use, but unproven-pthreads-0.16 is broken
350 	 * and will end up closing the wrong FD.  This will be fixed eventually,
351 	 * and these calls will be removed.
352 	 */
353 	if (devnullfd != -1) {
354 		if (devnullfd != STDIN_FILENO) {
355 			(void)close(STDIN_FILENO);
356 			(void)dup2(devnullfd, STDIN_FILENO);
357 		}
358 		if (devnullfd != STDOUT_FILENO) {
359 			(void)close(STDOUT_FILENO);
360 			(void)dup2(devnullfd, STDOUT_FILENO);
361 		}
362 		if (devnullfd != STDERR_FILENO && !named_g_keepstderr) {
363 			(void)close(STDERR_FILENO);
364 			(void)dup2(devnullfd, STDERR_FILENO);
365 		}
366 	}
367 }
368 
369 void
370 named_os_started(void) {
371 	char buf = 0;
372 
373 	/*
374 	 * Signal to the parent that we started successfully.
375 	 */
376 	if (dfd[0] != -1 && dfd[1] != -1) {
377 		if (write(dfd[1], &buf, 1) != 1) {
378 			named_main_earlyfatal("unable to signal parent that we "
379 					      "otherwise started "
380 					      "successfully.");
381 		}
382 		close(dfd[1]);
383 		dfd[0] = dfd[1] = -1;
384 	}
385 }
386 
387 void
388 named_os_opendevnull(void) {
389 	devnullfd = open("/dev/null", O_RDWR, 0);
390 }
391 
392 void
393 named_os_closedevnull(void) {
394 	if (devnullfd != STDIN_FILENO && devnullfd != STDOUT_FILENO &&
395 	    devnullfd != STDERR_FILENO)
396 	{
397 		close(devnullfd);
398 		devnullfd = -1;
399 	}
400 }
401 
402 static bool
403 all_digits(const char *s) {
404 	if (*s == '\0') {
405 		return false;
406 	}
407 	while (*s != '\0') {
408 		if (!isdigit((unsigned char)(*s))) {
409 			return false;
410 		}
411 		s++;
412 	}
413 	return true;
414 }
415 
416 void
417 named_os_chroot(const char *root) {
418 	char strbuf[ISC_STRERRORSIZE];
419 #ifdef HAVE_LIBSCF
420 	named_smf_chroot = 0;
421 #endif /* ifdef HAVE_LIBSCF */
422 	if (root != NULL) {
423 #ifdef HAVE_CHROOT
424 		if (chroot(root) < 0) {
425 			strerror_r(errno, strbuf, sizeof(strbuf));
426 			named_main_earlyfatal("chroot(): %s", strbuf);
427 		}
428 #else  /* ifdef HAVE_CHROOT */
429 		named_main_earlyfatal("chroot(): disabled");
430 #endif /* ifdef HAVE_CHROOT */
431 		if (chdir("/") < 0) {
432 			strerror_r(errno, strbuf, sizeof(strbuf));
433 			named_main_earlyfatal("chdir(/): %s", strbuf);
434 		}
435 #ifdef HAVE_LIBSCF
436 		/* Set named_smf_chroot flag on successful chroot. */
437 		named_smf_chroot = 1;
438 #endif /* ifdef HAVE_LIBSCF */
439 	}
440 }
441 
442 void
443 named_os_inituserinfo(const char *username) {
444 	if (username == NULL) {
445 		return;
446 	}
447 
448 	if (all_digits(username)) {
449 		runas_pw = getpwuid((uid_t)atoi(username));
450 	} else {
451 		runas_pw = getpwnam(username);
452 	}
453 	endpwent();
454 
455 	if (runas_pw == NULL) {
456 		named_main_earlyfatal("user '%s' unknown", username);
457 	}
458 
459 	if (getuid() == 0) {
460 		char strbuf[ISC_STRERRORSIZE];
461 		if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) {
462 			strerror_r(errno, strbuf, sizeof(strbuf));
463 			named_main_earlyfatal("initgroups(): %s", strbuf);
464 		}
465 	}
466 }
467 
468 void
469 named_os_restoreuser(void) {
470 	if (runas_pw == NULL || done_setuid) {
471 		return;
472 	}
473 
474 	REQUIRE(saved_uid != (uid_t)-1);
475 	REQUIRE(saved_gid != (gid_t)-1);
476 
477 	setperms(saved_uid, saved_gid);
478 }
479 
480 void
481 named_os_changeuser(bool permanent) {
482 	char strbuf[ISC_STRERRORSIZE];
483 	if (runas_pw == NULL || done_setuid) {
484 		return;
485 	}
486 
487 	if (!permanent) {
488 		saved_uid = getuid();
489 		saved_gid = getgid();
490 
491 		setperms(runas_pw->pw_uid, runas_pw->pw_gid);
492 
493 		return;
494 	}
495 
496 	done_setuid = true;
497 
498 	if (setgid(runas_pw->pw_gid) == -1) {
499 		strerror_r(errno, strbuf, sizeof(strbuf));
500 		named_main_earlyfatal("setgid(): %s", strbuf);
501 	}
502 
503 	if (setuid(runas_pw->pw_uid) == -1) {
504 		strerror_r(errno, strbuf, sizeof(strbuf));
505 		named_main_earlyfatal("setuid(): %s", strbuf);
506 	}
507 
508 #if HAVE_LIBCAP
509 	/*
510 	 * Restore the ability of named to drop core after the setuid()
511 	 * call has disabled it.
512 	 */
513 	if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
514 		strerror_r(errno, strbuf, sizeof(strbuf));
515 		named_main_earlywarning("prctl(PR_SET_DUMPABLE) failed: %s",
516 					strbuf);
517 	}
518 
519 	linux_minprivs();
520 #endif /* HAVE_LIBCAP */
521 }
522 
523 uid_t
524 named_os_uid(void) {
525 	if (runas_pw == NULL) {
526 		return 0;
527 	}
528 	return runas_pw->pw_uid;
529 }
530 
531 void
532 named_os_adjustnofile(void) {
533 	int r;
534 	struct rlimit rl;
535 	rlim_t rlim_old;
536 	char strbuf[ISC_STRERRORSIZE];
537 
538 	r = getrlimit(RLIMIT_NOFILE, &rl);
539 	if (r != 0) {
540 		goto fail;
541 	}
542 
543 	rlim_old = rl.rlim_cur;
544 
545 	if (rl.rlim_cur == rl.rlim_max) {
546 		isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
547 			      NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
548 			      "the limit on open files is already at the "
549 			      "maximum allowed value: "
550 			      "%" PRIu64,
551 			      (uint64_t)rl.rlim_max);
552 		return;
553 	}
554 
555 	rl.rlim_cur = rl.rlim_max;
556 	r = setrlimit(RLIMIT_NOFILE, &rl);
557 	if (r != 0) {
558 		goto fail;
559 	}
560 
561 	isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
562 		      NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE,
563 		      "adjusted limit on open files from "
564 		      "%" PRIu64 " to "
565 		      "%" PRIu64,
566 		      (uint64_t)rlim_old, (uint64_t)rl.rlim_cur);
567 	return;
568 
569 fail:
570 	strerror_r(errno, strbuf, sizeof(strbuf));
571 	named_main_earlywarning("adjusting limit on open files failed: %s",
572 				strbuf);
573 	return;
574 }
575 
576 void
577 named_os_minprivs(void) {
578 #if HAVE_LIBCAP
579 	linux_keepcaps();
580 	named_os_changeuser(true);
581 	linux_minprivs();
582 #endif /* HAVE_LIBCAP */
583 }
584 
585 static int
586 safe_open(const char *filename, mode_t mode, bool append) {
587 	int fd;
588 	struct stat sb;
589 
590 	if (stat(filename, &sb) == -1) {
591 		if (errno != ENOENT) {
592 			return -1;
593 		}
594 	} else if ((sb.st_mode & S_IFREG) == 0) {
595 		errno = EOPNOTSUPP;
596 		return -1;
597 	}
598 
599 	if (append) {
600 		fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, mode);
601 	} else {
602 		if (unlink(filename) < 0 && errno != ENOENT) {
603 			return -1;
604 		}
605 		fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, mode);
606 	}
607 	return fd;
608 }
609 
610 static void
611 cleanup_pidfile(void) {
612 	int n;
613 	if (pidfile != NULL) {
614 		n = unlink(pidfile);
615 		if (n == -1 && errno != ENOENT) {
616 			named_main_earlywarning("unlink '%s': failed", pidfile);
617 		}
618 		free(pidfile);
619 	}
620 	pidfile = NULL;
621 }
622 
623 /*
624  * Ensure that a directory exists.
625  * NOTE: This function overwrites the '/' characters in 'filename' with
626  * nulls. The caller should copy the filename to a fresh buffer first.
627  */
628 static int
629 mkdirpath(char *filename, void (*report)(const char *, ...)) {
630 	char *slash = strrchr(filename, '/');
631 	char strbuf[ISC_STRERRORSIZE];
632 	unsigned int mode;
633 
634 	if (slash != NULL && slash != filename) {
635 		struct stat sb;
636 		*slash = '\0';
637 
638 		if (stat(filename, &sb) == -1) {
639 			if (errno != ENOENT) {
640 				strerror_r(errno, strbuf, sizeof(strbuf));
641 				(*report)("couldn't stat '%s': %s", filename,
642 					  strbuf);
643 				goto error;
644 			}
645 			if (mkdirpath(filename, report) == -1) {
646 				goto error;
647 			}
648 			/*
649 			 * Handle "//", "/./" and "/../" in path.
650 			 */
651 			if (!strcmp(slash + 1, "") || !strcmp(slash + 1, ".") ||
652 			    !strcmp(slash + 1, ".."))
653 			{
654 				*slash = '/';
655 				return 0;
656 			}
657 			mode = S_IRUSR | S_IWUSR | S_IXUSR; /* u=rwx */
658 			mode |= S_IRGRP | S_IXGRP;	    /* g=rx */
659 			mode |= S_IROTH | S_IXOTH;	    /* o=rx */
660 			if (mkdir(filename, mode) == -1) {
661 				strerror_r(errno, strbuf, sizeof(strbuf));
662 				(*report)("couldn't mkdir '%s': %s", filename,
663 					  strbuf);
664 				goto error;
665 			}
666 			if (runas_pw != NULL &&
667 			    chown(filename, runas_pw->pw_uid,
668 				  runas_pw->pw_gid) == -1)
669 			{
670 				strerror_r(errno, strbuf, sizeof(strbuf));
671 				(*report)("couldn't chown '%s': %s", filename,
672 					  strbuf);
673 			}
674 		}
675 		*slash = '/';
676 	}
677 	return 0;
678 
679 error:
680 	*slash = '/';
681 	return -1;
682 }
683 
684 FILE *
685 named_os_openfile(const char *filename, mode_t mode, bool switch_user) {
686 	char strbuf[ISC_STRERRORSIZE], *f;
687 	FILE *fp;
688 	int fd;
689 
690 	/*
691 	 * Make the containing directory if it doesn't exist.
692 	 */
693 	f = strdup(filename);
694 	if (f == NULL) {
695 		strerror_r(errno, strbuf, sizeof(strbuf));
696 		named_main_earlywarning("couldn't strdup() '%s': %s", filename,
697 					strbuf);
698 		return NULL;
699 	}
700 	if (mkdirpath(f, named_main_earlywarning) == -1) {
701 		free(f);
702 		return NULL;
703 	}
704 	free(f);
705 
706 	if (switch_user && runas_pw != NULL) {
707 		/*
708 		 * Temporarily set UID/GID to the one we'll be running with
709 		 * eventually.
710 		 */
711 		named_os_changeuser(false);
712 
713 		fd = safe_open(filename, mode, false);
714 
715 		/* Restore UID/GID to previous uid/gid */
716 		named_os_restoreuser();
717 
718 		if (fd == -1) {
719 			fd = safe_open(filename, mode, false);
720 			if (fd != -1) {
721 				named_main_earlywarning("Required root "
722 							"permissions to open "
723 							"'%s'.",
724 							filename);
725 			} else {
726 				named_main_earlywarning("Could not open "
727 							"'%s'.",
728 							filename);
729 			}
730 			named_main_earlywarning("Please check file and "
731 						"directory permissions "
732 						"or reconfigure the filename.");
733 		}
734 	} else {
735 		fd = safe_open(filename, mode, false);
736 	}
737 
738 	if (fd < 0) {
739 		strerror_r(errno, strbuf, sizeof(strbuf));
740 		named_main_earlywarning("could not open file '%s': %s",
741 					filename, strbuf);
742 		return NULL;
743 	}
744 
745 	fp = fdopen(fd, "w");
746 	if (fp == NULL) {
747 		strerror_r(errno, strbuf, sizeof(strbuf));
748 		named_main_earlywarning("could not fdopen() file '%s': %s",
749 					filename, strbuf);
750 	}
751 
752 	return fp;
753 }
754 
755 void
756 named_os_writepidfile(const char *filename, bool first_time) {
757 	FILE *fh;
758 	pid_t pid;
759 	char strbuf[ISC_STRERRORSIZE];
760 	void (*report)(const char *, ...);
761 
762 	/*
763 	 * The caller must ensure any required synchronization.
764 	 */
765 
766 	report = first_time ? named_main_earlyfatal : named_main_earlywarning;
767 
768 	cleanup_pidfile();
769 
770 	if (filename == NULL) {
771 		return;
772 	}
773 
774 	pidfile = strdup(filename);
775 	if (pidfile == NULL) {
776 		strerror_r(errno, strbuf, sizeof(strbuf));
777 		(*report)("couldn't strdup() '%s': %s", filename, strbuf);
778 		return;
779 	}
780 
781 	fh = named_os_openfile(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH,
782 			       first_time);
783 	if (fh == NULL) {
784 		cleanup_pidfile();
785 		return;
786 	}
787 	pid = getpid();
788 	if (fprintf(fh, "%ld\n", (long)pid) < 0) {
789 		(*report)("fprintf() to pid file '%s' failed", filename);
790 		(void)fclose(fh);
791 		cleanup_pidfile();
792 		return;
793 	}
794 	if (fflush(fh) == EOF) {
795 		(*report)("fflush() to pid file '%s' failed", filename);
796 		(void)fclose(fh);
797 		cleanup_pidfile();
798 		return;
799 	}
800 	(void)fclose(fh);
801 }
802 
803 void
804 named_os_shutdown(void) {
805 	closelog();
806 	cleanup_pidfile();
807 }
808 
809 void
810 named_os_shutdownmsg(char *command, isc_buffer_t *text) {
811 	char *last, *ptr;
812 	pid_t pid;
813 
814 	/* Skip the command name. */
815 	if (strtok_r(command, " \t", &last) == NULL) {
816 		return;
817 	}
818 
819 	if ((ptr = strtok_r(NULL, " \t", &last)) == NULL) {
820 		return;
821 	}
822 
823 	if (strcmp(ptr, "-p") != 0) {
824 		return;
825 	}
826 
827 	pid = getpid();
828 
829 	(void)isc_buffer_printf(text, "pid: %ld", (long)pid);
830 }
831 
832 void
833 named_os_tzset(void) {
834 #ifdef HAVE_TZSET
835 	tzset();
836 #endif /* ifdef HAVE_TZSET */
837 }
838 
839 #ifdef HAVE_UNAME
840 static char unamebuf[sizeof(struct utsname)];
841 #else
842 static const char unamebuf[] = { "unknown architecture" };
843 #endif
844 static const char *unamep = NULL;
845 
846 static void
847 getuname(void) {
848 #ifdef HAVE_UNAME
849 	struct utsname uts;
850 
851 	memset(&uts, 0, sizeof(uts));
852 	if (uname(&uts) < 0) {
853 		snprintf(unamebuf, sizeof(unamebuf), "unknown architecture");
854 		return;
855 	}
856 
857 	snprintf(unamebuf, sizeof(unamebuf), "%s %s %s %s", uts.sysname,
858 		 uts.machine, uts.release, uts.version);
859 #endif /* ifdef HAVE_UNAME */
860 	unamep = unamebuf;
861 }
862 
863 const char *
864 named_os_uname(void) {
865 	if (unamep == NULL) {
866 		getuname();
867 	}
868 	return unamep;
869 }
870