darlingserver.cpp
1 /** 2 * This file is part of Darling. 3 * 4 * Copyright (C) 2021 Darling developers 5 * 6 * Darling is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * Darling is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #define _GNU_SOURCE 1 21 #include <stdio.h> 22 #include <unistd.h> 23 #include <sched.h> 24 #include <stdlib.h> 25 #include <sys/mount.h> 26 #include <errno.h> 27 #include <string.h> 28 #include <stdbool.h> 29 #include <sys/prctl.h> 30 #include <dirent.h> 31 #include <sys/stat.h> 32 #include <pwd.h> 33 #include <sys/mman.h> 34 #include <fcntl.h> 35 #include <sys/resource.h> 36 #include <iostream> 37 #include <linux/sched.h> 38 #include <sys/syscall.h> 39 #include <sys/signal.h> 40 #include <filesystem> 41 42 #include <darling-config.h> 43 44 #include <darlingserver/server.hpp> 45 #include <darlingserver/config.hpp> 46 47 #ifndef DARLINGSERVER_INIT_PROCESS 48 #define DARLINGSERVER_INIT_PROCESS "/sbin/launchd" 49 #endif 50 51 #ifndef DARLINGSERVER_XDG_USER_DIR_CMD 52 #define DARLINGSERVER_XDG_USER_DIR_CMD "xdg-user-dir" 53 #endif 54 55 #if DSERVER_ASAN 56 #include <sanitizer/lsan_interface.h> 57 #endif 58 59 // TODO: most of the code here was ported over from startup/darling.c; we should C++-ify it. 60 61 void fixPermissionsRecursive(const char* path, uid_t originalUID, gid_t originalGID) 62 { 63 DIR* dir; 64 struct dirent* ent; 65 66 if (chown(path, originalUID, originalGID) == -1) 67 fprintf(stderr, "Cannot chown %s: %s\n", path, strerror(errno)); 68 69 dir = opendir(path); 70 if (!dir) 71 return; 72 73 while ((ent = readdir(dir)) != NULL) 74 { 75 if (ent->d_type == DT_DIR) 76 { 77 char* subdir; 78 79 if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) 80 continue; 81 82 subdir = (char*) malloc(strlen(path) + 2 + strlen(ent->d_name)); 83 sprintf(subdir, "%s/%s", path, ent->d_name); 84 85 fixPermissionsRecursive(subdir, originalUID, originalGID); 86 87 free(subdir); 88 } 89 } 90 91 closedir(dir); 92 } 93 94 const char* xdgDirectory(const char* name) 95 { 96 static char dir[4096]; 97 char* cmd = (char*) malloc(sizeof(DARLINGSERVER_XDG_USER_DIR_CMD) + 1 + strlen(name)); 98 99 sprintf(cmd, DARLINGSERVER_XDG_USER_DIR_CMD " %s", name); 100 101 FILE* proc = popen(cmd, "r"); 102 103 free(cmd); 104 105 if (!proc) 106 return NULL; 107 108 fgets(dir, sizeof(dir)-1, proc); 109 110 pclose(proc); 111 112 size_t len = strlen(dir); 113 if (len <= 1) 114 return NULL; 115 116 if (dir[len-1] == '\n') 117 dir[len-1] = '\0'; 118 return dir; 119 } 120 121 void setupUserHome(const char* prefix, uid_t originalUID) 122 { 123 char buf[4096], buf2[4096]; 124 125 snprintf(buf, sizeof(buf), "%s/Users", prefix); 126 127 // Remove the old /Users symlink that may exist 128 unlink(buf); 129 130 // mkdir /Users 131 mkdir(buf, 0777); 132 133 // mkdir /Users/Shared 134 strcat(buf, "/Shared"); 135 mkdir(buf, 0777); 136 137 const char* home = getenv("HOME"); 138 139 const char* login = NULL; 140 struct passwd* pw = getpwuid(originalUID); 141 142 if (pw != NULL) 143 login = pw->pw_name; 144 145 if (!login) 146 login = getlogin(); 147 148 if (!login) 149 { 150 fprintf(stderr, "Cannot determine your user name\n"); 151 exit(1); 152 } 153 if (!home) 154 { 155 fprintf(stderr, "Cannot determine your home directory\n"); 156 exit(1); 157 } 158 159 snprintf(buf, sizeof(buf), "%s/Users/%s", prefix, login); 160 161 // mkdir /Users/$LOGIN 162 mkdir(buf, 0755); 163 164 snprintf(buf2, sizeof(buf2), "/Volumes/SystemRoot%s", home); 165 166 strcat(buf, "/LinuxHome"); 167 unlink(buf); 168 169 // symlink /Users/$LOGIN/LinuxHome -> $HOME 170 symlink(buf2, buf); 171 172 static const char* xdgmap[][2] = { 173 { "DESKTOP", "Desktop" }, 174 { "DOWNLOAD", "Downloads" }, 175 { "PUBLICSHARE", "Public" }, 176 { "DOCUMENTS", "Documents" }, 177 { "MUSIC", "Music" }, 178 { "PICTURES", "Pictures" }, 179 { "VIDEOS", "Movies" }, 180 }; 181 182 for (int i = 0; i < sizeof(xdgmap) / sizeof(xdgmap[0]); i++) 183 { 184 const char* dir = xdgDirectory(xdgmap[i][0]); 185 if (!dir) 186 continue; 187 188 snprintf(buf2, sizeof(buf2), "/Volumes/SystemRoot%s", dir); 189 snprintf(buf, sizeof(buf), "%s/Users/%s/%s", prefix, login, xdgmap[i][1]); 190 191 unlink(buf); 192 symlink(buf2, buf); 193 } 194 } 195 196 void setupCoredumpPattern(void) 197 { 198 FILE* f = fopen("/proc/sys/kernel/core_pattern", "w"); 199 if (f != NULL) 200 { 201 // This is how macOS saves core dumps 202 fputs("/cores/core.%p\n", f); 203 fclose(f); 204 } 205 } 206 207 static void wipeDir(const char* dirpath) 208 { 209 char path[4096]; 210 struct dirent* ent; 211 DIR* dir = opendir(dirpath); 212 213 if (!dir) 214 return; 215 216 while ((ent = readdir(dir)) != NULL) 217 { 218 if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) 219 continue; 220 221 snprintf(path, sizeof(path), "%s/%s", dirpath, ent->d_name); 222 223 if (ent->d_type == DT_DIR) 224 { 225 wipeDir(path); 226 rmdir(path); 227 } 228 else 229 unlink(path); 230 } 231 232 closedir(dir); 233 } 234 235 void darlingPreInit(const char* prefix) 236 { 237 // TODO: Run /usr/libexec/makewhatis 238 const char* dirs[] = { 239 "/var/tmp", 240 "/var/run" 241 }; 242 243 char fullpath[4096]; 244 strcpy(fullpath, prefix); 245 const size_t prefixLen = strlen(fullpath); 246 247 for (size_t i = 0; i < sizeof(dirs)/sizeof(dirs[0]); i++) 248 { 249 fullpath[prefixLen] = 0; 250 strcat(fullpath, dirs[i]); 251 wipeDir(fullpath); 252 } 253 } 254 255 void spawnLaunchd(const char* prefix) 256 { 257 puts("Bootstrapping the container with launchd..."); 258 259 // putenv("KQUEUE_DEBUG=1"); 260 261 auto tmp = (std::string(prefix) + "/.darlingserver.sock"); 262 263 const char* initPath = getenv("DSERVER_INIT"); 264 265 if (!initPath) { 266 initPath = DARLINGSERVER_INIT_PROCESS; 267 } 268 269 setenv("DYLD_ROOT_PATH", LIBEXEC_PATH, 1); 270 setenv("__mldr_sockpath", tmp.c_str(), 1); 271 execl(DarlingServer::Config::defaultMldrPath.data(), "mldr!" LIBEXEC_PATH "/usr/libexec/darling/vchroot", "vchroot", prefix, initPath, NULL); 272 273 fprintf(stderr, "Failed to exec launchd: %s\n", strerror(errno)); 274 abort(); 275 } 276 277 static bool testEnvVar(const char* var_name) { 278 const char* var = getenv(var_name); 279 280 if (!var) { 281 return false; 282 } 283 284 auto len = strlen(var); 285 286 if (len > 0 && (var[0] == '1' || var[0] == 't' || var[0] == 'T')) { 287 return true; 288 } 289 290 return false; 291 }; 292 293 static bool isOnWsl1() { 294 static bool initialized = false; 295 static bool result = false; 296 if (!initialized) { 297 initialized = true; 298 // All WSL systems have WSLENV set by default, while WSL_INTEROP is WSL2-specific. 299 result = getenv("WSLENV") && !getenv("WSL_INTEROP"); 300 } 301 302 return result; 303 } 304 305 static bool shouldUseOverlayFs() { 306 bool shouldUse = true; 307 bool explicitlySet = false; 308 309 if (testEnvVar("DARLING_NOOVERLAYFS")) { 310 shouldUse = false; 311 explicitlySet = true; 312 } else if (getenv("DARLING_NOOVERLAYFS")) { 313 shouldUse = true; 314 explicitlySet = true; 315 } 316 317 // https://github.com/microsoft/WSL/issues/8748 318 // Microsoft is being dumb with its overlayfs implementation and they don't seem to be willing to be fix WSL1-related bugs. 319 // We therefore have to enable this hack on WSL1. 320 if (!explicitlySet && isOnWsl1()) { 321 shouldUse = false; 322 } 323 324 return shouldUse; 325 } 326 327 static int compareTimespec(const timespec& a, const timespec& b) { 328 if (a.tv_sec != b.tv_sec) { 329 return (a.tv_sec > b.tv_sec) ? 1 : -1; 330 } else if (a.tv_nsec != b.tv_nsec) { 331 return (a.tv_nsec > b.tv_nsec) ? 1 : -1; 332 } else { 333 return 0; 334 } 335 } 336 337 static void copyAndSetAttributes(std::string& fromPath, std::string& toPath) { 338 struct stat fromStat, toStat; 339 if (lstat(fromPath.c_str(), &fromStat) == -1) { 340 fprintf(stderr, "Failed to stat file %s: %s\n", fromPath.c_str(), strerror(errno)); 341 abort(); 342 } 343 bool destinationExists = true; 344 bool updateAttributes = false; 345 if (lstat(toPath.c_str(), &toStat) == -1) { 346 if (errno != ENOENT) { 347 fprintf(stderr, "Failed to stat file %s: %s\n", toPath.c_str(), strerror(errno)); 348 abort(); 349 } 350 destinationExists = false; 351 } 352 353 if (S_ISDIR(fromStat.st_mode)) { 354 if (destinationExists && !S_ISDIR(toStat.st_mode)) { 355 return; 356 } else { 357 if (!destinationExists) { 358 if (mkdir(toPath.c_str(), fromStat.st_mode & ALLPERMS) == -1) { 359 fprintf(stderr, "Failed to create directory %s: %s\n", toPath.c_str(), strerror(errno)); 360 abort(); 361 } 362 updateAttributes = true; 363 } 364 DIR* fromDir = opendir(fromPath.c_str()); 365 if (fromDir == NULL) { 366 fprintf(stderr, "Failed to open directory %s: %s\n", fromPath.c_str(), strerror(errno)); 367 abort(); 368 } 369 370 struct dirent* entry = NULL; 371 while ((errno = 0) || ((entry = readdir(fromDir)) != NULL)) { 372 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { 373 continue; 374 } 375 376 size_t oldFromSize = fromPath.size(); 377 size_t oldToSize = toPath.size(); 378 379 fromPath.push_back('/'); 380 toPath.push_back('/'); 381 382 fromPath.append(entry->d_name); 383 toPath.append(entry->d_name); 384 385 copyAndSetAttributes(fromPath, toPath); 386 387 fromPath.resize(oldFromSize); 388 toPath.resize(oldToSize); 389 } 390 391 if (errno) { 392 fprintf(stderr, "Failed to read directory %s: %s\n", fromPath.c_str(), strerror(errno)); 393 abort(); 394 } 395 396 if (closedir(fromDir) == -1) { 397 fprintf(stderr, "Failed to close directory %s: %s\n", fromPath.c_str(), strerror(errno)); 398 } 399 } 400 } else { 401 if (destinationExists) { 402 if ((fromStat.st_mode & S_IFMT) != (toStat.st_mode & S_IFMT)) { 403 return; 404 } else { 405 int compareResult = compareTimespec(fromStat.st_mtim, toStat.st_mtim); 406 // the lower file is older, don't touch the newer file. 407 if (compareResult == -1) { 408 return; 409 // the lower file and the newer files should be the same. 410 // we still update the attributes though, in case ownership or permission changes. 411 } else if (compareResult == 0) { 412 updateAttributes = true; 413 } else if (compareResult == 1) { 414 if (unlink(toPath.c_str()) == -1) { 415 fprintf(stderr, "Failed to delete old destination file %s: %s\n", toPath.c_str(), strerror(errno)); 416 abort(); 417 } 418 std::filesystem::copy(fromPath, toPath, std::filesystem::copy_options::copy_symlinks); 419 updateAttributes = true; 420 } 421 } 422 } else { 423 std::filesystem::copy(fromPath, toPath, std::filesystem::copy_options::copy_symlinks); 424 updateAttributes = true; 425 } 426 } 427 428 if (updateAttributes) { 429 struct timespec times[] = { 430 fromStat.st_atim, 431 fromStat.st_mtim 432 }; 433 if (utimensat(-1, toPath.c_str(), times, AT_SYMLINK_NOFOLLOW) == -1) { 434 fprintf(stderr, "Failed to set timestamp for %s: %s\n", toPath.c_str(), strerror(errno)); 435 abort(); 436 } 437 if (fchownat(-1, toPath.c_str(), fromStat.st_uid, fromStat.st_gid, AT_SYMLINK_NOFOLLOW) == -1) { 438 fprintf(stderr, "Failed to set owner for %s: %s\n", toPath.c_str(), strerror(errno)); 439 abort(); 440 } 441 // POSIX said that AT_SYMLINK_NOFOLLOW is acceptable for links, but on Linux all calls with AT_SYMLINK_NOFOLLOW fails with ENOTSUP. 442 if (fchmodat(-1, toPath.c_str(), fromStat.st_mode & ALLPERMS, S_ISLNK(fromStat.st_mode) ? AT_SYMLINK_NOFOLLOW : 0) == -1) { 443 if (!(S_ISLNK(fromStat.st_mode) && (errno == ENOTSUP))) { 444 fprintf(stderr, "Failed to set permissions for %s: %s\n", toPath.c_str(), strerror(errno)); 445 abort(); 446 } 447 } 448 } 449 } 450 451 static void temp_drop_privileges(uid_t uid, gid_t gid) { 452 // it's important to drop GID first, because non-root users can't change their GID 453 if (setresgid(gid, gid, 0) < 0) { 454 fprintf(stderr, "Failed to temporarily drop group privileges\n"); 455 exit(1); 456 } 457 if (setresuid(uid, uid, 0) < 0) { 458 fprintf(stderr, "Failed to temporarily drop user privileges\n"); 459 exit(1); 460 } 461 }; 462 463 static void perma_drop_privileges(uid_t uid, gid_t gid) { 464 if (setresgid(gid, gid, gid) < 0) { 465 fprintf(stderr, "Failed to drop group privileges\n"); 466 exit(1); 467 } 468 if (setresuid(uid, uid, uid) < 0) { 469 fprintf(stderr, "Failed to drop user privileges\n"); 470 exit(1); 471 } 472 }; 473 474 static void regain_privileges() { 475 if (seteuid(0) < 0) { 476 fprintf(stderr, "Failed to regain root EUID\n"); 477 exit(1); 478 } 479 if (setegid(0) < 0) { 480 fprintf(stderr, "Failed to regain root EGID\n"); 481 exit(1); 482 } 483 484 if (setresuid(0, 0, 0) < 0) { 485 fprintf(stderr, "Failed to regain root privileges\n"); 486 exit(1); 487 } 488 if (setresgid(0, 0, 0) < 0) { 489 fprintf(stderr, "Failed to regain root privileges\n"); 490 exit(1); 491 } 492 }; 493 494 #if DSERVER_ASAN 495 static void handle_sigusr1(int signum) { 496 __lsan_do_recoverable_leak_check(); 497 }; 498 #endif 499 500 int main(int argc, char** argv) { 501 const char* prefix = NULL; 502 uid_t originalUID = -1; 503 gid_t originalGID = -1; 504 int pipefd = -1; 505 bool fix_permissions = false; 506 pid_t launchdGlobalPID = -1; 507 size_t prefix_length = 0; 508 struct rlimit default_limit; 509 struct rlimit increased_limit; 510 FILE* nr_open_file = NULL; 511 int childWaitFDs[2]; 512 struct rlimit core_limit; 513 514 char *opts; 515 char putOld[4096]; 516 char *p; 517 518 if (argc < 6) { 519 fprintf(stderr, "darlingserver is not meant to be started manually\n"); 520 exit(1); 521 } 522 523 #if DSERVER_EXTENDED_DEBUG 524 if (getenv("DSERVER_WAIT4DEBUGGER")) { 525 volatile bool debugged = false; 526 while (!debugged); 527 } 528 #endif 529 530 prefix = argv[1]; 531 sscanf(argv[2], "%d", &originalUID); 532 sscanf(argv[3], "%d", &originalGID); 533 sscanf(argv[4], "%d", &pipefd); 534 535 if (argv[5][0] == '1') { 536 fix_permissions = true; 537 } 538 539 prefix_length = strlen(prefix); 540 541 if (getuid() != 0 || getgid() != 0) { 542 fprintf(stderr, "darlingserver needs to start as root\n"); 543 exit(1); 544 } 545 546 // temporarily drop privileges to perform some prefix work 547 temp_drop_privileges(originalUID, originalGID); 548 setupUserHome(prefix, originalUID); 549 //setupCoredumpPattern(); 550 regain_privileges(); 551 552 // read the default rlimit so we can restore it for our children 553 if (getrlimit(RLIMIT_NOFILE, &default_limit) != 0) { 554 fprintf(stderr, "Failed to read default FD rlimit: %s\n", strerror(errno)); 555 //exit(1); 556 } else { 557 // ASAN uses a rather obtuse way of closing descriptors for child processes it spawns, 558 // and increasing the FD limit screws with it (it tries to close every single descriptor up to the FD limit). 559 #if !DSERVER_ASAN 560 // read the system maximum 561 nr_open_file = fopen("/proc/sys/fs/nr_open", "r"); 562 if (nr_open_file == NULL) { 563 fprintf(stderr, "Warning: failed to open /proc/sys/fs/nr_open: %s\n", strerror(errno)); 564 increased_limit.rlim_cur = increased_limit.rlim_max = default_limit.rlim_max; 565 //exit(1); 566 } else { 567 if (fscanf(nr_open_file, "%lu", &increased_limit.rlim_max) != 1) { 568 fprintf(stderr, "Failed to read /proc/sys/fs/nr_open: %s\n", strerror(errno)); 569 exit(1); 570 } 571 increased_limit.rlim_cur = increased_limit.rlim_max; 572 if (fclose(nr_open_file) != 0) { 573 fprintf(stderr, "Failed to close /proc/sys/fs/nr_open: %s\n", strerror(errno)); 574 exit(1); 575 } 576 } 577 578 // now set our increased rlimit 579 if (setrlimit(RLIMIT_NOFILE, &increased_limit) != 0) { 580 fprintf(stderr, "Warning: failed to increase FD rlimit: %s\n", strerror(errno)); 581 //exit(1); 582 } 583 584 #endif 585 } 586 587 if (getrlimit(RLIMIT_CORE, &core_limit) != 0) { 588 fprintf(stderr, "Failed to read default core rlimit: %s\n", strerror(errno)); 589 } else { 590 // increase the core limit to the maximum 591 core_limit.rlim_cur = core_limit.rlim_max; 592 593 if (setrlimit(RLIMIT_CORE, &core_limit) != 0) { 594 fprintf(stderr, "Warning: failed to increase core limit: %s\n", strerror(errno)); 595 } 596 } 597 598 // Since overlay cannot be mounted inside user namespaces, we have to setup a new mount namespace 599 // and do the mount while we can be root 600 if (unshare(CLONE_NEWNS) != 0) 601 { 602 fprintf(stderr, "Cannot unshare PID and mount namespaces: %s\n", strerror(errno)); 603 exit(1); 604 } 605 606 int shmMountFlags = MS_NOSUID | MS_NODEV; 607 // Workaround for dumb Microsoft bug: https://github.com/microsoft/WSL/issues/8777 608 if (!isOnWsl1()) 609 { 610 shmMountFlags |= MS_NOEXEC; 611 } 612 613 umount("/dev/shm"); 614 if (mount("tmpfs", "/dev/shm", "tmpfs", shmMountFlags, NULL) != 0) 615 { 616 fprintf(stderr, "Cannot mount new /dev/shm: %s\n", strerror(errno)); 617 exit(1); 618 } 619 620 if (shouldUseOverlayFs()) { 621 // Because systemd marks / as MS_SHARED and we would inherit this into the overlay mount, 622 // causing it not to be unmounted once the init process dies. 623 if (mount(NULL, "/", NULL, MS_REC | MS_SLAVE, NULL) != 0) 624 { 625 fprintf(stderr, "Cannot remount / as slave: %s\n", strerror(errno)); 626 exit(1); 627 } 628 629 opts = (char*) malloc(strlen(prefix)*2 + sizeof(LIBEXEC_PATH) + 100); 630 631 const char* opts_fmt = "lowerdir=%s,upperdir=%s,workdir=%s.workdir,index=off"; 632 633 sprintf(opts, opts_fmt, LIBEXEC_PATH, prefix, prefix); 634 635 // Mount overlay onto our prefix 636 if (mount("overlay", prefix, "overlay", 0, opts) != 0) 637 { 638 if (errno == EINVAL) { 639 opts_fmt = "lowerdir=%s,upperdir=%s,workdir=%s.workdir"; 640 sprintf(opts, opts_fmt, LIBEXEC_PATH, prefix, prefix); 641 if (mount("overlay", prefix, "overlay", 0, opts) == 0) { 642 goto mount_ok; 643 } 644 } 645 fprintf(stderr, "Cannot mount overlay: %s\n", strerror(errno)); 646 exit(1); 647 } 648 649 mount_ok: 650 free(opts); 651 } else { 652 std::string fromPath = LIBEXEC_PATH; 653 std::string toPath = prefix; 654 copyAndSetAttributes(fromPath, toPath); 655 } 656 657 // This is executed once at prefix creation 658 if (fix_permissions) { 659 const char* extra_paths[] = { 660 "/private/etc/passwd", 661 "/private/etc/master.passwd", 662 "/private/etc/group", 663 }; 664 char path[4096]; 665 666 fixPermissionsRecursive(prefix, originalUID, originalGID); 667 668 path[sizeof(path) - 1] = '\0'; 669 strncpy(path, prefix, sizeof(path) - 1); 670 for (size_t i = 0; i < sizeof(extra_paths) / sizeof(*extra_paths); ++i) { 671 path[prefix_length] = '\0'; 672 strncat(path, extra_paths[i], sizeof(path) - 1); 673 fixPermissionsRecursive(path, originalUID, originalGID); 674 } 675 } 676 677 // temporarily drop privileges and do some prefix work 678 temp_drop_privileges(originalUID, originalGID); 679 darlingPreInit(prefix); 680 regain_privileges(); 681 682 // Tell the parent we're ready 683 write(pipefd, ".", 1); 684 close(pipefd); 685 686 if (pipe(childWaitFDs) != 0) { 687 std::cerr << "Failed to create child waiting pipe: " << strerror(errno) << std::endl; 688 exit(1); 689 } 690 691 // we have to use `clone` rather than `fork` to create the process in its own PID namespace 692 // and still be able to spawn new processes and threads of our own 693 launchdGlobalPID = syscall(SYS_clone, CLONE_NEWPID | SIGCHLD, NULL, NULL, NULL, 0); 694 695 if (launchdGlobalPID < 0) { 696 fprintf(stderr, "Failed to fork to start launchd: %s\n", strerror(errno)); 697 exit(1); 698 } else if (launchdGlobalPID == 0) { 699 // this is the child 700 char buf[1]; 701 702 close(childWaitFDs[1]); 703 704 snprintf(putOld, sizeof(putOld), "%s/proc", prefix); 705 706 // mount procfs for our new PID namespace 707 if (mount("proc", putOld, "proc", 0, "") != 0) 708 { 709 fprintf(stderr, "Cannot mount procfs: %s\n", strerror(errno)); 710 exit(1); 711 } 712 713 // drop our privileges now 714 perma_drop_privileges(originalUID, originalGID); 715 prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); 716 717 // decrease the FD limit back to the default 718 if (setrlimit(RLIMIT_NOFILE, &default_limit) != 0) { 719 fprintf(stderr, "Warning: failed to decrease FD limit back down for launchd: %s\n", strerror(errno)); 720 //exit(1); 721 } 722 723 // wait for the parent to give us the green light 724 read(childWaitFDs[0], buf, 1); 725 close(childWaitFDs[0]); 726 727 spawnLaunchd(prefix); 728 __builtin_unreachable(); 729 } 730 731 // this is the parent 732 close(childWaitFDs[0]); 733 734 // drop our privileges 735 perma_drop_privileges(originalUID, originalGID); 736 prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); 737 738 #if DSERVER_ASAN 739 // set up a signal handler to print leak info 740 struct sigaction leak_info_action; 741 leak_info_action.sa_handler = handle_sigusr1; 742 leak_info_action.sa_flags = SA_RESTART; 743 sigemptyset(&leak_info_action.sa_mask); 744 sigaction(SIGUSR1, &leak_info_action, NULL); 745 #endif 746 747 // create the server 748 auto server = new DarlingServer::Server(prefix); 749 750 // tell the child to go ahead; the socket has been created 751 write(childWaitFDs[1], ".", 1); 752 close(childWaitFDs[1]); 753 754 // start the main loop 755 server->start(); 756 757 // this should never happen 758 std::cerr << "Server exited main loop!" << std::endl; 759 delete server; 760 761 return 1; 762 };