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