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