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