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