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