1/* 2 * Copyright (c) 2005-2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_APACHE_LICENSE_HEADER_START@ 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 * @APPLE_APACHE_LICENSE_HEADER_END@ 19 */ 20 21#include "config.h" 22#include "launch.h" 23#include "launch_priv.h" 24#include "bootstrap.h" 25#include "vproc.h" 26#include "vproc_priv.h" 27#include "vproc_internal.h" 28#include "bootstrap_priv.h" 29#include "launch_internal.h" 30 31#include <CoreFoundation/CoreFoundation.h> 32#include <CoreFoundation/CFPriv.h> 33#include <CoreFoundation/CFLogUtilities.h> 34#include <TargetConditionals.h> 35#include <IOKit/IOKitLib.h> 36#include <NSSystemDirectories.h> 37#include <mach/mach.h> 38#include <mach-o/getsect.h> 39#include <sys/types.h> 40#include <sys/sysctl.h> 41#include <sys/time.h> 42#include <sys/sysctl.h> 43#include <sys/stat.h> 44#include <sys/socket.h> 45#include <sys/un.h> 46#include <sys/fcntl.h> 47#include <sys/event.h> 48#include <sys/resource.h> 49#include <sys/param.h> 50#include <sys/mount.h> 51#include <sys/reboot.h> 52#include <net/if.h> 53#include <netinet/in.h> 54#include <netinet/in_var.h> 55#include <netinet6/nd6.h> 56#include <unistd.h> 57#include <dirent.h> 58#include <libgen.h> 59#include <libinfo.h> 60#include <pwd.h> 61#include <stdio.h> 62#include <stdlib.h> 63#include <pwd.h> 64#include <grp.h> 65#include <netdb.h> 66#include <syslog.h> 67#include <glob.h> 68#include <readline/readline.h> 69#include <readline/history.h> 70#include <dns_sd.h> 71#include <paths.h> 72#include <utmpx.h> 73#include <bootfiles.h> 74#include <sysexits.h> 75#include <util.h> 76#include <spawn.h> 77#include <sys/syslimits.h> 78#include <fnmatch.h> 79#include <os/assumes.h> 80#include <dlfcn.h> 81#if HAVE_SYSTEMSTATS 82#include <systemstats/systemstats.h> 83#endif 84 85#if HAVE_LIBAUDITD 86#include <bsm/auditd_lib.h> 87#ifndef AUDITD_PLIST_FILE 88#define AUDITD_PLIST_FILE "/System/Library/LaunchDaemons/com.apple.auditd.plist" 89#endif 90#endif 91 92extern char **environ; 93 94#define LAUNCH_SECDIR _PATH_TMP "launch-XXXXXX" 95#define LAUNCH_ENV_KEEPCONTEXT "LaunchKeepContext" 96#define LAUNCH_ENV_BOOTSTRAPPINGSYSTEM "LaunchBootstrappingSystem" 97 98#define CFTypeCheck(cf, type) (CFGetTypeID(cf) == type ## GetTypeID()) 99#define CFReleaseIfNotNULL(cf) if (cf) CFRelease(cf); 100 101#if TARGET_OS_EMBEDDED 102#include <sys/kern_memorystatus.h> 103 104#define XPC_PLIST_CACHE "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib" 105#define XPC_PLIST_CACHE_KEY "LaunchDaemons" 106 107#if JETSAM_PRIORITY_REVISION 108#define READ_JETSAM_DEFAULTS 1 109#define JETSAM_PROP_DIR "/System/Library/LaunchDaemons" 110#define JETSAM_PROP_DIR_LENGTH (sizeof(JETSAM_PROP_DIR) - 1) 111#define JETSAM_PROP_PREFIX "com.apple.jetsamproperties." 112#define JETSAM_PROP_PREFIX_LENGTH (sizeof(JETSAM_PROP_PREFIX) - 1) 113#define JETSAM_PROP_SUFFIX ".plist" 114#define JETSAM_PROP_SUFFIX_LENGTH (sizeof(JETSAM_PROP_SUFFIX) - 1) 115#endif 116#endif 117 118struct load_unload_state { 119 launch_data_t pass1; 120 char *session_type; 121 bool editondisk:1, load:1, forceload:1; 122}; 123 124static void launchctl_log(int level, const char *fmt, ...); 125static void launchctl_log_CFString(int level, CFStringRef string); 126static void myCFDictionaryApplyFunction(const void *key, const void *value, void *context); 127static CFTypeRef CFTypeCreateFromLaunchData(launch_data_t obj); 128static CFArrayRef CFArrayCreateFromLaunchArray(launch_data_t arr); 129static CFDictionaryRef CFDictionaryCreateFromLaunchDictionary(launch_data_t dict); 130static bool launch_data_array_append(launch_data_t a, launch_data_t o); 131static void insert_event(launch_data_t, const char *, const char *, launch_data_t); 132static void distill_jobs(launch_data_t); 133static void distill_config_file(launch_data_t); 134static void distill_fsevents(launch_data_t); 135static void sock_dict_cb(launch_data_t what, const char *key, void *context); 136static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob); 137static launch_data_t CF2launch_data(CFTypeRef); 138static launch_data_t read_plist_file(const char *file, bool editondisk, bool load); 139#if TARGET_OS_EMBEDDED 140static CFPropertyListRef GetPropertyListFromCache(void); 141static CFPropertyListRef CreateMyPropertyListFromCachedFile(const char *posixfile); 142static bool require_jobs_from_cache(void); 143#endif 144static CFPropertyListRef CreateMyPropertyListFromFile(const char *); 145static CFPropertyListRef CFPropertyListCreateFromFile(CFURLRef plistURL); 146static void WriteMyPropertyListToFile(CFPropertyListRef, const char *); 147static bool path_goodness_check(const char *path, bool forceload); 148static void readpath(const char *, struct load_unload_state *); 149static void readfile(const char *, struct load_unload_state *); 150static int _fd(int); 151static int demux_cmd(int argc, char *const argv[]); 152static void submit_job_pass(launch_data_t jobs); 153static void do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup); 154static mach_port_t str2bsport(const char *s); 155static void print_jobs(launch_data_t j, const char *key, void *context); 156static void print_obj(launch_data_t obj, const char *key, void *context); 157static bool str2lim(const char *buf, rlim_t *res); 158static const char *lim2str(rlim_t val, char *buf); 159static const char *num2name(int n); 160static ssize_t name2num(const char *n); 161static void unloadjob(launch_data_t job); 162static void print_key_value(launch_data_t obj, const char *key, void *context); 163static void print_launchd_env(launch_data_t obj, const char *key, void *context); 164static void loopback_setup_ipv4(void); 165static void loopback_setup_ipv6(void); 166static pid_t fwexec(const char *const *argv, int *wstatus); 167static void do_potential_fsck(void); 168static bool path_check(const char *path); 169static bool is_safeboot(void); 170static bool is_netboot(void); 171static void apply_sysctls_from_file(const char *thefile); 172static void empty_dir(const char *thedir, struct stat *psb); 173static int touch_file(const char *path, mode_t m); 174static void do_sysversion_sysctl(void); 175static void do_application_firewall_magic(int sfd, launch_data_t thejob); 176static void preheat_page_cache_hack(void); 177static void do_bootroot_magic(void); 178static void do_single_user_mode(bool); 179static bool do_single_user_mode2(void); 180static void do_crash_debug_mode(void); 181static bool do_crash_debug_mode2(void); 182static void read_launchd_conf(void); 183static bool job_disabled_logic(launch_data_t obj); 184static void fix_bogus_file_metadata(void); 185static void do_file_init(void) __attribute__((constructor)); 186static void setup_system_context(void); 187static void handle_system_bootstrapper_crashes_separately(void); 188static void fatal_signal_handler(int sig, siginfo_t *si, void *uap); 189 190typedef enum { 191 BOOTCACHE_START = 1, 192 BOOTCACHE_TAG, 193 BOOTCACHE_STOP, 194} BootCache_action_t; 195 196static void do_BootCache_magic(BootCache_action_t what); 197 198static int bootstrap_cmd(int argc, char *const argv[]); 199static int load_and_unload_cmd(int argc, char *const argv[]); 200//static int reload_cmd(int argc, char *const argv[]); 201static int start_stop_remove_cmd(int argc, char *const argv[]); 202static int submit_cmd(int argc, char *const argv[]); 203static int list_cmd(int argc, char *const argv[]); 204 205static int setenv_cmd(int argc, char *const argv[]); 206static int unsetenv_cmd(int argc, char *const argv[]); 207static int getenv_and_export_cmd(int argc, char *const argv[]); 208static int wait4debugger_cmd(int argc, char *const argv[]); 209 210static int limit_cmd(int argc, char *const argv[]); 211static int stdio_cmd(int argc, char *const argv[]); 212static int fyi_cmd(int argc, char *const argv[]); 213static int logupdate_cmd(int argc, char *const argv[]); 214static int umask_cmd(int argc, char *const argv[]); 215static int getrusage_cmd(int argc, char *const argv[]); 216static int bsexec_cmd(int argc, char *const argv[]); 217static int _bslist_cmd(mach_port_t bport, unsigned int depth, bool show_job, bool local_only); 218static int bslist_cmd(int argc, char *const argv[]); 219static int _bstree_cmd(mach_port_t bsport, unsigned int depth, bool show_jobs); 220static int bstree_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused))); 221static int managerpid_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused))); 222static int manageruid_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused))); 223static int managername_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused))); 224static int asuser_cmd(int argc, char * const argv[]); 225static int exit_cmd(int argc, char *const argv[]) __attribute__((noreturn)); 226static int help_cmd(int argc, char *const argv[]); 227 228static const struct { 229 const char *name; 230 int (*func)(int argc, char *const argv[]); 231 const char *desc; 232} cmds[] = { 233 { "load", load_and_unload_cmd, "Load configuration files and/or directories" }, 234 { "unload", load_and_unload_cmd, "Unload configuration files and/or directories" }, 235// { "reload", reload_cmd, "Reload configuration files and/or directories" }, 236 { "start", start_stop_remove_cmd, "Start specified job" }, 237 { "stop", start_stop_remove_cmd, "Stop specified job" }, 238 { "submit", submit_cmd, "Submit a job from the command line" }, 239 { "remove", start_stop_remove_cmd, "Remove specified job" }, 240 { "bootstrap", bootstrap_cmd, "Bootstrap launchd" }, 241 { "list", list_cmd, "List jobs and information about jobs" }, 242 { "setenv", setenv_cmd, "Set an environmental variable in launchd" }, 243 { "unsetenv", unsetenv_cmd, "Unset an environmental variable in launchd" }, 244 { "getenv", getenv_and_export_cmd, "Get an environmental variable from launchd" }, 245 { "export", getenv_and_export_cmd, "Export shell settings from launchd" }, 246 { "debug", wait4debugger_cmd, "Set the WaitForDebugger flag for the target job to true." }, 247 { "limit", limit_cmd, "View and adjust launchd resource limits" }, 248 { "stdout", stdio_cmd, "Redirect launchd's standard out to the given path" }, 249 { "stderr", stdio_cmd, "Redirect launchd's standard error to the given path" }, 250 { "shutdown", fyi_cmd, "Prepare for system shutdown" }, 251 { "singleuser", fyi_cmd, "Switch to single-user mode" }, 252 { "getrusage", getrusage_cmd, "Get resource usage statistics from launchd" }, 253 { "log", logupdate_cmd, "Adjust the logging level or mask of launchd" }, 254 { "umask", umask_cmd, "Change launchd's umask" }, 255 { "bsexec", bsexec_cmd, "Execute a process within a different Mach bootstrap subset" }, 256 { "bslist", bslist_cmd, "List Mach bootstrap services and optional servers" }, 257 { "bstree", bstree_cmd, "Show the entire Mach bootstrap tree. Requires root privileges." }, 258 { "managerpid", managerpid_cmd, "Print the PID of the launchd managing this Mach bootstrap." }, 259 { "manageruid", manageruid_cmd, "Print the UID of the launchd managing this Mach bootstrap." }, 260 { "managername", managername_cmd, "Print the name of this Mach bootstrap." }, 261 { "asuser", asuser_cmd, "Execute a subcommand in the given user's context." }, 262 { "exit", exit_cmd, "Exit the interactive invocation of launchctl" }, 263 { "quit", exit_cmd, "Quit the interactive invocation of launchctl" }, 264 { "help", help_cmd, "This help output" }, 265}; 266 267static bool _launchctl_istty; 268static bool _launchctl_verbose; 269static bool _launchctl_is_managed; 270static bool _launchctl_apple_internal; 271static bool _launchctl_system_context; 272static bool _launchctl_uid0_context; 273static bool _launchctl_system_bootstrap; 274static bool _launchctl_peruser_bootstrap; 275static bool _launchctl_verbose_boot = false; 276static bool _launchctl_startup_debugging = false; 277 278static bool _launchctl_overrides_db_changed = false; 279static CFMutableDictionaryRef _launchctl_overrides_db = NULL; 280 281static char *_launchctl_job_overrides_db_path; 282static char *_launchctl_managername = NULL; 283 284#if READ_JETSAM_DEFAULTS 285static CFDictionaryRef _launchctl_jetsam_defaults = NULL; 286static CFDictionaryRef _launchctl_jetsam_defaults_cached = NULL; 287#endif 288 289int 290main(int argc, char *const argv[]) 291{ 292 char *l; 293 294 if (getenv(LAUNCH_ENV_BOOTSTRAPPINGSYSTEM)) { 295 /* We're bootstrapping the install environment, so we can't talk to 296 * mDNSResponder or opendirectoryd. 297 * 298 * See <rdar://problem/9877230>. 299 */ 300 si_search_module_set_flags("mdns", 1); 301 si_search_module_set_flags("ds", 1); 302 } 303 304 int64_t is_managed = 0; 305 (void)vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &is_managed); 306 _launchctl_is_managed = is_managed; 307 308 _launchctl_istty = isatty(STDIN_FILENO); 309 argc--, argv++; 310 311 if (argc > 0 && argv[0][0] == '-') { 312 char *flago; 313 314 for (flago = argv[0] + 1; *flago; flago++) { 315 switch (*flago) { 316 case 'v': 317 _launchctl_verbose = true; 318 break; 319 case 'u': 320 if (argc > 1) { 321 if (strncmp(argv[1], "root", sizeof("root")) == 0) { 322 _launchctl_uid0_context = true; 323 } else { 324 launchctl_log(LOG_ERR, "Unknown user: %s", argv[1]); 325 exit(EXIT_FAILURE); 326 } 327 argc--, argv++; 328 } else { 329 launchctl_log(LOG_ERR, "-u option requires an argument."); 330 } 331 break; 332 case '1': 333 _launchctl_system_context = true; 334 break; 335 default: 336 launchctl_log(LOG_ERR, "Unknown argument: '-%c'", *flago); 337 break; 338 } 339 } 340 argc--, argv++; 341 } 342 343 /* Running in the context of the root user's per-user launchd is only from 344 * within that session. 345 */ 346 if (_launchctl_uid0_context) { 347 int64_t manager_uid = -1, manager_pid = -1; 348 (void)vproc_swap_integer(NULL, VPROC_GSK_MGR_UID, NULL, &manager_uid); 349 (void)vproc_swap_integer(NULL, VPROC_GSK_MGR_PID, NULL, &manager_pid); 350 if (manager_uid || manager_pid == 1) { 351 launchctl_log(LOG_ERR, "Running in the root user's per-user context is not supported outside of the root user's bootstrap."); 352 exit(EXIT_FAILURE); 353 } 354 } else if (!(_launchctl_system_context || _launchctl_uid0_context)) { 355 /* Running in the system context is implied when we're running as root 356 * and not running as a bootstrapper. 357 */ 358 _launchctl_system_context = (!_launchctl_is_managed && getuid() == 0); 359 } 360 361 if (_launchctl_system_context) { 362 if (getuid() == 0) { 363 setup_system_context(); 364 } else { 365 launchctl_log(LOG_ERR, "You must be root to run in the system context."); 366 exit(EXIT_FAILURE); 367 } 368 } else if (_launchctl_uid0_context) { 369 if (getuid() != 0) { 370 launchctl_log(LOG_ERR, "You must be root to run in the root user context."); 371 exit(EXIT_FAILURE); 372 } 373 } 374 375 if (!readline) { 376 launchctl_log(LOG_ERR, "missing library: readline"); 377 exit(EXIT_FAILURE); 378 } 379 380 if (argc == 0) { 381 while ((l = readline(_launchctl_istty ? "launchd% " : NULL))) { 382 char *inputstring = l, *argv2[100], **ap = argv2; 383 int i = 0; 384 385 while ((*ap = strsep(&inputstring, " \t"))) { 386 if (**ap != '\0') { 387 ap++; 388 i++; 389 } 390 } 391 392 if (i > 0) { 393 demux_cmd(i, argv2); 394 } 395 396 free(l); 397 } 398 399 if (_launchctl_istty) { 400 fputc('\n', stdout); 401 } 402 } 403 404 if (argc > 0) { 405 exit(demux_cmd(argc, argv)); 406 } 407 408 exit(EXIT_SUCCESS); 409} 410 411int 412demux_cmd(int argc, char *const argv[]) 413{ 414 size_t i; 415 416 optind = 1; 417 optreset = 1; 418 419 for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) { 420 if (!strcmp(cmds[i].name, argv[0])) { 421 return cmds[i].func(argc, argv); 422 } 423 } 424 425 launchctl_log(LOG_ERR, "%s: unknown subcommand \"%s\"", getprogname(), argv[0]); 426 return 1; 427} 428 429void 430launchctl_log(int level, const char *fmt, ...) 431{ 432 va_list ap; 433 va_start(ap, fmt); 434 435 if (_launchctl_is_managed) { 436 vsyslog(level, fmt, ap); 437 } else { 438 char *buff = NULL; 439 (void)vasprintf(&buff, fmt, ap); 440 441 FILE *where = stdout; 442 if (level < LOG_NOTICE) { 443 where = stderr; 444 } 445 446 fprintf(where, "%s\n", buff); 447 free(buff); 448 } 449 450 va_end(ap); 451} 452 453void 454launchctl_log_CFString(int level, CFStringRef string) 455{ 456 // Big enough. Don't feel like jumping through CF's hoops. 457 char *buff = malloc(4096); 458 (void)CFStringGetCString(string, buff, 4096, kCFStringEncodingUTF8); 459 launchctl_log(level, "%s", buff); 460 free(buff); 461} 462 463void 464read_launchd_conf(void) 465{ 466#if !TARGET_OS_EMBEDDED 467 char s[1000], *c, *av[100]; 468 const char *file; 469 size_t len; 470 int i; 471 FILE *f; 472 473 if (getppid() == 1) { 474 file = "/etc/launchd.conf"; 475 } else { 476 file = "/etc/launchd-user.conf"; 477 } 478 479 if (!(f = fopen(file, "r"))) { 480 return; 481 } 482 483 while ((c = fgets(s, (int) sizeof s, f))) { 484 len = strlen(c); 485 if (len && c[len - 1] == '\n') { 486 c[len - 1] = '\0'; 487 } 488 489 i = 0; 490 491 while ((av[i] = strsep(&c, " \t"))) { 492 if (*(av[i]) != '\0') { 493 i++; 494 } 495 } 496 497 if (i > 0) { 498 demux_cmd(i, av); 499 } 500 } 501 502 fclose(f); 503#endif // !TARGET_OS_EMBEDDED 504} 505 506static CFPropertyListRef 507CFPropertyListCreateFromFile(CFURLRef plistURL) 508{ 509 CFReadStreamRef plistReadStream = CFReadStreamCreateWithFile(NULL, plistURL); 510 511 CFErrorRef streamErr = NULL; 512 if (!CFReadStreamOpen(plistReadStream)) { 513 streamErr = CFReadStreamCopyError(plistReadStream); 514 CFStringRef errString = CFErrorCopyDescription(streamErr); 515 516 launchctl_log_CFString(LOG_ERR, errString); 517 518 CFRelease(errString); 519 CFRelease(streamErr); 520 } 521 522 CFPropertyListRef plist = NULL; 523 if (plistReadStream) { 524 CFStringRef errString = NULL; 525 CFPropertyListFormat plistFormat = 0; 526 plist = CFPropertyListCreateFromStream(NULL, plistReadStream, 0, kCFPropertyListImmutable, &plistFormat, &errString); 527 if (!plist) { 528 launchctl_log_CFString(LOG_ERR, errString); 529 CFRelease(errString); 530 } 531 } 532 533 CFReadStreamClose(plistReadStream); 534 CFRelease(plistReadStream); 535 536 return plist; 537} 538 539int 540unsetenv_cmd(int argc, char *const argv[]) 541{ 542 launch_data_t resp, tmp, msg; 543 544 if (argc != 2) { 545 launchctl_log(LOG_ERR, "%s usage: unsetenv <key>", getprogname()); 546 return 1; 547 } 548 549 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); 550 551 tmp = launch_data_new_string(argv[1]); 552 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_UNSETUSERENVIRONMENT); 553 554 resp = launch_msg(msg); 555 556 launch_data_free(msg); 557 558 if (resp) { 559 launch_data_free(resp); 560 } else { 561 launchctl_log(LOG_ERR, "launch_msg(\"%s\"): %s", LAUNCH_KEY_UNSETUSERENVIRONMENT, strerror(errno)); 562 } 563 564 return 0; 565} 566 567int 568setenv_cmd(int argc, char *const argv[]) 569{ 570 launch_data_t resp, tmp, tmpv, msg; 571 572 if (argc != 3) { 573 launchctl_log(LOG_ERR, "%s usage: setenv <key> <value>", getprogname()); 574 return 1; 575 } 576 577 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); 578 tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY); 579 580 tmpv = launch_data_new_string(argv[2]); 581 launch_data_dict_insert(tmp, tmpv, argv[1]); 582 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETUSERENVIRONMENT); 583 584 resp = launch_msg(msg); 585 launch_data_free(msg); 586 587 if (resp) { 588 launch_data_free(resp); 589 } else { 590 launchctl_log(LOG_ERR, "launch_msg(\"%s\"): %s", LAUNCH_KEY_SETUSERENVIRONMENT, strerror(errno)); 591 } 592 593 return 0; 594} 595 596void 597print_launchd_env(launch_data_t obj, const char *key, void *context) 598{ 599 bool *is_csh = context; 600 601 /* XXX escape the double quotes */ 602 if (*is_csh) { 603 launchctl_log(LOG_NOTICE, "setenv %s \"%s\";", key, launch_data_get_string(obj)); 604 } else { 605 launchctl_log(LOG_NOTICE, "%s=\"%s\"; export %s;", key, launch_data_get_string(obj), key); 606 } 607} 608 609void 610print_key_value(launch_data_t obj, const char *key, void *context) 611{ 612 const char *k = context; 613 614 if (!strcmp(key, k)) { 615 launchctl_log(LOG_NOTICE, "%s", launch_data_get_string(obj)); 616 } 617} 618 619int 620getenv_and_export_cmd(int argc, char *const argv[]) 621{ 622 launch_data_t resp; 623 bool is_csh = false; 624 char *k; 625 626 if (!strcmp(argv[0], "export")) { 627 char *s = getenv("SHELL"); 628 if (s) { 629 is_csh = strstr(s, "csh") ? true : false; 630 } 631 } else if (argc != 2) { 632 launchctl_log(LOG_ERR, "%s usage: getenv <key>", getprogname()); 633 return 1; 634 } 635 636 k = argv[1]; 637 638 if (vproc_swap_complex(NULL, VPROC_GSK_ENVIRONMENT, NULL, &resp) == NULL) { 639 if (!strcmp(argv[0], "export")) { 640 launch_data_dict_iterate(resp, print_launchd_env, &is_csh); 641 } else { 642 launch_data_dict_iterate(resp, print_key_value, k); 643 } 644 launch_data_free(resp); 645 return 0; 646 } else { 647 return 1; 648 } 649 650 return 0; 651} 652 653int 654wait4debugger_cmd(int argc, char * const argv[]) 655{ 656 if (argc != 3) { 657 launchctl_log(LOG_ERR, "%s usage: debug <label> <value>", argv[0]); 658 return 1; 659 } 660 661 int result = 1; 662 int64_t inval = 0; 663 if (strncmp(argv[2], "true", sizeof("true")) == 0) { 664 inval = 1; 665 } else if (strncmp(argv[2], "false", sizeof("false")) != 0) { 666 inval = atoi(argv[2]); 667 inval &= 1; 668 } 669 670 vproc_t vp = vprocmgr_lookup_vproc(argv[1]); 671 if (vp) { 672 vproc_err_t verr = vproc_swap_integer(vp, VPROC_GSK_WAITFORDEBUGGER, &inval, NULL); 673 if (verr) { 674 launchctl_log(LOG_ERR, "Failed to set WaitForDebugger flag on %s.", argv[1]); 675 } else { 676 result = 0; 677 } 678 vproc_release(vp); 679 } 680 681 return result; 682} 683 684void 685unloadjob(launch_data_t job) 686{ 687 launch_data_t tmps; 688 689 tmps = launch_data_dict_lookup(job, LAUNCH_JOBKEY_LABEL); 690 691 if (!tmps) { 692 launchctl_log(LOG_ERR, "%s: Error: Missing Key: %s", getprogname(), LAUNCH_JOBKEY_LABEL); 693 return; 694 } 695 696 if (_vproc_send_signal_by_label(launch_data_get_string(tmps), VPROC_MAGIC_UNLOAD_SIGNAL) != NULL) { 697 launchctl_log(LOG_ERR, "%s: Error unloading: %s", getprogname(), launch_data_get_string(tmps)); 698 } 699} 700 701#if READ_JETSAM_DEFAULTS 702 703static CFDictionaryRef 704read_jetsam_defaults_from_cache(void) { 705 CFPropertyListRef cache = GetPropertyListFromCache(); 706 CFPropertyListRef defaults = NULL; 707 const void **keys = 0; 708 CFIndex count, i; 709 710 if (!cache) { 711 return NULL; 712 } 713 714 CFPropertyListRef cachefiles = CFDictionaryGetValue(cache, CFSTR(XPC_PLIST_CACHE_KEY)); 715 if (!cachefiles) { 716 return NULL; 717 } 718 719 count = CFDictionaryGetCount(cachefiles); 720 keys = (const void **)malloc(sizeof(void *) * count); 721 if (!keys) { 722 return NULL; 723 } 724 725 CFDictionaryGetKeysAndValues(cachefiles, keys, NULL); 726 for (i = 0; i < count; i++) { 727 CFStringRef key = (CFStringRef)keys[i]; 728 CFIndex key_length = CFStringGetLength(key); 729 730 if (key_length <= (CFIndex)(JETSAM_PROP_DIR_LENGTH + JETSAM_PROP_PREFIX_LENGTH + JETSAM_PROP_SUFFIX_LENGTH + 1)) { 731 continue; 732 } 733 734 if (CFStringCompareWithOptions(key, CFSTR(JETSAM_PROP_DIR "/" JETSAM_PROP_PREFIX), 735 CFRangeMake(0, JETSAM_PROP_DIR_LENGTH + JETSAM_PROP_PREFIX_LENGTH + 1), 0)) { 736 continue; 737 } 738 739 if (CFStringCompareWithOptions(key, CFSTR(JETSAM_PROP_SUFFIX), 740 CFRangeMake(key_length - JETSAM_PROP_SUFFIX_LENGTH, JETSAM_PROP_SUFFIX_LENGTH), 0)) { 741 continue; 742 } 743 744 defaults = CFDictionaryGetValue(cachefiles, key); 745 break; 746 } 747 748 free(keys); 749 750 return defaults; 751} 752 753static CFDictionaryRef 754read_jetsam_defaults_from_file(void) { 755 DIR *dirp; 756 struct dirent *dp; 757 CFDictionaryRef defaults = NULL; 758 759 dirp = opendir(JETSAM_PROP_DIR); 760 while ((dp = readdir(dirp)) != NULL) { 761 char *fullpath; 762 763 if (dp->d_namlen <= (JETSAM_PROP_PREFIX_LENGTH + JETSAM_PROP_SUFFIX_LENGTH)) { 764 continue; 765 } 766 767 if (strncmp(dp->d_name, JETSAM_PROP_PREFIX, JETSAM_PROP_PREFIX_LENGTH)) { 768 continue; 769 } 770 771 if (strncmp(dp->d_name + dp->d_namlen - JETSAM_PROP_SUFFIX_LENGTH, JETSAM_PROP_SUFFIX, JETSAM_PROP_SUFFIX_LENGTH)) { 772 continue; 773 } 774 775 if (-1 != asprintf(&fullpath, "%s/%s", JETSAM_PROP_DIR, dp->d_name)) { 776 defaults = (CFDictionaryRef)CreateMyPropertyListFromFile(fullpath); 777 free(fullpath); 778 } 779 780 break; 781 } 782 783 if (dirp) { 784 closedir(dirp); 785 } 786 787 return defaults; 788} 789 790static bool 791submit_cached_defaults(void) { 792 launch_data_t msg, resp; 793 const void **keys = NULL; 794 int i; 795 796 if (_launchctl_jetsam_defaults_cached == NULL) { 797 return false; 798 } 799 800 /* The dictionary to transmit */ 801 CFMutableDictionaryRef payload_dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 802 803 /* Add a key to indicate that this is a special job */ 804 CFBooleanRef ID = kCFBooleanTrue; 805 CFDictionaryAddValue(payload_dict, CFSTR(LAUNCH_JOBKEY_DEFAULTS), ID); 806 807 CFMutableDictionaryRef defaults_dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 808 809 CFDictionaryAddValue(payload_dict, CFSTR(LAUNCHD_JOB_DEFAULTS), defaults_dict); 810 811 /* Compile appropriate launchd dictionary... */ 812 CFIndex count = CFDictionaryGetCount(_launchctl_jetsam_defaults_cached); 813 keys = (const void **)malloc(sizeof(void *) * count); 814 if (!keys) { 815 goto exit; 816 } 817 818 CFDictionaryGetKeysAndValues(_launchctl_jetsam_defaults_cached, keys, NULL); 819 820 for (i = 0; i < count; i++) { 821 CFStringRef label = (CFStringRef)keys[i]; 822 823 /* Get the defaults for the job */ 824 CFDictionaryRef job_defaults_dict = CFDictionaryGetValue(_launchctl_jetsam_defaults_cached, label); 825 if (!(job_defaults_dict && CFTypeCheck(job_defaults_dict, CFDictionary))) { 826 continue; 827 } 828 829 /* Create a new dictionary to represent the job */ 830 CFMutableDictionaryRef job_dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 831 832 /* Add the defaults */ 833 CFDictionaryAddValue(job_dict, CFSTR(LAUNCH_JOBKEY_JETSAMPROPERTIES), job_defaults_dict); 834 835 /* Finally, add the result to the main dictionary */ 836 CFDictionaryAddValue(defaults_dict, label, job_dict); 837 838 /* Cleanup */ 839 CFRelease(job_dict); 840 } 841 842 /* Send the payload */ 843 launch_data_t ldp = CF2launch_data(payload_dict); 844 845 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); 846 launch_data_dict_insert(msg, ldp, LAUNCH_KEY_SUBMITJOB); 847 848 resp = launch_msg(msg); 849 launch_data_free(msg); 850 851 launch_data_free(resp); 852 853exit: 854 CFRelease(defaults_dict); 855 CFRelease(payload_dict); 856 857 free(keys); 858 859 return true; 860} 861 862static boolean_t 863read_jetsam_defaults(void) 864{ 865 /* Current supported version */ 866 const int v = 3; 867 868 CFDictionaryRef jetsam_defaults = NULL; 869 870 if (require_jobs_from_cache()) { 871 jetsam_defaults = read_jetsam_defaults_from_cache(); 872 } else { 873 jetsam_defaults = read_jetsam_defaults_from_file(); 874 } 875 876 if (NULL == jetsam_defaults) { 877 launchctl_log(LOG_NOTICE, "%s: no jetsam property file found", getprogname()); 878 return false; 879 } 880 881 /* Validate the version */ 882 CFNumberRef defaults_vers = CFDictionaryGetValue(jetsam_defaults, CFSTR("Version")); 883 if (!(defaults_vers && CFTypeCheck(defaults_vers, CFNumber))) { 884 return false; 885 } 886 887 CFNumberRef supported_vers = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &v); 888 if (!(kCFCompareEqualTo == CFNumberCompare(defaults_vers, supported_vers, NULL ))) { 889 return false; 890 } 891 892 /* These defaults are merged within launchctl prior to submitting the job */ 893 _launchctl_jetsam_defaults = CFDictionaryGetValue(jetsam_defaults, CFSTR(LAUNCHD_JOB_DEFAULTS)); 894 if (!(_launchctl_jetsam_defaults && CFTypeCheck(_launchctl_jetsam_defaults, CFDictionary))) { 895 _launchctl_jetsam_defaults = NULL; 896 return false; 897 } 898 899 /* Cached defaults (applied by launchd) - parse and submit immediately as a fake job */ 900 _launchctl_jetsam_defaults_cached = CFDictionaryGetValue(jetsam_defaults, CFSTR(LAUNCHD_JOB_DEFAULTS_CACHED)); 901 if (!(_launchctl_jetsam_defaults_cached && CFTypeCheck(_launchctl_jetsam_defaults_cached, CFDictionary))) { 902 _launchctl_jetsam_defaults_cached = NULL; 903 return false; 904 } 905 906 submit_cached_defaults(); 907 908 return true; 909} 910 911#endif /* READ_JETSAM_DEFAULTS */ 912 913launch_data_t 914read_plist_file(const char *file, bool editondisk, bool load) 915{ 916 CFPropertyListRef plist; 917 launch_data_t r = NULL; 918#if TARGET_OS_EMBEDDED 919 if (require_jobs_from_cache()) { 920 plist = CreateMyPropertyListFromCachedFile(file); 921 } else { 922 plist = CreateMyPropertyListFromFile(file); 923 } 924#else 925 plist = CreateMyPropertyListFromFile(file); 926#endif 927 928 if (NULL == plist) { 929 launchctl_log(LOG_ERR, "%s: no plist was returned for: %s", getprogname(), file); 930 return NULL; 931 } 932 933 CFStringRef label = CFDictionaryGetValue(plist, CFSTR(LAUNCH_JOBKEY_LABEL)); 934 if (!(label && CFTypeCheck(label, CFString))) { 935 return NULL; 936 } 937 938 if (_launchctl_overrides_db) { 939 CFDictionaryRef overrides = CFDictionaryGetValue(_launchctl_overrides_db, label); 940 if (overrides && CFTypeCheck(overrides, CFDictionary)) { 941 CFBooleanRef disabled = CFDictionaryGetValue(overrides, CFSTR(LAUNCH_JOBKEY_DISABLED)); 942 if (disabled && CFTypeCheck(disabled, CFBoolean)) { 943 CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED), disabled); 944 } 945 } 946 } 947 948 if (editondisk) { 949 if (_launchctl_overrides_db) { 950 CFMutableDictionaryRef job = (CFMutableDictionaryRef)CFDictionaryGetValue(_launchctl_overrides_db, label); 951 if (!job || !CFTypeCheck(job, CFDictionary)) { 952 job = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 953 CFDictionarySetValue(_launchctl_overrides_db, label, job); 954 CFRelease(job); 955 } 956 957 CFDictionarySetValue(job, CFSTR(LAUNCH_JOBKEY_DISABLED), load ? kCFBooleanFalse : kCFBooleanTrue); 958 CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED), load ? kCFBooleanFalse : kCFBooleanTrue); 959 _launchctl_overrides_db_changed = true; 960 } else { 961 if (load) { 962 CFDictionaryRemoveValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED)); 963 } else { 964 CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED), kCFBooleanTrue); 965 } 966 WriteMyPropertyListToFile(plist, file); 967 } 968 } 969 970#if READ_JETSAM_DEFAULTS 971 if (_launchctl_jetsam_defaults) { 972 CFDictionaryRef job_defaults_dict = CFDictionaryGetValue(_launchctl_jetsam_defaults, label); 973 if (job_defaults_dict) { 974 CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_JETSAMPROPERTIES), job_defaults_dict); 975 } 976 } else { 977 /* The plist is missing. Set a default memory limit, since the device will be otherwise unusable */ 978 long default_limit = 0; 979 CFMutableDictionaryRef job_defaults_dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 980 CFNumberRef memory_limit = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &default_limit); 981 if (memory_limit) { 982 CFDictionaryAddValue(job_defaults_dict, CFSTR(LAUNCH_JOBKEY_JETSAMMEMORYLIMIT), memory_limit); 983 CFRelease(memory_limit); 984 } 985 CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_JETSAMPROPERTIES), job_defaults_dict); 986 CFRelease(job_defaults_dict); 987 } 988#endif /* READ_JETSAM_DEFAULTS */ 989 990 r = CF2launch_data(plist); 991 992 CFRelease(plist); 993 994 return r; 995} 996 997static bool 998sysctl_hw_streq(int mib_slot, const char *str) 999{ 1000 char buf[1000]; 1001 size_t bufsz = sizeof(buf); 1002 int mib[] = { CTL_HW, mib_slot }; 1003 1004 if (sysctl(mib, 2, buf, &bufsz, NULL, 0) != -1) { 1005 if (strcmp(buf, str) == 0) { 1006 return true; 1007 } 1008 } 1009 1010 return false; 1011} 1012 1013static void 1014limitloadtohardware_iterator(launch_data_t val, const char *key, void *ctx) 1015{ 1016 bool *result = ctx; 1017 1018 char name[128]; 1019 (void)snprintf(name, sizeof(name), "hw.%s", key); 1020 1021 int mib[2]; 1022 size_t sz = 2; 1023 if (*result != true && os_assumes_zero(sysctlnametomib(name, mib, &sz)) == 0) { 1024 if (launch_data_get_type(val) == LAUNCH_DATA_ARRAY) { 1025 size_t c = launch_data_array_get_count(val); 1026 1027 size_t i = 0; 1028 for (i = 0; i < c; i++) { 1029 launch_data_t oai = launch_data_array_get_index(val, i); 1030 if (sysctl_hw_streq(mib[1], launch_data_get_string(oai))) { 1031 *result = true; 1032 i = c; 1033 } 1034 } 1035 } 1036 } 1037} 1038 1039void 1040readfile(const char *what, struct load_unload_state *lus) 1041{ 1042 char ourhostname[1024]; 1043 launch_data_t tmpd, tmps, thejob, tmpa; 1044 bool job_disabled = false; 1045 size_t i, c; 1046 1047 gethostname(ourhostname, sizeof(ourhostname)); 1048 1049 if (NULL == (thejob = read_plist_file(what, lus->editondisk, lus->load))) { 1050 launchctl_log(LOG_ERR, "%s: no plist was returned for: %s", getprogname(), what); 1051 return; 1052 } 1053 1054 1055 if (NULL == launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LABEL)) { 1056 launchctl_log(LOG_ERR, "%s: missing the Label key: %s", getprogname(), what); 1057 goto out_bad; 1058 } 1059 1060 if ((launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_PROGRAM) == NULL) && 1061 (launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_PROGRAMARGUMENTS) == NULL)) { 1062 launchctl_log(LOG_ERR, "%s: neither a Program nor a ProgramArguments key was specified: %s", getprogname(), what); 1063 goto out_bad; 1064 } 1065 1066 if (NULL != (tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADFROMHOSTS))) { 1067 c = launch_data_array_get_count(tmpa); 1068 1069 for (i = 0; i < c; i++) { 1070 launch_data_t oai = launch_data_array_get_index(tmpa, i); 1071 if (!strcasecmp(ourhostname, launch_data_get_string(oai))) { 1072 goto out_bad; 1073 } 1074 } 1075 } 1076 1077 if (NULL != (tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOHOSTS))) { 1078 c = launch_data_array_get_count(tmpa); 1079 1080 for (i = 0; i < c; i++) { 1081 launch_data_t oai = launch_data_array_get_index(tmpa, i); 1082 if (!strcasecmp(ourhostname, launch_data_get_string(oai))) { 1083 break; 1084 } 1085 } 1086 1087 if (i == c) { 1088 goto out_bad; 1089 } 1090 } 1091 1092 if (NULL != (tmpd = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOHARDWARE))) { 1093 bool result = false; 1094 launch_data_dict_iterate(tmpd, limitloadtohardware_iterator, &result); 1095 if (!result) { 1096 goto out_bad; 1097 } 1098 } 1099 1100 if (NULL != (tmpd = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADFROMHARDWARE))) { 1101 bool result = false; 1102 launch_data_dict_iterate(tmpd, limitloadtohardware_iterator, &result); 1103 if (result) { 1104 goto out_bad; 1105 } 1106 } 1107 1108 /* If the manager is Aqua, the LimitLoadToSessionType should default to 1109 * "Aqua". 1110 * 1111 * <rdar://problem/8297909> 1112 */ 1113 if (!_launchctl_managername) { 1114 if (vproc_swap_string(NULL, VPROC_GSK_MGR_NAME, NULL, &_launchctl_managername)) { 1115 if (bootstrap_port) { 1116 /* This is only an error if we are running with a neutered 1117 * bootstrap port, otherwise we wouldn't expect this operating to 1118 * succeed. 1119 * 1120 * <rdar://problem/10514286> 1121 */ 1122 launchctl_log(LOG_ERR, "Could not obtain manager name: ppid/bootstrap: %d/0x%x", getppid(), bootstrap_port); 1123 } 1124 1125 _launchctl_managername = ""; 1126 } 1127 } 1128 1129 if (!lus->session_type) { 1130 if (strcmp(_launchctl_managername, "Aqua") == 0) { 1131 lus->session_type = "Aqua"; 1132 } 1133 } 1134 1135 if (lus->session_type && !(tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE))) { 1136 tmpa = launch_data_new_string("Aqua"); 1137 launch_data_dict_insert(thejob, tmpa, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE); 1138 } 1139 1140 if ((tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE))) { 1141 const char *allowed_session; 1142 bool skipjob = true; 1143 1144 /* My sincere apologies to anyone who has to deal with this 1145 * LimitLoadToSessionType madness. It was like this when I got here, but 1146 * I've knowingly made it worse, hopefully to the benefit of the end 1147 * user. 1148 * 1149 * See <rdar://problem/8769211> and <rdar://problem/7114980>. 1150 */ 1151 if (!lus->session_type && launch_data_get_type(tmpa) == LAUNCH_DATA_STRING) { 1152 if (strcasecmp("System", _launchctl_managername) == 0 && strcasecmp("System", launch_data_get_string(tmpa)) == 0) { 1153 skipjob = false; 1154 } 1155 } 1156 1157 if (lus->session_type) switch (launch_data_get_type(tmpa)) { 1158 case LAUNCH_DATA_ARRAY: 1159 c = launch_data_array_get_count(tmpa); 1160 for (i = 0; i < c; i++) { 1161 tmps = launch_data_array_get_index(tmpa, i); 1162 allowed_session = launch_data_get_string(tmps); 1163 if (strcasecmp(lus->session_type, allowed_session) == 0) { 1164 skipjob = false; 1165 /* we have to do the following so job_reparent_hack() works within launchd */ 1166 tmpa = launch_data_new_string(lus->session_type); 1167 launch_data_dict_insert(thejob, tmpa, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE); 1168 break; 1169 } 1170 } 1171 break; 1172 case LAUNCH_DATA_STRING: 1173 allowed_session = launch_data_get_string(tmpa); 1174 if (strcasecmp(lus->session_type, allowed_session) == 0) { 1175 skipjob = false; 1176 } 1177 break; 1178 default: 1179 break; 1180 } 1181 1182 if (skipjob) { 1183 goto out_bad; 1184 } 1185 } 1186 1187 if ((tmpd = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_DISABLED))) { 1188 job_disabled = job_disabled_logic(tmpd); 1189 } 1190 1191 if (lus->forceload) { 1192 job_disabled = false; 1193 } 1194 1195 if (job_disabled && lus->load) { 1196 goto out_bad; 1197 } 1198 1199 if (_launchctl_system_bootstrap || _launchctl_peruser_bootstrap) { 1200 uuid_t uuid; 1201 uuid_clear(uuid); 1202 1203 launch_data_t lduuid = launch_data_new_opaque(uuid, sizeof(uuid_t)); 1204 launch_data_dict_insert(thejob, lduuid, LAUNCH_JOBKEY_SECURITYSESSIONUUID); 1205 } 1206 1207 launch_data_array_append(lus->pass1, thejob); 1208 1209 if (_launchctl_verbose) { 1210 launchctl_log(LOG_NOTICE, "Will load: %s", what); 1211 } 1212 1213 return; 1214out_bad: 1215 if (_launchctl_verbose) { 1216 launchctl_log(LOG_NOTICE, "Ignored: %s", what); 1217 } 1218 launch_data_free(thejob); 1219} 1220 1221static void 1222job_disabled_dict_logic(launch_data_t obj, const char *key, void *context) 1223{ 1224 bool *r = context; 1225 1226 if (launch_data_get_type(obj) != LAUNCH_DATA_STRING) { 1227 return; 1228 } 1229 1230 if (strcasecmp(key, LAUNCH_JOBKEY_DISABLED_MACHINETYPE) == 0) { 1231 if (sysctl_hw_streq(HW_MACHINE, launch_data_get_string(obj))) { 1232 *r = true; 1233 } 1234 } else if (strcasecmp(key, LAUNCH_JOBKEY_DISABLED_MODELNAME) == 0) { 1235 if (sysctl_hw_streq(HW_MODEL, launch_data_get_string(obj))) { 1236 *r = true; 1237 } 1238 } 1239} 1240 1241bool 1242job_disabled_logic(launch_data_t obj) 1243{ 1244 bool r = false; 1245 1246 switch (launch_data_get_type(obj)) { 1247 case LAUNCH_DATA_DICTIONARY: 1248 launch_data_dict_iterate(obj, job_disabled_dict_logic, &r); 1249 break; 1250 case LAUNCH_DATA_BOOL: 1251 r = launch_data_get_bool(obj); 1252 break; 1253 default: 1254 break; 1255 } 1256 1257 return r; 1258} 1259 1260bool 1261path_goodness_check(const char *path, bool forceload) 1262{ 1263 struct stat sb; 1264 1265 if (stat(path, &sb) == -1) { 1266 launchctl_log(LOG_ERR, "%s: Couldn't stat(\"%s\"): %s", getprogname(), path, strerror(errno)); 1267 return false; 1268 } 1269 1270 if (forceload) { 1271 return true; 1272 } 1273 1274 if (sb.st_mode & (S_IWOTH|S_IWGRP)) { 1275 launchctl_log(LOG_ERR, "%s: Dubious permissions on file (skipping): %s", getprogname(), path); 1276 return false; 1277 } 1278 1279 if (sb.st_uid != 0 && sb.st_uid != getuid()) { 1280 launchctl_log(LOG_ERR, "%s: Dubious ownership on file (skipping): %s", getprogname(), path); 1281 return false; 1282 } 1283 1284 if (!(S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))) { 1285 launchctl_log(LOG_ERR, "%s: Dubious path. Not a regular file or directory (skipping): %s", getprogname(), path); 1286 return false; 1287 } 1288 1289 if ((!S_ISDIR(sb.st_mode)) && (fnmatch("*.plist", path, FNM_CASEFOLD) == FNM_NOMATCH)) { 1290 launchctl_log(LOG_ERR, "%s: Dubious file. Not of type .plist (skipping): %s", getprogname(), path); 1291 return false; 1292 } 1293 1294 return true; 1295} 1296 1297void 1298readpath(const char *what, struct load_unload_state *lus) 1299{ 1300 char buf[MAXPATHLEN]; 1301 struct stat sb; 1302 struct dirent *de; 1303 DIR *d; 1304 1305 if (!path_goodness_check(what, lus->forceload)) { 1306 return; 1307 } 1308 1309 if (stat(what, &sb) == -1) { 1310 return; 1311 } 1312 1313 if (S_ISREG(sb.st_mode)) { 1314 readfile(what, lus); 1315 } else if (S_ISDIR(sb.st_mode)) { 1316 if ((d = opendir(what)) == NULL) { 1317 launchctl_log(LOG_ERR, "%s: opendir() failed to open the directory", getprogname()); 1318 return; 1319 } 1320 1321 while ((de = readdir(d))) { 1322 if (de->d_name[0] == '.') { 1323 continue; 1324 } 1325 snprintf(buf, sizeof(buf), "%s/%s", what, de->d_name); 1326 1327 if (!path_goodness_check(buf, lus->forceload)) { 1328 continue; 1329 } 1330 1331 readfile(buf, lus); 1332 } 1333 closedir(d); 1334 } 1335} 1336 1337void 1338insert_event(launch_data_t job, const char *stream, const char *key, launch_data_t event) 1339{ 1340 launch_data_t launchevents, streamdict; 1341 1342 launchevents = launch_data_dict_lookup(job, LAUNCH_JOBKEY_LAUNCHEVENTS); 1343 if (launchevents == NULL) { 1344 launchevents = launch_data_alloc(LAUNCH_DATA_DICTIONARY); 1345 launch_data_dict_insert(job, launchevents, LAUNCH_JOBKEY_LAUNCHEVENTS); 1346 } 1347 1348 streamdict = launch_data_dict_lookup(launchevents, stream); 1349 if (streamdict == NULL) { 1350 streamdict = launch_data_alloc(LAUNCH_DATA_DICTIONARY); 1351 launch_data_dict_insert(launchevents, streamdict, stream); 1352 } 1353 1354 launch_data_dict_insert(streamdict, event, key); 1355} 1356 1357struct distill_context { 1358 launch_data_t base; 1359 launch_data_t newsockdict; 1360}; 1361 1362void 1363distill_jobs(launch_data_t jobs) 1364{ 1365 size_t i, c = launch_data_array_get_count(jobs); 1366 launch_data_t job; 1367 1368 for (i = 0; i < c; i++) { 1369 job = launch_data_array_get_index(jobs, i); 1370 distill_config_file(job); 1371 distill_fsevents(job); 1372 } 1373} 1374 1375void 1376distill_config_file(launch_data_t id_plist) 1377{ 1378 struct distill_context dc = { id_plist, NULL }; 1379 launch_data_t tmp; 1380 1381 if ((tmp = launch_data_dict_lookup(dc.base, LAUNCH_JOBKEY_SOCKETS))) { 1382 dc.newsockdict = launch_data_alloc(LAUNCH_DATA_DICTIONARY); 1383 launch_data_dict_iterate(tmp, sock_dict_cb, &dc); 1384 launch_data_dict_insert(dc.base, dc.newsockdict, LAUNCH_JOBKEY_SOCKETS); 1385 } 1386} 1387 1388void 1389sock_dict_cb(launch_data_t what, const char *key, void *context) 1390{ 1391 struct distill_context *dc = context; 1392 launch_data_t fdarray = launch_data_alloc(LAUNCH_DATA_ARRAY); 1393 1394 launch_data_dict_insert(dc->newsockdict, fdarray, key); 1395 1396 if (launch_data_get_type(what) == LAUNCH_DATA_DICTIONARY) { 1397 sock_dict_edit_entry(what, key, fdarray, dc->base); 1398 } else if (launch_data_get_type(what) == LAUNCH_DATA_ARRAY) { 1399 launch_data_t tmp; 1400 size_t i; 1401 1402 for (i = 0; i < launch_data_array_get_count(what); i++) { 1403 tmp = launch_data_array_get_index(what, i); 1404 sock_dict_edit_entry(tmp, key, fdarray, dc->base); 1405 } 1406 } 1407} 1408 1409void 1410sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob) 1411{ 1412 launch_data_t a, val; 1413 int sfd, st = SOCK_STREAM; 1414 bool passive = true; 1415 1416 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_TYPE))) { 1417 if (!strcasecmp(launch_data_get_string(val), "stream")) { 1418 st = SOCK_STREAM; 1419 } else if (!strcasecmp(launch_data_get_string(val), "dgram")) { 1420 st = SOCK_DGRAM; 1421 } else if (!strcasecmp(launch_data_get_string(val), "seqpacket")) { 1422 st = SOCK_SEQPACKET; 1423 } 1424 } 1425 1426 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PASSIVE))) { 1427 passive = launch_data_get_bool(val); 1428 } 1429 1430 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_SECUREWITHKEY))) { 1431 char secdir[] = LAUNCH_SECDIR, buf[1024]; 1432 launch_data_t uenv = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES); 1433 1434 if (NULL == uenv) { 1435 uenv = launch_data_alloc(LAUNCH_DATA_DICTIONARY); 1436 launch_data_dict_insert(thejob, uenv, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES); 1437 } 1438 1439 mkdtemp(secdir); 1440 1441 sprintf(buf, "%s/%s", secdir, key); 1442 1443 a = launch_data_new_string(buf); 1444 launch_data_dict_insert(tmp, a, LAUNCH_JOBSOCKETKEY_PATHNAME); 1445 a = launch_data_new_string(buf); 1446 launch_data_dict_insert(uenv, a, launch_data_get_string(val)); 1447 } 1448 1449 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PATHNAME))) { 1450 struct sockaddr_un sun; 1451 mode_t sun_mode = 0; 1452 mode_t oldmask; 1453 bool setm = false; 1454 1455 memset(&sun, 0, sizeof(sun)); 1456 1457 sun.sun_family = AF_UNIX; 1458 1459 strncpy(sun.sun_path, launch_data_get_string(val), sizeof(sun.sun_path)); 1460 1461 if (posix_assumes_zero(sfd = _fd(socket(AF_UNIX, st, 0))) == -1) { 1462 return; 1463 } 1464 1465 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PATHMODE))) { 1466 sun_mode = (mode_t)launch_data_get_integer(val); 1467 setm = true; 1468 } 1469 1470 if (passive) { 1471 if (unlink(sun.sun_path) == -1 && errno != ENOENT) { 1472 close(sfd); 1473 return; 1474 } 1475 oldmask = umask(S_IRWXG|S_IRWXO); 1476 if (bind(sfd, (struct sockaddr *)&sun, (socklen_t) sizeof sun) == -1) { 1477 close(sfd); 1478 umask(oldmask); 1479 return; 1480 } 1481 umask(oldmask); 1482 if (setm) { 1483 chmod(sun.sun_path, sun_mode); 1484 } 1485 if ((st == SOCK_STREAM || st == SOCK_SEQPACKET) && listen(sfd, -1) == -1) { 1486 close(sfd); 1487 return; 1488 } 1489 } else if (connect(sfd, (struct sockaddr *)&sun, (socklen_t) sizeof sun) == -1) { 1490 close(sfd); 1491 return; 1492 } 1493 1494 val = launch_data_new_fd(sfd); 1495 launch_data_array_append(fdarray, val); 1496 } else { 1497 launch_data_t rnames = NULL; 1498 const char *node = NULL, *serv = NULL, *mgroup = NULL; 1499 char servnbuf[50]; 1500 struct addrinfo hints, *res0, *res; 1501 int gerr, sock_opt = 1; 1502 1503 memset(&hints, 0, sizeof(hints)); 1504 1505 hints.ai_socktype = st; 1506 if (passive) { 1507 hints.ai_flags |= AI_PASSIVE; 1508 } 1509 1510 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_NODENAME))) { 1511 node = launch_data_get_string(val); 1512 } 1513 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_MULTICASTGROUP))) { 1514 mgroup = launch_data_get_string(val); 1515 } 1516 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_SERVICENAME))) { 1517 if (LAUNCH_DATA_INTEGER == launch_data_get_type(val)) { 1518 sprintf(servnbuf, "%lld", launch_data_get_integer(val)); 1519 serv = servnbuf; 1520 } else { 1521 serv = launch_data_get_string(val); 1522 } 1523 } 1524 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_FAMILY))) { 1525 if (!strcasecmp("IPv4", launch_data_get_string(val))) { 1526 hints.ai_family = AF_INET; 1527 } else if (!strcasecmp("IPv6", launch_data_get_string(val))) { 1528 hints.ai_family = AF_INET6; 1529 } 1530 } 1531 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PROTOCOL))) { 1532 if (!strcasecmp("TCP", launch_data_get_string(val))) { 1533 hints.ai_protocol = IPPROTO_TCP; 1534 } else if (!strcasecmp("UDP", launch_data_get_string(val))) { 1535 hints.ai_protocol = IPPROTO_UDP; 1536 } 1537 } 1538 if ((rnames = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_BONJOUR))) { 1539 if (LAUNCH_DATA_BOOL != launch_data_get_type(rnames) || launch_data_get_bool(rnames)) { 1540 launch_data_t newevent; 1541 char eventkey[100]; 1542 1543 newevent = launch_data_copy(tmp); 1544 snprintf(eventkey, sizeof(eventkey), "com.apple.launchd.%s", key); 1545 insert_event(thejob, "com.apple.bonjour.registration", eventkey, newevent); 1546 } 1547 } 1548 1549 if ((gerr = getaddrinfo(node, serv, &hints, &res0)) != 0) { 1550 launchctl_log(LOG_ERR, "getaddrinfo(): %s", gai_strerror(gerr)); 1551 return; 1552 } 1553 1554 for (res = res0; res; res = res->ai_next) { 1555 if ((sfd = _fd(socket(res->ai_family, res->ai_socktype, res->ai_protocol))) == -1) { 1556 launchctl_log(LOG_ERR, "socket(): %s", strerror(errno)); 1557 return; 1558 } 1559 1560 do_application_firewall_magic(sfd, thejob); 1561 1562 if (hints.ai_flags & AI_PASSIVE) { 1563 if (AF_INET6 == res->ai_family && -1 == setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY, 1564 (void *)&sock_opt, (socklen_t) sizeof sock_opt)) { 1565 launchctl_log(LOG_ERR, "setsockopt(IPV6_V6ONLY): %m"); 1566 return; 1567 } 1568 if (mgroup) { 1569 if (setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, (void *)&sock_opt, (socklen_t) sizeof sock_opt) == -1) { 1570 launchctl_log(LOG_ERR, "setsockopt(SO_REUSEPORT): %s", strerror(errno)); 1571 return; 1572 } 1573 } else { 1574 if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt, (socklen_t) sizeof sock_opt) == -1) { 1575 launchctl_log(LOG_ERR, "setsockopt(SO_REUSEADDR): %s", strerror(errno)); 1576 return; 1577 } 1578 } 1579 if (bind(sfd, res->ai_addr, res->ai_addrlen) == -1) { 1580 launchctl_log(LOG_ERR, "bind(): %s", strerror(errno)); 1581 return; 1582 } 1583 /* The kernel may have dynamically assigned some part of the 1584 * address. (The port being a common example.) 1585 */ 1586 if (getsockname(sfd, res->ai_addr, &res->ai_addrlen) == -1) { 1587 launchctl_log(LOG_ERR, "getsockname(): %s", strerror(errno)); 1588 return; 1589 } 1590 1591 if (mgroup) { 1592 do_mgroup_join(sfd, res->ai_family, res->ai_socktype, res->ai_protocol, mgroup); 1593 } 1594 if ((res->ai_socktype == SOCK_STREAM || res->ai_socktype == SOCK_SEQPACKET) && listen(sfd, -1) == -1) { 1595 launchctl_log(LOG_ERR, "listen(): %s", strerror(errno)); 1596 return; 1597 } 1598 } else { 1599 if (connect(sfd, res->ai_addr, res->ai_addrlen) == -1) { 1600 launchctl_log(LOG_ERR, "connect(): %s", strerror(errno)); 1601 return; 1602 } 1603 } 1604 val = launch_data_new_fd(sfd); 1605 launch_data_array_append(fdarray, val); 1606 } 1607 } 1608} 1609 1610void 1611distill_fsevents(launch_data_t id_plist) 1612{ 1613 launch_data_t copy, newevent; 1614 launch_data_t tmp, tmp2; 1615 1616 if ((tmp = launch_data_dict_lookup(id_plist, LAUNCH_JOBKEY_QUEUEDIRECTORIES))) { 1617 copy = launch_data_copy(tmp); 1618 (void)launch_data_dict_remove(id_plist, LAUNCH_JOBKEY_QUEUEDIRECTORIES); 1619 1620 newevent = launch_data_alloc(LAUNCH_DATA_DICTIONARY); 1621 launch_data_dict_insert(newevent, copy, LAUNCH_JOBKEY_QUEUEDIRECTORIES); 1622 insert_event(id_plist, "com.apple.fsevents.matching", "com.apple.launchd." LAUNCH_JOBKEY_QUEUEDIRECTORIES, newevent); 1623 } 1624 1625 if ((tmp = launch_data_dict_lookup(id_plist, LAUNCH_JOBKEY_WATCHPATHS))) { 1626 copy = launch_data_copy(tmp); 1627 (void)launch_data_dict_remove(id_plist, LAUNCH_JOBKEY_WATCHPATHS); 1628 1629 newevent = launch_data_alloc(LAUNCH_DATA_DICTIONARY); 1630 launch_data_dict_insert(newevent, copy, LAUNCH_JOBKEY_WATCHPATHS); 1631 insert_event(id_plist, "com.apple.fsevents.matching", "com.apple.launchd." LAUNCH_JOBKEY_WATCHPATHS, newevent); 1632 } 1633 1634 if ((tmp = launch_data_dict_lookup(id_plist, LAUNCH_JOBKEY_KEEPALIVE))) { 1635 if ((tmp2 = launch_data_dict_lookup(tmp, LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE))) { 1636 copy = launch_data_copy(tmp2); 1637 (void)launch_data_dict_remove(tmp, LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE); 1638 1639 newevent = launch_data_alloc(LAUNCH_DATA_DICTIONARY); 1640 launch_data_dict_insert(newevent, copy, LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE); 1641 insert_event(id_plist, "com.apple.fsevents.matching", "com.apple.launchd." LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE, newevent); 1642 } 1643 } 1644} 1645 1646void 1647do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup) 1648{ 1649 struct addrinfo hints, *res0, *res; 1650 struct ip_mreq mreq; 1651 struct ipv6_mreq m6req; 1652 int gerr; 1653 1654 memset(&hints, 0, sizeof(hints)); 1655 1656 hints.ai_flags |= AI_PASSIVE; 1657 hints.ai_family = family; 1658 hints.ai_socktype = socktype; 1659 hints.ai_protocol = protocol; 1660 1661 if ((gerr = getaddrinfo(mgroup, NULL, &hints, &res0)) != 0) { 1662 launchctl_log(LOG_ERR, "getaddrinfo(): %s", gai_strerror(gerr)); 1663 return; 1664 } 1665 1666 for (res = res0; res; res = res->ai_next) { 1667 if (AF_INET == family) { 1668 memset(&mreq, 0, sizeof(mreq)); 1669 mreq.imr_multiaddr = ((struct sockaddr_in *)res->ai_addr)->sin_addr; 1670 if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, (socklen_t) sizeof mreq) == -1) { 1671 launchctl_log(LOG_ERR, "setsockopt(IP_ADD_MEMBERSHIP): %s", strerror(errno)); 1672 continue; 1673 } 1674 break; 1675 } else if (AF_INET6 == family) { 1676 memset(&m6req, 0, sizeof(m6req)); 1677 m6req.ipv6mr_multiaddr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; 1678 if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &m6req, (socklen_t) sizeof m6req) == -1) { 1679 launchctl_log(LOG_ERR, "setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno)); 1680 continue; 1681 } 1682 break; 1683 } else { 1684 launchctl_log(LOG_ERR, "unknown family during multicast group bind!"); 1685 break; 1686 } 1687 } 1688 1689 freeaddrinfo(res0); 1690} 1691 1692#pragma mark XPC Cache 1693 1694#if TARGET_OS_EMBEDDED 1695 1696CFPropertyListRef 1697GetPropertyListFromCache(void) 1698{ 1699 static CFPropertyListRef propertyList; 1700 CFDataRef cacheData; 1701 CFErrorRef error; 1702 1703 if (!propertyList) { 1704 uint8_t *data = NULL; 1705 unsigned long sz = 0; 1706 1707 void *handle = dlopen(XPC_PLIST_CACHE, RTLD_NOW); 1708 1709 if (handle) { 1710 void *fnptr = dlsym(handle, "__xpcd_cache"); 1711 1712 if (fnptr) { 1713 Dl_info image_info; 1714 1715 int rv = dladdr(fnptr, &image_info); 1716 if (rv != 0) { 1717 data = getsectiondata(image_info.dli_fbase, "__TEXT", "__xpcd_cache", &sz); 1718 } else { 1719 launchctl_log(LOG_ERR, "cache loading failed: failed to find address of __xpcd_cache symbol."); 1720 } 1721 } else { 1722 launchctl_log(LOG_ERR, "cache loading failed: failed to find __xpcd_cache symbol in cache."); 1723 } 1724 } else { 1725 launchctl_log(LOG_ERR, "cache loading failed: dlopen returned %s.", dlerror()); 1726 } 1727 1728 if (data) { 1729 cacheData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, data, sz, kCFAllocatorNull); 1730 if (cacheData) { 1731 propertyList = CFPropertyListCreateWithData(kCFAllocatorDefault, cacheData, kCFPropertyListMutableContainersAndLeaves, NULL, &error); 1732 CFRelease(cacheData); 1733 } else { 1734 launchctl_log(LOG_ERR, "cache loading failed: unable to create data out of memory region."); 1735 } 1736 } else { 1737 launchctl_log(LOG_ERR, "cache loading failed: no cache data found in __TEXT,__xpcd_cache segment."); 1738 } 1739 } 1740 1741 return propertyList; 1742} 1743 1744CFPropertyListRef 1745CreateMyPropertyListFromCachedFile(const char *posixfile) 1746{ 1747 CFPropertyListRef cache = GetPropertyListFromCache(); 1748 CFPropertyListRef job = NULL; 1749 1750 if (cache) { 1751 CFPropertyListRef jobs = CFDictionaryGetValue(cache, CFSTR(XPC_PLIST_CACHE_KEY)); 1752 1753 if (jobs) { 1754 CFStringRef key = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, posixfile, kCFStringEncodingUTF8, kCFAllocatorNull); 1755 1756 if (key) { 1757 job = CFDictionaryGetValue(jobs, key); 1758 CFRelease(key); 1759 } 1760 } 1761 } 1762 1763 if (job) { 1764 CFRetain(job); 1765 } 1766 return job; 1767} 1768 1769bool 1770require_jobs_from_cache(void) 1771{ 1772 char buf[1024]; 1773 size_t len; 1774 char *ptr; 1775 unsigned long val; 1776 bool cs_disabled = false; 1777 len = sizeof(buf); 1778 1779 if (sysctlbyname("kern.bootargs", buf, &len, NULL, 0) == 0) { 1780 ptr = strnstr(buf, "cs_enforcement_disable=", len); 1781 if (ptr != NULL) { 1782 val = strtoul(ptr + strlen("cs_enforcement_disable="), NULL, 10); 1783 cs_disabled = (val != 0); 1784 } 1785 ptr = strnstr(buf, "launchctl_enforce_codesign=", len); 1786 if (ptr != NULL) { 1787 char *endptr = NULL; 1788 char *startptr = ptr + strlen("launchctl_enforce_codesign="); 1789 val = strtoul(startptr, &endptr, 10); 1790 cs_disabled = (val == 0 && startptr != endptr); 1791 } 1792 } 1793 1794 return !cs_disabled; 1795} 1796 1797#endif 1798 1799#pragma mark File-based Property Lists 1800 1801CFPropertyListRef 1802CreateMyPropertyListFromFile(const char *posixfile) 1803{ 1804 CFPropertyListRef propertyList; 1805 CFStringRef errorString; 1806 CFDataRef resourceData; 1807 SInt32 errorCode; 1808 CFURLRef fileURL; 1809 1810 fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false); 1811 if (!fileURL) { 1812 launchctl_log(LOG_ERR, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed", getprogname(), posixfile); 1813 } 1814 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode)) { 1815 launchctl_log(LOG_ERR, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d", getprogname(), posixfile, (int)errorCode); 1816 } 1817 1818 propertyList = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resourceData, kCFPropertyListMutableContainersAndLeaves, &errorString); 1819 if (fileURL) { 1820 CFRelease(fileURL); 1821 } 1822 1823 if (resourceData) { 1824 CFRelease(resourceData); 1825 } 1826 1827 return propertyList; 1828} 1829 1830void 1831WriteMyPropertyListToFile(CFPropertyListRef plist, const char *posixfile) 1832{ 1833 CFDataRef resourceData; 1834 CFURLRef fileURL; 1835 SInt32 errorCode; 1836 1837 fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false); 1838 if (!fileURL) { 1839 launchctl_log(LOG_ERR, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed", getprogname(), posixfile); 1840 } 1841 resourceData = CFPropertyListCreateXMLData(kCFAllocatorDefault, plist); 1842 if (resourceData == NULL) { 1843 launchctl_log(LOG_ERR, "%s: CFPropertyListCreateXMLData(%s) failed", getprogname(), posixfile); 1844 } 1845 if (!CFURLWriteDataAndPropertiesToResource(fileURL, resourceData, NULL, &errorCode)) { 1846 launchctl_log(LOG_ERR, "%s: CFURLWriteDataAndPropertiesToResource(%s) failed: %d", getprogname(), posixfile, (int)errorCode); 1847 } 1848 1849 if (resourceData) { 1850 CFRelease(resourceData); 1851 } 1852} 1853 1854static inline Boolean 1855_is_launch_data_t(launch_data_t obj) 1856{ 1857 Boolean result = true; 1858 1859 switch (launch_data_get_type(obj)) { 1860 case LAUNCH_DATA_STRING : break; 1861 case LAUNCH_DATA_INTEGER : break; 1862 case LAUNCH_DATA_REAL : break; 1863 case LAUNCH_DATA_BOOL : break; 1864 case LAUNCH_DATA_ARRAY : break; 1865 case LAUNCH_DATA_DICTIONARY : break; 1866 case LAUNCH_DATA_FD : break; 1867 case LAUNCH_DATA_MACHPORT : break; 1868 default : result = false; 1869 } 1870 1871 return result; 1872} 1873 1874static void 1875_launch_data_iterate(launch_data_t obj, const char *key, CFMutableDictionaryRef dict) 1876{ 1877 if (obj && _is_launch_data_t(obj)) { 1878 CFStringRef cfKey = CFStringCreateWithCString(NULL, key, kCFStringEncodingUTF8); 1879 CFTypeRef cfVal = CFTypeCreateFromLaunchData(obj); 1880 1881 if (cfVal) { 1882 CFDictionarySetValue(dict, cfKey, cfVal); 1883 CFRelease(cfVal); 1884 } 1885 CFRelease(cfKey); 1886 } 1887} 1888 1889static CFTypeRef 1890CFTypeCreateFromLaunchData(launch_data_t obj) 1891{ 1892 CFTypeRef cfObj = NULL; 1893 1894 switch (launch_data_get_type(obj)) { 1895 case LAUNCH_DATA_STRING: { 1896 const char *str = launch_data_get_string(obj); 1897 cfObj = CFStringCreateWithCString(NULL, str, kCFStringEncodingUTF8); 1898 break; 1899 } 1900 case LAUNCH_DATA_INTEGER: { 1901 long long integer = launch_data_get_integer(obj); 1902 cfObj = CFNumberCreate(NULL, kCFNumberLongLongType, &integer); 1903 break; 1904 } 1905 case LAUNCH_DATA_REAL: { 1906 double real = launch_data_get_real(obj); 1907 cfObj = CFNumberCreate(NULL, kCFNumberDoubleType, &real); 1908 break; 1909 } 1910 case LAUNCH_DATA_BOOL: { 1911 bool yesno = launch_data_get_bool(obj); 1912 cfObj = yesno ? kCFBooleanTrue : kCFBooleanFalse; 1913 break; 1914 } 1915 case LAUNCH_DATA_ARRAY: { 1916 cfObj = (CFTypeRef)CFArrayCreateFromLaunchArray(obj); 1917 break; 1918 } 1919 case LAUNCH_DATA_DICTIONARY: { 1920 cfObj = (CFTypeRef)CFDictionaryCreateFromLaunchDictionary(obj); 1921 break; 1922 } 1923 case LAUNCH_DATA_FD: { 1924 int fd = launch_data_get_fd(obj); 1925 cfObj = CFNumberCreate(NULL, kCFNumberIntType, &fd); 1926 break; 1927 } 1928 case LAUNCH_DATA_MACHPORT: { 1929 mach_port_t port = launch_data_get_machport(obj); 1930 cfObj = CFNumberCreate(NULL, kCFNumberIntType, &port); 1931 break; 1932 } 1933 default: 1934 break; 1935 } 1936 1937 return cfObj; 1938} 1939 1940#pragma mark CFArray 1941static CFArrayRef 1942CFArrayCreateFromLaunchArray(launch_data_t arr) 1943{ 1944 CFArrayRef result = NULL; 1945 CFMutableArrayRef mutResult = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 1946 1947 if (launch_data_get_type(arr) == LAUNCH_DATA_ARRAY) { 1948 unsigned int count = launch_data_array_get_count(arr); 1949 unsigned int i = 0; 1950 1951 for (i = 0; i < count; i++) { 1952 launch_data_t launch_obj = launch_data_array_get_index(arr, i); 1953 CFTypeRef obj = CFTypeCreateFromLaunchData(launch_obj); 1954 1955 if (obj) { 1956 CFArrayAppendValue(mutResult, obj); 1957 CFRelease(obj); 1958 } 1959 } 1960 1961 result = CFArrayCreateCopy(NULL, mutResult); 1962 } 1963 1964 if (mutResult) { 1965 CFRelease(mutResult); 1966 } 1967 return result; 1968} 1969 1970#pragma mark CFDictionary / CFPropertyList 1971static CFDictionaryRef 1972CFDictionaryCreateFromLaunchDictionary(launch_data_t dict) 1973{ 1974 CFDictionaryRef result = NULL; 1975 1976 if (launch_data_get_type(dict) == LAUNCH_DATA_DICTIONARY) { 1977 CFMutableDictionaryRef mutResult = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1978 1979 launch_data_dict_iterate(dict, (void (*)(launch_data_t, const char *, void *))_launch_data_iterate, mutResult); 1980 1981 result = CFDictionaryCreateCopy(NULL, mutResult); 1982 CFRelease(mutResult); 1983 } 1984 1985 return result; 1986} 1987 1988void 1989myCFDictionaryApplyFunction(const void *key, const void *value, void *context) 1990{ 1991 launch_data_t ik, iw, where = context; 1992 1993 ik = CF2launch_data(key); 1994 iw = CF2launch_data(value); 1995 1996 launch_data_dict_insert(where, iw, launch_data_get_string(ik)); 1997 launch_data_free(ik); 1998} 1999 2000launch_data_t 2001CF2launch_data(CFTypeRef cfr) 2002{ 2003 launch_data_t r; 2004 CFTypeID cft = CFGetTypeID(cfr); 2005 2006 if (cft == CFStringGetTypeID()) { 2007 char buf[4096]; 2008 CFStringGetCString(cfr, buf, sizeof(buf), kCFStringEncodingUTF8); 2009 r = launch_data_alloc(LAUNCH_DATA_STRING); 2010 launch_data_set_string(r, buf); 2011 } else if (cft == CFBooleanGetTypeID()) { 2012 r = launch_data_alloc(LAUNCH_DATA_BOOL); 2013 launch_data_set_bool(r, CFBooleanGetValue(cfr)); 2014 } else if (cft == CFArrayGetTypeID()) { 2015 CFIndex i, ac = CFArrayGetCount(cfr); 2016 r = launch_data_alloc(LAUNCH_DATA_ARRAY); 2017 for (i = 0; i < ac; i++) { 2018 CFTypeRef v = CFArrayGetValueAtIndex(cfr, i); 2019 if (v) { 2020 launch_data_t iv = CF2launch_data(v); 2021 launch_data_array_set_index(r, iv, i); 2022 } 2023 } 2024 } else if (cft == CFDictionaryGetTypeID()) { 2025 r = launch_data_alloc(LAUNCH_DATA_DICTIONARY); 2026 CFDictionaryApplyFunction(cfr, myCFDictionaryApplyFunction, r); 2027 } else if (cft == CFDataGetTypeID()) { 2028 r = launch_data_alloc(LAUNCH_DATA_OPAQUE); 2029 launch_data_set_opaque(r, CFDataGetBytePtr(cfr), CFDataGetLength(cfr)); 2030 } else if (cft == CFNumberGetTypeID()) { 2031 long long n; 2032 double d; 2033 CFNumberType cfnt = CFNumberGetType(cfr); 2034 switch (cfnt) { 2035 case kCFNumberSInt8Type: 2036 case kCFNumberSInt16Type: 2037 case kCFNumberSInt32Type: 2038 case kCFNumberSInt64Type: 2039 case kCFNumberCharType: 2040 case kCFNumberShortType: 2041 case kCFNumberIntType: 2042 case kCFNumberLongType: 2043 case kCFNumberLongLongType: 2044 CFNumberGetValue(cfr, kCFNumberLongLongType, &n); 2045 r = launch_data_alloc(LAUNCH_DATA_INTEGER); 2046 launch_data_set_integer(r, n); 2047 break; 2048 case kCFNumberFloat32Type: 2049 case kCFNumberFloat64Type: 2050 case kCFNumberFloatType: 2051 case kCFNumberDoubleType: 2052 CFNumberGetValue(cfr, kCFNumberDoubleType, &d); 2053 r = launch_data_alloc(LAUNCH_DATA_REAL); 2054 launch_data_set_real(r, d); 2055 break; 2056 default: 2057 r = NULL; 2058 break; 2059 } 2060 } else { 2061 r = NULL; 2062 } 2063 return r; 2064} 2065 2066int 2067help_cmd(int argc, char *const argv[]) 2068{ 2069 size_t i, l, cmdwidth = 0; 2070 2071 int level = LOG_NOTICE; 2072 if (argc == 0 || argv == NULL) { 2073 level = LOG_ERR; 2074 } 2075 2076 launchctl_log(level, "usage: %s <subcommand>", getprogname()); 2077 2078 for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) { 2079 l = strlen(cmds[i].name); 2080 if (l > cmdwidth) { 2081 cmdwidth = l; 2082 } 2083 } 2084 2085 for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) { 2086 launchctl_log(level, "\t%-*s\t%s", (int)cmdwidth, cmds[i].name, cmds[i].desc); 2087 } 2088 2089 return 0; 2090} 2091 2092int 2093exit_cmd(int argc __attribute__((unused)), char *const argv[] __attribute__((unused))) 2094{ 2095 exit(0); 2096} 2097 2098int 2099_fd(int fd) 2100{ 2101 if (fd >= 0) 2102 fcntl(fd, F_SETFD, 1); 2103 return fd; 2104} 2105 2106void 2107do_single_user_mode(bool sflag) 2108{ 2109 if (sflag) { 2110 while (!do_single_user_mode2()) { 2111 sleep(1); 2112 } 2113 } 2114} 2115 2116bool 2117do_single_user_mode2(void) 2118{ 2119 bool runcom_fsck = true; /* should_fsck(); */ 2120 int wstatus; 2121 int fd; 2122 pid_t p; 2123 2124 switch ((p = fork())) { 2125 case -1: 2126 syslog(LOG_ERR, "can't fork single-user shell, trying again: %m"); 2127 return false; 2128 case 0: 2129 break; 2130 default: 2131 (void)os_assumes_zero(waitpid(p, &wstatus, 0)); 2132 if (WIFEXITED(wstatus)) { 2133 if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) { 2134 return true; 2135 } else { 2136 launchctl_log(LOG_NOTICE, "single user mode: exit status: %d", WEXITSTATUS(wstatus)); 2137 } 2138 } else { 2139 launchctl_log(LOG_NOTICE, "single user mode shell: %s", strsignal(WTERMSIG(wstatus))); 2140 } 2141 return false; 2142 } 2143 2144 revoke(_PATH_CONSOLE); 2145 if (posix_assumes_zero((fd = open(_PATH_CONSOLE, O_RDWR))) == -1) { 2146 _exit(EXIT_FAILURE); 2147 } 2148 if (posix_assumes_zero(login_tty(fd)) == -1) { 2149 _exit(EXIT_FAILURE); 2150 } 2151 2152 mach_timespec_t wt = { 5, 0 }; 2153 IOKitWaitQuiet(kIOMasterPortDefault, &wt); /* This will hopefully return after all the kexts have shut up. */ 2154 2155 setenv("TERM", "vt100", 1); 2156 if (runcom_fsck) { 2157 fprintf(stdout, "Singleuser boot -- fsck not done\n"); 2158 fprintf(stdout, "Root device is mounted read-only\n"); 2159 fprintf(stdout, "If you want to make modifications to files:\n"); 2160 fprintf(stdout, "\t/sbin/fsck -fy\n\t/sbin/mount -uw /\n"); 2161 fprintf(stdout, "If you wish to boot the system:\n"); 2162 fprintf(stdout, "\texit\n"); 2163 fflush(stdout); 2164 } 2165 2166 execl(_PATH_BSHELL, "-sh", NULL); 2167 fprintf(stderr, "can't exec %s for single user: %m\n", _PATH_BSHELL); 2168 _exit(EXIT_FAILURE); 2169} 2170 2171void 2172do_crash_debug_mode(void) 2173{ 2174 while (!do_crash_debug_mode2()) { 2175 sleep(1); 2176 } 2177} 2178 2179bool 2180do_crash_debug_mode2(void) 2181{ 2182 int wstatus; 2183 int fd; 2184 pid_t p; 2185 2186 switch ((p = fork())) { 2187 case -1: 2188 syslog(LOG_ERR, "can't fork crash debug shell, trying again: %m"); 2189 return false; 2190 case 0: 2191 break; 2192 default: 2193 (void)os_assumes_zero(waitpid(p, &wstatus, 0)); 2194 if (WIFEXITED(wstatus)) { 2195 if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) { 2196 return true; 2197 } else { 2198 launchctl_log(LOG_NOTICE, "crash debug mode: exit status: %d", WEXITSTATUS(wstatus)); 2199 } 2200 } else { 2201 launchctl_log(LOG_NOTICE, "crash debug mode shell: %s", strsignal(WTERMSIG(wstatus))); 2202 } 2203 return false; 2204 } 2205 2206 revoke(_PATH_CONSOLE); 2207 if (posix_assumes_zero((fd = open(_PATH_CONSOLE, O_RDWR))) == -1) { 2208 _exit(EXIT_FAILURE); 2209 } 2210 if (posix_assumes_zero(login_tty(fd)) == -1) { 2211 _exit(EXIT_FAILURE); 2212 } 2213 2214 /* The idea is to wait until all the kexts have quiesced to prevent a bunch 2215 * of log messages from being slammed onto the console prompt. It mostly 2216 * works. 2217 */ 2218 mach_timespec_t wt = { 5, 0 }; 2219 IOKitWaitQuiet(kIOMasterPortDefault, &wt); 2220 2221 setenv("TERM", "vt100", 1); 2222 fprintf(stdout, "Entering boot-time debugging mode...\n"); 2223 fprintf(stdout, "The system bootstrapper process has crashed. To debug:\n"); 2224 fprintf(stdout, "\tgdb attach %i\n", getppid()); 2225 fprintf(stdout, "You can try booting the system with:\n"); 2226 fprintf(stdout, "\tlaunchctl load -S System -D All\n"); 2227 2228 execl(_PATH_BSHELL, "-sh", NULL); 2229 fprintf(stderr, "can't exec %s for crash debug: %m\n", _PATH_BSHELL); 2230 _exit(EXIT_FAILURE); 2231} 2232 2233static void 2234exit_at_sigterm(int sig) 2235{ 2236 if (sig == SIGTERM) { 2237 _exit(EXIT_SUCCESS); 2238 } 2239} 2240 2241void 2242fatal_signal_handler(int sig __attribute__((unused)), siginfo_t *si __attribute__((unused)), void *uap __attribute__((unused))) 2243{ 2244 do_crash_debug_mode(); 2245} 2246 2247void 2248handle_system_bootstrapper_crashes_separately(void) 2249{ 2250 if (!_launchctl_startup_debugging) { 2251 return; 2252 } 2253 2254 fprintf(stdout, "com.apple.launchctl.System\t\t\t*** Handling system bootstrapper crashes separately. ***\n"); 2255 struct sigaction fsa; 2256 2257 fsa.sa_sigaction = fatal_signal_handler; 2258 fsa.sa_flags = SA_SIGINFO; 2259 sigemptyset(&fsa.sa_mask); 2260 2261 (void)posix_assumes_zero(sigaction(SIGILL, &fsa, NULL)); 2262 (void)posix_assumes_zero(sigaction(SIGFPE, &fsa, NULL)); 2263 (void)posix_assumes_zero(sigaction(SIGBUS, &fsa, NULL)); 2264 (void)posix_assumes_zero(sigaction(SIGTRAP, &fsa, NULL)); 2265 (void)posix_assumes_zero(sigaction(SIGABRT, &fsa, NULL)); 2266} 2267 2268#if TARGET_OS_EMBEDDED 2269static void 2270init_data_protection(void) 2271{ 2272 if (path_check("/usr/libexec/init_data_protection")) { 2273 const char *init_cp[] = { "/usr/libexec/init_data_protection", NULL }; 2274 if (fwexec(init_cp, NULL) == -1) { 2275 launchctl_log(LOG_ERR, "Couldn't init content protection: %d: %s", errno, strerror(errno)); 2276 (void)reboot(RB_HALT); 2277 2278 _exit(EXIT_FAILURE); 2279 } 2280 } 2281} 2282#endif 2283 2284static void 2285system_specific_bootstrap(bool sflag) 2286{ 2287 int hnmib[] = { CTL_KERN, KERN_HOSTNAME }; 2288 struct kevent kev; 2289 int kq; 2290#if HAVE_LIBAUDITD 2291 launch_data_t lda, ldb; 2292#endif 2293 2294 handle_system_bootstrapper_crashes_separately(); 2295 2296 // Disable Libinfo lookups to mdns and ds while bootstrapping (8698260) 2297 si_search_module_set_flags("mdns", 1); 2298 si_search_module_set_flags("ds", 1); 2299 2300 /* rc.cdrom's hack to load the system means that we're not the real system 2301 * bootstrapper. So we set this environment variable, and if the real 2302 * bootstrapper detects it, it will disable lookups to mDNSResponder and 2303 * opendirectoryd to prevent deadlocks at boot. 2304 * 2305 * See <rdar://problem/9877230>. 2306 */ 2307 (void)setenv(LAUNCH_ENV_BOOTSTRAPPINGSYSTEM, "1", 1); 2308 2309 do_sysversion_sysctl(); 2310 2311 do_single_user_mode(sflag); 2312 2313 (void)posix_assumes_zero(kq = kqueue()); 2314 EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, 60, 0); 2315 (void)posix_assumes_zero(kevent(kq, &kev, 1, NULL, 0, NULL)); 2316 2317 __OS_COMPILETIME_ASSERT__(SIG_ERR == (typeof(SIG_ERR))-1); 2318 EV_SET(&kev, SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, 0); 2319 (void)posix_assumes_zero(kevent(kq, &kev, 1, NULL, 0, NULL)); 2320 (void)posix_assumes_zero(signal(SIGTERM, SIG_IGN)); 2321 (void)posix_assumes_zero(sysctl(hnmib, 2, NULL, NULL, "localhost", sizeof("localhost"))); 2322 2323 loopback_setup_ipv4(); 2324 loopback_setup_ipv6(); 2325 2326 apply_sysctls_from_file("/etc/sysctl.conf"); 2327 2328#if TARGET_OS_EMBEDDED 2329 if (path_check("/etc/rc.boot")) { 2330 const char *rcboot_tool[] = { "/etc/rc.boot", NULL }; 2331 2332 (void)posix_assumes_zero(signal(SIGTERM, exit_at_sigterm)); 2333 (void)posix_assumes_zero(fwexec(rcboot_tool, NULL)); 2334 } 2335#endif 2336 2337 if (path_check("/etc/rc.cdrom")) { 2338 const char *rccdrom_tool[] = { _PATH_BSHELL, "/etc/rc.cdrom", "multiuser", NULL }; 2339 2340 /* The bootstrapper should always be killable during install-time. This 2341 * is a special case for /etc/rc.cdrom, which runs a process and never 2342 * exits. 2343 * 2344 * <rdar://problem/6103485> 2345 */ 2346 (void)posix_assumes_zero(signal(SIGTERM, exit_at_sigterm)); 2347 (void)posix_assumes_zero(fwexec(rccdrom_tool, NULL)); 2348 (void)reboot(RB_HALT); 2349 _exit(EXIT_FAILURE); 2350 } else if (is_netboot()) { 2351 const char *rcnetboot_tool[] = { _PATH_BSHELL, "/etc/rc.netboot", "init", NULL }; 2352 if (posix_assumes_zero(fwexec(rcnetboot_tool, NULL)) == -1) { 2353 (void)reboot(RB_HALT); 2354 _exit(EXIT_FAILURE); 2355 } 2356 } else { 2357 do_potential_fsck(); 2358 } 2359 2360#if TARGET_OS_EMBEDDED 2361 if (path_check("/usr/libexec/tzinit")) { 2362 const char *tzinit_tool[] = { "/usr/libexec/tzinit", NULL }; 2363 (void)posix_assumes_zero(fwexec(tzinit_tool, NULL)); 2364 } 2365#endif 2366 2367#if TARGET_OS_EMBEDDED 2368 if (path_check("/usr/libexec/FinishRestoreFromBackup")) { 2369 const char *finish_restore[] = { "/usr/libexec/FinishRestoreFromBackup", NULL }; 2370 if (fwexec(finish_restore, NULL) == -1) { 2371 launchctl_log(LOG_ERR, "Couldn't finish restore: %d: %s", errno, strerror(errno)); 2372 (void)reboot(RB_HALT); 2373 2374 _exit(EXIT_FAILURE); 2375 } 2376 } 2377#endif 2378 2379 if (path_check("/usr/libexec/cc_fips_test")) { 2380 const char *fips_tool[] = { "/usr/libexec/cc_fips_test", "-P", NULL }; 2381 if (fwexec(fips_tool, NULL) == -1) { 2382 launchctl_log(LOG_ERR, "FIPS self check failure: %d: %s", errno, strerror(errno)); 2383 (void)reboot(RB_HALT); 2384 2385 _exit(EXIT_FAILURE); 2386 } 2387 } 2388 2389 if (path_check("/etc/rc.server")) { 2390 const char *rcserver_tool[] = { _PATH_BSHELL, "/etc/rc.server", NULL }; 2391 (void)posix_assumes_zero(fwexec(rcserver_tool, NULL)); 2392 } 2393 2394 read_launchd_conf(); 2395 2396 if (path_check("/var/account/acct")) { 2397 (void)posix_assumes_zero(acct("/var/account/acct")); 2398 } 2399 2400#if !TARGET_OS_EMBEDDED 2401 if (path_check("/etc/fstab")) { 2402 const char *mount_tool[] = { "mount", "-vat", "nonfs", NULL }; 2403 (void)posix_assumes_zero(fwexec(mount_tool, NULL)); 2404 } 2405#endif 2406 2407 if (path_check("/etc/rc.installer_cleanup")) { 2408 const char *rccleanup_tool[] = { _PATH_BSHELL, "/etc/rc.installer_cleanup", "multiuser", NULL }; 2409 (void)posix_assumes_zero(fwexec(rccleanup_tool, NULL)); 2410 } 2411 2412 if (path_check("/etc/rc.deferred_install")) { 2413 int status = 0; 2414 const char *deferredinstall_tool[] = { _PATH_BSHELL, "/etc/rc.deferred_install", NULL }; 2415 if (posix_assumes_zero(fwexec(deferredinstall_tool, &status)) == 0) { 2416 if (WEXITSTATUS(status) == EXIT_SUCCESS) { 2417 if (_launchctl_apple_internal) { 2418 launchctl_log(LOG_NOTICE, "Deferred install script completed successfully. Rebooting in 3 seconds..."); 2419 sleep(3); 2420 } 2421 2422 (void)remove(deferredinstall_tool[1]); 2423 (void)reboot(RB_AUTOBOOT); 2424 exit(EXIT_FAILURE); 2425 } else { 2426 launchctl_log(LOG_NOTICE, "Deferred install script exited with status %i. Continuing boot and hoping it'll work...", WEXITSTATUS(status)); 2427 (void)remove(deferredinstall_tool[1]); 2428 } 2429 } 2430 } 2431 2432 empty_dir(_PATH_VARRUN, NULL); 2433 empty_dir(_PATH_TMP, NULL); 2434 (void)remove(_PATH_NOLOGIN); 2435 2436 if (path_check("/usr/libexec/dirhelper")) { 2437 const char *dirhelper_tool[] = { "/usr/libexec/dirhelper", "-machineBoot", NULL }; 2438 (void)posix_assumes_zero(fwexec(dirhelper_tool, NULL)); 2439 } 2440 2441 (void)posix_assumes_zero(touch_file(_PATH_UTMPX, DEFFILEMODE)); 2442#if !TARGET_OS_EMBEDDED 2443 (void)posix_assumes_zero(touch_file(_PATH_VARRUN "/.systemStarterRunning", DEFFILEMODE)); 2444#endif 2445 2446#if HAVE_LIBAUDITD 2447 /* Only start auditing if not "Disabled" in auditd plist. */ 2448 if ((lda = read_plist_file(AUDITD_PLIST_FILE, false, false)) != NULL && ((ldb = launch_data_dict_lookup(lda, LAUNCH_JOBKEY_DISABLED)) == NULL || job_disabled_logic(ldb) == false)) { 2449 (void)os_assumes_zero(audit_quick_start()); 2450 launch_data_free(lda); 2451 } 2452#else 2453 if (path_check("/etc/security/rc.audit")) { 2454 const char *audit_tool[] = { _PATH_BSHELL, "/etc/security/rc.audit", NULL }; 2455 (void)posix_assumes_zero(fwexec(audit_tool, NULL)); 2456 } 2457#endif 2458 2459#if HAVE_SYSTEMSTATS 2460 systemstats_boot(); 2461#endif 2462 2463 do_BootCache_magic(BOOTCACHE_START); 2464 2465 preheat_page_cache_hack(); 2466 2467 _vproc_set_global_on_demand(true); 2468 2469 char *load_launchd_items[] = { "load", "-D", "all", NULL }; 2470 int load_launchd_items_cnt = 3; 2471 2472 if (is_safeboot()) { 2473 load_launchd_items[2] = "system"; 2474 } 2475 2476 (void)posix_assumes_zero(load_and_unload_cmd(load_launchd_items_cnt, load_launchd_items)); 2477 2478 /* See <rdar://problem/5066316>. */ 2479 if (!_launchctl_apple_internal) { 2480 mach_timespec_t w = { 5, 0 }; 2481 IOKitWaitQuiet(kIOMasterPortDefault, &w); 2482 } 2483 2484 do_BootCache_magic(BOOTCACHE_TAG); 2485 2486 do_bootroot_magic(); 2487 2488 _vproc_set_global_on_demand(false); 2489 2490 (void)posix_assumes_zero(kevent(kq, NULL, 0, &kev, 1, NULL)); 2491 2492 /* warmd now handles cutting off the BootCache. We just kick it off. */ 2493 (void)close(kq); 2494} 2495 2496void 2497do_BootCache_magic(BootCache_action_t what) 2498{ 2499 const char *bcc_tool[] = { "/usr/sbin/BootCacheControl", NULL, NULL }; 2500 2501 if (is_safeboot() || !path_check(bcc_tool[0])) { 2502 return; 2503 } 2504 2505 switch (what) { 2506 case BOOTCACHE_START: 2507 bcc_tool[1] = "start"; 2508 break; 2509 case BOOTCACHE_TAG: 2510 bcc_tool[1] = "tag"; 2511 break; 2512 case BOOTCACHE_STOP: 2513 bcc_tool[1] = "stop"; 2514 break; 2515 } 2516 2517 fwexec(bcc_tool, NULL); 2518} 2519 2520int 2521bootstrap_cmd(int argc, char *const argv[]) 2522{ 2523 char *session = NULL; 2524 bool sflag = false; 2525 int ch; 2526 2527 while ((ch = getopt(argc, argv, "sS:")) != -1) { 2528 switch (ch) { 2529 case 's': 2530 sflag = true; 2531 break; 2532 case 'S': 2533 session = optarg; 2534 break; 2535 case '?': 2536 default: 2537 break; 2538 } 2539 } 2540 2541 optind = 1; 2542 optreset = 1; 2543 2544 if (!session) { 2545 launchctl_log(LOG_ERR, "usage: %s bootstrap [-s] -S <session-type>", getprogname()); 2546 return 1; 2547 } 2548 2549 if (strcasecmp(session, "System") == 0) { 2550 _launchctl_system_bootstrap = true; 2551 system_specific_bootstrap(sflag); 2552 } else { 2553 char *load_launchd_items[] = { 2554 "load", 2555 "-S", 2556 session, 2557 "-D", 2558 "all", 2559 NULL, 2560 NULL, 2561 NULL, 2562 }; 2563 size_t the_argc = 5; 2564 2565 bool bootstrap_login_items = false; 2566 if (strcasecmp(session, VPROCMGR_SESSION_AQUA) == 0) { 2567 bootstrap_login_items = true; 2568 } else if (strcasecmp(session, VPROCMGR_SESSION_BACKGROUND) == 0 2569 || strcasecmp(session, VPROCMGR_SESSION_LOGINWINDOW) == 0) { 2570 /* If we're bootstrapping either the LoginWindow or Background 2571 * sessions, then we only load items from /System and /Library. We 2572 * do not attempt to load anything from a user's home directory, as 2573 * it might not be available at this time. 2574 */ 2575 load_launchd_items[4] = "system"; 2576 if (!is_safeboot()) { 2577 load_launchd_items[5] = "-D"; 2578 load_launchd_items[6] = "local"; 2579 the_argc += 2; 2580 } 2581 2582 if (strcasecmp(session, VPROCMGR_SESSION_BACKGROUND) == 0) { 2583 /* This is to force a bootstrapped job to inherit its security 2584 * session from the launchd that it resides in. 2585 */ 2586 _launchctl_peruser_bootstrap = true; 2587 read_launchd_conf(); 2588 } 2589 } 2590 2591 if (is_safeboot()) { 2592 load_launchd_items[4] = "system"; 2593 } 2594 2595 int result = load_and_unload_cmd(the_argc, load_launchd_items); 2596 if (result) { 2597 syslog(LOG_ERR, "Could not bootstrap session: %s", session); 2598 return 1; 2599 } 2600 2601 /* This will tell launchd to start listening on MachServices again. When 2602 * bootstrapping, launchd ignores requests from everyone but the 2603 * bootstrapper (us), so this unsets the "weird bootstrap" mode. 2604 */ 2605 int64_t junk = 0; 2606 vproc_err_t verr = vproc_swap_integer(NULL, VPROC_GSK_WEIRD_BOOTSTRAP, &junk, NULL); 2607 if (!verr) { 2608#if !TARGET_OS_EMBEDDED 2609 if (bootstrap_login_items) { 2610 void *smf = dlopen("/System/Library/Frameworks/ServiceManagement.framework/Versions/A/ServiceManagement", 0); 2611 if (smf) { 2612 void (*_SMLoginItemBootstrapItemsFunc)(void) = dlsym(smf, "_SMLoginItemBootstrapItems"); 2613 if (_SMLoginItemBootstrapItemsFunc) { 2614 _SMLoginItemBootstrapItemsFunc(); 2615 } else { 2616 launchctl_log(LOG_ERR, "Could not find login item bootstrap function. LoginItems will be unavailable."); 2617 } 2618 } else { 2619 launchctl_log(LOG_ERR, "Failed to open ServiceManagement framework. LoginItems will be unavailable."); 2620 } 2621 } 2622#endif 2623 } else if (bootstrap_login_items) { 2624 launchctl_log(LOG_ERR, "Failed to unset weird bootstrap. LoginItems will be unavailable."); 2625 } 2626 } 2627 2628 return 0; 2629} 2630 2631int 2632load_and_unload_cmd(int argc, char *const argv[]) 2633{ 2634 NSSearchPathEnumerationState es = 0; 2635 char nspath[PATH_MAX * 2]; /* safe side, we need to append */ 2636 bool badopts = false; 2637 struct load_unload_state lus; 2638 size_t i; 2639 int ch; 2640 2641 memset(&lus, 0, sizeof(lus)); 2642 2643 if (strcmp(argv[0], "load") == 0) { 2644 lus.load = true; 2645 } 2646 2647 while ((ch = getopt(argc, argv, "wFS:D:")) != -1) { 2648 switch (ch) { 2649 case 'w': 2650 lus.editondisk = true; 2651 break; 2652 case 'F': 2653 lus.forceload = true; 2654 break; 2655 case 'S': 2656 lus.session_type = optarg; 2657 break; 2658 case 'D': 2659 if (strcasecmp(optarg, "all") == 0) { 2660 es |= NSAllDomainsMask; 2661 } else if (strcasecmp(optarg, "user") == 0) { 2662 es |= NSUserDomainMask; 2663 } else if (strcasecmp(optarg, "local") == 0) { 2664 es |= NSLocalDomainMask; 2665 } else if (strcasecmp(optarg, "network") == 0) { 2666 es |= NSNetworkDomainMask; 2667 } else if (strcasecmp(optarg, "system") == 0) { 2668 es |= NSSystemDomainMask; 2669 } else { 2670 badopts = true; 2671 } 2672 break; 2673 case '?': 2674 default: 2675 badopts = true; 2676 break; 2677 } 2678 } 2679 argc -= optind; 2680 argv += optind; 2681 2682 if (lus.session_type == NULL) { 2683 es &= ~NSUserDomainMask; 2684 } 2685 2686 if (argc == 0 && es == 0) { 2687 badopts = true; 2688 } 2689 2690 if (badopts) { 2691 launchctl_log(LOG_ERR, "usage: %s load [-wF] [-D <user|local|network|system|all>] paths...", getprogname()); 2692 return 1; 2693 } 2694 2695 int dbfd = -1; 2696 vproc_err_t verr = vproc_swap_string(NULL, VPROC_GSK_JOB_OVERRIDES_DB, NULL, &_launchctl_job_overrides_db_path); 2697 if (verr) { 2698 if (bootstrap_port) { 2699 launchctl_log(LOG_ERR, "Could not get location of job overrides database: ppid/bootstrap: %d/0x%x", getppid(), bootstrap_port); 2700 } 2701 } else { 2702 dbfd = open(_launchctl_job_overrides_db_path, O_RDONLY | O_EXLOCK | O_CREAT, S_IRUSR | S_IWUSR); 2703 if (dbfd != -1) { 2704 _launchctl_overrides_db = (CFMutableDictionaryRef)CreateMyPropertyListFromFile(_launchctl_job_overrides_db_path); 2705 if (!_launchctl_overrides_db) { 2706 _launchctl_overrides_db = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 2707 } 2708 } else if (errno != EROFS) { 2709 launchctl_log(LOG_ERR, "Could not open job overrides database at: %s: %d: %s", _launchctl_job_overrides_db_path, errno, strerror(errno)); 2710 } 2711 } 2712 2713#if READ_JETSAM_DEFAULTS 2714 if (!read_jetsam_defaults()) { 2715 launchctl_log(LOG_NOTICE, "Failed to read jetsam defaults; no process limits applied"); 2716 } 2717#endif 2718 2719 /* Only one pass! */ 2720 lus.pass1 = launch_data_alloc(LAUNCH_DATA_ARRAY); 2721 2722 es = NSStartSearchPathEnumeration(NSLibraryDirectory, es); 2723 2724 while ((es = NSGetNextSearchPathEnumeration(es, nspath))) { 2725 if (lus.session_type) { 2726 strcat(nspath, "/LaunchAgents"); 2727 } else { 2728 strcat(nspath, "/LaunchDaemons"); 2729 } 2730 2731 bool should_glob = true; 2732 2733#if TARGET_OS_EMBEDDED 2734 if (require_jobs_from_cache()) { 2735 CFDictionaryRef cache = GetPropertyListFromCache(); 2736 if (cache) { 2737 CFDictionaryRef launchdJobs = CFDictionaryGetValue(cache, CFSTR(XPC_PLIST_CACHE_KEY)); 2738 if (launchdJobs) { 2739 CFIndex sz = CFDictionaryGetCount(launchdJobs); 2740 2741 CFStringRef *keys = malloc(sz * sizeof(CFStringRef)); 2742 CFDictionaryGetKeysAndValues(launchdJobs, (const void**)keys, NULL); 2743 2744 for (i=0; i < (size_t)sz; i++) { 2745 char path[PATH_MAX]; 2746 if (CFStringGetCString(keys[i], path, PATH_MAX, kCFStringEncodingUTF8) && (strncmp(path, nspath, strlen(nspath)) == 0)) { 2747 readpath(path, &lus); 2748 } 2749 } 2750 } 2751 } 2752 2753 should_glob = false; 2754 } 2755#endif 2756 2757 if (should_glob) { 2758 glob_t g; 2759 2760 if (glob(nspath, GLOB_TILDE|GLOB_NOSORT, NULL, &g) == 0) { 2761 for (i = 0; i < g.gl_pathc; i++) { 2762 readpath(g.gl_pathv[i], &lus); 2763 } 2764 globfree(&g); 2765 } 2766 } 2767 } 2768 2769 for (i = 0; i < (size_t)argc; i++) { 2770 readpath(argv[i], &lus); 2771 } 2772 2773 if (launch_data_array_get_count(lus.pass1) == 0) { 2774 if (!_launchctl_is_managed) { 2775 launchctl_log(LOG_ERR, "nothing found to %s", lus.load ? "load" : "unload"); 2776 } 2777 launch_data_free(lus.pass1); 2778 return _launchctl_is_managed ? 0 : 1; 2779 } 2780 2781 if (lus.load) { 2782 distill_jobs(lus.pass1); 2783 submit_job_pass(lus.pass1); 2784 } else { 2785 for (i = 0; i < launch_data_array_get_count(lus.pass1); i++) { 2786 unloadjob(launch_data_array_get_index(lus.pass1, i)); 2787 } 2788 } 2789 2790 if (_launchctl_overrides_db_changed) { 2791 WriteMyPropertyListToFile(_launchctl_overrides_db, _launchctl_job_overrides_db_path); 2792 } 2793 2794 flock(dbfd, LOCK_UN); 2795 close(dbfd); 2796 return 0; 2797} 2798 2799void 2800submit_job_pass(launch_data_t jobs) 2801{ 2802 launch_data_t msg, resp; 2803 size_t i; 2804 int e; 2805 2806 if (launch_data_array_get_count(jobs) == 0) 2807 return; 2808 2809 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); 2810 2811 launch_data_dict_insert(msg, jobs, LAUNCH_KEY_SUBMITJOB); 2812 2813 resp = launch_msg(msg); 2814 2815 if (resp) { 2816 switch (launch_data_get_type(resp)) { 2817 case LAUNCH_DATA_ERRNO: 2818 if ((e = launch_data_get_errno(resp))) 2819 launchctl_log(LOG_ERR, "%s", strerror(e)); 2820 break; 2821 case LAUNCH_DATA_ARRAY: 2822 for (i = 0; i < launch_data_array_get_count(jobs); i++) { 2823 launch_data_t obatind = launch_data_array_get_index(resp, i); 2824 launch_data_t jatind = launch_data_array_get_index(jobs, i); 2825 const char *lab4job = launch_data_get_string(launch_data_dict_lookup(jatind, LAUNCH_JOBKEY_LABEL)); 2826 if (LAUNCH_DATA_ERRNO == launch_data_get_type(obatind)) { 2827 e = launch_data_get_errno(obatind); 2828 switch (e) { 2829 case EEXIST: 2830 launchctl_log(LOG_ERR, "%s: %s", lab4job, "Already loaded"); 2831 break; 2832 case ESRCH: 2833 launchctl_log(LOG_ERR, "%s: %s", lab4job, "Not loaded"); 2834 break; 2835 case ENEEDAUTH: 2836 launchctl_log(LOG_ERR, "%s: %s", lab4job, "Could not set security session"); 2837 default: 2838 launchctl_log(LOG_ERR, "%s: %s", lab4job, strerror(e)); 2839 case 0: 2840 break; 2841 } 2842 } 2843 } 2844 break; 2845 default: 2846 launchctl_log(LOG_ERR, "unknown respose from launchd!"); 2847 break; 2848 } 2849 launch_data_free(resp); 2850 } else { 2851 launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno)); 2852 } 2853 2854 launch_data_free(msg); 2855} 2856 2857int 2858start_stop_remove_cmd(int argc, char *const argv[]) 2859{ 2860 launch_data_t resp, msg; 2861 const char *lmsgcmd = LAUNCH_KEY_STOPJOB; 2862 int e, r = 0; 2863 2864 if (0 == strcmp(argv[0], "start")) 2865 lmsgcmd = LAUNCH_KEY_STARTJOB; 2866 2867 if (0 == strcmp(argv[0], "remove")) 2868 lmsgcmd = LAUNCH_KEY_REMOVEJOB; 2869 2870 if (argc != 2) { 2871 launchctl_log(LOG_ERR, "usage: %s %s <job label>", getprogname(), argv[0]); 2872 return 1; 2873 } 2874 2875 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); 2876 launch_data_dict_insert(msg, launch_data_new_string(argv[1]), lmsgcmd); 2877 2878 resp = launch_msg(msg); 2879 launch_data_free(msg); 2880 2881 if (resp == NULL) { 2882 launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno)); 2883 return 1; 2884 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) { 2885 if ((e = launch_data_get_errno(resp))) { 2886 launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], strerror(e)); 2887 r = 1; 2888 } 2889 } else { 2890 launchctl_log(LOG_ERR, "%s %s returned unknown response", getprogname(), argv[0]); 2891 r = 1; 2892 } 2893 2894 launch_data_free(resp); 2895 return r; 2896} 2897 2898void 2899print_jobs(launch_data_t j, const char *key __attribute__((unused)), void *context __attribute__((unused))) 2900{ 2901 static size_t depth = 0; 2902 launch_data_t lo = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LABEL); 2903 launch_data_t pido = launch_data_dict_lookup(j, LAUNCH_JOBKEY_PID); 2904 launch_data_t stato = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LASTEXITSTATUS); 2905 const char *label = launch_data_get_string(lo); 2906 size_t i; 2907 2908 if (pido) { 2909 fprintf(stdout, "%lld\t-\t%s\n", launch_data_get_integer(pido), label); 2910 } else if (stato) { 2911 int wstatus = (int)launch_data_get_integer(stato); 2912 if (WIFEXITED(wstatus)) { 2913 fprintf(stdout, "-\t%d\t%s\n", WEXITSTATUS(wstatus), label); 2914 } else if (WIFSIGNALED(wstatus)) { 2915 fprintf(stdout, "-\t-%d\t%s\n", WTERMSIG(wstatus), label); 2916 } else { 2917 fprintf(stdout, "-\t???\t%s\n", label); 2918 } 2919 } else { 2920 fprintf(stdout, "-\t-\t%s\n", label); 2921 } 2922 for (i = 0; i < depth; i++) { 2923 fprintf(stdout, "\t"); 2924 } 2925} 2926 2927void 2928print_obj(launch_data_t obj, const char *key, void *context __attribute__((unused))) 2929{ 2930 static size_t indent = 0; 2931 size_t i, c; 2932 2933 for (i = 0; i < indent; i++) { 2934 fprintf(stdout, "\t"); 2935 } 2936 2937 if (key) { 2938 fprintf(stdout, "\"%s\" = ", key); 2939 } 2940 2941 switch (launch_data_get_type(obj)) { 2942 case LAUNCH_DATA_STRING: 2943 fprintf(stdout, "\"%s\";\n", launch_data_get_string(obj)); 2944 break; 2945 case LAUNCH_DATA_INTEGER: 2946 fprintf(stdout, "%lld;\n", launch_data_get_integer(obj)); 2947 break; 2948 case LAUNCH_DATA_REAL: 2949 fprintf(stdout, "%f;\n", launch_data_get_real(obj)); 2950 break; 2951 case LAUNCH_DATA_BOOL: 2952 fprintf(stdout, "%s;\n", launch_data_get_bool(obj) ? "true" : "false"); 2953 break; 2954 case LAUNCH_DATA_ARRAY: 2955 c = launch_data_array_get_count(obj); 2956 fprintf(stdout, "(\n"); 2957 indent++; 2958 for (i = 0; i < c; i++) { 2959 print_obj(launch_data_array_get_index(obj, i), NULL, NULL); 2960 } 2961 indent--; 2962 for (i = 0; i < indent; i++) { 2963 fprintf(stdout, "\t"); 2964 } 2965 fprintf(stdout, ");\n"); 2966 break; 2967 case LAUNCH_DATA_DICTIONARY: 2968 fprintf(stdout, "{\n"); 2969 indent++; 2970 launch_data_dict_iterate(obj, print_obj, NULL); 2971 indent--; 2972 for (i = 0; i < indent; i++) { 2973 fprintf(stdout, "\t"); 2974 } 2975 fprintf(stdout, "};\n"); 2976 break; 2977 case LAUNCH_DATA_FD: 2978 fprintf(stdout, "file-descriptor-object;\n"); 2979 break; 2980 case LAUNCH_DATA_MACHPORT: 2981 fprintf(stdout, "mach-port-object;\n"); 2982 break; 2983 default: 2984 fprintf(stdout, "???;\n"); 2985 break; 2986 } 2987} 2988 2989int 2990list_cmd(int argc, char *const argv[]) 2991{ 2992 if (_launchctl_is_managed) { 2993 /* This output is meant for a command line, so don't print anything if 2994 * we're managed by launchd. 2995 */ 2996 return 1; 2997 } 2998 2999 launch_data_t resp, msg = NULL; 3000 int r = 0; 3001 3002 bool plist_output = false; 3003 char *label = NULL; 3004 if (argc > 3) { 3005 launchctl_log(LOG_ERR, "usage: %s list [-x] [label]", getprogname()); 3006 return 1; 3007 } else if (argc >= 2) { 3008 plist_output = (strncmp(argv[1], "-x", sizeof("-x")) == 0); 3009 label = plist_output ? argv[2] : argv[1]; 3010 } 3011 3012 if (label) { 3013 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); 3014 launch_data_dict_insert(msg, launch_data_new_string(label), LAUNCH_KEY_GETJOB); 3015 3016 resp = launch_msg(msg); 3017 launch_data_free(msg); 3018 3019 if (resp == NULL) { 3020 launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno)); 3021 r = 1; 3022 } else if (launch_data_get_type(resp) == LAUNCH_DATA_DICTIONARY) { 3023 if (plist_output) { 3024 CFDictionaryRef respDict = CFDictionaryCreateFromLaunchDictionary(resp); 3025 CFStringRef plistStr = NULL; 3026 if (respDict) { 3027 CFDataRef plistData = CFPropertyListCreateXMLData(NULL, (CFPropertyListRef)respDict); 3028 CFRelease(respDict); 3029 if (plistData) { 3030 plistStr = CFStringCreateWithBytes(NULL, CFDataGetBytePtr(plistData), CFDataGetLength(plistData), kCFStringEncodingUTF8, false); 3031 CFRelease(plistData); 3032 } else { 3033 r = 1; 3034 } 3035 } else { 3036 r = 1; 3037 } 3038 3039 if (plistStr) { 3040 launchctl_log_CFString(LOG_NOTICE, plistStr); 3041 CFRelease(plistStr); 3042 r = 0; 3043 } 3044 } else { 3045 print_obj(resp, NULL, NULL); 3046 r = 0; 3047 } 3048 launch_data_free(resp); 3049 } else { 3050 launchctl_log(LOG_ERR, "%s %s returned unknown response", getprogname(), argv[0]); 3051 r = 1; 3052 launch_data_free(resp); 3053 } 3054 } else if (vproc_swap_complex(NULL, VPROC_GSK_ALLJOBS, NULL, &resp) == NULL) { 3055 fprintf(stdout, "PID\tStatus\tLabel\n"); 3056 launch_data_dict_iterate(resp, print_jobs, NULL); 3057 launch_data_free(resp); 3058 3059 r = 0; 3060 } 3061 3062 return r; 3063} 3064 3065int 3066stdio_cmd(int argc __attribute__((unused)), char *const argv[]) 3067{ 3068 launchctl_log(LOG_ERR, "%s %s: This sub-command no longer does anything", getprogname(), argv[0]); 3069 return 1; 3070} 3071 3072int 3073fyi_cmd(int argc, char *const argv[]) 3074{ 3075 launch_data_t resp, msg; 3076 const char *lmsgk = NULL; 3077 int e, r = 0; 3078 3079 if (argc != 1) { 3080 launchctl_log(LOG_ERR, "usage: %s %s", getprogname(), argv[0]); 3081 return 1; 3082 } 3083 3084 if (!strcmp(argv[0], "shutdown")) { 3085 lmsgk = LAUNCH_KEY_SHUTDOWN; 3086 } else if (!strcmp(argv[0], "singleuser")) { 3087 lmsgk = LAUNCH_KEY_SINGLEUSER; 3088 } else { 3089 return 1; 3090 } 3091 3092 msg = launch_data_new_string(lmsgk); 3093 resp = launch_msg(msg); 3094 launch_data_free(msg); 3095 3096 if (resp == NULL) { 3097 launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno)); 3098 return 1; 3099 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) { 3100 if ((e = launch_data_get_errno(resp))) { 3101 launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], strerror(e)); 3102 r = 1; 3103 } 3104 } else { 3105 launchctl_log(LOG_ERR, "%s %s returned unknown response", getprogname(), argv[0]); 3106 r = 1; 3107 } 3108 3109 launch_data_free(resp); 3110 3111 return r; 3112} 3113 3114int 3115logupdate_cmd(int argc, char *const argv[]) 3116{ 3117 int64_t inval, outval; 3118 bool badargs = false, maskmode = false, onlymode = false, levelmode = false; 3119 static const struct { 3120 const char *name; 3121 int level; 3122 } logtbl[] = { 3123 { "debug", LOG_DEBUG }, 3124 { "info", LOG_INFO }, 3125 { "notice", LOG_NOTICE }, 3126 { "warning", LOG_WARNING }, 3127 { "error", LOG_ERR }, 3128 { "critical", LOG_CRIT }, 3129 { "alert", LOG_ALERT }, 3130 { "emergency", LOG_EMERG }, 3131 }; 3132 size_t i, j, logtblsz = sizeof logtbl / sizeof logtbl[0]; 3133 int m = 0; 3134 3135 if (argc >= 2) { 3136 if (!strcmp(argv[1], "mask")) 3137 maskmode = true; 3138 else if (!strcmp(argv[1], "only")) 3139 onlymode = true; 3140 else if (!strcmp(argv[1], "level")) 3141 levelmode = true; 3142 else 3143 badargs = true; 3144 } 3145 3146 if (maskmode) 3147 m = LOG_UPTO(LOG_DEBUG); 3148 3149 if (argc > 2 && (maskmode || onlymode)) { 3150 for (i = 2; i < (size_t)argc; i++) { 3151 for (j = 0; j < logtblsz; j++) { 3152 if (!strcmp(argv[i], logtbl[j].name)) { 3153 if (maskmode) 3154 m &= ~(LOG_MASK(logtbl[j].level)); 3155 else 3156 m |= LOG_MASK(logtbl[j].level); 3157 break; 3158 } 3159 } 3160 if (j == logtblsz) { 3161 badargs = true; 3162 break; 3163 } 3164 } 3165 } else if (argc > 2 && levelmode) { 3166 for (j = 0; j < logtblsz; j++) { 3167 if (!strcmp(argv[2], logtbl[j].name)) { 3168 m = LOG_UPTO(logtbl[j].level); 3169 break; 3170 } 3171 } 3172 if (j == logtblsz) 3173 badargs = true; 3174 } else if (argc != 1) { 3175 badargs = true; 3176 } 3177 3178 if (badargs) { 3179 launchctl_log(LOG_ERR, "usage: %s [[mask loglevels...] | [only loglevels...] [level loglevel]]", getprogname()); 3180 return 1; 3181 } 3182 3183 inval = m; 3184 3185 if (vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_LOG_MASK, argc != 1 ? &inval : NULL, &outval) == NULL) { 3186 if (argc == 1) { 3187 for (j = 0; j < logtblsz; j++) { 3188 if (outval & LOG_MASK(logtbl[j].level)) { 3189 launchctl_log(LOG_NOTICE, "%s ", logtbl[j].name); 3190 } 3191 } 3192 launchctl_log(LOG_NOTICE, ""); 3193 } 3194 return 0; 3195 } else { 3196 return 1; 3197 } 3198} 3199 3200static const struct { 3201 const char *name; 3202 int lim; 3203} limlookup[] = { 3204 { "cpu", RLIMIT_CPU }, 3205 { "filesize", RLIMIT_FSIZE }, 3206 { "data", RLIMIT_DATA }, 3207 { "stack", RLIMIT_STACK }, 3208 { "core", RLIMIT_CORE }, 3209 { "rss", RLIMIT_RSS }, 3210 { "memlock", RLIMIT_MEMLOCK }, 3211 { "maxproc", RLIMIT_NPROC }, 3212 { "maxfiles", RLIMIT_NOFILE } 3213}; 3214 3215static const size_t limlookupcnt = sizeof limlookup / sizeof limlookup[0]; 3216 3217ssize_t 3218name2num(const char *n) 3219{ 3220 size_t i; 3221 3222 for (i = 0; i < limlookupcnt; i++) { 3223 if (!strcmp(limlookup[i].name, n)) { 3224 return limlookup[i].lim; 3225 } 3226 } 3227 return -1; 3228} 3229 3230const char * 3231num2name(int n) 3232{ 3233 size_t i; 3234 3235 for (i = 0; i < limlookupcnt; i++) { 3236 if (limlookup[i].lim == n) 3237 return limlookup[i].name; 3238 } 3239 return NULL; 3240} 3241 3242const char * 3243lim2str(rlim_t val, char *buf) 3244{ 3245 if (val == RLIM_INFINITY) 3246 strcpy(buf, "unlimited"); 3247 else 3248 sprintf(buf, "%lld", val); 3249 return buf; 3250} 3251 3252bool 3253str2lim(const char *buf, rlim_t *res) 3254{ 3255 char *endptr; 3256 *res = strtoll(buf, &endptr, 10); 3257 if (!strcmp(buf, "unlimited")) { 3258 *res = RLIM_INFINITY; 3259 return false; 3260 } else if (*endptr == '\0') { 3261 return false; 3262 } 3263 return true; 3264} 3265 3266int 3267limit_cmd(int argc, char *const argv[]) 3268{ 3269 char slimstr[100]; 3270 char hlimstr[100]; 3271 struct rlimit *lmts = NULL; 3272 launch_data_t resp, resp1 = NULL, msg, tmp; 3273 int r = 0; 3274 size_t i, lsz = -1; 3275 ssize_t which = 0; 3276 rlim_t slim = -1, hlim = -1; 3277 bool badargs = false; 3278 3279 if (argc > 4) 3280 badargs = true; 3281 3282 if (argc >= 3 && str2lim(argv[2], &slim)) 3283 badargs = true; 3284 else 3285 hlim = slim; 3286 3287 if (argc == 4 && str2lim(argv[3], &hlim)) 3288 badargs = true; 3289 3290 if (argc >= 2 && -1 == (which = name2num(argv[1]))) 3291 badargs = true; 3292 3293 if (badargs) { 3294 launchctl_log(LOG_ERR, "usage: %s %s [", getprogname(), argv[0]); 3295 for (i = 0; i < limlookupcnt; i++) 3296 launchctl_log(LOG_ERR, "%s %s", limlookup[i].name, (i + 1) == limlookupcnt ? "" : "| "); 3297 launchctl_log(LOG_ERR, "[both | soft hard]]"); 3298 return 1; 3299 } 3300 3301 msg = launch_data_new_string(LAUNCH_KEY_GETRESOURCELIMITS); 3302 resp = launch_msg(msg); 3303 launch_data_free(msg); 3304 3305 if (resp == NULL) { 3306 launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno)); 3307 return 1; 3308 } else if (launch_data_get_type(resp) == LAUNCH_DATA_OPAQUE) { 3309 lmts = launch_data_get_opaque(resp); 3310 lsz = launch_data_get_opaque_size(resp); 3311 if (argc <= 2) { 3312 for (i = 0; i < (lsz / sizeof(struct rlimit)); i++) { 3313 if (argc == 2 && (size_t)which != i) 3314 continue; 3315 launchctl_log(LOG_NOTICE, "\t%-12s%-15s%-15s", num2name((int)i), 3316 lim2str(lmts[i].rlim_cur, slimstr), 3317 lim2str(lmts[i].rlim_max, hlimstr)); 3318 } 3319 } 3320 } else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) { 3321 launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], launch_data_get_string(resp)); 3322 r = 1; 3323 } else { 3324 launchctl_log(LOG_ERR, "%s %s returned unknown response", getprogname(), argv[0]); 3325 r = 1; 3326 } 3327 3328 if (argc <= 2 || r != 0) { 3329 launch_data_free(resp); 3330 return r; 3331 } else { 3332 resp1 = resp; 3333 } 3334 3335 lmts[which].rlim_cur = slim; 3336 lmts[which].rlim_max = hlim; 3337 3338 bool maxfiles_exceeded = false; 3339 if (strncmp(argv[1], "maxfiles", sizeof("maxfiles")) == 0) { 3340 if (argc > 2) { 3341 maxfiles_exceeded = (strncmp(argv[2], "unlimited", sizeof("unlimited")) == 0); 3342 } 3343 3344 if (argc > 3) { 3345 maxfiles_exceeded = (maxfiles_exceeded || strncmp(argv[3], "unlimited", sizeof("unlimited")) == 0); 3346 } 3347 3348 if (maxfiles_exceeded) { 3349 launchctl_log(LOG_ERR, "Neither the hard nor soft limit for \"maxfiles\" can be unlimited. Please use a numeric parameter for both."); 3350 return 1; 3351 } 3352 } 3353 3354 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); 3355 tmp = launch_data_new_opaque(lmts, lsz); 3356 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETRESOURCELIMITS); 3357 resp = launch_msg(msg); 3358 launch_data_free(msg); 3359 3360 if (resp == NULL) { 3361 launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno)); 3362 return 1; 3363 } else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) { 3364 launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], launch_data_get_string(resp)); 3365 r = 1; 3366 } else if (launch_data_get_type(resp) != LAUNCH_DATA_OPAQUE) { 3367 launchctl_log(LOG_ERR, "%s %s returned unknown response", getprogname(), argv[0]); 3368 r = 1; 3369 } 3370 3371 launch_data_free(resp); 3372 launch_data_free(resp1); 3373 3374 return r; 3375} 3376 3377int 3378umask_cmd(int argc, char *const argv[]) 3379{ 3380 bool badargs = false; 3381 char *endptr; 3382 long m = 0; 3383 int64_t inval, outval; 3384 3385 if (argc == 2) { 3386 m = strtol(argv[1], &endptr, 8); 3387 if (*endptr != '\0' || m > 0777) 3388 badargs = true; 3389 } 3390 3391 if (argc > 2 || badargs) { 3392 launchctl_log(LOG_ERR, "usage: %s %s <mask>", getprogname(), argv[0]); 3393 return 1; 3394 } 3395 3396 inval = m; 3397 3398 if (vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_UMASK, argc == 2 ? &inval : NULL, &outval) == NULL) { 3399 if (argc == 1) { 3400 launchctl_log(LOG_NOTICE, "%o", (unsigned int)outval); 3401 } 3402 return 0; 3403 } else { 3404 return 1; 3405 } 3406} 3407 3408void 3409setup_system_context(void) 3410{ 3411 if (getenv(LAUNCHD_SOCKET_ENV)) { 3412 return; 3413 } 3414 3415 if (getenv(LAUNCH_ENV_KEEPCONTEXT)) { 3416 return; 3417 } 3418 3419 if (geteuid() != 0) { 3420 launchctl_log(LOG_ERR, "You must be the root user to perform this operation."); 3421 return; 3422 } 3423 3424 /* Use the system launchd's socket. */ 3425 setenv("__USE_SYSTEM_LAUNCHD", "1", 0); 3426 3427 /* Put ourselves in the system launchd's bootstrap. */ 3428 mach_port_t rootbs = str2bsport("/"); 3429 mach_port_deallocate(mach_task_self(), bootstrap_port); 3430 task_set_bootstrap_port(mach_task_self(), rootbs); 3431 bootstrap_port = rootbs; 3432} 3433 3434int 3435submit_cmd(int argc, char *const argv[]) 3436{ 3437 launch_data_t msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY); 3438 launch_data_t job = launch_data_alloc(LAUNCH_DATA_DICTIONARY); 3439 launch_data_t resp, largv = launch_data_alloc(LAUNCH_DATA_ARRAY); 3440 int ch, i, r = 0; 3441 3442 launch_data_dict_insert(job, launch_data_new_bool(false), LAUNCH_JOBKEY_ONDEMAND); 3443 3444 while ((ch = getopt(argc, argv, "l:p:o:e:")) != -1) { 3445 switch (ch) { 3446 case 'l': 3447 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_LABEL); 3448 break; 3449 case 'p': 3450 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_PROGRAM); 3451 break; 3452 case 'o': 3453 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_STANDARDOUTPATH); 3454 break; 3455 case 'e': 3456 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_STANDARDERRORPATH); 3457 break; 3458 default: 3459 launchctl_log(LOG_ERR, "usage: %s submit ...", getprogname()); 3460 return 1; 3461 } 3462 } 3463 argc -= optind; 3464 argv += optind; 3465 3466 for (i = 0; argv[i]; i++) { 3467 launch_data_array_append(largv, launch_data_new_string(argv[i])); 3468 } 3469 3470 launch_data_dict_insert(job, largv, LAUNCH_JOBKEY_PROGRAMARGUMENTS); 3471 3472 launch_data_dict_insert(msg, job, LAUNCH_KEY_SUBMITJOB); 3473 3474 resp = launch_msg(msg); 3475 launch_data_free(msg); 3476 3477 if (resp == NULL) { 3478 launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno)); 3479 return 1; 3480 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) { 3481 errno = launch_data_get_errno(resp); 3482 if (errno) { 3483 launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], strerror(errno)); 3484 r = 1; 3485 } 3486 } else { 3487 launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], "unknown response"); 3488 } 3489 3490 launch_data_free(resp); 3491 3492 return r; 3493} 3494 3495int 3496getrusage_cmd(int argc, char *const argv[]) 3497{ 3498 launch_data_t resp, msg; 3499 bool badargs = false; 3500 int r = 0; 3501 3502 if (argc != 2) 3503 badargs = true; 3504 else if (strcmp(argv[1], "self") && strcmp(argv[1], "children")) 3505 badargs = true; 3506 3507 if (badargs) { 3508 launchctl_log(LOG_ERR, "usage: %s %s self | children", getprogname(), argv[0]); 3509 return 1; 3510 } 3511 3512 if (!strcmp(argv[1], "self")) { 3513 msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGESELF); 3514 } else { 3515 msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGECHILDREN); 3516 } 3517 3518 resp = launch_msg(msg); 3519 launch_data_free(msg); 3520 3521 if (resp == NULL) { 3522 launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno)); 3523 return 1; 3524 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) { 3525 launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], strerror(launch_data_get_errno(resp))); 3526 r = 1; 3527 } else if (launch_data_get_type(resp) == LAUNCH_DATA_OPAQUE) { 3528 struct rusage *rusage = launch_data_get_opaque(resp); 3529 launchctl_log(LOG_NOTICE, "\t%-10f\tuser time used", 3530 (double)rusage->ru_utime.tv_sec + (double)rusage->ru_utime.tv_usec / (double)1000000); 3531 launchctl_log(LOG_NOTICE, "\t%-10f\tsystem time used", 3532 (double)rusage->ru_stime.tv_sec + (double)rusage->ru_stime.tv_usec / (double)1000000); 3533 launchctl_log(LOG_NOTICE, "\t%-10ld\tmax resident set size", rusage->ru_maxrss); 3534 launchctl_log(LOG_NOTICE, "\t%-10ld\tshared text memory size", rusage->ru_ixrss); 3535 launchctl_log(LOG_NOTICE, "\t%-10ld\tunshared data size", rusage->ru_idrss); 3536 launchctl_log(LOG_NOTICE, "\t%-10ld\tunshared stack size", rusage->ru_isrss); 3537 launchctl_log(LOG_NOTICE, "\t%-10ld\tpage reclaims", rusage->ru_minflt); 3538 launchctl_log(LOG_NOTICE, "\t%-10ld\tpage faults", rusage->ru_majflt); 3539 launchctl_log(LOG_NOTICE, "\t%-10ld\tswaps", rusage->ru_nswap); 3540 launchctl_log(LOG_NOTICE, "\t%-10ld\tblock input operations", rusage->ru_inblock); 3541 launchctl_log(LOG_NOTICE, "\t%-10ld\tblock output operations", rusage->ru_oublock); 3542 launchctl_log(LOG_NOTICE, "\t%-10ld\tmessages sent", rusage->ru_msgsnd); 3543 launchctl_log(LOG_NOTICE, "\t%-10ld\tmessages received", rusage->ru_msgrcv); 3544 launchctl_log(LOG_NOTICE, "\t%-10ld\tsignals received", rusage->ru_nsignals); 3545 launchctl_log(LOG_NOTICE, "\t%-10ld\tvoluntary context switches", rusage->ru_nvcsw); 3546 launchctl_log(LOG_NOTICE, "\t%-10ld\tinvoluntary context switches", rusage->ru_nivcsw); 3547 } else { 3548 launchctl_log(LOG_ERR, "%s %s returned unknown response", getprogname(), argv[0]); 3549 r = 1; 3550 } 3551 3552 launch_data_free(resp); 3553 3554 return r; 3555} 3556 3557bool 3558launch_data_array_append(launch_data_t a, launch_data_t o) 3559{ 3560 size_t offt = launch_data_array_get_count(a); 3561 3562 return launch_data_array_set_index(a, o, offt); 3563} 3564 3565mach_port_t 3566str2bsport(const char *s) 3567{ 3568 bool getrootbs = strcmp(s, "/") == 0; 3569 mach_port_t last_bport, bport = bootstrap_port; 3570 task_t task = mach_task_self(); 3571 kern_return_t result; 3572 3573 if (strcmp(s, "..") == 0 || getrootbs) { 3574 do { 3575 last_bport = bport; 3576 result = bootstrap_parent(last_bport, &bport); 3577 3578 if (result == BOOTSTRAP_NOT_PRIVILEGED) { 3579 launchctl_log(LOG_ERR, "Permission denied"); 3580 return 1; 3581 } else if (result != BOOTSTRAP_SUCCESS) { 3582 launchctl_log(LOG_ERR, "bootstrap_parent() %d", result); 3583 return 1; 3584 } 3585 } while (getrootbs && last_bport != bport); 3586 } else if (strcmp(s, "0") == 0 || strcmp(s, "NULL") == 0) { 3587 bport = MACH_PORT_NULL; 3588 } else { 3589 int pid = atoi(s); 3590 3591 result = task_for_pid(mach_task_self(), pid, &task); 3592 3593 if (result != KERN_SUCCESS) { 3594 launchctl_log(LOG_ERR, "task_for_pid() %s", mach_error_string(result)); 3595 return 1; 3596 } 3597 3598 result = task_get_bootstrap_port(task, &bport); 3599 3600 if (result != KERN_SUCCESS) { 3601 launchctl_log(LOG_ERR, "Couldn't get bootstrap port: %s", mach_error_string(result)); 3602 return 1; 3603 } 3604 } 3605 3606 return bport; 3607} 3608 3609int 3610bsexec_cmd(int argc, char *const argv[]) 3611{ 3612 kern_return_t result; 3613 mach_port_t bport; 3614 3615 if (argc < 3) { 3616 launchctl_log(LOG_ERR, "usage: %s bsexec <PID> prog...", getprogname()); 3617 return 1; 3618 } 3619 3620 bport = str2bsport(argv[1]); 3621 3622 result = task_set_bootstrap_port(mach_task_self(), bport); 3623 3624 if (result != KERN_SUCCESS) { 3625 launchctl_log(LOG_ERR, "Couldn't switch to new bootstrap port: %s", mach_error_string(result)); 3626 return 1; 3627 } 3628 3629 setgid(getgid()); 3630 setuid(getuid()); 3631 3632 setenv(LAUNCH_ENV_KEEPCONTEXT, "1", 1); 3633 if (fwexec((const char *const *)argv + 2, NULL) == -1) { 3634 launchctl_log(LOG_ERR, "%s bsexec failed: %s", getprogname(), strerror(errno)); 3635 return 1; 3636 } 3637 3638 return 0; 3639} 3640 3641int 3642_bslist_cmd(mach_port_t bport, unsigned int depth, bool show_job, bool local_only) 3643{ 3644 kern_return_t result; 3645 name_array_t service_names; 3646 name_array_t service_jobs; 3647 mach_msg_type_number_t service_cnt, service_jobs_cnt, service_active_cnt; 3648 bootstrap_status_array_t service_actives; 3649 unsigned int i; 3650 3651 if (bport == MACH_PORT_NULL) { 3652 launchctl_log(LOG_ERR, "Invalid bootstrap port"); 3653 return 1; 3654 } 3655 3656 uint64_t flags = 0; 3657 flags |= local_only ? BOOTSTRAP_FORCE_LOCAL : 0; 3658 result = bootstrap_info(bport, &service_names, &service_cnt, &service_jobs, &service_jobs_cnt, &service_actives, &service_active_cnt, flags); 3659 if (result != BOOTSTRAP_SUCCESS) { 3660 launchctl_log(LOG_ERR, "bootstrap_info(): %d", result); 3661 return 1; 3662 } 3663 3664#define bport_state(x) (((x) == BOOTSTRAP_STATUS_ACTIVE) ? "A" : ((x) == BOOTSTRAP_STATUS_ON_DEMAND) ? "D" : "I") 3665 3666 for (i = 0; i < service_cnt ; i++) { 3667 if (!show_job) { 3668 fprintf(stdout, "%*s%-3s%s\n", depth, "", bport_state((service_actives[i])), service_names[i]); 3669 } else { 3670 fprintf(stdout, "%*s%-3s%s (%s)\n", depth, "", bport_state((service_actives[i])), service_names[i], service_jobs[i]); 3671 } 3672 } 3673 3674 return 0; 3675} 3676 3677int 3678bslist_cmd(int argc, char *const argv[]) 3679{ 3680 if (_launchctl_is_managed) { 3681 /* This output is meant for a command line, so don't print anything if 3682 * we're managed by launchd. 3683 */ 3684 return 1; 3685 } 3686 3687 mach_port_t bport = bootstrap_port; 3688 bool show_jobs = false; 3689 if (argc > 2 && strcmp(argv[2], "-j") == 0) { 3690 show_jobs = true; 3691 } 3692 3693 if (argc > 1) { 3694 if (show_jobs) { 3695 bport = str2bsport(argv[1]); 3696 } else if (strcmp(argv[1], "-j") == 0) { 3697 show_jobs = true; 3698 } 3699 } 3700 3701 if (bport == MACH_PORT_NULL) { 3702 launchctl_log(LOG_ERR, "Invalid bootstrap port"); 3703 return 1; 3704 } 3705 3706 return _bslist_cmd(bport, 0, show_jobs, false); 3707} 3708 3709int 3710_bstree_cmd(mach_port_t bsport, unsigned int depth, bool show_jobs) 3711{ 3712 if (bsport == MACH_PORT_NULL) { 3713 launchctl_log(LOG_ERR, "No root port!"); 3714 return 1; 3715 } 3716 3717 mach_port_array_t child_ports = NULL; 3718 name_array_t child_names = NULL; 3719 bootstrap_property_array_t child_props = NULL; 3720 unsigned int cnt = 0; 3721 3722 kern_return_t kr = bootstrap_lookup_children(bsport, &child_ports, &child_names, &child_props, (mach_msg_type_number_t *)&cnt); 3723 if (kr != BOOTSTRAP_SUCCESS && kr != BOOTSTRAP_NO_CHILDREN) { 3724 if (kr == BOOTSTRAP_NOT_PRIVILEGED) { 3725 launchctl_log(LOG_ERR, "You must be root to perform this operation."); 3726 } else { 3727 launchctl_log(LOG_ERR, "bootstrap_lookup_children(): %d", kr); 3728 } 3729 3730 return 1; 3731 } 3732 3733 unsigned int i = 0; 3734 _bslist_cmd(bsport, depth, show_jobs, true); 3735 3736 for (i = 0; i < cnt; i++) { 3737 char *type = NULL; 3738 if (child_props[i] & BOOTSTRAP_PROPERTY_PERUSER) { 3739 type = "Per-user"; 3740 } else if (child_props[i] & BOOTSTRAP_PROPERTY_EXPLICITSUBSET) { 3741 type = "Explicit Subset"; 3742 } else if (child_props[i] & BOOTSTRAP_PROPERTY_IMPLICITSUBSET) { 3743 type = "Implicit Subset"; 3744 } else if (child_props[i] & BOOTSTRAP_PROPERTY_MOVEDSUBSET) { 3745 type = "Moved Subset"; 3746 } else if (child_props[i] & BOOTSTRAP_PROPERTY_XPC_SINGLETON) { 3747 type = "XPC Singleton Domain"; 3748 } else if (child_props[i] & BOOTSTRAP_PROPERTY_XPC_DOMAIN) { 3749 type = "XPC Private Domain"; 3750 } else { 3751 type = "Unknown"; 3752 } 3753 3754 fprintf(stdout, "%*s%s (%s)/\n", depth, "", child_names[i], type); 3755 if (child_ports[i] != MACH_PORT_NULL) { 3756 _bstree_cmd(child_ports[i], depth + 4, show_jobs); 3757 } 3758 } 3759 3760 return 0; 3761} 3762 3763int 3764bstree_cmd(int argc, char * const argv[]) 3765{ 3766 if (_launchctl_is_managed) { 3767 /* This output is meant for a command line, so don't print anything if 3768 * we're managed by launchd. 3769 */ 3770 return 1; 3771 } 3772 3773 bool show_jobs = false; 3774 if (geteuid() != 0) { 3775 launchctl_log(LOG_ERR, "You must be root to perform this operation."); 3776 return 1; 3777 } else { 3778 if (argc == 2 && strcmp(argv[1], "-j") == 0) { 3779 show_jobs = true; 3780 } 3781 fprintf(stdout, "System/\n"); 3782 } 3783 3784 return _bstree_cmd(str2bsport("/"), 4, show_jobs); 3785} 3786 3787int 3788managerpid_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused))) 3789{ 3790 int64_t manager_pid = 0; 3791 vproc_err_t verr = vproc_swap_integer(NULL, VPROC_GSK_MGR_PID, NULL, (int64_t *)&manager_pid); 3792 if (verr) { 3793 launchctl_log(LOG_NOTICE, "Unknown job manager!"); 3794 return 1; 3795 } 3796 3797 launchctl_log(LOG_NOTICE, "%d", (pid_t)manager_pid); 3798 return 0; 3799} 3800 3801int 3802manageruid_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused))) 3803{ 3804 int64_t manager_uid = 0; 3805 vproc_err_t verr = vproc_swap_integer(NULL, VPROC_GSK_MGR_UID, NULL, (int64_t *)&manager_uid); 3806 if (verr) { 3807 launchctl_log(LOG_NOTICE, "Unknown job manager!"); 3808 return 1; 3809 } 3810 3811 launchctl_log(LOG_NOTICE, "%lli", manager_uid); 3812 return 0; 3813} 3814 3815int 3816managername_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused))) 3817{ 3818 char *manager_name = NULL; 3819 vproc_err_t verr = vproc_swap_string(NULL, VPROC_GSK_MGR_NAME, NULL, &manager_name); 3820 if (verr) { 3821 launchctl_log(LOG_NOTICE, "Unknown job manager!"); 3822 return 1; 3823 } 3824 3825 launchctl_log(LOG_NOTICE, "%s", manager_name); 3826 free(manager_name); 3827 3828 return 0; 3829} 3830 3831int 3832asuser_cmd(int argc, char * const argv[]) 3833{ 3834 /* This code plays fast and loose with Mach ports. Do NOT use it as any sort 3835 * of reference for port handling. Or really anything else in this file. 3836 */ 3837 uid_t req_uid = (uid_t)-2; 3838 if (argc > 2) { 3839 req_uid = atoi(argv[1]); 3840 if (req_uid == (uid_t)-2) { 3841 launchctl_log(LOG_ERR, "You cannot run a command nobody."); 3842 return 1; 3843 } 3844 } else { 3845 launchctl_log(LOG_ERR, "Usage: launchctl asuser <UID> <command> [arguments...]."); 3846 return 1; 3847 } 3848 3849 if (geteuid() != 0) { 3850 launchctl_log(LOG_ERR, "You must be root to run a command as another user."); 3851 return 1; 3852 } 3853 3854 mach_port_t rbs = MACH_PORT_NULL; 3855 kern_return_t kr = bootstrap_get_root(bootstrap_port, &rbs); 3856 if (kr != BOOTSTRAP_SUCCESS) { 3857 launchctl_log(LOG_ERR, "bootstrap_get_root(): %u", kr); 3858 return 1; 3859 } 3860 3861 mach_port_t bp = MACH_PORT_NULL; 3862 kr = bootstrap_look_up_per_user(rbs, NULL, req_uid, &bp); 3863 if (kr != BOOTSTRAP_SUCCESS) { 3864 launchctl_log(LOG_ERR, "bootstrap_look_up_per_user(): %u", kr); 3865 return 1; 3866 } 3867 3868 bootstrap_port = bp; 3869 kr = task_set_bootstrap_port(mach_task_self(), bp); 3870 if (kr != KERN_SUCCESS) { 3871 launchctl_log(LOG_ERR, "task_set_bootstrap_port(): 0x%x: %s", kr, mach_error_string(kr)); 3872 return 1; 3873 } 3874 3875 name_t sockpath; 3876 sockpath[0] = 0; 3877 kr = _vprocmgr_getsocket(sockpath); 3878 if (kr != BOOTSTRAP_SUCCESS) { 3879 launchctl_log(LOG_ERR, "_vprocmgr_getsocket(): %u", kr); 3880 return 1; 3881 } 3882 3883 setenv(LAUNCHD_SOCKET_ENV, sockpath, 1); 3884 setenv(LAUNCH_ENV_KEEPCONTEXT, "1", 1); 3885 if (fwexec((const char *const *)argv + 2, NULL) == -1) { 3886 launchctl_log(LOG_ERR, "Couldn't spawn command: %s", argv[2]); 3887 return 1; 3888 } 3889 3890 return 0; 3891} 3892 3893void 3894loopback_setup_ipv4(void) 3895{ 3896 struct ifaliasreq ifra; 3897 struct ifreq ifr; 3898 int s; 3899 3900 memset(&ifr, 0, sizeof(ifr)); 3901 strcpy(ifr.ifr_name, "lo0"); 3902 3903 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 3904 return; 3905 3906 if (posix_assumes_zero(ioctl(s, SIOCGIFFLAGS, &ifr)) != -1) { 3907 ifr.ifr_flags |= IFF_UP; 3908 (void)posix_assumes_zero(ioctl(s, SIOCSIFFLAGS, &ifr)); 3909 } 3910 3911 memset(&ifra, 0, sizeof(ifra)); 3912 strcpy(ifra.ifra_name, "lo0"); 3913 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_family = AF_INET; 3914 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); 3915 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_len = sizeof(struct sockaddr_in); 3916 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_family = AF_INET; 3917 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_addr.s_addr = htonl(IN_CLASSA_NET); 3918 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_len = sizeof(struct sockaddr_in); 3919 3920 (void)posix_assumes_zero(ioctl(s, SIOCAIFADDR, &ifra)); 3921 (void)close(s); 3922} 3923 3924void 3925loopback_setup_ipv6(void) 3926{ 3927 struct in6_aliasreq ifra6; 3928 struct ifreq ifr; 3929 int s6; 3930 3931 memset(&ifr, 0, sizeof(ifr)); 3932 strcpy(ifr.ifr_name, "lo0"); 3933 3934 if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) 3935 return; 3936 3937 memset(&ifr, 0, sizeof(ifr)); 3938 strcpy(ifr.ifr_name, "lo0"); 3939 3940 if (posix_assumes_zero(ioctl(s6, SIOCGIFFLAGS, &ifr)) != -1) { 3941 ifr.ifr_flags |= IFF_UP; 3942 (void)posix_assumes_zero(ioctl(s6, SIOCSIFFLAGS, &ifr)); 3943 } 3944 3945 memset(&ifra6, 0, sizeof(ifra6)); 3946 strcpy(ifra6.ifra_name, "lo0"); 3947 3948 ifra6.ifra_addr.sin6_family = AF_INET6; 3949 ifra6.ifra_addr.sin6_addr = in6addr_loopback; 3950 ifra6.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); 3951 ifra6.ifra_prefixmask.sin6_family = AF_INET6; 3952 memset(&ifra6.ifra_prefixmask.sin6_addr, 0xff, sizeof(struct in6_addr)); 3953 ifra6.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); 3954 ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; 3955 ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; 3956 3957 if (ioctl(s6, SIOCAIFADDR_IN6, &ifra6) == -1 && errno != EEXIST) { 3958 (void)os_assumes_zero(errno); 3959 } 3960 3961 (void)close(s6); 3962} 3963 3964pid_t 3965fwexec(const char *const *argv, int *wstatus) 3966{ 3967 int wstatus2; 3968 pid_t p; 3969 3970 /* We'd use posix_spawnp(), but we want to workaround: 6288899 */ 3971 if ((p = vfork()) == -1) { 3972 return -1; 3973 } else if (p == 0) { 3974 execvp(argv[0], (char *const *)argv); 3975 _exit(EXIT_FAILURE); 3976 } 3977 3978 if (waitpid(p, wstatus ? wstatus : &wstatus2, 0) == -1) { 3979 return -1; 3980 } 3981 3982 if (wstatus) { 3983 return p; 3984 } else if (WIFEXITED(wstatus2) && WEXITSTATUS(wstatus2) == EXIT_SUCCESS) { 3985 return p; 3986 } 3987 3988 return -1; 3989} 3990 3991void 3992do_potential_fsck(void) 3993{ 3994 /* XXX: This whole function's logic needs to be redone. */ 3995 3996 const char *safe_fsck_tool[] = { "fsck", "-fy", NULL }; 3997 const char *fsck_tool[] = { "fsck", "-q", NULL }; 3998 const char *remount_tool[] = { "mount", "-uw", "/", NULL }; 3999#if TARGET_OS_EMBEDDED 4000 const char *nvram_tool[] = { "/usr/sbin/nvram", "auto-boot=false", NULL }; 4001#endif /* TARGET_OS_EMBEDDED */ 4002 struct statfs sfs; 4003 int status = 0; 4004 4005 if (posix_assumes_zero(statfs("/", &sfs)) == -1) { 4006 return; 4007 } 4008 4009 if (!(sfs.f_flags & MNT_RDONLY)) { 4010 return; 4011 } 4012 4013 if (!is_safeboot()) { 4014#if 0 4015 /* We have disabled this block for now. We need to revisit this optimization after Leopard. */ 4016 if (sfs.f_flags & MNT_JOURNALED) { 4017 goto out; 4018 } 4019#endif 4020 launchctl_log(LOG_NOTICE, "Running fsck on the boot volume..."); 4021 if (fwexec(fsck_tool, &status) != -1) { 4022 if (WEXITSTATUS(status) != 0) { 4023 launchctl_log(LOG_NOTICE, "fsck exited with status: %d", WEXITSTATUS(status)); 4024 } else { 4025 goto out; 4026 } 4027 } else { 4028 launchctl_log(LOG_NOTICE, "fwexec(): %d: %s", errno, strerror(errno)); 4029 } 4030 } 4031 4032 launchctl_log(LOG_NOTICE, "Running safe fsck on the boot volume..."); 4033 if (fwexec(safe_fsck_tool, &status) != -1) { 4034 if (WEXITSTATUS(status) != 0) { 4035 launchctl_log(LOG_NOTICE, "Safe fsck exited with status: %d", WEXITSTATUS(status)); 4036 } else { 4037 goto out; 4038 } 4039 } else { 4040 launchctl_log(LOG_NOTICE, "fwexec(): %d: %s", errno, strerror(errno)); 4041 } 4042 4043 /* someday, we should keep booting read-only, but as of today, other sub-systems cannot handle that scenario */ 4044#if TARGET_OS_EMBEDDED 4045 launchctl_log(LOG_NOTICE, "fsck failed! Booting into restore mode..."); 4046 (void)posix_assumes_zero(fwexec(nvram_tool, NULL)); 4047 (void)reboot(RB_AUTOBOOT); 4048#else 4049 launchctl_log(LOG_NOTICE, "fsck failed! Shutting down in 3 seconds."); 4050 sleep(3); 4051 (void)reboot(RB_HALT); 4052#endif 4053 4054 return; 4055out: 4056 4057#if TARGET_OS_EMBEDDED 4058 /* Once we've validated the root filesystem, kick off any 4059 * tasks needed for data protection before we mount other file 4060 * systems. 4061 */ 4062 init_data_protection(); 4063#endif 4064 4065 /* 4066 * Once this is fixed: 4067 * 4068 * <rdar://problem/3948774> Mount flag updates should be possible with NULL as the forth argument to mount() 4069 * 4070 * We can then do this one system call instead of calling out a full blown process. 4071 * 4072 * assumes(mount(sfs.f_fstypename, "/", MNT_UPDATE, NULL) != -1); 4073 */ 4074#if TARGET_OS_EMBEDDED 4075 if (path_check("/etc/fstab")) { 4076 const char *mount_tool[] = { "mount", "-vat", "nonfs", NULL }; 4077 if (posix_assumes_zero(fwexec(mount_tool, NULL)) == -1) { 4078 (void)fwexec(nvram_tool, NULL); 4079 (void)reboot(RB_AUTOBOOT); 4080 } 4081 } else 4082#endif 4083 { 4084 (void)posix_assumes_zero(fwexec(remount_tool, NULL)); 4085 } 4086 4087 fix_bogus_file_metadata(); 4088} 4089 4090void 4091fix_bogus_file_metadata(void) 4092{ 4093 // Don't do any of this on embedded: <rdar://problem/13212363> 4094#if !TARGET_OS_EMBEDDED 4095 static const struct { 4096 const char *path; 4097 const uid_t owner; 4098 const gid_t group; 4099 const mode_t needed_bits; 4100 const mode_t bad_bits; 4101 const bool create; 4102 } f[] = { 4103 { "/sbin/launchd", 0, 0, S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH, S_ISUID|S_ISGID|S_ISVTX|S_IWOTH, false }, 4104 { _PATH_TMP, 0, 0, S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO, S_ISUID|S_ISGID, true }, 4105 { _PATH_VARTMP, 0, 0, S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO, S_ISUID|S_ISGID, true }, 4106 { "/var/folders", 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_ISUID | S_ISGID, true }, 4107 { LAUNCHD_DB_PREFIX, 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IWGRP | S_IWOTH, true }, 4108 { LAUNCHD_DB_PREFIX "/com.apple.launchd", 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IWGRP | S_IWOTH, true }, 4109 // Fixing <rdar://problem/7571633>. 4110 { _PATH_VARDB, 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IWGRP | S_IWOTH | S_ISUID | S_ISGID, true }, 4111 { _PATH_VARDB "mds/", 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IWGRP | S_IWOTH | S_ISUID | S_ISGID, true }, 4112 // Similar fix for <rdar://problem/6550172>. 4113 { "/Library/StartupItems", 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IWGRP | S_IWOTH | S_ISUID | S_ISGID, true }, 4114 }; 4115 struct stat sb; 4116 size_t i; 4117 4118 for (i = 0; i < (sizeof(f) / sizeof(f[0])); i++) { 4119 mode_t i_needed_bits; 4120 mode_t i_bad_bits; 4121 bool fix_mode = false; 4122 bool fix_id = false; 4123 4124 if (stat(f[i].path, &sb) == -1) { 4125 launchctl_log(LOG_NOTICE, "Crucial filesystem check: Path not present: %s. %s", f[i].path, f[i].create ? "Will create." : ""); 4126 if (f[i].create) { 4127 if (posix_assumes_zero(mkdir(f[i].path, f[i].needed_bits)) == -1) { 4128 continue; 4129 } else if (posix_assumes_zero(stat(f[i].path, &sb)) == -1) { 4130 continue; 4131 } 4132 } else { 4133 continue; 4134 } 4135 } 4136 4137 i_needed_bits = ~sb.st_mode & f[i].needed_bits; 4138 i_bad_bits = sb.st_mode & f[i].bad_bits; 4139 4140 if (i_bad_bits) { 4141 launchctl_log(LOG_ERR, "Crucial filesystem check: Removing bogus mode bits 0%o on path: %s", i_bad_bits, f[i].path); 4142 fix_mode = true; 4143 } 4144 if (i_needed_bits) { 4145 launchctl_log(LOG_ERR, "Crucial filesystem check: Adding missing mode bits 0%o on path: %s", i_needed_bits, f[i].path); 4146 fix_mode = true; 4147 } 4148 if (sb.st_uid != f[i].owner) { 4149 launchctl_log(LOG_ERR, "Crucial filesystem check: Fixing bogus UID %u on path: %s", sb.st_uid, f[i].path); 4150 fix_id = true; 4151 } 4152 if (sb.st_gid != f[i].group) { 4153 launchctl_log(LOG_ERR, "Crucial filesystem check: Fixing bogus GID %u on path: %s", sb.st_gid, f[i].path); 4154 fix_id = true; 4155 } 4156 4157 if (fix_mode) { 4158 (void)posix_assumes_zero(chmod(f[i].path, (sb.st_mode & ~i_bad_bits) | i_needed_bits)); 4159 } 4160 if (fix_id) { 4161 (void)posix_assumes_zero(chown(f[i].path, f[i].owner, f[i].group)); 4162 } 4163 } 4164#endif 4165} 4166 4167 4168bool 4169path_check(const char *path) 4170{ 4171 struct stat sb; 4172 4173 if (stat(path, &sb) == 0) 4174 return true; 4175 return false; 4176} 4177 4178bool 4179is_safeboot(void) 4180{ 4181 int sbmib[] = { CTL_KERN, KERN_SAFEBOOT }; 4182 uint32_t sb = 0; 4183 size_t sbsz = sizeof(sb); 4184 4185 if (posix_assumes_zero(sysctl(sbmib, 2, &sb, &sbsz, NULL, 0)) == -1) { 4186 return false; 4187 } 4188 4189 return (bool)sb; 4190} 4191 4192bool 4193is_netboot(void) 4194{ 4195 int nbmib[] = { CTL_KERN, KERN_NETBOOT }; 4196 uint32_t nb = 0; 4197 size_t nbsz = sizeof(nb); 4198 4199 if (posix_assumes_zero(sysctl(nbmib, 2, &nb, &nbsz, NULL, 0)) == -1) { 4200 return false; 4201 } 4202 4203 return (bool)nb; 4204} 4205 4206void 4207empty_dir(const char *thedir, struct stat *psb) 4208{ 4209 struct dirent *de; 4210 struct stat psb2; 4211 DIR *od; 4212 int currend_dir_fd; 4213 4214 if (!psb) { 4215 psb = &psb2; 4216 if (posix_assumes_zero(lstat(thedir, psb)) == -1) { 4217 return; 4218 } 4219 } 4220 4221 if (posix_assumes_zero(currend_dir_fd = open(".", 0)) == -1) { 4222 return; 4223 } 4224 4225 if (posix_assumes_zero(chdir(thedir)) == -1) { 4226 goto out; 4227 } 4228 4229 if (!(od = opendir("."))) { 4230 (void)os_assumes_zero(errno); 4231 goto out; 4232 } 4233 4234 while ((de = readdir(od))) { 4235 struct stat sb; 4236 4237 if (strcmp(de->d_name, ".") == 0) { 4238 continue; 4239 } 4240 4241 if (strcmp(de->d_name, "..") == 0) { 4242 continue; 4243 } 4244 4245 if (posix_assumes_zero(lstat(de->d_name, &sb)) == -1) { 4246 continue; 4247 } 4248 4249 if (psb->st_dev != sb.st_dev) { 4250 (void)posix_assumes_zero(unmount(de->d_name, MNT_FORCE)); 4251 4252 /* Let's lstat() again to see if the unmount() worked and what was 4253 * under it. 4254 */ 4255 if (posix_assumes_zero(lstat(de->d_name, &sb)) == -1) { 4256 continue; 4257 } 4258 4259 if (os_assumes(psb->st_dev == sb.st_dev)) { 4260 continue; 4261 } 4262 } 4263 4264 if (S_ISDIR(sb.st_mode)) { 4265 empty_dir(de->d_name, &sb); 4266 } 4267 4268 (void)posix_assumes_zero(lchflags(de->d_name, 0)); 4269 (void)posix_assumes_zero(remove(de->d_name)); 4270 } 4271 4272 (void)closedir(od); 4273 4274out: 4275 (void)posix_assumes_zero(fchdir(currend_dir_fd)); 4276 (void)posix_assumes_zero(close(currend_dir_fd)); 4277} 4278 4279int 4280touch_file(const char *path, mode_t m) 4281{ 4282 int fd = open(path, O_CREAT, m); 4283 4284 if (fd == -1) 4285 return -1; 4286 4287 return close(fd); 4288} 4289 4290void 4291apply_sysctls_from_file(const char *thefile) 4292{ 4293 const char *sysctl_tool[] = { "sysctl", "-w", NULL, NULL }; 4294 size_t ln_len = 0; 4295 char *val, *tmpstr; 4296 FILE *sf; 4297 4298 if (!(sf = fopen(thefile, "r"))) 4299 return; 4300 4301 while ((val = fgetln(sf, &ln_len))) { 4302 if (ln_len == 0) { 4303 continue; 4304 } 4305 if (!(tmpstr = malloc(ln_len + 1))) { 4306 (void)os_assumes_zero(errno); 4307 continue; 4308 } 4309 memcpy(tmpstr, val, ln_len); 4310 tmpstr[ln_len] = 0; 4311 val = tmpstr; 4312 4313 if (val[ln_len - 1] == '\n' || val[ln_len - 1] == '\r') { 4314 val[ln_len - 1] = '\0'; 4315 } 4316 4317 while (*val && isspace(*val)) 4318 val++; 4319 if (*val == '\0' || *val == '#') { 4320 goto skip_sysctl_tool; 4321 } 4322 sysctl_tool[2] = val; 4323 (void)posix_assumes_zero(fwexec(sysctl_tool, NULL)); 4324skip_sysctl_tool: 4325 free(tmpstr); 4326 } 4327 4328 (void)fclose(sf); 4329} 4330 4331static CFStringRef 4332copySystemBuildVersion(void) 4333{ 4334 CFStringRef build = NULL; 4335 const char path[] = "/System/Library/CoreServices/SystemVersion.plist"; 4336 CFURLRef plistURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (const uint8_t *)path, sizeof(path) - 1, false); 4337 4338 CFPropertyListRef plist = NULL; 4339 if (plistURL && (plist = CFPropertyListCreateFromFile(plistURL))) { 4340 if (CFTypeCheck(plist, CFDictionary)) { 4341 build = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)plist, _kCFSystemVersionBuildVersionKey); 4342 if (build && CFTypeCheck(build, CFString)) { 4343 CFRetain(build); 4344 } else { 4345 build = CFSTR("99Z999"); 4346 } 4347 } 4348 4349 CFRelease(plist); 4350 } else { 4351 build = CFSTR("99Z999"); 4352 } 4353 4354 if (plistURL) { 4355 CFRelease(plistURL); 4356 } 4357 4358 return build; 4359} 4360 4361void 4362do_sysversion_sysctl(void) 4363{ 4364 int mib[] = { CTL_KERN, KERN_OSVERSION }; 4365 CFStringRef buildvers; 4366 char buf[1024]; 4367 size_t bufsz = sizeof(buf); 4368 4369 /* <rdar://problem/4477682> ER: launchd should set kern.osversion very early in boot */ 4370 4371 if (sysctl(mib, 2, buf, &bufsz, NULL, 0) == -1) { 4372 launchctl_log(LOG_ERR, "sysctl(): %s", strerror(errno)); 4373 return; 4374 } 4375 4376 if (buf[0] != '\0') { 4377 return; 4378 } 4379 4380 buildvers = copySystemBuildVersion(); 4381 if (buildvers) { 4382 CFStringGetCString(buildvers, buf, sizeof(buf), kCFStringEncodingUTF8); 4383 (void)posix_assumes_zero(sysctl(mib, 2, NULL, 0, buf, strlen(buf) + 1)); 4384 } 4385 4386 CFRelease(buildvers); 4387} 4388 4389void 4390do_application_firewall_magic(int sfd, launch_data_t thejob) 4391{ 4392 const char *prog = NULL, *partialprog = NULL; 4393 char *path, *pathtmp, **pathstmp; 4394 char *paths[100]; 4395 launch_data_t tmp; 4396 4397 /* 4398 * Sigh... 4399 * <rdar://problem/4684434> setsockopt() with the executable path as the argument 4400 */ 4401 4402 if ((tmp = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_PROGRAM))) { 4403 prog = launch_data_get_string(tmp); 4404 } 4405 4406 if (!prog) { 4407 if ((tmp = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_PROGRAMARGUMENTS))) { 4408 if ((tmp = launch_data_array_get_index(tmp, 0))) { 4409 if ((partialprog = launch_data_get_string(tmp))) { 4410 if (partialprog[0] == '/') { 4411 prog = partialprog; 4412 } 4413 } 4414 } 4415 } 4416 } 4417 4418 if (!prog) { 4419 pathtmp = path = strdup(getenv("PATH")); 4420 4421 pathstmp = paths; 4422 4423 while ((*pathstmp = strsep(&pathtmp, ":"))) { 4424 if (**pathstmp != '\0') { 4425 pathstmp++; 4426 } 4427 } 4428 4429 free(path); 4430 pathtmp = alloca(MAXPATHLEN); 4431 4432 pathstmp = paths; 4433 4434 for (; *pathstmp; pathstmp++) { 4435 snprintf(pathtmp, MAXPATHLEN, "%s/%s", *pathstmp, partialprog); 4436 if (path_check(pathtmp)) { 4437 prog = pathtmp; 4438 break; 4439 } 4440 } 4441 } 4442 4443 if (prog != NULL) { 4444 /* The networking team has asked us to ignore the failure of this API if 4445 * errno == ENOPROTOOPT. 4446 */ 4447 if (setsockopt(sfd, SOL_SOCKET, SO_EXECPATH, prog, (socklen_t)(strlen(prog) + 1)) == -1 && errno != ENOPROTOOPT) { 4448 (void)os_assumes_zero(errno); 4449 } 4450 } 4451} 4452 4453 4454void 4455preheat_page_cache_hack(void) 4456{ 4457 struct dirent *de; 4458 DIR *thedir; 4459 4460 /* Disable this hack for now */ 4461 return; 4462 4463 if ((thedir = opendir("/etc/preheat_at_boot")) == NULL) { 4464 return; 4465 } 4466 4467 while ((de = readdir(thedir))) { 4468 struct stat sb; 4469 void *junkbuf; 4470 int fd; 4471 4472 if (de->d_name[0] == '.') { 4473 continue; 4474 } 4475 4476 if ((fd = open(de->d_name, O_RDONLY)) == -1) { 4477 continue; 4478 } 4479 4480 if (fstat(fd, &sb) != -1) { 4481 if ((sb.st_size < 10*1024*1024) && (junkbuf = malloc((size_t)sb.st_size)) != NULL) { 4482 ssize_t n = read(fd, junkbuf, (size_t)sb.st_size); 4483 if (posix_assumes_zero(n) != -1 && n != (ssize_t)sb.st_size) { 4484 (void)os_assumes_zero(n); 4485 } 4486 free(junkbuf); 4487 } 4488 } 4489 4490 close(fd); 4491 } 4492 4493 closedir(thedir); 4494} 4495 4496void 4497do_bootroot_magic(void) 4498{ 4499 const char *kextcache_tool[] = { "kextcache", "-U", "/", NULL }; 4500 CFTypeRef bootrootProp; 4501 io_service_t chosen; 4502 int wstatus; 4503 pid_t p; 4504 4505 chosen = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/chosen"); 4506 4507 if (!os_assumes(chosen)) { 4508 return; 4509 } 4510 4511 bootrootProp = IORegistryEntryCreateCFProperty(chosen, CFSTR(kBootRootActiveKey), kCFAllocatorDefault, 0); 4512 4513 IOObjectRelease(chosen); 4514 4515 if (!bootrootProp) { 4516 return; 4517 } 4518 4519 CFRelease(bootrootProp); 4520 4521 if (posix_assumes_zero(p = fwexec(kextcache_tool, &wstatus)) == -1) { 4522 return; 4523 } 4524 4525 if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == EX_OSFILE) { 4526 (void)reboot(RB_AUTOBOOT); 4527 } 4528} 4529 4530void 4531do_file_init(void) 4532{ 4533 struct stat sb; 4534 4535 if (stat("/AppleInternal", &sb) == 0 && stat("/var/db/disableAppleInternal", &sb) == -1) { 4536 _launchctl_apple_internal = true; 4537 } 4538 4539 char bootargs[128]; 4540 size_t len = sizeof(bootargs); 4541 int r = sysctlbyname("kern.bootargs", bootargs, &len, NULL, 0); 4542 if (r == 0 && (strnstr(bootargs, "-v", len) != NULL || strnstr(bootargs, "-s", len))) { 4543 _launchctl_verbose_boot = true; 4544 } 4545 4546 if (stat("/var/db/.launchd_shutdown_debugging", &sb) == 0 && _launchctl_verbose_boot) { 4547 _launchctl_startup_debugging = true; 4548 } 4549} 4550