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