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