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