]> git.saurik.com Git - apple/launchd.git/blob - launchd/src/launchctl.c
4c943dbca3b1164f8085e800f4ffca809d7eec60
[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: 23792 $";
22
23 #include "launch.h"
24 #include "launch_priv.h"
25 #include "bootstrap.h"
26 #include "vproc.h"
27 #include "vproc_priv.h"
28 #include "vproc_internal.h"
29
30 #include <CoreFoundation/CoreFoundation.h>
31 #include <CoreFoundation/CFPriv.h>
32 #include <TargetConditionals.h>
33 #if HAVE_SECURITY
34 #include <Security/Security.h>
35 #include <Security/AuthSession.h>
36 #endif
37 #include <IOKit/IOKitLib.h>
38 #include <NSSystemDirectories.h>
39 #include <mach/mach.h>
40 #include <sys/types.h>
41 #include <sys/sysctl.h>
42 #include <sys/time.h>
43 #include <sys/sysctl.h>
44 #include <sys/stat.h>
45 #include <sys/socket.h>
46 #include <sys/un.h>
47 #include <sys/fcntl.h>
48 #include <sys/event.h>
49 #include <sys/resource.h>
50 #include <sys/param.h>
51 #include <sys/mount.h>
52 #include <sys/reboot.h>
53 #include <net/if.h>
54 #include <netinet/in.h>
55 #include <netinet/in_var.h>
56 #include <netinet6/nd6.h>
57 #include <unistd.h>
58 #include <dirent.h>
59 #include <libgen.h>
60 #include <pwd.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <pwd.h>
64 #include <grp.h>
65 #include <netdb.h>
66 #include <syslog.h>
67 #include <glob.h>
68 #include <readline/readline.h>
69 #include <readline/history.h>
70 #include <dns_sd.h>
71 #include <paths.h>
72 #include <utmpx.h>
73 #include <bootfiles.h>
74 #include <sysexits.h>
75 #include <util.h>
76
77
78 #define LAUNCH_SECDIR "/tmp/launch-XXXXXX"
79
80 #define MACHINIT_JOBKEY_ONDEMAND "OnDemand"
81 #define MACHINIT_JOBKEY_SERVICENAME "ServiceName"
82 #define MACHINIT_JOBKEY_COMMAND "Command"
83 #define MACHINIT_JOBKEY_SERVERPORT "ServerPort"
84 #define MACHINIT_JOBKEY_SERVICEPORT "ServicePort"
85
86 #define assumes(e) \
87 (__builtin_expect(!(e), 0) ? _log_launchctl_bug(__rcs_file_version__, __FILE__, __LINE__, #e), false : true)
88
89
90 struct load_unload_state {
91 launch_data_t pass0;
92 launch_data_t pass1;
93 launch_data_t pass2;
94 char *session_type;
95 unsigned int editondisk:1, load:1, forceload:1, __pad:29;
96 };
97
98 static void myCFDictionaryApplyFunction(const void *key, const void *value, void *context);
99 static bool launch_data_array_append(launch_data_t a, launch_data_t o);
100 static void distill_jobs(launch_data_t);
101 static void distill_config_file(launch_data_t);
102 static void sock_dict_cb(launch_data_t what, const char *key, void *context);
103 static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob);
104 static launch_data_t CF2launch_data(CFTypeRef);
105 static launch_data_t read_plist_file(const char *file, bool editondisk, bool load);
106 static CFPropertyListRef CreateMyPropertyListFromFile(const char *);
107 static void WriteMyPropertyListToFile(CFPropertyListRef, const char *);
108 static bool path_goodness_check(const char *path, bool forceload);
109 static void readpath(const char *, struct load_unload_state *);
110 static void readfile(const char *, struct load_unload_state *);
111 static int _fd(int);
112 static int demux_cmd(int argc, char *const argv[]);
113 static launch_data_t do_rendezvous_magic(const struct addrinfo *res, const char *serv);
114 static void submit_job_pass(launch_data_t jobs);
115 static void submit_mach_jobs(launch_data_t jobs);
116 static void let_go_of_mach_jobs(launch_data_t jobs);
117 static void do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup);
118 static mach_port_t str2bsport(const char *s);
119 static void print_jobs(launch_data_t j, const char *key, void *context);
120 static void print_obj(launch_data_t obj, const char *key, void *context);
121 static bool is_legacy_mach_job(launch_data_t obj);
122 static bool delay_to_second_pass(launch_data_t o);
123 static void delay_to_second_pass2(launch_data_t o, const char *key, void *context);
124 static bool str2lim(const char *buf, rlim_t *res);
125 static const char *lim2str(rlim_t val, char *buf);
126 static const char *num2name(int n);
127 static ssize_t name2num(const char *n);
128 static void unloadjob(launch_data_t job);
129 static void print_key_value(launch_data_t obj, const char *key, void *context);
130 static void print_launchd_env(launch_data_t obj, const char *key, void *context);
131 static void _log_launchctl_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test);
132 static void loopback_setup_ipv4(void);
133 static void loopback_setup_ipv6(void);
134 static pid_t fwexec(const char *const *argv, bool _wait);
135 static void do_potential_fsck(void);
136 static bool path_check(const char *path);
137 static bool is_safeboot(void);
138 static bool is_netboot(void);
139 static void apply_sysctls_from_file(const char *thefile);
140 static void empty_dir(const char *thedir, struct stat *psb);
141 static int touch_file(const char *path, mode_t m);
142 static void do_sysversion_sysctl(void);
143 static void do_application_firewall_magic(int sfd, launch_data_t thejob);
144 static void preheat_page_cache_hack(void);
145 static void do_bootroot_magic(void);
146 static void do_single_user_mode(bool);
147 static bool do_single_user_mode2(void);
148 static void read_launchd_conf(void);
149 static bool job_disabled_logic(launch_data_t obj);
150 static void fix_bogus_file_metadata(void);
151
152 typedef enum {
153 BOOTCACHE_START = 1,
154 BOOTCACHE_TAG,
155 BOOTCACHE_STOP,
156 } BootCache_action_t;
157
158 static void do_BootCache_magic(BootCache_action_t what);
159
160 static int bootstrap_cmd(int argc, char *const argv[]);
161 static int load_and_unload_cmd(int argc, char *const argv[]);
162 //static int reload_cmd(int argc, char *const argv[]);
163 static int start_stop_remove_cmd(int argc, char *const argv[]);
164 static int submit_cmd(int argc, char *const argv[]);
165 static int list_cmd(int argc, char *const argv[]);
166
167 static int setenv_cmd(int argc, char *const argv[]);
168 static int unsetenv_cmd(int argc, char *const argv[]);
169 static int getenv_and_export_cmd(int argc, char *const argv[]);
170
171 static int limit_cmd(int argc, char *const argv[]);
172 static int stdio_cmd(int argc, char *const argv[]);
173 static int fyi_cmd(int argc, char *const argv[]);
174 static int logupdate_cmd(int argc, char *const argv[]);
175 static int umask_cmd(int argc, char *const argv[]);
176 static int getrusage_cmd(int argc, char *const argv[]);
177 static int bsexec_cmd(int argc, char *const argv[]);
178 static int bslist_cmd(int argc, char *const argv[]);
179
180 static int exit_cmd(int argc, char *const argv[]) __attribute__((noreturn));
181 static int help_cmd(int argc, char *const argv[]);
182
183 static const struct {
184 const char *name;
185 int (*func)(int argc, char *const argv[]);
186 const char *desc;
187 } cmds[] = {
188 { "load", load_and_unload_cmd, "Load configuration files and/or directories" },
189 { "unload", load_and_unload_cmd, "Unload configuration files and/or directories" },
190 // { "reload", reload_cmd, "Reload configuration files and/or directories" },
191 { "start", start_stop_remove_cmd, "Start specified job" },
192 { "stop", start_stop_remove_cmd, "Stop specified job" },
193 { "submit", submit_cmd, "Submit a job from the command line" },
194 { "remove", start_stop_remove_cmd, "Remove specified job" },
195 { "bootstrap", bootstrap_cmd, "Bootstrap launchd" },
196 { "list", list_cmd, "List jobs and information about jobs" },
197 { "setenv", setenv_cmd, "Set an environmental variable in launchd" },
198 { "unsetenv", unsetenv_cmd, "Unset an environmental variable in launchd" },
199 { "getenv", getenv_and_export_cmd, "Get an environmental variable from launchd" },
200 { "export", getenv_and_export_cmd, "Export shell settings from launchd" },
201 { "limit", limit_cmd, "View and adjust launchd resource limits" },
202 { "stdout", stdio_cmd, "Redirect launchd's standard out to the given path" },
203 { "stderr", stdio_cmd, "Redirect launchd's standard error to the given path" },
204 { "shutdown", fyi_cmd, "Prepare for system shutdown" },
205 { "singleuser", fyi_cmd, "Switch to single-user mode" },
206 { "getrusage", getrusage_cmd, "Get resource usage statistics from launchd" },
207 { "log", logupdate_cmd, "Adjust the logging level or mask of launchd" },
208 { "umask", umask_cmd, "Change launchd's umask" },
209 { "bsexec", bsexec_cmd, "Execute a process within a different Mach bootstrap subset" },
210 { "bslist", bslist_cmd, "List Mach bootstrap services and optional servers" },
211 { "exit", exit_cmd, "Exit the interactive invocation of launchctl" },
212 { "quit", exit_cmd, "Quit the interactive invocation of launchctl" },
213 { "help", help_cmd, "This help output" },
214 };
215
216 static bool istty;
217 static bool verbose;
218 static bool is_managed;
219
220 int
221 main(int argc, char *const argv[])
222 {
223 int64_t is_managed_val = 0;
224 char *l;
225
226 if (vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &is_managed_val) == NULL && is_managed_val) {
227 is_managed = true;
228 }
229
230 if (getuid() == 0 && !is_managed) {
231 mach_port_t root_bs = str2bsport("/");
232 task_set_bootstrap_port(mach_task_self(), root_bs);
233 mach_port_deallocate(mach_task_self(), bootstrap_port);
234 bootstrap_port = root_bs;
235 }
236
237 istty = isatty(STDIN_FILENO);
238
239 argc--, argv++;
240
241 if (argc > 0 && argv[0][0] == '-') {
242 char *flago;
243
244 for (flago = argv[0] + 1; *flago; flago++) {
245 switch (*flago) {
246 case 'v':
247 verbose = true;
248 break;
249 default:
250 fprintf(stderr, "Unknown argument: '-%c'\n", *flago);
251 break;
252 }
253 }
254 argc--, argv++;
255 }
256
257 if (NULL == readline) {
258 fprintf(stderr, "missing library: readline\n");
259 exit(EXIT_FAILURE);
260 }
261
262 if (argc == 0) {
263 while ((l = readline(istty ? "launchd% " : NULL))) {
264 char *inputstring = l, *argv2[100], **ap = argv2;
265 int i = 0;
266
267 while ((*ap = strsep(&inputstring, " \t"))) {
268 if (**ap != '\0') {
269 ap++;
270 i++;
271 }
272 }
273
274 if (i > 0) {
275 demux_cmd(i, argv2);
276 }
277
278 free(l);
279 }
280
281 if (istty) {
282 fputc('\n', stdout);
283 }
284 }
285
286 if (argc > 0) {
287 exit(demux_cmd(argc, argv));
288 }
289
290 exit(EXIT_SUCCESS);
291 }
292
293 int
294 demux_cmd(int argc, char *const argv[])
295 {
296 size_t i;
297
298 optind = 1;
299 optreset = 1;
300
301 for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) {
302 if (!strcmp(cmds[i].name, argv[0])) {
303 return cmds[i].func(argc, argv);
304 }
305 }
306
307 fprintf(stderr, "%s: unknown subcommand \"%s\"\n", getprogname(), argv[0]);
308 return 1;
309 }
310
311 void
312 read_launchd_conf(void)
313 {
314 char s[1000], *c, *av[100];
315 const char *file;
316 size_t len, i;
317 FILE *f;
318
319 if (getppid() == 1) {
320 file = "/etc/launchd.conf";
321 } else {
322 file = "/etc/launchd-user.conf";
323 }
324
325 if (!(f = fopen(file, "r"))) {
326 return;
327 }
328
329 while ((c = fgets(s, sizeof(s), f))) {
330 len = strlen(c);
331 if (len && c[len - 1] == '\n') {
332 c[len - 1] = '\0';
333 }
334
335 i = 0;
336
337 while ((av[i] = strsep(&c, " \t"))) {
338 if (*(av[i]) != '\0') {
339 i++;
340 }
341 }
342
343 if (i > 0) {
344 demux_cmd(i, av);
345 }
346 }
347
348 fclose(f);
349 }
350
351 int
352 unsetenv_cmd(int argc, char *const argv[])
353 {
354 launch_data_t resp, tmp, msg;
355
356 if (argc != 2) {
357 fprintf(stderr, "%s usage: unsetenv <key>\n", getprogname());
358 return 1;
359 }
360
361 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
362
363 tmp = launch_data_new_string(argv[1]);
364 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_UNSETUSERENVIRONMENT);
365
366 resp = launch_msg(msg);
367
368 launch_data_free(msg);
369
370 if (resp) {
371 launch_data_free(resp);
372 } else {
373 fprintf(stderr, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_UNSETUSERENVIRONMENT, strerror(errno));
374 }
375
376 return 0;
377 }
378
379 int
380 setenv_cmd(int argc, char *const argv[])
381 {
382 launch_data_t resp, tmp, tmpv, msg;
383
384 if (argc != 3) {
385 fprintf(stderr, "%s usage: setenv <key> <value>\n", getprogname());
386 return 1;
387 }
388
389 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
390 tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
391
392 tmpv = launch_data_new_string(argv[2]);
393 launch_data_dict_insert(tmp, tmpv, argv[1]);
394 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETUSERENVIRONMENT);
395
396 resp = launch_msg(msg);
397 launch_data_free(msg);
398
399 if (resp) {
400 launch_data_free(resp);
401 } else {
402 fprintf(stderr, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_SETUSERENVIRONMENT, strerror(errno));
403 }
404
405 return 0;
406 }
407
408 void
409 print_launchd_env(launch_data_t obj, const char *key, void *context)
410 {
411 bool *is_csh = context;
412
413 /* XXX escape the double quotes */
414 if (*is_csh) {
415 fprintf(stdout, "setenv %s \"%s\";\n", key, launch_data_get_string(obj));
416 } else {
417 fprintf(stdout, "%s=\"%s\"; export %s;\n", key, launch_data_get_string(obj), key);
418 }
419 }
420
421 void
422 print_key_value(launch_data_t obj, const char *key, void *context)
423 {
424 const char *k = context;
425
426 if (!strcmp(key, k)) {
427 fprintf(stdout, "%s\n", launch_data_get_string(obj));
428 }
429 }
430
431 int
432 getenv_and_export_cmd(int argc, char *const argv[])
433 {
434 launch_data_t resp;
435 bool is_csh = false;
436 char *k;
437
438 if (!strcmp(argv[0], "export")) {
439 char *s = getenv("SHELL");
440 if (s) {
441 is_csh = strstr(s, "csh") ? true : false;
442 }
443 } else if (argc != 2) {
444 fprintf(stderr, "%s usage: getenv <key>\n", getprogname());
445 return 1;
446 }
447
448 k = argv[1];
449
450 if (vproc_swap_complex(NULL, VPROC_GSK_ENVIRONMENT, NULL, &resp) == NULL) {
451 if (!strcmp(argv[0], "export")) {
452 launch_data_dict_iterate(resp, print_launchd_env, &is_csh);
453 } else {
454 launch_data_dict_iterate(resp, print_key_value, k);
455 }
456 launch_data_free(resp);
457 return 0;
458 } else {
459 return 1;
460 }
461
462 return 0;
463 }
464
465 void
466 unloadjob(launch_data_t job)
467 {
468 launch_data_t tmps;
469
470 tmps = launch_data_dict_lookup(job, LAUNCH_JOBKEY_LABEL);
471
472 if (!tmps) {
473 fprintf(stderr, "%s: Error: Missing Key: %s\n", getprogname(), LAUNCH_JOBKEY_LABEL);
474 return;
475 }
476
477 if (_vproc_send_signal_by_label(launch_data_get_string(tmps), VPROC_MAGIC_UNLOAD_SIGNAL) != NULL) {
478 fprintf(stderr, "%s: Error unloading: %s\n", getprogname(), launch_data_get_string(tmps));
479 }
480 }
481
482 launch_data_t
483 read_plist_file(const char *file, bool editondisk, bool load)
484 {
485 CFPropertyListRef plist = CreateMyPropertyListFromFile(file);
486 launch_data_t r = NULL;
487
488 if (NULL == plist) {
489 fprintf(stderr, "%s: no plist was returned for: %s\n", getprogname(), file);
490 return NULL;
491 }
492
493 if (editondisk) {
494 if (load) {
495 CFDictionaryRemoveValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED));
496 } else {
497 CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED), kCFBooleanTrue);
498 }
499 WriteMyPropertyListToFile(plist, file);
500 }
501
502 r = CF2launch_data(plist);
503
504 CFRelease(plist);
505
506 return r;
507 }
508
509 void
510 delay_to_second_pass2(launch_data_t o, const char *key, void *context)
511 {
512 bool *res = context;
513 size_t i;
514
515 if (key && 0 == strcmp(key, LAUNCH_JOBSOCKETKEY_BONJOUR)) {
516 *res = true;
517 return;
518 }
519
520 switch (launch_data_get_type(o)) {
521 case LAUNCH_DATA_DICTIONARY:
522 launch_data_dict_iterate(o, delay_to_second_pass2, context);
523 break;
524 case LAUNCH_DATA_ARRAY:
525 for (i = 0; i < launch_data_array_get_count(o); i++) {
526 delay_to_second_pass2(launch_data_array_get_index(o, i), NULL, context);
527 }
528 break;
529 default:
530 break;
531 }
532 }
533
534 bool
535 delay_to_second_pass(launch_data_t o)
536 {
537 bool res = false;
538
539 launch_data_t socks = launch_data_dict_lookup(o, LAUNCH_JOBKEY_SOCKETS);
540
541 if (NULL == socks) {
542 return false;
543 }
544
545 delay_to_second_pass2(socks, NULL, &res);
546
547 return res;
548 }
549
550 void
551 readfile(const char *what, struct load_unload_state *lus)
552 {
553 char ourhostname[1024];
554 launch_data_t tmpd, tmps, thejob, tmpa;
555 bool job_disabled = false;
556 size_t i, c;
557
558 gethostname(ourhostname, sizeof(ourhostname));
559
560 if (NULL == (thejob = read_plist_file(what, lus->editondisk, lus->load))) {
561 fprintf(stderr, "%s: no plist was returned for: %s\n", getprogname(), what);
562 return;
563 }
564
565 if (is_legacy_mach_job(thejob)) {
566 fprintf(stderr, "%s: Please convert the following to launchd: %s\n", getprogname(), what);
567 launch_data_array_append(lus->pass0, thejob);
568 return;
569 }
570
571 if (NULL == launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LABEL)) {
572 fprintf(stderr, "%s: missing the Label key: %s\n", getprogname(), what);
573 goto out_bad;
574 }
575
576 if (NULL != (tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADFROMHOSTS))) {
577 c = launch_data_array_get_count(tmpa);
578
579 for (i = 0; i < c; i++) {
580 launch_data_t oai = launch_data_array_get_index(tmpa, i);
581 if (!strcasecmp(ourhostname, launch_data_get_string(oai))) {
582 goto out_bad;
583 }
584 }
585 }
586
587 if (NULL != (tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOHOSTS))) {
588 c = launch_data_array_get_count(tmpa);
589
590 for (i = 0; i < c; i++) {
591 launch_data_t oai = launch_data_array_get_index(tmpa, i);
592 if (!strcasecmp(ourhostname, launch_data_get_string(oai))) {
593 break;
594 }
595 }
596
597 if (i == c) {
598 goto out_bad;
599 }
600 }
601
602 if (lus->session_type && !(tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE))) {
603 tmpa = launch_data_new_string("Aqua");
604 launch_data_dict_insert(thejob, tmpa, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE);
605 }
606
607 if ((tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE))) {
608 const char *allowed_session;
609 bool skipjob = true;
610
611 if (lus->session_type) switch (launch_data_get_type(tmpa)) {
612 case LAUNCH_DATA_ARRAY:
613 c = launch_data_array_get_count(tmpa);
614 for (i = 0; i < c; i++) {
615 tmps = launch_data_array_get_index(tmpa, i);
616 allowed_session = launch_data_get_string(tmps);
617 if (strcasecmp(lus->session_type, allowed_session) == 0) {
618 skipjob = false;
619 /* we have to do the following so job_reparent_hack() works within launchd */
620 tmpa = launch_data_new_string(lus->session_type);
621 launch_data_dict_insert(thejob, tmpa, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE);
622 break;
623 }
624 }
625 break;
626 case LAUNCH_DATA_STRING:
627 allowed_session = launch_data_get_string(tmpa);
628 if (strcasecmp(lus->session_type, allowed_session) == 0) {
629 skipjob = false;
630 }
631 break;
632 default:
633 break;
634 }
635
636 if (skipjob) {
637 goto out_bad;
638 }
639 }
640
641 if ((tmpd = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_DISABLED))) {
642 job_disabled = job_disabled_logic(tmpd);
643 }
644
645 if (lus->forceload) {
646 job_disabled = false;
647 }
648
649 if (job_disabled && lus->load) {
650 goto out_bad;
651 }
652
653 if (delay_to_second_pass(thejob)) {
654 launch_data_array_append(lus->pass2, thejob);
655 } else {
656 launch_data_array_append(lus->pass1, thejob);
657 }
658
659 if (verbose) {
660 fprintf(stdout, "Will load: %s\n", what);
661 }
662
663 return;
664 out_bad:
665 if (verbose) {
666 fprintf(stdout, "Ignored: %s\n", what);
667 }
668 launch_data_free(thejob);
669 }
670
671 static bool
672 sysctl_hw_streq(int mib_slot, const char *str)
673 {
674 char buf[1000];
675 size_t bufsz = sizeof(buf);
676 int mib[] = { CTL_HW, mib_slot };
677
678 if (sysctl(mib, 2, buf, &bufsz, NULL, 0) != -1) {
679 if (strcmp(buf, str) == 0) {
680 return true;
681 }
682 }
683
684 return false;
685 }
686
687 static void
688 job_disabled_dict_logic(launch_data_t obj, const char *key, void *context)
689 {
690 bool *r = context;
691
692 if (launch_data_get_type(obj) != LAUNCH_DATA_STRING) {
693 return;
694 }
695
696 if (strcasecmp(key, LAUNCH_JOBKEY_DISABLED_MACHINETYPE) == 0) {
697 if (sysctl_hw_streq(HW_MACHINE, launch_data_get_string(obj))) {
698 *r = true;
699 }
700 } else if (strcasecmp(key, LAUNCH_JOBKEY_DISABLED_MODELNAME) == 0) {
701 if (sysctl_hw_streq(HW_MODEL, launch_data_get_string(obj))) {
702 *r = true;
703 }
704 }
705 }
706
707 bool
708 job_disabled_logic(launch_data_t obj)
709 {
710 bool r = false;
711
712 switch (launch_data_get_type(obj)) {
713 case LAUNCH_DATA_DICTIONARY:
714 launch_data_dict_iterate(obj, job_disabled_dict_logic, &r);
715 break;
716 case LAUNCH_DATA_BOOL:
717 r = launch_data_get_bool(obj);
718 break;
719 default:
720 break;
721 }
722
723 return r;
724 }
725
726 bool
727 path_goodness_check(const char *path, bool forceload)
728 {
729 struct stat sb;
730
731 if (stat(path, &sb) == -1) {
732 fprintf(stderr, "%s: Couldn't stat(\"%s\"): %s\n", getprogname(), path, strerror(errno));
733 return false;
734 }
735
736 if (forceload) {
737 return true;
738 }
739
740 if (sb.st_mode & (S_IWOTH|S_IWGRP)) {
741 fprintf(stderr, "%s: Dubious permissions on file (skipping): %s\n", getprogname(), path);
742 return false;
743 }
744
745 if (sb.st_uid != 0 && sb.st_uid != getuid()) {
746 fprintf(stderr, "%s: Dubious ownership on file (skipping): %s\n", getprogname(), path);
747 return false;
748 }
749
750 if (!(S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))) {
751 fprintf(stderr, "%s: Dubious path. Not a regular file or directory (skipping): %s\n", getprogname(), path);
752 return false;
753 }
754
755 return true;
756 }
757
758 void
759 readpath(const char *what, struct load_unload_state *lus)
760 {
761 char buf[MAXPATHLEN];
762 struct stat sb;
763 struct dirent *de;
764 DIR *d;
765
766 if (!path_goodness_check(what, lus->forceload)) {
767 return;
768 }
769
770 if (stat(what, &sb) == -1) {
771 return;
772 }
773
774 if (S_ISREG(sb.st_mode)) {
775 readfile(what, lus);
776 } else if (S_ISDIR(sb.st_mode)) {
777 if ((d = opendir(what)) == NULL) {
778 fprintf(stderr, "%s: opendir() failed to open the directory\n", getprogname());
779 return;
780 }
781
782 while ((de = readdir(d))) {
783 if ((de->d_name[0] == '.')) {
784 continue;
785 }
786 snprintf(buf, sizeof(buf), "%s/%s", what, de->d_name);
787
788 if (!path_goodness_check(buf, lus->forceload)) {
789 continue;
790 }
791
792 readfile(buf, lus);
793 }
794 closedir(d);
795 }
796 }
797
798 struct distill_context {
799 launch_data_t base;
800 launch_data_t newsockdict;
801 };
802
803 void
804 distill_jobs(launch_data_t jobs)
805 {
806 size_t i, c = launch_data_array_get_count(jobs);
807
808 for (i = 0; i < c; i++)
809 distill_config_file(launch_data_array_get_index(jobs, i));
810 }
811
812 void
813 distill_config_file(launch_data_t id_plist)
814 {
815 struct distill_context dc = { id_plist, NULL };
816 launch_data_t tmp;
817
818 if ((tmp = launch_data_dict_lookup(dc.base, LAUNCH_JOBKEY_SOCKETS))) {
819 dc.newsockdict = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
820 launch_data_dict_iterate(tmp, sock_dict_cb, &dc);
821 launch_data_dict_insert(dc.base, dc.newsockdict, LAUNCH_JOBKEY_SOCKETS);
822 }
823 }
824
825 void
826 sock_dict_cb(launch_data_t what, const char *key, void *context)
827 {
828 struct distill_context *dc = context;
829 launch_data_t fdarray = launch_data_alloc(LAUNCH_DATA_ARRAY);
830
831 launch_data_dict_insert(dc->newsockdict, fdarray, key);
832
833 if (launch_data_get_type(what) == LAUNCH_DATA_DICTIONARY) {
834 sock_dict_edit_entry(what, key, fdarray, dc->base);
835 } else if (launch_data_get_type(what) == LAUNCH_DATA_ARRAY) {
836 launch_data_t tmp;
837 size_t i;
838
839 for (i = 0; i < launch_data_array_get_count(what); i++) {
840 tmp = launch_data_array_get_index(what, i);
841 sock_dict_edit_entry(tmp, key, fdarray, dc->base);
842 }
843 }
844 }
845
846 void
847 sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob)
848 {
849 launch_data_t a, val;
850 int sfd, st = SOCK_STREAM;
851 bool passive = true;
852
853 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_TYPE))) {
854 if (!strcasecmp(launch_data_get_string(val), "stream")) {
855 st = SOCK_STREAM;
856 } else if (!strcasecmp(launch_data_get_string(val), "dgram")) {
857 st = SOCK_DGRAM;
858 } else if (!strcasecmp(launch_data_get_string(val), "seqpacket")) {
859 st = SOCK_SEQPACKET;
860 }
861 }
862
863 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PASSIVE))) {
864 passive = launch_data_get_bool(val);
865 }
866
867 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_SECUREWITHKEY))) {
868 char secdir[] = LAUNCH_SECDIR, buf[1024];
869 launch_data_t uenv = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES);
870
871 if (NULL == uenv) {
872 uenv = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
873 launch_data_dict_insert(thejob, uenv, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES);
874 }
875
876 mkdtemp(secdir);
877
878 sprintf(buf, "%s/%s", secdir, key);
879
880 a = launch_data_new_string(buf);
881 launch_data_dict_insert(tmp, a, LAUNCH_JOBSOCKETKEY_PATHNAME);
882 a = launch_data_new_string(buf);
883 launch_data_dict_insert(uenv, a, launch_data_get_string(val));
884 }
885
886 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PATHNAME))) {
887 struct sockaddr_un sun;
888 mode_t sun_mode = 0;
889 mode_t oldmask;
890 bool setm = false;
891
892 memset(&sun, 0, sizeof(sun));
893
894 sun.sun_family = AF_UNIX;
895
896 strncpy(sun.sun_path, launch_data_get_string(val), sizeof(sun.sun_path));
897
898 if ((sfd = _fd(socket(AF_UNIX, st, 0))) == -1) {
899 return;
900 }
901
902 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PATHMODE))) {
903 sun_mode = (mode_t)launch_data_get_integer(val);
904 setm = true;
905 }
906
907 if (passive) {
908 if (unlink(sun.sun_path) == -1 && errno != ENOENT) {
909 close(sfd);
910 return;
911 }
912 oldmask = umask(S_IRWXG|S_IRWXO);
913 if (bind(sfd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
914 close(sfd);
915 umask(oldmask);
916 return;
917 }
918 umask(oldmask);
919 if (setm) {
920 chmod(sun.sun_path, sun_mode);
921 }
922 if ((st == SOCK_STREAM || st == SOCK_SEQPACKET) && listen(sfd, SOMAXCONN) == -1) {
923 close(sfd);
924 return;
925 }
926 } else if (connect(sfd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
927 close(sfd);
928 return;
929 }
930
931 val = launch_data_new_fd(sfd);
932 launch_data_array_append(fdarray, val);
933 } else {
934 launch_data_t rnames = NULL;
935 const char *node = NULL, *serv = NULL, *mgroup = NULL;
936 char servnbuf[50];
937 struct addrinfo hints, *res0, *res;
938 int gerr, sock_opt = 1;
939 bool rendezvous = false;
940
941 memset(&hints, 0, sizeof(hints));
942
943 hints.ai_socktype = st;
944 if (passive) {
945 hints.ai_flags |= AI_PASSIVE;
946 }
947
948 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_NODENAME))) {
949 node = launch_data_get_string(val);
950 }
951 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_MULTICASTGROUP))) {
952 mgroup = launch_data_get_string(val);
953 }
954 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_SERVICENAME))) {
955 if (LAUNCH_DATA_INTEGER == launch_data_get_type(val)) {
956 sprintf(servnbuf, "%lld", launch_data_get_integer(val));
957 serv = servnbuf;
958 } else {
959 serv = launch_data_get_string(val);
960 }
961 }
962 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_FAMILY))) {
963 if (!strcasecmp("IPv4", launch_data_get_string(val))) {
964 hints.ai_family = AF_INET;
965 } else if (!strcasecmp("IPv6", launch_data_get_string(val))) {
966 hints.ai_family = AF_INET6;
967 }
968 }
969 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PROTOCOL))) {
970 if (!strcasecmp("TCP", launch_data_get_string(val))) {
971 hints.ai_protocol = IPPROTO_TCP;
972 } else if (!strcasecmp("UDP", launch_data_get_string(val))) {
973 hints.ai_protocol = IPPROTO_UDP;
974 }
975 }
976 if ((rnames = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_BONJOUR))) {
977 rendezvous = true;
978 if (LAUNCH_DATA_BOOL == launch_data_get_type(rnames)) {
979 rendezvous = launch_data_get_bool(rnames);
980 rnames = NULL;
981 }
982 }
983
984 if ((gerr = getaddrinfo(node, serv, &hints, &res0)) != 0) {
985 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(gerr));
986 return;
987 }
988
989 for (res = res0; res; res = res->ai_next) {
990 launch_data_t rvs_fd = NULL;
991 if ((sfd = _fd(socket(res->ai_family, res->ai_socktype, res->ai_protocol))) == -1) {
992 fprintf(stderr, "socket(): %s\n", strerror(errno));
993 return;
994 }
995
996 do_application_firewall_magic(sfd, thejob);
997
998 if (hints.ai_flags & AI_PASSIVE) {
999 if (AF_INET6 == res->ai_family && -1 == setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY,
1000 (void *)&sock_opt, sizeof(sock_opt))) {
1001 fprintf(stderr, "setsockopt(IPV6_V6ONLY): %m");
1002 return;
1003 }
1004 if (mgroup) {
1005 if (setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, (void *)&sock_opt, sizeof(sock_opt)) == -1) {
1006 fprintf(stderr, "setsockopt(SO_REUSEPORT): %s\n", strerror(errno));
1007 return;
1008 }
1009 } else {
1010 if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt, sizeof(sock_opt)) == -1) {
1011 fprintf(stderr, "setsockopt(SO_REUSEADDR): %s\n", strerror(errno));
1012 return;
1013 }
1014 }
1015 if (bind(sfd, res->ai_addr, res->ai_addrlen) == -1) {
1016 fprintf(stderr, "bind(): %s\n", strerror(errno));
1017 return;
1018 }
1019 /* The kernel may have dynamically assigned some part of the
1020 * address. (The port being a common example.)
1021 */
1022 if (getsockname(sfd, res->ai_addr, &res->ai_addrlen) == -1) {
1023 fprintf(stderr, "getsockname(): %s\n", strerror(errno));
1024 return;
1025 }
1026
1027 if (mgroup) {
1028 do_mgroup_join(sfd, res->ai_family, res->ai_socktype, res->ai_protocol, mgroup);
1029 }
1030 if ((res->ai_socktype == SOCK_STREAM || res->ai_socktype == SOCK_SEQPACKET) && listen(sfd, SOMAXCONN) == -1) {
1031 fprintf(stderr, "listen(): %s\n", strerror(errno));
1032 return;
1033 }
1034 if (rendezvous && (res->ai_family == AF_INET || res->ai_family == AF_INET6) &&
1035 (res->ai_socktype == SOCK_STREAM || res->ai_socktype == SOCK_DGRAM)) {
1036 launch_data_t rvs_fds = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_BONJOURFDS);
1037 if (NULL == rvs_fds) {
1038 rvs_fds = launch_data_alloc(LAUNCH_DATA_ARRAY);
1039 launch_data_dict_insert(thejob, rvs_fds, LAUNCH_JOBKEY_BONJOURFDS);
1040 }
1041 if (NULL == rnames) {
1042 rvs_fd = do_rendezvous_magic(res, serv);
1043 if (rvs_fd) {
1044 launch_data_array_append(rvs_fds, rvs_fd);
1045 }
1046 } else if (LAUNCH_DATA_STRING == launch_data_get_type(rnames)) {
1047 rvs_fd = do_rendezvous_magic(res, launch_data_get_string(rnames));
1048 if (rvs_fd) {
1049 launch_data_array_append(rvs_fds, rvs_fd);
1050 }
1051 } else if (LAUNCH_DATA_ARRAY == launch_data_get_type(rnames)) {
1052 size_t rn_i, rn_ac = launch_data_array_get_count(rnames);
1053
1054 for (rn_i = 0; rn_i < rn_ac; rn_i++) {
1055 launch_data_t rn_tmp = launch_data_array_get_index(rnames, rn_i);
1056
1057 rvs_fd = do_rendezvous_magic(res, launch_data_get_string(rn_tmp));
1058 if (rvs_fd) {
1059 launch_data_array_append(rvs_fds, rvs_fd);
1060 }
1061 }
1062 }
1063 }
1064 } else {
1065 if (connect(sfd, res->ai_addr, res->ai_addrlen) == -1) {
1066 fprintf(stderr, "connect(): %s\n", strerror(errno));
1067 return;
1068 }
1069 }
1070 val = launch_data_new_fd(sfd);
1071 if (rvs_fd) {
1072 /* <rdar://problem/3964648> Launchd should not register the same service more than once */
1073 /* <rdar://problem/3965154> Switch to DNSServiceRegisterAddrInfo() */
1074 rendezvous = false;
1075 }
1076 launch_data_array_append(fdarray, val);
1077 }
1078 }
1079 }
1080
1081 void
1082 do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup)
1083 {
1084 struct addrinfo hints, *res0, *res;
1085 struct ip_mreq mreq;
1086 struct ipv6_mreq m6req;
1087 int gerr;
1088
1089 memset(&hints, 0, sizeof(hints));
1090
1091 hints.ai_flags |= AI_PASSIVE;
1092 hints.ai_family = family;
1093 hints.ai_socktype = socktype;
1094 hints.ai_protocol = protocol;
1095
1096 if ((gerr = getaddrinfo(mgroup, NULL, &hints, &res0)) != 0) {
1097 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(gerr));
1098 return;
1099 }
1100
1101 for (res = res0; res; res = res->ai_next) {
1102 if (AF_INET == family) {
1103 memset(&mreq, 0, sizeof(mreq));
1104 mreq.imr_multiaddr = ((struct sockaddr_in *)res->ai_addr)->sin_addr;
1105 if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) {
1106 fprintf(stderr, "setsockopt(IP_ADD_MEMBERSHIP): %s\n", strerror(errno));
1107 continue;
1108 }
1109 break;
1110 } else if (AF_INET6 == family) {
1111 memset(&m6req, 0, sizeof(m6req));
1112 m6req.ipv6mr_multiaddr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
1113 if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &m6req, sizeof(m6req)) == -1) {
1114 fprintf(stderr, "setsockopt(IPV6_JOIN_GROUP): %s\n", strerror(errno));
1115 continue;
1116 }
1117 break;
1118 } else {
1119 fprintf(stderr, "unknown family during multicast group bind!\n");
1120 break;
1121 }
1122 }
1123
1124 freeaddrinfo(res0);
1125 }
1126
1127
1128 launch_data_t
1129 do_rendezvous_magic(const struct addrinfo *res, const char *serv)
1130 {
1131 struct stat sb;
1132 DNSServiceRef service;
1133 DNSServiceErrorType error;
1134 char rvs_buf[200];
1135 short port;
1136 static int statres = 1;
1137
1138 if (1 == statres) {
1139 statres = stat("/usr/sbin/mDNSResponder", &sb);
1140 }
1141
1142 if (-1 == statres) {
1143 return NULL;
1144 }
1145
1146 sprintf(rvs_buf, "_%s._%s.", serv, res->ai_socktype == SOCK_STREAM ? "tcp" : "udp");
1147
1148 if (res->ai_family == AF_INET) {
1149 port = ((struct sockaddr_in *)res->ai_addr)->sin_port;
1150 } else {
1151 port = ((struct sockaddr_in6 *)res->ai_addr)->sin6_port;
1152 }
1153
1154 error = DNSServiceRegister(&service, 0, 0, NULL, rvs_buf, NULL, NULL, port, 0, NULL, NULL, NULL);
1155
1156 if (error == kDNSServiceErr_NoError) {
1157 return launch_data_new_fd(DNSServiceRefSockFD(service));
1158 }
1159
1160 fprintf(stderr, "DNSServiceRegister(\"%s\"): %d\n", serv, error);
1161 return NULL;
1162 }
1163
1164 CFPropertyListRef
1165 CreateMyPropertyListFromFile(const char *posixfile)
1166 {
1167 CFPropertyListRef propertyList;
1168 CFStringRef errorString;
1169 CFDataRef resourceData;
1170 SInt32 errorCode;
1171 CFURLRef fileURL;
1172
1173 fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false);
1174 if (!fileURL) {
1175 fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile);
1176 }
1177 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode)) {
1178 fprintf(stderr, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d\n", getprogname(), posixfile, (int)errorCode);
1179 }
1180 propertyList = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resourceData, kCFPropertyListMutableContainers, &errorString);
1181 if (!propertyList) {
1182 fprintf(stderr, "%s: propertyList is NULL\n", getprogname());
1183 }
1184
1185 return propertyList;
1186 }
1187
1188 void
1189 WriteMyPropertyListToFile(CFPropertyListRef plist, const char *posixfile)
1190 {
1191 CFDataRef resourceData;
1192 CFURLRef fileURL;
1193 SInt32 errorCode;
1194
1195 fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false);
1196 if (!fileURL) {
1197 fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile);
1198 }
1199 resourceData = CFPropertyListCreateXMLData(kCFAllocatorDefault, plist);
1200 if (resourceData == NULL) {
1201 fprintf(stderr, "%s: CFPropertyListCreateXMLData(%s) failed", getprogname(), posixfile);
1202 }
1203 if (!CFURLWriteDataAndPropertiesToResource(fileURL, resourceData, NULL, &errorCode)) {
1204 fprintf(stderr, "%s: CFURLWriteDataAndPropertiesToResource(%s) failed: %d\n", getprogname(), posixfile, (int)errorCode);
1205 }
1206 }
1207
1208 void
1209 myCFDictionaryApplyFunction(const void *key, const void *value, void *context)
1210 {
1211 launch_data_t ik, iw, where = context;
1212
1213 ik = CF2launch_data(key);
1214 iw = CF2launch_data(value);
1215
1216 launch_data_dict_insert(where, iw, launch_data_get_string(ik));
1217 launch_data_free(ik);
1218 }
1219
1220 launch_data_t
1221 CF2launch_data(CFTypeRef cfr)
1222 {
1223 launch_data_t r;
1224 CFTypeID cft = CFGetTypeID(cfr);
1225
1226 if (cft == CFStringGetTypeID()) {
1227 char buf[4096];
1228 CFStringGetCString(cfr, buf, sizeof(buf), kCFStringEncodingUTF8);
1229 r = launch_data_alloc(LAUNCH_DATA_STRING);
1230 launch_data_set_string(r, buf);
1231 } else if (cft == CFBooleanGetTypeID()) {
1232 r = launch_data_alloc(LAUNCH_DATA_BOOL);
1233 launch_data_set_bool(r, CFBooleanGetValue(cfr));
1234 } else if (cft == CFArrayGetTypeID()) {
1235 CFIndex i, ac = CFArrayGetCount(cfr);
1236 r = launch_data_alloc(LAUNCH_DATA_ARRAY);
1237 for (i = 0; i < ac; i++) {
1238 CFTypeRef v = CFArrayGetValueAtIndex(cfr, i);
1239 if (v) {
1240 launch_data_t iv = CF2launch_data(v);
1241 launch_data_array_set_index(r, iv, i);
1242 }
1243 }
1244 } else if (cft == CFDictionaryGetTypeID()) {
1245 r = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1246 CFDictionaryApplyFunction(cfr, myCFDictionaryApplyFunction, r);
1247 } else if (cft == CFDataGetTypeID()) {
1248 r = launch_data_alloc(LAUNCH_DATA_ARRAY);
1249 launch_data_set_opaque(r, CFDataGetBytePtr(cfr), CFDataGetLength(cfr));
1250 } else if (cft == CFNumberGetTypeID()) {
1251 long long n;
1252 double d;
1253 CFNumberType cfnt = CFNumberGetType(cfr);
1254 switch (cfnt) {
1255 case kCFNumberSInt8Type:
1256 case kCFNumberSInt16Type:
1257 case kCFNumberSInt32Type:
1258 case kCFNumberSInt64Type:
1259 case kCFNumberCharType:
1260 case kCFNumberShortType:
1261 case kCFNumberIntType:
1262 case kCFNumberLongType:
1263 case kCFNumberLongLongType:
1264 CFNumberGetValue(cfr, kCFNumberLongLongType, &n);
1265 r = launch_data_alloc(LAUNCH_DATA_INTEGER);
1266 launch_data_set_integer(r, n);
1267 break;
1268 case kCFNumberFloat32Type:
1269 case kCFNumberFloat64Type:
1270 case kCFNumberFloatType:
1271 case kCFNumberDoubleType:
1272 CFNumberGetValue(cfr, kCFNumberDoubleType, &d);
1273 r = launch_data_alloc(LAUNCH_DATA_REAL);
1274 launch_data_set_real(r, d);
1275 break;
1276 default:
1277 r = NULL;
1278 break;
1279 }
1280 } else {
1281 r = NULL;
1282 }
1283 return r;
1284 }
1285
1286 int
1287 help_cmd(int argc, char *const argv[])
1288 {
1289 FILE *where = stdout;
1290 int l, cmdwidth = 0;
1291 size_t i;
1292
1293 if (argc == 0 || argv == NULL)
1294 where = stderr;
1295
1296 fprintf(where, "usage: %s <subcommand>\n", getprogname());
1297
1298 for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) {
1299 l = strlen(cmds[i].name);
1300 if (l > cmdwidth)
1301 cmdwidth = l;
1302 }
1303
1304 for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) {
1305 fprintf(where, "\t%-*s\t%s\n", cmdwidth, cmds[i].name, cmds[i].desc);
1306 }
1307
1308 return 0;
1309 }
1310
1311 int
1312 exit_cmd(int argc __attribute__((unused)), char *const argv[] __attribute__((unused)))
1313 {
1314 exit(0);
1315 }
1316
1317 int
1318 _fd(int fd)
1319 {
1320 if (fd >= 0)
1321 fcntl(fd, F_SETFD, 1);
1322 return fd;
1323 }
1324
1325 void
1326 do_single_user_mode(bool sflag)
1327 {
1328 if (sflag) {
1329 while (!do_single_user_mode2()) {
1330 sleep(1);
1331 }
1332 }
1333 }
1334
1335 bool
1336 do_single_user_mode2(void)
1337 {
1338 bool runcom_fsck = true; /* should_fsck(); */
1339 int wstatus;
1340 int fd;
1341 pid_t p;
1342
1343 switch ((p = fork())) {
1344 case -1:
1345 syslog(LOG_ERR, "can't fork single-user shell, trying again: %m");
1346 return false;
1347 case 0:
1348 break;
1349 default:
1350 assumes(waitpid(p, &wstatus, 0) != -1);
1351 if (WIFEXITED(wstatus)) {
1352 if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
1353 return true;
1354 } else {
1355 fprintf(stdout, "single user mode: exit status: %d\n", WEXITSTATUS(wstatus));
1356 }
1357 } else {
1358 fprintf(stdout, "single user mode shell: %s\n", strsignal(WTERMSIG(wstatus)));
1359 }
1360 return false;
1361 }
1362
1363 revoke(_PATH_CONSOLE);
1364 if (!assumes((fd = open(_PATH_CONSOLE, O_RDWR)) != -1)) {
1365 _exit(EXIT_FAILURE);
1366 }
1367 if (!assumes(login_tty(fd) != -1)) {
1368 _exit(EXIT_FAILURE);
1369 }
1370 setenv("TERM", "vt100", 1);
1371 if (runcom_fsck) {
1372 fprintf(stdout, "Singleuser boot -- fsck not done\n");
1373 fprintf(stdout, "Root device is mounted read-only\n\n");
1374 fprintf(stdout, "If you want to make modifications to files:\n");
1375 fprintf(stdout, "\t/sbin/fsck -fy\n\t/sbin/mount -uw /\n\n");
1376 fprintf(stdout, "If you wish to boot the system:\n");
1377 fprintf(stdout, "\texit\n\n");
1378 fflush(stdout);
1379 }
1380
1381 execl(_PATH_BSHELL, "-sh", NULL);
1382 syslog(LOG_ERR, "can't exec %s for single user: %m", _PATH_BSHELL);
1383 _exit(EXIT_FAILURE);
1384 }
1385
1386 static void
1387 system_specific_bootstrap(bool sflag)
1388 {
1389 int hnmib[] = { CTL_KERN, KERN_HOSTNAME };
1390 struct kevent kev;
1391 int kq;
1392
1393
1394 do_sysversion_sysctl();
1395
1396 do_single_user_mode(sflag);
1397
1398 assumes((kq = kqueue()) != -1);
1399
1400 EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, 60, 0);
1401 assumes(kevent(kq, &kev, 1, NULL, 0, NULL) != -1);
1402
1403 EV_SET(&kev, SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, 0);
1404 assumes(kevent(kq, &kev, 1, NULL, 0, NULL) != -1);
1405 assumes(signal(SIGTERM, SIG_IGN) != SIG_ERR);
1406
1407 assumes(sysctl(hnmib, 2, NULL, NULL, "localhost", sizeof("localhost")) != -1);
1408
1409 loopback_setup_ipv4();
1410 loopback_setup_ipv6();
1411
1412 #if TARGET_OS_EMBEDDED
1413 if (path_check("/etc/rc.boot")) {
1414 const char *rcboot_tool[] = { "/etc/rc.boot", NULL };
1415 assumes(fwexec(rcboot_tool, true) != -1);
1416 }
1417 #endif
1418
1419 apply_sysctls_from_file("/etc/sysctl.conf");
1420
1421 if (path_check("/etc/rc.cdrom")) {
1422 const char *rccdrom_tool[] = { _PATH_BSHELL, "/etc/rc.cdrom", "multiuser", NULL };
1423 assumes(fwexec(rccdrom_tool, true) != -1);
1424 assumes(reboot(RB_HALT) != -1);
1425 _exit(EXIT_FAILURE);
1426 } else if (is_netboot()) {
1427 const char *rcnetboot_tool[] = { _PATH_BSHELL, "/etc/rc.netboot", "init", NULL };
1428 if (!assumes(fwexec(rcnetboot_tool, true) != -1)) {
1429 assumes(reboot(RB_HALT) != -1);
1430 _exit(EXIT_FAILURE);
1431 }
1432 } else {
1433 do_potential_fsck();
1434 }
1435
1436 if (path_check("/etc/rc.server")) {
1437 const char *rcserver_tool[] = { _PATH_BSHELL, "/etc/rc.server", NULL };
1438 assumes(fwexec(rcserver_tool, true) != -1);
1439 }
1440
1441 read_launchd_conf();
1442
1443 if (path_check("/var/account/acct")) {
1444 assumes(acct("/var/account/acct") != -1);
1445 }
1446
1447 #if !TARGET_OS_EMBEDDED
1448 if (path_check("/etc/fstab")) {
1449 const char *mount_tool[] = { "mount", "-vat", "nonfs", NULL };
1450 assumes(fwexec(mount_tool, true) != -1);
1451 }
1452 #endif
1453
1454 if (path_check("/etc/rc.installer_cleanup")) {
1455 const char *rccleanup_tool[] = { _PATH_BSHELL, "/etc/rc.installer_cleanup", "multiuser", NULL };
1456 assumes(fwexec(rccleanup_tool, true) != -1);
1457 }
1458
1459 empty_dir(_PATH_VARRUN, NULL);
1460 empty_dir(_PATH_TMP, NULL);
1461 remove(_PATH_NOLOGIN);
1462
1463 if (path_check("/usr/libexec/dirhelper")) {
1464 const char *dirhelper_tool[] = { "/usr/libexec/dirhelper", "-machineBoot", NULL };
1465 assumes(fwexec(dirhelper_tool, true) != -1);
1466 }
1467
1468 assumes(touch_file(_PATH_UTMPX, DEFFILEMODE) != -1);
1469 assumes(touch_file(_PATH_VARRUN "/.systemStarterRunning", DEFFILEMODE) != -1);
1470
1471 if (path_check("/etc/security/rc.audit")) {
1472 const char *audit_tool[] = { _PATH_BSHELL, "/etc/security/rc.audit", NULL };
1473 assumes(fwexec(audit_tool, true) != -1);
1474 }
1475
1476 do_BootCache_magic(BOOTCACHE_START);
1477
1478 preheat_page_cache_hack();
1479
1480 _vproc_set_global_on_demand(true);
1481
1482 char *load_launchd_items[] = { "load", "-D", "all", "/etc/mach_init.d",
1483 #if TARGET_OS_EMBEDDED
1484 "/var/mobile/Library/LaunchAgents",
1485 #endif
1486 NULL };
1487
1488 if (is_safeboot()) {
1489 load_launchd_items[2] = "system";
1490 }
1491
1492 assumes(load_and_unload_cmd(4, load_launchd_items) == 0);
1493
1494 /*
1495 * 5066316
1496 *
1497 * We need to revisit this after Leopard ships.
1498 *
1499 * I want a plist defined knob for jobs to give advisory hints that
1500 * will "hopefully" serialize bootstrap. Reasons for doing so include
1501 * pragmatic performance optimizations and attempts to workaround bugs
1502 * in jobs. My current thought is something like what follows.
1503 *
1504 * The BootCache would switch to launchd and add this to the plist:
1505 *
1506 * <key>HopefullyStartsSerially<key>
1507 * <dict>
1508 * <key>ReadyTimeout</key>
1509 * <integer>2</integer>
1510 * </dict>
1511 *
1512 * And kextd would add the following:
1513 *
1514 * <key>HopefullyStartsSerially<key>
1515 * <dict>
1516 * <key>ReadyTimeout</key>
1517 * <integer>5</integer>
1518 * <key>HopefullyStartsAfter</key>
1519 * <string>com.apple.BootCache.daemon</string>
1520 * </dict>
1521 *
1522 *
1523 * Then both the BootCache and kextd could call something like:
1524 *
1525 * vproc_declare_ready_state();
1526 *
1527 * To tell launchd to short circuit the readiness timeout and let the
1528 * next wave of jobs start.
1529 *
1530 * Yes, this mechanism smells a lot like SystemStarter, rc.d and
1531 * friends. I think as long as we document that artificial
1532 * serialization is only advisory and not guaranteed, we should be
1533 * fine. Remember: IPC is the preferred way to serialize operations.
1534 *
1535 */
1536 mach_timespec_t w = { 5, 0 };
1537 IOKitWaitQuiet(kIOMasterPortDefault, &w);
1538
1539 do_BootCache_magic(BOOTCACHE_TAG);
1540
1541 do_bootroot_magic();
1542
1543 _vproc_set_global_on_demand(false);
1544
1545 assumes(kevent(kq, NULL, 0, &kev, 1, NULL) == 1);
1546
1547 do_BootCache_magic(BOOTCACHE_STOP);
1548
1549 assumes(close(kq) != -1);
1550 }
1551
1552 void
1553 do_BootCache_magic(BootCache_action_t what)
1554 {
1555 const char *bcc_tool[] = { "/usr/sbin/BootCacheControl", "-f", "/var/db/BootCache.playlist", NULL, NULL };
1556
1557 if (is_safeboot() || !path_check(bcc_tool[0])) {
1558 return;
1559 }
1560
1561 switch (what) {
1562 case BOOTCACHE_START:
1563 bcc_tool[3] = "start";
1564 break;
1565 case BOOTCACHE_TAG:
1566 bcc_tool[3] = "tag";
1567 break;
1568 case BOOTCACHE_STOP:
1569 bcc_tool[3] = "stop";
1570 break;
1571 default:
1572 assumes(false);
1573 return;
1574 }
1575
1576 fwexec(bcc_tool, true);
1577 }
1578
1579 int
1580 bootstrap_cmd(int argc, char *const argv[])
1581 {
1582 char *session_type = NULL;
1583 bool sflag = false;
1584 int ch;
1585
1586 while ((ch = getopt(argc, argv, "sS:")) != -1) {
1587 switch (ch) {
1588 case 's':
1589 sflag = true;
1590 break;
1591 case 'S':
1592 session_type = optarg;
1593 break;
1594 case '?':
1595 default:
1596 break;
1597 }
1598 }
1599
1600 optind = 1;
1601 optreset = 1;
1602
1603 if (!session_type) {
1604 fprintf(stderr, "usage: %s bootstrap [-s] -S <session-type>\n", getprogname());
1605 return 1;
1606 }
1607
1608 if (strcasecmp(session_type, "System") == 0) {
1609 system_specific_bootstrap(sflag);
1610 } else {
1611 char *load_launchd_items[] = { "load", "-S", session_type, "-D", "all", NULL, NULL, NULL, NULL };
1612 int the_argc = 5;
1613
1614 if (is_safeboot()) {
1615 load_launchd_items[4] = "system";
1616 }
1617
1618 if (strcasecmp(session_type, VPROCMGR_SESSION_BACKGROUND) == 0 || strcasecmp(session_type, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
1619 load_launchd_items[4] = "system";
1620 if (!is_safeboot()) {
1621 load_launchd_items[5] = "-D";
1622 load_launchd_items[6] = "local";
1623 the_argc += 2;
1624 }
1625 if (strcasecmp(session_type, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
1626 load_launchd_items[the_argc] = "/etc/mach_init_per_login_session.d";
1627 the_argc += 1;
1628 }
1629 } else if (strcasecmp(session_type, VPROCMGR_SESSION_AQUA) == 0) {
1630 load_launchd_items[5] = "/etc/mach_init_per_user.d";
1631 the_argc += 1;
1632 }
1633
1634 if (strcasecmp(session_type, VPROCMGR_SESSION_BACKGROUND) == 0) {
1635 read_launchd_conf();
1636 #if HAVE_SECURITY
1637 assumes(SessionCreate(sessionKeepCurrentBootstrap, 0) == 0);
1638 #endif
1639 }
1640
1641 return load_and_unload_cmd(the_argc, load_launchd_items);
1642 }
1643
1644 return 0;
1645 }
1646
1647 int
1648 load_and_unload_cmd(int argc, char *const argv[])
1649 {
1650 NSSearchPathEnumerationState es = 0;
1651 char nspath[PATH_MAX * 2]; /* safe side, we need to append */
1652 bool badopts = false;
1653 struct load_unload_state lus;
1654 size_t i;
1655 int ch;
1656
1657 memset(&lus, 0, sizeof(lus));
1658
1659 if (strcmp(argv[0], "load") == 0) {
1660 lus.load = true;
1661 }
1662
1663 while ((ch = getopt(argc, argv, "wFS:D:")) != -1) {
1664 switch (ch) {
1665 case 'w':
1666 lus.editondisk = true;
1667 break;
1668 case 'F':
1669 lus.forceload = true;
1670 break;
1671 case 'S':
1672 lus.session_type = optarg;
1673 break;
1674 case 'D':
1675 if (strcasecmp(optarg, "all") == 0) {
1676 es |= NSAllDomainsMask;
1677 } else if (strcasecmp(optarg, "user") == 0) {
1678 es |= NSUserDomainMask;
1679 } else if (strcasecmp(optarg, "local") == 0) {
1680 es |= NSLocalDomainMask;
1681 } else if (strcasecmp(optarg, "network") == 0) {
1682 es |= NSNetworkDomainMask;
1683 } else if (strcasecmp(optarg, "system") == 0) {
1684 es |= NSSystemDomainMask;
1685 } else {
1686 badopts = true;
1687 }
1688 break;
1689 case '?':
1690 default:
1691 badopts = true;
1692 break;
1693 }
1694 }
1695 argc -= optind;
1696 argv += optind;
1697
1698 if (lus.session_type == NULL) {
1699 es &= ~NSUserDomainMask;
1700 }
1701
1702 if (argc == 0 && es == 0) {
1703 badopts = true;
1704 }
1705
1706 if (badopts) {
1707 fprintf(stderr, "usage: %s load [-wF] [-D <user|local|network|system|all>] paths...\n", getprogname());
1708 return 1;
1709 }
1710
1711 /* I wish I didn't need to do three passes, but I need to load mDNSResponder and use it too.
1712 * And loading legacy mach init jobs is extra fun.
1713 *
1714 * In later versions of launchd, I hope to load everything in the first pass,
1715 * then do the Bonjour magic on the jobs that need it, and reload them, but for now,
1716 * I haven't thought through the various complexities of reloading jobs, and therefore
1717 * launchd doesn't have reload support right now.
1718 */
1719
1720 lus.pass0 = launch_data_alloc(LAUNCH_DATA_ARRAY);
1721 lus.pass1 = launch_data_alloc(LAUNCH_DATA_ARRAY);
1722 lus.pass2 = launch_data_alloc(LAUNCH_DATA_ARRAY);
1723
1724 es = NSStartSearchPathEnumeration(NSLibraryDirectory, es);
1725
1726 while ((es = NSGetNextSearchPathEnumeration(es, nspath))) {
1727 glob_t g;
1728
1729 if (lus.session_type) {
1730 strcat(nspath, "/LaunchAgents");
1731 } else {
1732 strcat(nspath, "/LaunchDaemons");
1733 }
1734
1735 if (glob(nspath, GLOB_TILDE|GLOB_NOSORT, NULL, &g) == 0) {
1736 for (i = 0; i < g.gl_pathc; i++) {
1737 readpath(g.gl_pathv[i], &lus);
1738 }
1739 globfree(&g);
1740 }
1741 }
1742
1743 for (i = 0; i < (size_t)argc; i++) {
1744 readpath(argv[i], &lus);
1745 }
1746
1747 if (launch_data_array_get_count(lus.pass0) == 0 &&
1748 launch_data_array_get_count(lus.pass1) == 0 &&
1749 launch_data_array_get_count(lus.pass2) == 0) {
1750 if (!is_managed) {
1751 fprintf(stderr, "nothing found to %s\n", lus.load ? "load" : "unload");
1752 }
1753 launch_data_free(lus.pass0);
1754 launch_data_free(lus.pass1);
1755 launch_data_free(lus.pass2);
1756 return is_managed ? 0 : 1;
1757 }
1758
1759 if (lus.load) {
1760 distill_jobs(lus.pass1);
1761 submit_mach_jobs(lus.pass0);
1762 submit_job_pass(lus.pass1);
1763 let_go_of_mach_jobs(lus.pass0);
1764 distill_jobs(lus.pass2);
1765 submit_job_pass(lus.pass2);
1766 } else {
1767 for (i = 0; i < launch_data_array_get_count(lus.pass1); i++) {
1768 unloadjob(launch_data_array_get_index(lus.pass1, i));
1769 }
1770 for (i = 0; i < launch_data_array_get_count(lus.pass2); i++) {
1771 unloadjob(launch_data_array_get_index(lus.pass2, i));
1772 }
1773 }
1774
1775 return 0;
1776 }
1777
1778 void
1779 submit_mach_jobs(launch_data_t jobs)
1780 {
1781 size_t i, c;
1782
1783 c = launch_data_array_get_count(jobs);
1784
1785 for (i = 0; i < c; i++) {
1786 launch_data_t tmp, oai = launch_data_array_get_index(jobs, i);
1787 const char *sn = NULL, *cmd = NULL;
1788 bool d = true;
1789 mach_port_t msr, msv;
1790 kern_return_t kr;
1791 uid_t u = getuid();
1792
1793 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_ONDEMAND)))
1794 d = launch_data_get_bool(tmp);
1795 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVICENAME)))
1796 sn = launch_data_get_string(tmp);
1797 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_COMMAND)))
1798 cmd = launch_data_get_string(tmp);
1799
1800 if ((kr = bootstrap_create_server(bootstrap_port, (char *)cmd, u, d, &msr)) != KERN_SUCCESS) {
1801 fprintf(stderr, "%s: bootstrap_create_server(): %d\n", getprogname(), kr);
1802 continue;
1803 }
1804 if ((kr = bootstrap_create_service(msr, (char*)sn, &msv)) != KERN_SUCCESS) {
1805 fprintf(stderr, "%s: bootstrap_create_service(): %d\n", getprogname(), kr);
1806 mach_port_destroy(mach_task_self(), msr);
1807 continue;
1808 }
1809 launch_data_dict_insert(oai, launch_data_new_machport(msr), MACHINIT_JOBKEY_SERVERPORT);
1810 launch_data_dict_insert(oai, launch_data_new_machport(msv), MACHINIT_JOBKEY_SERVICEPORT);
1811 }
1812 }
1813
1814 void
1815 let_go_of_mach_jobs(launch_data_t jobs)
1816 {
1817 size_t i, c = launch_data_array_get_count(jobs);
1818
1819 for (i = 0; i < c; i++) {
1820 launch_data_t tmp, oai = launch_data_array_get_index(jobs, i);
1821 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVICEPORT))) {
1822 mach_port_destroy(mach_task_self(), launch_data_get_machport(tmp));
1823 } else {
1824 fprintf(stderr, "%s: ack! missing service port!\n", getprogname());
1825 }
1826 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVERPORT))) {
1827 mach_port_destroy(mach_task_self(), launch_data_get_machport(tmp));
1828 } else {
1829 fprintf(stderr, "%s: ack! missing server port!\n", getprogname());
1830 }
1831 }
1832 }
1833
1834 void
1835 submit_job_pass(launch_data_t jobs)
1836 {
1837 launch_data_t msg, resp;
1838 size_t i;
1839 int e;
1840
1841 if (launch_data_array_get_count(jobs) == 0)
1842 return;
1843
1844 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1845
1846 launch_data_dict_insert(msg, jobs, LAUNCH_KEY_SUBMITJOB);
1847
1848 resp = launch_msg(msg);
1849
1850 if (resp) {
1851 switch (launch_data_get_type(resp)) {
1852 case LAUNCH_DATA_ERRNO:
1853 if ((e = launch_data_get_errno(resp)))
1854 fprintf(stderr, "%s\n", strerror(e));
1855 break;
1856 case LAUNCH_DATA_ARRAY:
1857 for (i = 0; i < launch_data_array_get_count(jobs); i++) {
1858 launch_data_t obatind = launch_data_array_get_index(resp, i);
1859 launch_data_t jatind = launch_data_array_get_index(jobs, i);
1860 const char *lab4job = launch_data_get_string(launch_data_dict_lookup(jatind, LAUNCH_JOBKEY_LABEL));
1861 if (LAUNCH_DATA_ERRNO == launch_data_get_type(obatind)) {
1862 e = launch_data_get_errno(obatind);
1863 switch (e) {
1864 case EEXIST:
1865 fprintf(stderr, "%s: %s\n", lab4job, "Already loaded");
1866 break;
1867 case ESRCH:
1868 fprintf(stderr, "%s: %s\n", lab4job, "Not loaded");
1869 break;
1870 default:
1871 fprintf(stderr, "%s: %s\n", lab4job, strerror(e));
1872 case 0:
1873 break;
1874 }
1875 }
1876 }
1877 break;
1878 default:
1879 fprintf(stderr, "unknown respose from launchd!\n");
1880 break;
1881 }
1882 launch_data_free(resp);
1883 } else {
1884 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1885 }
1886
1887 launch_data_free(msg);
1888 }
1889
1890 int
1891 start_stop_remove_cmd(int argc, char *const argv[])
1892 {
1893 launch_data_t resp, msg;
1894 const char *lmsgcmd = LAUNCH_KEY_STOPJOB;
1895 int e, r = 0;
1896
1897 if (0 == strcmp(argv[0], "start"))
1898 lmsgcmd = LAUNCH_KEY_STARTJOB;
1899
1900 if (0 == strcmp(argv[0], "remove"))
1901 lmsgcmd = LAUNCH_KEY_REMOVEJOB;
1902
1903 if (argc != 2) {
1904 fprintf(stderr, "usage: %s %s <job label>\n", getprogname(), argv[0]);
1905 return 1;
1906 }
1907
1908 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1909 launch_data_dict_insert(msg, launch_data_new_string(argv[1]), lmsgcmd);
1910
1911 resp = launch_msg(msg);
1912 launch_data_free(msg);
1913
1914 if (resp == NULL) {
1915 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1916 return 1;
1917 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
1918 if ((e = launch_data_get_errno(resp))) {
1919 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e));
1920 r = 1;
1921 }
1922 } else {
1923 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
1924 r = 1;
1925 }
1926
1927 launch_data_free(resp);
1928 return r;
1929 }
1930
1931 void
1932 print_jobs(launch_data_t j, const char *key __attribute__((unused)), void *context __attribute__((unused)))
1933 {
1934 static size_t depth = 0;
1935 launch_data_t lo = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LABEL);
1936 launch_data_t pido = launch_data_dict_lookup(j, LAUNCH_JOBKEY_PID);
1937 launch_data_t stato = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LASTEXITSTATUS);
1938 const char *label = launch_data_get_string(lo);
1939 size_t i;
1940
1941 if (pido) {
1942 fprintf(stdout, "%lld\t-\t", launch_data_get_integer(pido));
1943 } else if (stato) {
1944 int wstatus = (int)launch_data_get_integer(stato);
1945 if (WIFEXITED(wstatus)) {
1946 fprintf(stdout, "-\t%d\t", WEXITSTATUS(wstatus));
1947 } else if (WIFSIGNALED(wstatus)) {
1948 fprintf(stdout, "-\t-%d\t", WTERMSIG(wstatus));
1949 } else {
1950 fprintf(stdout, "-\t???\t");
1951 }
1952 } else {
1953 fprintf(stdout, "-\t-\t");
1954 }
1955 for (i = 0; i < depth; i++)
1956 fprintf(stdout, "\t");
1957
1958 fprintf(stdout, "%s\n", label);
1959 }
1960
1961 void
1962 print_obj(launch_data_t obj, const char *key, void *context __attribute__((unused)))
1963 {
1964 static size_t indent = 0;
1965 size_t i, c;
1966
1967 for (i = 0; i < indent; i++)
1968 fprintf(stdout, "\t");
1969
1970 if (key)
1971 fprintf(stdout, "\"%s\" = ", key);
1972
1973 switch (launch_data_get_type(obj)) {
1974 case LAUNCH_DATA_STRING:
1975 fprintf(stdout, "\"%s\";\n", launch_data_get_string(obj));
1976 break;
1977 case LAUNCH_DATA_INTEGER:
1978 fprintf(stdout, "%lld;\n", launch_data_get_integer(obj));
1979 break;
1980 case LAUNCH_DATA_REAL:
1981 fprintf(stdout, "%f;\n", launch_data_get_real(obj));
1982 break;
1983 case LAUNCH_DATA_BOOL:
1984 fprintf(stdout, "%s;\n", launch_data_get_bool(obj) ? "true" : "false");
1985 break;
1986 case LAUNCH_DATA_ARRAY:
1987 c = launch_data_array_get_count(obj);
1988 fprintf(stdout, "(\n");
1989 indent++;
1990 for (i = 0; i < c; i++)
1991 print_obj(launch_data_array_get_index(obj, i), NULL, NULL);
1992 indent--;
1993 for (i = 0; i < indent; i++)
1994 fprintf(stdout, "\t");
1995 fprintf(stdout, ");\n");
1996 break;
1997 case LAUNCH_DATA_DICTIONARY:
1998 fprintf(stdout, "{\n");
1999 indent++;
2000 launch_data_dict_iterate(obj, print_obj, NULL);
2001 indent--;
2002 for (i = 0; i < indent; i++)
2003 fprintf(stdout, "\t");
2004 fprintf(stdout, "};\n");
2005 break;
2006 case LAUNCH_DATA_FD:
2007 fprintf(stdout, "file-descriptor-object;\n");
2008 break;
2009 case LAUNCH_DATA_MACHPORT:
2010 fprintf(stdout, "mach-port-object;\n");
2011 break;
2012 default:
2013 fprintf(stdout, "???;\n");
2014 break;
2015 }
2016 }
2017
2018 int
2019 list_cmd(int argc, char *const argv[])
2020 {
2021 launch_data_t resp, msg;
2022 int r = 0;
2023
2024 if (argc > 2) {
2025 fprintf(stderr, "usage: %s list [label]\n", getprogname());
2026 return 1;
2027 } else if (argc == 2) {
2028 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2029 launch_data_dict_insert(msg, launch_data_new_string(argv[1]), LAUNCH_KEY_GETJOB);
2030 } else if (vproc_swap_complex(NULL, VPROC_GSK_ALLJOBS, NULL, &resp) == NULL) {
2031 fprintf(stdout, "PID\tStatus\tLabel\n");
2032 launch_data_dict_iterate(resp, print_jobs, NULL);
2033 launch_data_free(resp);
2034 return 0;
2035 } else {
2036 return 1;
2037 }
2038
2039 resp = launch_msg(msg);
2040 launch_data_free(msg);
2041
2042 if (resp == NULL) {
2043 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2044 return 1;
2045 } else if (launch_data_get_type(resp) == LAUNCH_DATA_DICTIONARY) {
2046 print_obj(resp, NULL, NULL);
2047 } else {
2048 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
2049 r = 1;
2050 }
2051
2052 launch_data_free(resp);
2053
2054 return r;
2055 }
2056
2057 int
2058 stdio_cmd(int argc __attribute__((unused)), char *const argv[])
2059 {
2060 fprintf(stderr, "%s %s: This sub-command no longer does anything\n", getprogname(), argv[0]);
2061 return 1;
2062 }
2063
2064 int
2065 fyi_cmd(int argc, char *const argv[])
2066 {
2067 launch_data_t resp, msg;
2068 const char *lmsgk = NULL;
2069 int e, r = 0;
2070
2071 if (argc != 1) {
2072 fprintf(stderr, "usage: %s %s\n", getprogname(), argv[0]);
2073 return 1;
2074 }
2075
2076 if (!strcmp(argv[0], "shutdown")) {
2077 lmsgk = LAUNCH_KEY_SHUTDOWN;
2078 } else if (!strcmp(argv[0], "singleuser")) {
2079 lmsgk = LAUNCH_KEY_SINGLEUSER;
2080 } else {
2081 return 1;
2082 }
2083
2084 msg = launch_data_new_string(lmsgk);
2085 resp = launch_msg(msg);
2086 launch_data_free(msg);
2087
2088 if (resp == NULL) {
2089 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2090 return 1;
2091 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
2092 if ((e = launch_data_get_errno(resp))) {
2093 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e));
2094 r = 1;
2095 }
2096 } else {
2097 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
2098 r = 1;
2099 }
2100
2101 launch_data_free(resp);
2102
2103 return r;
2104 }
2105
2106 int
2107 logupdate_cmd(int argc, char *const argv[])
2108 {
2109 int64_t inval, outval;
2110 int i, j, m = 0;
2111 bool badargs = false, maskmode = false, onlymode = false, levelmode = false;
2112 static const struct {
2113 const char *name;
2114 int level;
2115 } logtbl[] = {
2116 { "debug", LOG_DEBUG },
2117 { "info", LOG_INFO },
2118 { "notice", LOG_NOTICE },
2119 { "warning", LOG_WARNING },
2120 { "error", LOG_ERR },
2121 { "critical", LOG_CRIT },
2122 { "alert", LOG_ALERT },
2123 { "emergency", LOG_EMERG },
2124 };
2125 int logtblsz = sizeof logtbl / sizeof logtbl[0];
2126
2127 if (argc >= 2) {
2128 if (!strcmp(argv[1], "mask"))
2129 maskmode = true;
2130 else if (!strcmp(argv[1], "only"))
2131 onlymode = true;
2132 else if (!strcmp(argv[1], "level"))
2133 levelmode = true;
2134 else
2135 badargs = true;
2136 }
2137
2138 if (maskmode)
2139 m = LOG_UPTO(LOG_DEBUG);
2140
2141 if (argc > 2 && (maskmode || onlymode)) {
2142 for (i = 2; i < argc; i++) {
2143 for (j = 0; j < logtblsz; j++) {
2144 if (!strcmp(argv[i], logtbl[j].name)) {
2145 if (maskmode)
2146 m &= ~(LOG_MASK(logtbl[j].level));
2147 else
2148 m |= LOG_MASK(logtbl[j].level);
2149 break;
2150 }
2151 }
2152 if (j == logtblsz) {
2153 badargs = true;
2154 break;
2155 }
2156 }
2157 } else if (argc > 2 && levelmode) {
2158 for (j = 0; j < logtblsz; j++) {
2159 if (!strcmp(argv[2], logtbl[j].name)) {
2160 m = LOG_UPTO(logtbl[j].level);
2161 break;
2162 }
2163 }
2164 if (j == logtblsz)
2165 badargs = true;
2166 } else if (argc != 1) {
2167 badargs = true;
2168 }
2169
2170 if (badargs) {
2171 fprintf(stderr, "usage: %s [[mask loglevels...] | [only loglevels...] [level loglevel]]\n", getprogname());
2172 return 1;
2173 }
2174
2175 inval = m;
2176
2177 if (vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_LOG_MASK, argc != 1 ? &inval : NULL, &outval) == NULL) {
2178 if (argc == 1) {
2179 for (j = 0; j < logtblsz; j++) {
2180 if (outval & LOG_MASK(logtbl[j].level)) {
2181 fprintf(stdout, "%s ", logtbl[j].name);
2182 }
2183 }
2184 fprintf(stdout, "\n");
2185 }
2186 return 0;
2187 } else {
2188 return 1;
2189 }
2190 }
2191
2192 static const struct {
2193 const char *name;
2194 int lim;
2195 } limlookup[] = {
2196 { "cpu", RLIMIT_CPU },
2197 { "filesize", RLIMIT_FSIZE },
2198 { "data", RLIMIT_DATA },
2199 { "stack", RLIMIT_STACK },
2200 { "core", RLIMIT_CORE },
2201 { "rss", RLIMIT_RSS },
2202 { "memlock", RLIMIT_MEMLOCK },
2203 { "maxproc", RLIMIT_NPROC },
2204 { "maxfiles", RLIMIT_NOFILE }
2205 };
2206
2207 static const size_t limlookupcnt = sizeof limlookup / sizeof limlookup[0];
2208
2209 ssize_t
2210 name2num(const char *n)
2211 {
2212 size_t i;
2213
2214 for (i = 0; i < limlookupcnt; i++) {
2215 if (!strcmp(limlookup[i].name, n)) {
2216 return limlookup[i].lim;
2217 }
2218 }
2219 return -1;
2220 }
2221
2222 const char *
2223 num2name(int n)
2224 {
2225 size_t i;
2226
2227 for (i = 0; i < limlookupcnt; i++) {
2228 if (limlookup[i].lim == n)
2229 return limlookup[i].name;
2230 }
2231 return NULL;
2232 }
2233
2234 const char *
2235 lim2str(rlim_t val, char *buf)
2236 {
2237 if (val == RLIM_INFINITY)
2238 strcpy(buf, "unlimited");
2239 else
2240 sprintf(buf, "%lld", val);
2241 return buf;
2242 }
2243
2244 bool
2245 str2lim(const char *buf, rlim_t *res)
2246 {
2247 char *endptr;
2248 *res = strtoll(buf, &endptr, 10);
2249 if (!strcmp(buf, "unlimited")) {
2250 *res = RLIM_INFINITY;
2251 return false;
2252 } else if (*endptr == '\0') {
2253 return false;
2254 }
2255 return true;
2256 }
2257
2258 int
2259 limit_cmd(int argc __attribute__((unused)), char *const argv[])
2260 {
2261 char slimstr[100];
2262 char hlimstr[100];
2263 struct rlimit *lmts = NULL;
2264 launch_data_t resp, resp1 = NULL, msg, tmp;
2265 int r = 0;
2266 size_t i, lsz = -1;
2267 ssize_t which = 0;
2268 rlim_t slim = -1, hlim = -1;
2269 bool badargs = false;
2270
2271 if (argc > 4)
2272 badargs = true;
2273
2274 if (argc >= 3 && str2lim(argv[2], &slim))
2275 badargs = true;
2276 else
2277 hlim = slim;
2278
2279 if (argc == 4 && str2lim(argv[3], &hlim))
2280 badargs = true;
2281
2282 if (argc >= 2 && -1 == (which = name2num(argv[1])))
2283 badargs = true;
2284
2285 if (badargs) {
2286 fprintf(stderr, "usage: %s %s [", getprogname(), argv[0]);
2287 for (i = 0; i < limlookupcnt; i++)
2288 fprintf(stderr, "%s %s", limlookup[i].name, (i + 1) == limlookupcnt ? "" : "| ");
2289 fprintf(stderr, "[both | soft hard]]\n");
2290 return 1;
2291 }
2292
2293 msg = launch_data_new_string(LAUNCH_KEY_GETRESOURCELIMITS);
2294 resp = launch_msg(msg);
2295 launch_data_free(msg);
2296
2297 if (resp == NULL) {
2298 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2299 return 1;
2300 } else if (launch_data_get_type(resp) == LAUNCH_DATA_OPAQUE) {
2301 lmts = launch_data_get_opaque(resp);
2302 lsz = launch_data_get_opaque_size(resp);
2303 if (argc <= 2) {
2304 for (i = 0; i < (lsz / sizeof(struct rlimit)); i++) {
2305 if (argc == 2 && (size_t)which != i)
2306 continue;
2307 fprintf(stdout, "\t%-12s%-15s%-15s\n", num2name(i),
2308 lim2str(lmts[i].rlim_cur, slimstr),
2309 lim2str(lmts[i].rlim_max, hlimstr));
2310 }
2311 }
2312 } else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) {
2313 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], launch_data_get_string(resp));
2314 r = 1;
2315 } else {
2316 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
2317 r = 1;
2318 }
2319
2320 if (argc <= 2 || r != 0) {
2321 launch_data_free(resp);
2322 return r;
2323 } else {
2324 resp1 = resp;
2325 }
2326
2327 lmts[which].rlim_cur = slim;
2328 lmts[which].rlim_max = hlim;
2329
2330 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2331 tmp = launch_data_new_opaque(lmts, lsz);
2332 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETRESOURCELIMITS);
2333 resp = launch_msg(msg);
2334 launch_data_free(msg);
2335
2336 if (resp == NULL) {
2337 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2338 return 1;
2339 } else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) {
2340 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], launch_data_get_string(resp));
2341 r = 1;
2342 } else if (launch_data_get_type(resp) != LAUNCH_DATA_OPAQUE) {
2343 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
2344 r = 1;
2345 }
2346
2347 launch_data_free(resp);
2348 launch_data_free(resp1);
2349
2350 return r;
2351 }
2352
2353 int
2354 umask_cmd(int argc, char *const argv[])
2355 {
2356 bool badargs = false;
2357 char *endptr;
2358 long m = 0;
2359 int64_t inval, outval;
2360
2361 if (argc == 2) {
2362 m = strtol(argv[1], &endptr, 8);
2363 if (*endptr != '\0' || m > 0777)
2364 badargs = true;
2365 }
2366
2367 if (argc > 2 || badargs) {
2368 fprintf(stderr, "usage: %s %s <mask>\n", getprogname(), argv[0]);
2369 return 1;
2370 }
2371
2372 inval = m;
2373
2374 if (vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_UMASK, argc == 2 ? &inval : NULL, &outval) == NULL) {
2375 if (argc == 1) {
2376 fprintf(stdout, "%o\n", (unsigned int)outval);
2377 }
2378 return 0;
2379 } else {
2380 return 1;
2381 }
2382 }
2383
2384 int
2385 submit_cmd(int argc, char *const argv[])
2386 {
2387 launch_data_t msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2388 launch_data_t job = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2389 launch_data_t resp, largv = launch_data_alloc(LAUNCH_DATA_ARRAY);
2390 int ch, i, r = 0;
2391
2392 launch_data_dict_insert(job, launch_data_new_bool(false), LAUNCH_JOBKEY_ONDEMAND);
2393
2394 while ((ch = getopt(argc, argv, "l:p:o:e:")) != -1) {
2395 switch (ch) {
2396 case 'l':
2397 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_LABEL);
2398 break;
2399 case 'p':
2400 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_PROGRAM);
2401 break;
2402 case 'o':
2403 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_STANDARDOUTPATH);
2404 break;
2405 case 'e':
2406 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_STANDARDERRORPATH);
2407 break;
2408 default:
2409 fprintf(stderr, "usage: %s submit ...\n", getprogname());
2410 return 1;
2411 }
2412 }
2413 argc -= optind;
2414 argv += optind;
2415
2416 for (i = 0; argv[i]; i++) {
2417 launch_data_array_append(largv, launch_data_new_string(argv[i]));
2418 }
2419
2420 launch_data_dict_insert(job, largv, LAUNCH_JOBKEY_PROGRAMARGUMENTS);
2421
2422 launch_data_dict_insert(msg, job, LAUNCH_KEY_SUBMITJOB);
2423
2424 resp = launch_msg(msg);
2425 launch_data_free(msg);
2426
2427 if (resp == NULL) {
2428 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2429 return 1;
2430 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
2431 errno = launch_data_get_errno(resp);
2432 if (errno) {
2433 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(errno));
2434 r = 1;
2435 }
2436 } else {
2437 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], "unknown response");
2438 }
2439
2440 launch_data_free(resp);
2441
2442 return r;
2443 }
2444
2445 int
2446 getrusage_cmd(int argc, char *const argv[])
2447 {
2448 launch_data_t resp, msg;
2449 bool badargs = false;
2450 int r = 0;
2451
2452 if (argc != 2)
2453 badargs = true;
2454 else if (strcmp(argv[1], "self") && strcmp(argv[1], "children"))
2455 badargs = true;
2456
2457 if (badargs) {
2458 fprintf(stderr, "usage: %s %s self | children\n", getprogname(), argv[0]);
2459 return 1;
2460 }
2461
2462 if (!strcmp(argv[1], "self")) {
2463 msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGESELF);
2464 } else {
2465 msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGECHILDREN);
2466 }
2467
2468 resp = launch_msg(msg);
2469 launch_data_free(msg);
2470
2471 if (resp == NULL) {
2472 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2473 return 1;
2474 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
2475 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(launch_data_get_errno(resp)));
2476 r = 1;
2477 } else if (launch_data_get_type(resp) == LAUNCH_DATA_OPAQUE) {
2478 struct rusage *rusage = launch_data_get_opaque(resp);
2479 fprintf(stdout, "\t%-10f\tuser time used\n",
2480 (double)rusage->ru_utime.tv_sec + (double)rusage->ru_utime.tv_usec / (double)1000000);
2481 fprintf(stdout, "\t%-10f\tsystem time used\n",
2482 (double)rusage->ru_stime.tv_sec + (double)rusage->ru_stime.tv_usec / (double)1000000);
2483 fprintf(stdout, "\t%-10ld\tmax resident set size\n", rusage->ru_maxrss);
2484 fprintf(stdout, "\t%-10ld\tshared text memory size\n", rusage->ru_ixrss);
2485 fprintf(stdout, "\t%-10ld\tunshared data size\n", rusage->ru_idrss);
2486 fprintf(stdout, "\t%-10ld\tunshared stack size\n", rusage->ru_isrss);
2487 fprintf(stdout, "\t%-10ld\tpage reclaims\n", rusage->ru_minflt);
2488 fprintf(stdout, "\t%-10ld\tpage faults\n", rusage->ru_majflt);
2489 fprintf(stdout, "\t%-10ld\tswaps\n", rusage->ru_nswap);
2490 fprintf(stdout, "\t%-10ld\tblock input operations\n", rusage->ru_inblock);
2491 fprintf(stdout, "\t%-10ld\tblock output operations\n", rusage->ru_oublock);
2492 fprintf(stdout, "\t%-10ld\tmessages sent\n", rusage->ru_msgsnd);
2493 fprintf(stdout, "\t%-10ld\tmessages received\n", rusage->ru_msgrcv);
2494 fprintf(stdout, "\t%-10ld\tsignals received\n", rusage->ru_nsignals);
2495 fprintf(stdout, "\t%-10ld\tvoluntary context switches\n", rusage->ru_nvcsw);
2496 fprintf(stdout, "\t%-10ld\tinvoluntary context switches\n", rusage->ru_nivcsw);
2497 } else {
2498 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
2499 r = 1;
2500 }
2501
2502 launch_data_free(resp);
2503
2504 return r;
2505 }
2506
2507 bool
2508 launch_data_array_append(launch_data_t a, launch_data_t o)
2509 {
2510 size_t offt = launch_data_array_get_count(a);
2511
2512 return launch_data_array_set_index(a, o, offt);
2513 }
2514
2515 mach_port_t
2516 str2bsport(const char *s)
2517 {
2518 bool getrootbs = strcmp(s, "/") == 0;
2519 mach_port_t last_bport, bport = bootstrap_port;
2520 task_t task = mach_task_self();
2521 kern_return_t result;
2522
2523 if (strcmp(s, "..") == 0 || getrootbs) {
2524 do {
2525 last_bport = bport;
2526 result = bootstrap_parent(last_bport, &bport);
2527
2528 if (result == BOOTSTRAP_NOT_PRIVILEGED) {
2529 fprintf(stderr, "Permission denied\n");
2530 return 1;
2531 } else if (result != BOOTSTRAP_SUCCESS) {
2532 fprintf(stderr, "bootstrap_parent() %d\n", result);
2533 return 1;
2534 }
2535 } while (getrootbs && last_bport != bport);
2536 } else {
2537 int pid = atoi(s);
2538
2539 result = task_for_pid(mach_task_self(), pid, &task);
2540
2541 if (result != KERN_SUCCESS) {
2542 fprintf(stderr, "task_for_pid() %s\n", mach_error_string(result));
2543 return 1;
2544 }
2545
2546 result = task_get_bootstrap_port(task, &bport);
2547
2548 if (result != KERN_SUCCESS) {
2549 fprintf(stderr, "Couldn't get bootstrap port: %s\n", mach_error_string(result));
2550 return 1;
2551 }
2552 }
2553
2554 return bport;
2555 }
2556
2557 int
2558 bsexec_cmd(int argc, char *const argv[])
2559 {
2560 kern_return_t result;
2561 mach_port_t bport;
2562
2563 if (argc < 3) {
2564 fprintf(stderr, "usage: %s bsexec <PID> prog...\n", getprogname());
2565 return 1;
2566 }
2567
2568 bport = str2bsport(argv[1]);
2569
2570 result = task_set_bootstrap_port(mach_task_self(), bport);
2571
2572 if (result != KERN_SUCCESS) {
2573 fprintf(stderr, "Couldn't switch to new bootstrap port: %s\n", mach_error_string(result));
2574 return 1;
2575 }
2576
2577 setgid(getgid());
2578 setuid(getuid());
2579
2580 if (fwexec((const char *const *)argv + 2, true) == -1) {
2581 fprintf(stderr, "%s bsexec failed: %s\n", getprogname(), strerror(errno));
2582 return 1;
2583 }
2584
2585 return 0;
2586 }
2587
2588 int
2589 bslist_cmd(int argc, char *const argv[])
2590 {
2591 kern_return_t result;
2592 mach_port_t bport = bootstrap_port;
2593 name_array_t service_names;
2594 mach_msg_type_number_t service_cnt, service_active_cnt;
2595 bootstrap_status_array_t service_actives;
2596 unsigned int i;
2597
2598 if (argc == 2)
2599 bport = str2bsport(argv[1]);
2600
2601 if (bport == MACH_PORT_NULL) {
2602 fprintf(stderr, "Invalid bootstrap port\n");
2603 return 1;
2604 }
2605
2606 result = bootstrap_info(bport, &service_names, &service_cnt, &service_actives, &service_active_cnt);
2607 if (result != BOOTSTRAP_SUCCESS) {
2608 fprintf(stderr, "bootstrap_info(): %d\n", result);
2609 return 1;
2610 }
2611
2612 #define bport_state(x) (((x) == BOOTSTRAP_STATUS_ACTIVE) ? "A" : ((x) == BOOTSTRAP_STATUS_ON_DEMAND) ? "D" : "I")
2613
2614 for (i = 0; i < service_cnt ; i++)
2615 fprintf(stdout, "%-3s%s\n", bport_state((service_actives[i])), service_names[i]);
2616
2617 return 0;
2618 }
2619
2620 bool
2621 is_legacy_mach_job(launch_data_t obj)
2622 {
2623 bool has_servicename = launch_data_dict_lookup(obj, MACHINIT_JOBKEY_SERVICENAME);
2624 bool has_command = launch_data_dict_lookup(obj, MACHINIT_JOBKEY_COMMAND);
2625 bool has_label = launch_data_dict_lookup(obj, LAUNCH_JOBKEY_LABEL);
2626
2627 return has_command && has_servicename && !has_label;
2628 }
2629
2630 void
2631 _log_launchctl_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test)
2632 {
2633 int saved_errno = errno;
2634 char buf[100];
2635 const char *file = strrchr(path, '/');
2636 char *rcs_rev_tmp = strchr(rcs_rev, ' ');
2637
2638 if (!file) {
2639 file = path;
2640 } else {
2641 file += 1;
2642 }
2643
2644 if (!rcs_rev_tmp) {
2645 strlcpy(buf, rcs_rev, sizeof(buf));
2646 } else {
2647 strlcpy(buf, rcs_rev_tmp + 1, sizeof(buf));
2648 rcs_rev_tmp = strchr(buf, ' ');
2649 if (rcs_rev_tmp)
2650 *rcs_rev_tmp = '\0';
2651 }
2652
2653 fprintf(stderr, "Bug: %s:%u (%s):%u: %s\n", file, line, buf, saved_errno, test);
2654 }
2655
2656 void
2657 loopback_setup_ipv4(void)
2658 {
2659 struct ifaliasreq ifra;
2660 struct ifreq ifr;
2661 int s;
2662
2663 memset(&ifr, 0, sizeof(ifr));
2664 strcpy(ifr.ifr_name, "lo0");
2665
2666 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
2667 return;
2668
2669 if (assumes(ioctl(s, SIOCGIFFLAGS, &ifr) != -1)) {
2670 ifr.ifr_flags |= IFF_UP;
2671 assumes(ioctl(s, SIOCSIFFLAGS, &ifr) != -1);
2672 }
2673
2674 memset(&ifra, 0, sizeof(ifra));
2675 strcpy(ifra.ifra_name, "lo0");
2676 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_family = AF_INET;
2677 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
2678 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_len = sizeof(struct sockaddr_in);
2679 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_family = AF_INET;
2680 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_addr.s_addr = htonl(IN_CLASSA_NET);
2681 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_len = sizeof(struct sockaddr_in);
2682
2683 assumes(ioctl(s, SIOCAIFADDR, &ifra) != -1);
2684
2685 assumes(close(s) == 0);
2686 }
2687
2688 void
2689 loopback_setup_ipv6(void)
2690 {
2691 struct in6_aliasreq ifra6;
2692 struct ifreq ifr;
2693 int s6;
2694
2695 memset(&ifr, 0, sizeof(ifr));
2696 strcpy(ifr.ifr_name, "lo0");
2697
2698 if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
2699 return;
2700
2701 memset(&ifr, 0, sizeof(ifr));
2702 strcpy(ifr.ifr_name, "lo0");
2703
2704 if (assumes(ioctl(s6, SIOCGIFFLAGS, &ifr) != -1)) {
2705 ifr.ifr_flags |= IFF_UP;
2706 assumes(ioctl(s6, SIOCSIFFLAGS, &ifr) != -1);
2707 }
2708
2709 memset(&ifra6, 0, sizeof(ifra6));
2710 strcpy(ifra6.ifra_name, "lo0");
2711
2712 ifra6.ifra_addr.sin6_family = AF_INET6;
2713 ifra6.ifra_addr.sin6_addr = in6addr_loopback;
2714 ifra6.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
2715 ifra6.ifra_prefixmask.sin6_family = AF_INET6;
2716 memset(&ifra6.ifra_prefixmask.sin6_addr, 0xff, sizeof(struct in6_addr));
2717 ifra6.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
2718 ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
2719 ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
2720
2721 assumes(ioctl(s6, SIOCAIFADDR_IN6, &ifra6) != -1);
2722
2723 assumes(close(s6) == 0);
2724 }
2725
2726 pid_t
2727 fwexec(const char *const *argv, bool _wait)
2728 {
2729 int wstatus;
2730 pid_t p;
2731
2732 switch ((p = fork())) {
2733 case -1:
2734 break;
2735 case 0:
2736 if (!_wait) {
2737 setsid();
2738 }
2739 execvp(argv[0], (char *const *)argv);
2740 _exit(EXIT_FAILURE);
2741 break;
2742 default:
2743 if (!_wait)
2744 return p;
2745 if (p == waitpid(p, &wstatus, 0)) {
2746 if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == EXIT_SUCCESS)
2747 return p;
2748 }
2749 break;
2750 }
2751
2752 return -1;
2753 }
2754
2755 void
2756 do_potential_fsck(void)
2757 {
2758 const char *safe_fsck_tool[] = { "fsck", "-fy", NULL };
2759 const char *fsck_tool[] = { "fsck", "-p", NULL };
2760 const char *remount_tool[] = { "mount", "-uw", "/", NULL };
2761 struct statfs sfs;
2762
2763 if (!assumes(statfs("/", &sfs) != -1)) {
2764 return;
2765 }
2766
2767 if (!(sfs.f_flags & MNT_RDONLY)) {
2768 return;
2769 }
2770
2771 if (!is_safeboot()) {
2772 #if 0
2773 /* We have disabled this block for now. We need to revisit this optimization after Leopard. */
2774 if (sfs.f_flags & MNT_JOURNALED) {
2775 goto out;
2776 }
2777 #endif
2778
2779 if (fwexec(fsck_tool, true) != -1) {
2780 goto out;
2781 }
2782 }
2783
2784 if (fwexec(safe_fsck_tool, true) != -1) {
2785 goto out;
2786 }
2787
2788 fprintf(stderr, "fsck failed!\n");
2789
2790 /* someday, we should keep booting read-only, but as of today, other sub-systems cannot handle that scenario */
2791 #if TARGET_OS_EMBEDDED
2792 const char *nvram_tool[] = { "/usr/sbin/nvram", "auto-boot=false", NULL };
2793 assumes(fwexec(nvram_tool, true) != -1);
2794 assumes(reboot(RB_AUTOBOOT) != -1);
2795 #else
2796 assumes(reboot(RB_HALT) != -1);
2797 #endif
2798
2799 return;
2800 out:
2801 /*
2802 * Once this is fixed:
2803 *
2804 * <rdar://problem/3948774> Mount flag updates should be possible with NULL as the forth argument to mount()
2805 *
2806 * We can then do this one system call instead of calling out a full blown process.
2807 *
2808 * assumes(mount(sfs.f_fstypename, "/", MNT_UPDATE, NULL) != -1);
2809 */
2810
2811 #if TARGET_OS_EMBEDDED
2812 if (path_check("/etc/fstab")) {
2813 const char *mount_tool[] = { "mount", "-vat", "nonfs", NULL };
2814 if (!assumes(fwexec(mount_tool, true) != -1)) {
2815 assumes(fwexec(nvram_tool, true) != -1);
2816 assumes(reboot(RB_AUTOBOOT) != -1);
2817 }
2818 } else
2819 #endif
2820 {
2821 assumes(fwexec(remount_tool, true) != -1);
2822 }
2823
2824 fix_bogus_file_metadata();
2825 }
2826
2827 void
2828 fix_bogus_file_metadata(void)
2829 {
2830 static const struct {
2831 const char *path;
2832 const uid_t owner;
2833 const gid_t group;
2834 const mode_t needed_bits;
2835 const mode_t bad_bits;
2836 } f[] = {
2837 { "/sbin/launchd", 0, 0, S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH, S_ISUID|S_ISGID|S_ISVTX|S_IWOTH },
2838 { _PATH_TMP, 0, 0, S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO, S_ISUID|S_ISGID },
2839 { _PATH_VARTMP, 0, 0, S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO, S_ISUID|S_ISGID },
2840 };
2841 struct stat sb;
2842 size_t i;
2843
2844 for (i = 0; i < (sizeof(f) / sizeof(f[0])); i++) {
2845 mode_t i_needed_bits;
2846 mode_t i_bad_bits;
2847 bool fix_mode = false;
2848 bool fix_id = false;
2849
2850 if (!assumes(stat(f[i].path, &sb) != -1)) {
2851 continue;
2852 }
2853
2854 i_needed_bits = ~sb.st_mode & f[i].needed_bits;
2855 i_bad_bits = sb.st_mode & f[i].bad_bits;
2856
2857 if (i_bad_bits) {
2858 fprintf(stderr, "Crucial filesystem check: Removing bogus mode bits 0%o on path: %s\n", i_bad_bits, f[i].path);
2859 fix_mode = true;
2860 }
2861 if (i_needed_bits) {
2862 fprintf(stderr, "Crucial filesystem check: Adding missing mode bits 0%o on path: %s\n", i_needed_bits, f[i].path);
2863 fix_mode = true;
2864 }
2865 if (sb.st_uid != f[i].owner) {
2866 fprintf(stderr, "Crucial filesystem check: Fixing bogus UID %u on path: %s\n", sb.st_uid, f[i].path);
2867 fix_id = true;
2868 }
2869 if (sb.st_gid != f[i].group) {
2870 fprintf(stderr, "Crucial filesystem check: Fixing bogus GID %u on path: %s\n", sb.st_gid, f[i].path);
2871 fix_id = true;
2872 }
2873
2874 if (fix_mode) {
2875 assumes(chmod(f[i].path, (sb.st_mode & ~i_bad_bits) | i_needed_bits) != -1);
2876 }
2877 if (fix_id) {
2878 assumes(chown(f[i].path, f[i].owner, f[i].group) != -1);
2879 }
2880 }
2881 }
2882
2883 bool
2884 path_check(const char *path)
2885 {
2886 struct stat sb;
2887
2888 if (stat(path, &sb) == 0)
2889 return true;
2890 return false;
2891 }
2892
2893 bool
2894 is_safeboot(void)
2895 {
2896 int sbmib[] = { CTL_KERN, KERN_SAFEBOOT };
2897 uint32_t sb = 0;
2898 size_t sbsz = sizeof(sb);
2899
2900 if (!assumes(sysctl(sbmib, 2, &sb, &sbsz, NULL, 0) == 0))
2901 return false;
2902
2903 return (bool)sb;
2904 }
2905
2906 bool
2907 is_netboot(void)
2908 {
2909 int nbmib[] = { CTL_KERN, KERN_NETBOOT };
2910 uint32_t nb = 0;
2911 size_t nbsz = sizeof(nb);
2912
2913 if (!assumes(sysctl(nbmib, 2, &nb, &nbsz, NULL, 0) == 0))
2914 return false;
2915
2916 return (bool)nb;
2917 }
2918
2919 void
2920 empty_dir(const char *thedir, struct stat *psb)
2921 {
2922 struct dirent *de;
2923 struct stat psb2;
2924 DIR *od;
2925 int currend_dir_fd;
2926
2927 if (!psb) {
2928 psb = &psb2;
2929 if (!assumes(lstat(thedir, psb) != -1)) {
2930 return;
2931 }
2932 }
2933
2934 if (!assumes((currend_dir_fd = open(".", 0)) != -1)) {
2935 return;
2936 }
2937
2938 if (!assumes(chdir(thedir) != -1)) {
2939 goto out;
2940 }
2941
2942 if (!assumes(od = opendir("."))) {
2943 goto out;
2944 }
2945
2946 while ((de = readdir(od))) {
2947 struct stat sb;
2948
2949 if (strcmp(de->d_name, ".") == 0) {
2950 continue;
2951 }
2952
2953 if (strcmp(de->d_name, "..") == 0) {
2954 continue;
2955 }
2956
2957 if (!assumes(lstat(de->d_name, &sb) != -1)) {
2958 continue;
2959 }
2960
2961 if (psb->st_dev != sb.st_dev) {
2962 assumes(unmount(de->d_name, MNT_FORCE) != -1);
2963
2964 /* Let's lstat() again to see if the unmount() worked and what was under it */
2965 if (!assumes(lstat(de->d_name, &sb) != -1)) {
2966 continue;
2967 }
2968
2969 if (!assumes(psb->st_dev == sb.st_dev)) {
2970 continue;
2971 }
2972 }
2973
2974 if (S_ISDIR(sb.st_mode)) {
2975 empty_dir(de->d_name, &sb);
2976 }
2977
2978 assumes(lchflags(de->d_name, 0) != -1);
2979 assumes(remove(de->d_name) != -1);
2980 }
2981
2982 assumes(closedir(od) != -1);
2983
2984 out:
2985 assumes(fchdir(currend_dir_fd) != -1);
2986 assumes(close(currend_dir_fd) != -1);
2987 }
2988
2989 int
2990 touch_file(const char *path, mode_t m)
2991 {
2992 int fd = open(path, O_CREAT, m);
2993
2994 if (fd == -1)
2995 return -1;
2996
2997 return close(fd);
2998 }
2999
3000 void
3001 apply_sysctls_from_file(const char *thefile)
3002 {
3003 const char *sysctl_tool[] = { "sysctl", "-w", NULL, NULL };
3004 size_t ln_len = 0;
3005 char *val, *tmpstr;
3006 FILE *sf;
3007
3008 if (!(sf = fopen(thefile, "r")))
3009 return;
3010
3011 while ((val = fgetln(sf, &ln_len))) {
3012 if (ln_len == 0) {
3013 continue;
3014 }
3015 if (!assumes((tmpstr = malloc(ln_len + 1)) != NULL)) {
3016 continue;
3017 }
3018 memcpy(tmpstr, val, ln_len);
3019 tmpstr[ln_len] = 0;
3020 val = tmpstr;
3021
3022 if (val[ln_len - 1] == '\n' || val[ln_len - 1] == '\r') {
3023 val[ln_len - 1] = '\0';
3024 }
3025
3026 while (*val && isspace(*val))
3027 val++;
3028 if (*val == '\0' || *val == '#') {
3029 goto skip_sysctl_tool;
3030 }
3031 sysctl_tool[2] = val;
3032 assumes(fwexec(sysctl_tool, true) != -1);
3033 skip_sysctl_tool:
3034 free(tmpstr);
3035 }
3036
3037 assumes(fclose(sf) == 0);
3038 }
3039
3040 void
3041 do_sysversion_sysctl(void)
3042 {
3043 int mib[] = { CTL_KERN, KERN_OSVERSION };
3044 CFDictionaryRef versdict;
3045 CFStringRef buildvers;
3046 char buf[1024];
3047 size_t bufsz = sizeof(buf);
3048
3049 /* <rdar://problem/4477682> ER: launchd should set kern.osversion very early in boot */
3050
3051 if (sysctl(mib, 2, buf, &bufsz, NULL, 0) == -1) {
3052 fprintf(stderr, "sysctl(): %s\n", strerror(errno));
3053 return;
3054 }
3055
3056 if (buf[0] != '\0') {
3057 return;
3058 }
3059
3060 if (!assumes((versdict = _CFCopySystemVersionDictionary()))) {
3061 return;
3062 }
3063
3064 if (assumes((buildvers = CFDictionaryGetValue(versdict, _kCFSystemVersionBuildVersionKey)))) {
3065 CFStringGetCString(buildvers, buf, sizeof(buf), kCFStringEncodingUTF8);
3066
3067 assumes(sysctl(mib, 2, NULL, 0, buf, strlen(buf) + 1) != -1);
3068 }
3069
3070 CFRelease(versdict);
3071 }
3072
3073 void
3074 do_application_firewall_magic(int sfd, launch_data_t thejob)
3075 {
3076 const char *prog = NULL, *partialprog = NULL;
3077 char *path, *pathtmp, **pathstmp;
3078 char *paths[100];
3079 launch_data_t tmp;
3080
3081 /*
3082 * Sigh...
3083 * <rdar://problem/4684434> setsockopt() with the executable path as the argument
3084 */
3085
3086 if ((tmp = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_PROGRAM))) {
3087 prog = launch_data_get_string(tmp);
3088 }
3089
3090 if (!prog) {
3091 if ((tmp = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_PROGRAMARGUMENTS))) {
3092 if ((tmp = launch_data_array_get_index(tmp, 0))) {
3093 if (assumes((partialprog = launch_data_get_string(tmp)) != NULL)) {
3094 if (partialprog[0] == '/') {
3095 prog = partialprog;
3096 }
3097 }
3098 }
3099 }
3100 }
3101
3102 if (!prog) {
3103 pathtmp = path = strdup(getenv("PATH"));
3104
3105 pathstmp = paths;
3106
3107 while ((*pathstmp = strsep(&pathtmp, ":"))) {
3108 if (**pathstmp != '\0') {
3109 pathstmp++;
3110 }
3111 }
3112
3113 free(path);
3114 pathtmp = alloca(MAXPATHLEN);
3115
3116 pathstmp = paths;
3117
3118 for (; *pathstmp; pathstmp++) {
3119 snprintf(pathtmp, MAXPATHLEN, "%s/%s", *pathstmp, partialprog);
3120 if (path_check(pathtmp)) {
3121 prog = pathtmp;
3122 break;
3123 }
3124 }
3125 }
3126
3127 if (assumes(prog != NULL)) {
3128 /* The networking team has asked us to ignore the failure of this API if errno == ENOPROTOOPT */
3129 assumes(setsockopt(sfd, SOL_SOCKET, SO_EXECPATH, prog, strlen(prog) + 1) != -1 || errno == ENOPROTOOPT);
3130 }
3131 }
3132
3133
3134 void
3135 preheat_page_cache_hack(void)
3136 {
3137 struct dirent *de;
3138 DIR *thedir;
3139
3140 /* Disable this hack for now */
3141 return;
3142
3143 if ((thedir = opendir("/etc/preheat_at_boot")) == NULL) {
3144 return;
3145 }
3146
3147 while ((de = readdir(thedir))) {
3148 struct stat sb;
3149 void *junkbuf;
3150 int fd;
3151
3152 if (de->d_name[0] == '.') {
3153 continue;
3154 }
3155
3156 if ((fd = open(de->d_name, O_RDONLY)) == -1) {
3157 continue;
3158 }
3159
3160 if (fstat(fd, &sb) != -1) {
3161 if ((sb.st_size < 10*1024*1024) && (junkbuf = malloc((size_t)sb.st_size)) != NULL) {
3162 assumes(read(fd, junkbuf, (size_t)sb.st_size) == (ssize_t)sb.st_size);
3163 free(junkbuf);
3164 }
3165 }
3166
3167 close(fd);
3168 }
3169
3170 closedir(thedir);
3171 }
3172
3173
3174 void
3175 do_bootroot_magic(void)
3176 {
3177 const char *kextcache_tool[] = { "kextcache", "-U", "/", NULL };
3178 CFTypeRef bootrootProp;
3179 io_service_t chosen;
3180 int wstatus;
3181 pid_t p;
3182
3183 chosen = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/chosen");
3184
3185 if (!assumes(chosen)) {
3186 return;
3187 }
3188
3189 bootrootProp = IORegistryEntryCreateCFProperty(chosen, CFSTR(kBootRootActiveKey), kCFAllocatorDefault, 0);
3190
3191 IOObjectRelease(chosen);
3192
3193 if (!bootrootProp) {
3194 return;
3195 }
3196
3197 CFRelease(bootrootProp);
3198
3199 if (!assumes((p = fwexec(kextcache_tool, false)) != -1)) {
3200 return;
3201 }
3202
3203 if (!assumes(waitpid(p, &wstatus, 0) != -1)) {
3204 return;
3205 }
3206
3207 if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == EX_OSFILE) {
3208 assumes(reboot(RB_AUTOBOOT) != -1);
3209 }
3210 }