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