]> git.saurik.com Git - apple/launchd.git/blob - launchd/src/launchctl.c
launchd-258.18.tar.gz
[apple/launchd.git] / launchd / src / launchctl.c
1 /*
2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_APACHE_LICENSE_HEADER_START@
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * @APPLE_APACHE_LICENSE_HEADER_END@
19 */
20
21 static const char *const __rcs_file_version__ = "$Revision: 23642 $";
22
23 #include "liblaunch_public.h"
24 #include "liblaunch_private.h"
25 #include "libbootstrap_public.h"
26 #include "libvproc_public.h"
27 #include "libvproc_private.h"
28 #include "libvproc_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 (path_check("/etc/rc.server")) {
1413 const char *rcserver_tool[] = { _PATH_BSHELL, "/etc/rc.server", NULL };
1414 assumes(fwexec(rcserver_tool, true) != -1);
1415 }
1416
1417 apply_sysctls_from_file("/etc/sysctl.conf");
1418
1419 if (path_check("/etc/rc.cdrom")) {
1420 const char *rccdrom_tool[] = { _PATH_BSHELL, "/etc/rc.cdrom", "multiuser", NULL };
1421 assumes(fwexec(rccdrom_tool, true) != -1);
1422 assumes(reboot(RB_HALT) != -1);
1423 _exit(EXIT_FAILURE);
1424 } else if (is_netboot()) {
1425 const char *rcnetboot_tool[] = { _PATH_BSHELL, "/etc/rc.netboot", "init", NULL };
1426 if (!assumes(fwexec(rcnetboot_tool, true) != -1)) {
1427 assumes(reboot(RB_HALT) != -1);
1428 _exit(EXIT_FAILURE);
1429 }
1430 } else {
1431 do_potential_fsck();
1432 }
1433
1434 read_launchd_conf();
1435
1436 if (path_check("/var/account/acct")) {
1437 assumes(acct("/var/account/acct") != -1);
1438 }
1439
1440 #if !TARGET_OS_EMBEDDED
1441 if (path_check("/etc/fstab")) {
1442 const char *mount_tool[] = { "mount", "-vat", "nonfs", NULL };
1443 assumes(fwexec(mount_tool, true) != -1);
1444 }
1445 #endif
1446
1447 if (path_check("/etc/rc.installer_cleanup")) {
1448 const char *rccleanup_tool[] = { _PATH_BSHELL, "/etc/rc.installer_cleanup", "multiuser", NULL };
1449 assumes(fwexec(rccleanup_tool, true) != -1);
1450 }
1451
1452 empty_dir(_PATH_VARRUN, NULL);
1453 empty_dir(_PATH_TMP, NULL);
1454 remove(_PATH_NOLOGIN);
1455
1456 if (path_check("/usr/libexec/dirhelper")) {
1457 const char *dirhelper_tool[] = { "/usr/libexec/dirhelper", "-machineBoot", NULL };
1458 assumes(fwexec(dirhelper_tool, true) != -1);
1459 }
1460
1461 assumes(touch_file(_PATH_UTMPX, DEFFILEMODE) != -1);
1462 assumes(touch_file(_PATH_VARRUN "/.systemStarterRunning", DEFFILEMODE) != -1);
1463
1464 if (path_check("/etc/security/rc.audit")) {
1465 const char *audit_tool[] = { _PATH_BSHELL, "/etc/security/rc.audit", NULL };
1466 assumes(fwexec(audit_tool, true) != -1);
1467 }
1468
1469 do_BootCache_magic(BOOTCACHE_START);
1470
1471 preheat_page_cache_hack();
1472
1473 _vproc_set_global_on_demand(true);
1474
1475 char *load_launchd_items[] = { "load", "-D", "all", "/etc/mach_init.d",
1476 #if TARGET_OS_EMBEDDED
1477 "/var/mobile/Library/LaunchAgents",
1478 #endif
1479 NULL };
1480
1481 if (is_safeboot()) {
1482 load_launchd_items[2] = "system";
1483 }
1484
1485 assumes(load_and_unload_cmd(4, load_launchd_items) == 0);
1486
1487 /*
1488 * 5066316
1489 *
1490 * We need to revisit this after Leopard ships.
1491 *
1492 * I want a plist defined knob for jobs to give advisory hints that
1493 * will "hopefully" serialize bootstrap. Reasons for doing so include
1494 * pragmatic performance optimizations and attempts to workaround bugs
1495 * in jobs. My current thought is something like what follows.
1496 *
1497 * The BootCache would switch to launchd and add this to the plist:
1498 *
1499 * <key>HopefullyStartsSerially<key>
1500 * <dict>
1501 * <key>ReadyTimeout</key>
1502 * <integer>2</integer>
1503 * </dict>
1504 *
1505 * And kextd would add the following:
1506 *
1507 * <key>HopefullyStartsSerially<key>
1508 * <dict>
1509 * <key>ReadyTimeout</key>
1510 * <integer>5</integer>
1511 * <key>HopefullyStartsAfter</key>
1512 * <string>com.apple.BootCache.daemon</string>
1513 * </dict>
1514 *
1515 *
1516 * Then both the BootCache and kextd could call something like:
1517 *
1518 * vproc_declare_ready_state();
1519 *
1520 * To tell launchd to short circuit the readiness timeout and let the
1521 * next wave of jobs start.
1522 *
1523 * Yes, this mechanism smells a lot like SystemStarter, rc.d and
1524 * friends. I think as long as we document that artificial
1525 * serialization is only advisory and not guaranteed, we should be
1526 * fine. Remember: IPC is the preferred way to serialize operations.
1527 *
1528 */
1529 mach_timespec_t w = { 5, 0 };
1530 IOKitWaitQuiet(kIOMasterPortDefault, &w);
1531
1532 do_BootCache_magic(BOOTCACHE_TAG);
1533
1534 do_bootroot_magic();
1535
1536 _vproc_set_global_on_demand(false);
1537
1538 assumes(kevent(kq, NULL, 0, &kev, 1, NULL) == 1);
1539
1540 do_BootCache_magic(BOOTCACHE_STOP);
1541
1542 assumes(close(kq) != -1);
1543 }
1544
1545 void
1546 do_BootCache_magic(BootCache_action_t what)
1547 {
1548 const char *bcc_tool[] = { "/usr/sbin/BootCacheControl", "-f", "/var/db/BootCache.playlist", NULL, NULL };
1549
1550 if (is_safeboot() || !path_check(bcc_tool[0])) {
1551 return;
1552 }
1553
1554 switch (what) {
1555 case BOOTCACHE_START:
1556 bcc_tool[3] = "start";
1557 break;
1558 case BOOTCACHE_TAG:
1559 bcc_tool[3] = "tag";
1560 break;
1561 case BOOTCACHE_STOP:
1562 bcc_tool[3] = "stop";
1563 break;
1564 default:
1565 assumes(false);
1566 return;
1567 }
1568
1569 fwexec(bcc_tool, true);
1570 }
1571
1572 int
1573 bootstrap_cmd(int argc, char *const argv[])
1574 {
1575 char *session_type = NULL;
1576 bool sflag = false;
1577 int ch;
1578
1579 while ((ch = getopt(argc, argv, "sS:")) != -1) {
1580 switch (ch) {
1581 case 's':
1582 sflag = true;
1583 break;
1584 case 'S':
1585 session_type = optarg;
1586 break;
1587 case '?':
1588 default:
1589 break;
1590 }
1591 }
1592
1593 optind = 1;
1594 optreset = 1;
1595
1596 if (!session_type) {
1597 fprintf(stderr, "usage: %s bootstrap [-s] -S <session-type>\n", getprogname());
1598 return 1;
1599 }
1600
1601 if (strcasecmp(session_type, "System") == 0) {
1602 system_specific_bootstrap(sflag);
1603 } else {
1604 char *load_launchd_items[] = { "load", "-S", session_type, "-D", "all", NULL, NULL, NULL, NULL };
1605 int the_argc = 5;
1606
1607 if (is_safeboot()) {
1608 load_launchd_items[4] = "system";
1609 }
1610
1611 if (strcasecmp(session_type, VPROCMGR_SESSION_BACKGROUND) == 0 || strcasecmp(session_type, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
1612 load_launchd_items[4] = "system";
1613 if (!is_safeboot()) {
1614 load_launchd_items[5] = "-D";
1615 load_launchd_items[6] = "local";
1616 the_argc += 2;
1617 }
1618 if (strcasecmp(session_type, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
1619 load_launchd_items[the_argc] = "/etc/mach_init_per_login_session.d";
1620 the_argc += 1;
1621 }
1622 } else if (strcasecmp(session_type, VPROCMGR_SESSION_AQUA) == 0) {
1623 load_launchd_items[5] = "/etc/mach_init_per_user.d";
1624 the_argc += 1;
1625 }
1626
1627 if (strcasecmp(session_type, VPROCMGR_SESSION_BACKGROUND) == 0) {
1628 read_launchd_conf();
1629 #if HAVE_SECURITY
1630 assumes(SessionCreate(sessionKeepCurrentBootstrap, 0) == 0);
1631 #endif
1632 }
1633
1634 return load_and_unload_cmd(the_argc, load_launchd_items);
1635 }
1636
1637 return 0;
1638 }
1639
1640 int
1641 load_and_unload_cmd(int argc, char *const argv[])
1642 {
1643 NSSearchPathEnumerationState es = 0;
1644 char nspath[PATH_MAX * 2]; /* safe side, we need to append */
1645 bool badopts = false;
1646 struct load_unload_state lus;
1647 size_t i;
1648 int ch;
1649
1650 memset(&lus, 0, sizeof(lus));
1651
1652 if (strcmp(argv[0], "load") == 0) {
1653 lus.load = true;
1654 }
1655
1656 while ((ch = getopt(argc, argv, "wFS:D:")) != -1) {
1657 switch (ch) {
1658 case 'w':
1659 lus.editondisk = true;
1660 break;
1661 case 'F':
1662 lus.forceload = true;
1663 break;
1664 case 'S':
1665 lus.session_type = optarg;
1666 break;
1667 case 'D':
1668 if (strcasecmp(optarg, "all") == 0) {
1669 es |= NSAllDomainsMask;
1670 } else if (strcasecmp(optarg, "user") == 0) {
1671 es |= NSUserDomainMask;
1672 } else if (strcasecmp(optarg, "local") == 0) {
1673 es |= NSLocalDomainMask;
1674 } else if (strcasecmp(optarg, "network") == 0) {
1675 es |= NSNetworkDomainMask;
1676 } else if (strcasecmp(optarg, "system") == 0) {
1677 es |= NSSystemDomainMask;
1678 } else {
1679 badopts = true;
1680 }
1681 break;
1682 case '?':
1683 default:
1684 badopts = true;
1685 break;
1686 }
1687 }
1688 argc -= optind;
1689 argv += optind;
1690
1691 if (lus.session_type == NULL) {
1692 es &= ~NSUserDomainMask;
1693 }
1694
1695 if (argc == 0 && es == 0) {
1696 badopts = true;
1697 }
1698
1699 if (badopts) {
1700 fprintf(stderr, "usage: %s load [-wF] [-D <user|local|network|system|all>] paths...\n", getprogname());
1701 return 1;
1702 }
1703
1704 /* I wish I didn't need to do three passes, but I need to load mDNSResponder and use it too.
1705 * And loading legacy mach init jobs is extra fun.
1706 *
1707 * In later versions of launchd, I hope to load everything in the first pass,
1708 * then do the Bonjour magic on the jobs that need it, and reload them, but for now,
1709 * I haven't thought through the various complexities of reloading jobs, and therefore
1710 * launchd doesn't have reload support right now.
1711 */
1712
1713 lus.pass0 = launch_data_alloc(LAUNCH_DATA_ARRAY);
1714 lus.pass1 = launch_data_alloc(LAUNCH_DATA_ARRAY);
1715 lus.pass2 = launch_data_alloc(LAUNCH_DATA_ARRAY);
1716
1717 es = NSStartSearchPathEnumeration(NSLibraryDirectory, es);
1718
1719 while ((es = NSGetNextSearchPathEnumeration(es, nspath))) {
1720 glob_t g;
1721
1722 if (lus.session_type) {
1723 strcat(nspath, "/LaunchAgents");
1724 } else {
1725 strcat(nspath, "/LaunchDaemons");
1726 }
1727
1728 if (glob(nspath, GLOB_TILDE|GLOB_NOSORT, NULL, &g) == 0) {
1729 for (i = 0; i < g.gl_pathc; i++) {
1730 readpath(g.gl_pathv[i], &lus);
1731 }
1732 globfree(&g);
1733 }
1734 }
1735
1736 for (i = 0; i < (size_t)argc; i++) {
1737 readpath(argv[i], &lus);
1738 }
1739
1740 if (launch_data_array_get_count(lus.pass0) == 0 &&
1741 launch_data_array_get_count(lus.pass1) == 0 &&
1742 launch_data_array_get_count(lus.pass2) == 0) {
1743 if (!is_managed) {
1744 fprintf(stderr, "nothing found to %s\n", lus.load ? "load" : "unload");
1745 }
1746 launch_data_free(lus.pass0);
1747 launch_data_free(lus.pass1);
1748 launch_data_free(lus.pass2);
1749 return is_managed ? 0 : 1;
1750 }
1751
1752 if (lus.load) {
1753 distill_jobs(lus.pass1);
1754 submit_mach_jobs(lus.pass0);
1755 submit_job_pass(lus.pass1);
1756 let_go_of_mach_jobs(lus.pass0);
1757 distill_jobs(lus.pass2);
1758 submit_job_pass(lus.pass2);
1759 } else {
1760 for (i = 0; i < launch_data_array_get_count(lus.pass1); i++) {
1761 unloadjob(launch_data_array_get_index(lus.pass1, i));
1762 }
1763 for (i = 0; i < launch_data_array_get_count(lus.pass2); i++) {
1764 unloadjob(launch_data_array_get_index(lus.pass2, i));
1765 }
1766 }
1767
1768 return 0;
1769 }
1770
1771 void
1772 submit_mach_jobs(launch_data_t jobs)
1773 {
1774 size_t i, c;
1775
1776 c = launch_data_array_get_count(jobs);
1777
1778 for (i = 0; i < c; i++) {
1779 launch_data_t tmp, oai = launch_data_array_get_index(jobs, i);
1780 const char *sn = NULL, *cmd = NULL;
1781 bool d = true;
1782 mach_port_t msr, msv;
1783 kern_return_t kr;
1784 uid_t u = getuid();
1785
1786 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_ONDEMAND)))
1787 d = launch_data_get_bool(tmp);
1788 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVICENAME)))
1789 sn = launch_data_get_string(tmp);
1790 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_COMMAND)))
1791 cmd = launch_data_get_string(tmp);
1792
1793 if ((kr = bootstrap_create_server(bootstrap_port, (char *)cmd, u, d, &msr)) != KERN_SUCCESS) {
1794 fprintf(stderr, "%s: bootstrap_create_server(): %d\n", getprogname(), kr);
1795 continue;
1796 }
1797 if ((kr = bootstrap_create_service(msr, (char*)sn, &msv)) != KERN_SUCCESS) {
1798 fprintf(stderr, "%s: bootstrap_create_service(): %d\n", getprogname(), kr);
1799 mach_port_destroy(mach_task_self(), msr);
1800 continue;
1801 }
1802 launch_data_dict_insert(oai, launch_data_new_machport(msr), MACHINIT_JOBKEY_SERVERPORT);
1803 launch_data_dict_insert(oai, launch_data_new_machport(msv), MACHINIT_JOBKEY_SERVICEPORT);
1804 }
1805 }
1806
1807 void
1808 let_go_of_mach_jobs(launch_data_t jobs)
1809 {
1810 size_t i, c = launch_data_array_get_count(jobs);
1811
1812 for (i = 0; i < c; i++) {
1813 launch_data_t tmp, oai = launch_data_array_get_index(jobs, i);
1814 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVICEPORT))) {
1815 mach_port_destroy(mach_task_self(), launch_data_get_machport(tmp));
1816 } else {
1817 fprintf(stderr, "%s: ack! missing service port!\n", getprogname());
1818 }
1819 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVERPORT))) {
1820 mach_port_destroy(mach_task_self(), launch_data_get_machport(tmp));
1821 } else {
1822 fprintf(stderr, "%s: ack! missing server port!\n", getprogname());
1823 }
1824 }
1825 }
1826
1827 void
1828 submit_job_pass(launch_data_t jobs)
1829 {
1830 launch_data_t msg, resp;
1831 size_t i;
1832 int e;
1833
1834 if (launch_data_array_get_count(jobs) == 0)
1835 return;
1836
1837 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1838
1839 launch_data_dict_insert(msg, jobs, LAUNCH_KEY_SUBMITJOB);
1840
1841 resp = launch_msg(msg);
1842
1843 if (resp) {
1844 switch (launch_data_get_type(resp)) {
1845 case LAUNCH_DATA_ERRNO:
1846 if ((e = launch_data_get_errno(resp)))
1847 fprintf(stderr, "%s\n", strerror(e));
1848 break;
1849 case LAUNCH_DATA_ARRAY:
1850 for (i = 0; i < launch_data_array_get_count(jobs); i++) {
1851 launch_data_t obatind = launch_data_array_get_index(resp, i);
1852 launch_data_t jatind = launch_data_array_get_index(jobs, i);
1853 const char *lab4job = launch_data_get_string(launch_data_dict_lookup(jatind, LAUNCH_JOBKEY_LABEL));
1854 if (LAUNCH_DATA_ERRNO == launch_data_get_type(obatind)) {
1855 e = launch_data_get_errno(obatind);
1856 switch (e) {
1857 case EEXIST:
1858 fprintf(stderr, "%s: %s\n", lab4job, "Already loaded");
1859 break;
1860 case ESRCH:
1861 fprintf(stderr, "%s: %s\n", lab4job, "Not loaded");
1862 break;
1863 default:
1864 fprintf(stderr, "%s: %s\n", lab4job, strerror(e));
1865 case 0:
1866 break;
1867 }
1868 }
1869 }
1870 break;
1871 default:
1872 fprintf(stderr, "unknown respose from launchd!\n");
1873 break;
1874 }
1875 launch_data_free(resp);
1876 } else {
1877 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1878 }
1879
1880 launch_data_free(msg);
1881 }
1882
1883 int
1884 start_stop_remove_cmd(int argc, char *const argv[])
1885 {
1886 launch_data_t resp, msg;
1887 const char *lmsgcmd = LAUNCH_KEY_STOPJOB;
1888 int e, r = 0;
1889
1890 if (0 == strcmp(argv[0], "start"))
1891 lmsgcmd = LAUNCH_KEY_STARTJOB;
1892
1893 if (0 == strcmp(argv[0], "remove"))
1894 lmsgcmd = LAUNCH_KEY_REMOVEJOB;
1895
1896 if (argc != 2) {
1897 fprintf(stderr, "usage: %s %s <job label>\n", getprogname(), argv[0]);
1898 return 1;
1899 }
1900
1901 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1902 launch_data_dict_insert(msg, launch_data_new_string(argv[1]), lmsgcmd);
1903
1904 resp = launch_msg(msg);
1905 launch_data_free(msg);
1906
1907 if (resp == NULL) {
1908 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1909 return 1;
1910 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
1911 if ((e = launch_data_get_errno(resp))) {
1912 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e));
1913 r = 1;
1914 }
1915 } else {
1916 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
1917 r = 1;
1918 }
1919
1920 launch_data_free(resp);
1921 return r;
1922 }
1923
1924 void
1925 print_jobs(launch_data_t j, const char *key __attribute__((unused)), void *context __attribute__((unused)))
1926 {
1927 static size_t depth = 0;
1928 launch_data_t lo = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LABEL);
1929 launch_data_t pido = launch_data_dict_lookup(j, LAUNCH_JOBKEY_PID);
1930 launch_data_t stato = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LASTEXITSTATUS);
1931 const char *label = launch_data_get_string(lo);
1932 size_t i;
1933
1934 if (pido) {
1935 fprintf(stdout, "%lld\t-\t", launch_data_get_integer(pido));
1936 } else if (stato) {
1937 int wstatus = (int)launch_data_get_integer(stato);
1938 if (WIFEXITED(wstatus)) {
1939 fprintf(stdout, "-\t%d\t", WEXITSTATUS(wstatus));
1940 } else if (WIFSIGNALED(wstatus)) {
1941 fprintf(stdout, "-\t-%d\t", WTERMSIG(wstatus));
1942 } else {
1943 fprintf(stdout, "-\t???\t");
1944 }
1945 } else {
1946 fprintf(stdout, "-\t-\t");
1947 }
1948 for (i = 0; i < depth; i++)
1949 fprintf(stdout, "\t");
1950
1951 fprintf(stdout, "%s\n", label);
1952 }
1953
1954 void
1955 print_obj(launch_data_t obj, const char *key, void *context __attribute__((unused)))
1956 {
1957 static size_t indent = 0;
1958 size_t i, c;
1959
1960 for (i = 0; i < indent; i++)
1961 fprintf(stdout, "\t");
1962
1963 if (key)
1964 fprintf(stdout, "\"%s\" = ", key);
1965
1966 switch (launch_data_get_type(obj)) {
1967 case LAUNCH_DATA_STRING:
1968 fprintf(stdout, "\"%s\";\n", launch_data_get_string(obj));
1969 break;
1970 case LAUNCH_DATA_INTEGER:
1971 fprintf(stdout, "%lld;\n", launch_data_get_integer(obj));
1972 break;
1973 case LAUNCH_DATA_REAL:
1974 fprintf(stdout, "%f;\n", launch_data_get_real(obj));
1975 break;
1976 case LAUNCH_DATA_BOOL:
1977 fprintf(stdout, "%s;\n", launch_data_get_bool(obj) ? "true" : "false");
1978 break;
1979 case LAUNCH_DATA_ARRAY:
1980 c = launch_data_array_get_count(obj);
1981 fprintf(stdout, "(\n");
1982 indent++;
1983 for (i = 0; i < c; i++)
1984 print_obj(launch_data_array_get_index(obj, i), NULL, NULL);
1985 indent--;
1986 for (i = 0; i < indent; i++)
1987 fprintf(stdout, "\t");
1988 fprintf(stdout, ");\n");
1989 break;
1990 case LAUNCH_DATA_DICTIONARY:
1991 fprintf(stdout, "{\n");
1992 indent++;
1993 launch_data_dict_iterate(obj, print_obj, NULL);
1994 indent--;
1995 for (i = 0; i < indent; i++)
1996 fprintf(stdout, "\t");
1997 fprintf(stdout, "};\n");
1998 break;
1999 case LAUNCH_DATA_FD:
2000 fprintf(stdout, "file-descriptor-object;\n");
2001 break;
2002 case LAUNCH_DATA_MACHPORT:
2003 fprintf(stdout, "mach-port-object;\n");
2004 break;
2005 default:
2006 fprintf(stdout, "???;\n");
2007 break;
2008 }
2009 }
2010
2011 int
2012 list_cmd(int argc, char *const argv[])
2013 {
2014 launch_data_t resp, msg;
2015 int r = 0;
2016
2017 if (argc > 2) {
2018 fprintf(stderr, "usage: %s list [label]\n", getprogname());
2019 return 1;
2020 } else if (argc == 2) {
2021 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2022 launch_data_dict_insert(msg, launch_data_new_string(argv[1]), LAUNCH_KEY_GETJOB);
2023 } else if (vproc_swap_complex(NULL, VPROC_GSK_ALLJOBS, NULL, &resp) == NULL) {
2024 fprintf(stdout, "PID\tStatus\tLabel\n");
2025 launch_data_dict_iterate(resp, print_jobs, NULL);
2026 launch_data_free(resp);
2027 return 0;
2028 } else {
2029 return 1;
2030 }
2031
2032 resp = launch_msg(msg);
2033 launch_data_free(msg);
2034
2035 if (resp == NULL) {
2036 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2037 return 1;
2038 } else if (launch_data_get_type(resp) == LAUNCH_DATA_DICTIONARY) {
2039 print_obj(resp, NULL, NULL);
2040 } else {
2041 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
2042 r = 1;
2043 }
2044
2045 launch_data_free(resp);
2046
2047 return r;
2048 }
2049
2050 int
2051 stdio_cmd(int argc __attribute__((unused)), char *const argv[])
2052 {
2053 fprintf(stderr, "%s %s: This sub-command no longer does anything\n", getprogname(), argv[0]);
2054 return 1;
2055 }
2056
2057 int
2058 fyi_cmd(int argc, char *const argv[])
2059 {
2060 launch_data_t resp, msg;
2061 const char *lmsgk = NULL;
2062 int e, r = 0;
2063
2064 if (argc != 1) {
2065 fprintf(stderr, "usage: %s %s\n", getprogname(), argv[0]);
2066 return 1;
2067 }
2068
2069 if (!strcmp(argv[0], "shutdown")) {
2070 lmsgk = LAUNCH_KEY_SHUTDOWN;
2071 } else if (!strcmp(argv[0], "singleuser")) {
2072 lmsgk = LAUNCH_KEY_SINGLEUSER;
2073 } else {
2074 return 1;
2075 }
2076
2077 msg = launch_data_new_string(lmsgk);
2078 resp = launch_msg(msg);
2079 launch_data_free(msg);
2080
2081 if (resp == NULL) {
2082 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2083 return 1;
2084 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
2085 if ((e = launch_data_get_errno(resp))) {
2086 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e));
2087 r = 1;
2088 }
2089 } else {
2090 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
2091 r = 1;
2092 }
2093
2094 launch_data_free(resp);
2095
2096 return r;
2097 }
2098
2099 int
2100 logupdate_cmd(int argc, char *const argv[])
2101 {
2102 int64_t inval, outval;
2103 int i, j, m = 0;
2104 bool badargs = false, maskmode = false, onlymode = false, levelmode = false;
2105 static const struct {
2106 const char *name;
2107 int level;
2108 } logtbl[] = {
2109 { "debug", LOG_DEBUG },
2110 { "info", LOG_INFO },
2111 { "notice", LOG_NOTICE },
2112 { "warning", LOG_WARNING },
2113 { "error", LOG_ERR },
2114 { "critical", LOG_CRIT },
2115 { "alert", LOG_ALERT },
2116 { "emergency", LOG_EMERG },
2117 };
2118 int logtblsz = sizeof logtbl / sizeof logtbl[0];
2119
2120 if (argc >= 2) {
2121 if (!strcmp(argv[1], "mask"))
2122 maskmode = true;
2123 else if (!strcmp(argv[1], "only"))
2124 onlymode = true;
2125 else if (!strcmp(argv[1], "level"))
2126 levelmode = true;
2127 else
2128 badargs = true;
2129 }
2130
2131 if (maskmode)
2132 m = LOG_UPTO(LOG_DEBUG);
2133
2134 if (argc > 2 && (maskmode || onlymode)) {
2135 for (i = 2; i < argc; i++) {
2136 for (j = 0; j < logtblsz; j++) {
2137 if (!strcmp(argv[i], logtbl[j].name)) {
2138 if (maskmode)
2139 m &= ~(LOG_MASK(logtbl[j].level));
2140 else
2141 m |= LOG_MASK(logtbl[j].level);
2142 break;
2143 }
2144 }
2145 if (j == logtblsz) {
2146 badargs = true;
2147 break;
2148 }
2149 }
2150 } else if (argc > 2 && levelmode) {
2151 for (j = 0; j < logtblsz; j++) {
2152 if (!strcmp(argv[2], logtbl[j].name)) {
2153 m = LOG_UPTO(logtbl[j].level);
2154 break;
2155 }
2156 }
2157 if (j == logtblsz)
2158 badargs = true;
2159 } else if (argc != 1) {
2160 badargs = true;
2161 }
2162
2163 if (badargs) {
2164 fprintf(stderr, "usage: %s [[mask loglevels...] | [only loglevels...] [level loglevel]]\n", getprogname());
2165 return 1;
2166 }
2167
2168 inval = m;
2169
2170 if (vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_LOG_MASK, argc != 1 ? &inval : NULL, &outval) == NULL) {
2171 if (argc == 1) {
2172 for (j = 0; j < logtblsz; j++) {
2173 if (outval & LOG_MASK(logtbl[j].level)) {
2174 fprintf(stdout, "%s ", logtbl[j].name);
2175 }
2176 }
2177 fprintf(stdout, "\n");
2178 }
2179 return 0;
2180 } else {
2181 return 1;
2182 }
2183 }
2184
2185 static const struct {
2186 const char *name;
2187 int lim;
2188 } limlookup[] = {
2189 { "cpu", RLIMIT_CPU },
2190 { "filesize", RLIMIT_FSIZE },
2191 { "data", RLIMIT_DATA },
2192 { "stack", RLIMIT_STACK },
2193 { "core", RLIMIT_CORE },
2194 { "rss", RLIMIT_RSS },
2195 { "memlock", RLIMIT_MEMLOCK },
2196 { "maxproc", RLIMIT_NPROC },
2197 { "maxfiles", RLIMIT_NOFILE }
2198 };
2199
2200 static const size_t limlookupcnt = sizeof limlookup / sizeof limlookup[0];
2201
2202 ssize_t
2203 name2num(const char *n)
2204 {
2205 size_t i;
2206
2207 for (i = 0; i < limlookupcnt; i++) {
2208 if (!strcmp(limlookup[i].name, n)) {
2209 return limlookup[i].lim;
2210 }
2211 }
2212 return -1;
2213 }
2214
2215 const char *
2216 num2name(int n)
2217 {
2218 size_t i;
2219
2220 for (i = 0; i < limlookupcnt; i++) {
2221 if (limlookup[i].lim == n)
2222 return limlookup[i].name;
2223 }
2224 return NULL;
2225 }
2226
2227 const char *
2228 lim2str(rlim_t val, char *buf)
2229 {
2230 if (val == RLIM_INFINITY)
2231 strcpy(buf, "unlimited");
2232 else
2233 sprintf(buf, "%lld", val);
2234 return buf;
2235 }
2236
2237 bool
2238 str2lim(const char *buf, rlim_t *res)
2239 {
2240 char *endptr;
2241 *res = strtoll(buf, &endptr, 10);
2242 if (!strcmp(buf, "unlimited")) {
2243 *res = RLIM_INFINITY;
2244 return false;
2245 } else if (*endptr == '\0') {
2246 return false;
2247 }
2248 return true;
2249 }
2250
2251 int
2252 limit_cmd(int argc __attribute__((unused)), char *const argv[])
2253 {
2254 char slimstr[100];
2255 char hlimstr[100];
2256 struct rlimit *lmts = NULL;
2257 launch_data_t resp, resp1 = NULL, msg, tmp;
2258 int r = 0;
2259 size_t i, lsz = -1;
2260 ssize_t which = 0;
2261 rlim_t slim = -1, hlim = -1;
2262 bool badargs = false;
2263
2264 if (argc > 4)
2265 badargs = true;
2266
2267 if (argc >= 3 && str2lim(argv[2], &slim))
2268 badargs = true;
2269 else
2270 hlim = slim;
2271
2272 if (argc == 4 && str2lim(argv[3], &hlim))
2273 badargs = true;
2274
2275 if (argc >= 2 && -1 == (which = name2num(argv[1])))
2276 badargs = true;
2277
2278 if (badargs) {
2279 fprintf(stderr, "usage: %s %s [", getprogname(), argv[0]);
2280 for (i = 0; i < limlookupcnt; i++)
2281 fprintf(stderr, "%s %s", limlookup[i].name, (i + 1) == limlookupcnt ? "" : "| ");
2282 fprintf(stderr, "[both | soft hard]]\n");
2283 return 1;
2284 }
2285
2286 msg = launch_data_new_string(LAUNCH_KEY_GETRESOURCELIMITS);
2287 resp = launch_msg(msg);
2288 launch_data_free(msg);
2289
2290 if (resp == NULL) {
2291 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2292 return 1;
2293 } else if (launch_data_get_type(resp) == LAUNCH_DATA_OPAQUE) {
2294 lmts = launch_data_get_opaque(resp);
2295 lsz = launch_data_get_opaque_size(resp);
2296 if (argc <= 2) {
2297 for (i = 0; i < (lsz / sizeof(struct rlimit)); i++) {
2298 if (argc == 2 && (size_t)which != i)
2299 continue;
2300 fprintf(stdout, "\t%-12s%-15s%-15s\n", num2name(i),
2301 lim2str(lmts[i].rlim_cur, slimstr),
2302 lim2str(lmts[i].rlim_max, hlimstr));
2303 }
2304 }
2305 } else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) {
2306 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], launch_data_get_string(resp));
2307 r = 1;
2308 } else {
2309 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
2310 r = 1;
2311 }
2312
2313 if (argc <= 2 || r != 0) {
2314 launch_data_free(resp);
2315 return r;
2316 } else {
2317 resp1 = resp;
2318 }
2319
2320 lmts[which].rlim_cur = slim;
2321 lmts[which].rlim_max = hlim;
2322
2323 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2324 tmp = launch_data_new_opaque(lmts, lsz);
2325 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETRESOURCELIMITS);
2326 resp = launch_msg(msg);
2327 launch_data_free(msg);
2328
2329 if (resp == NULL) {
2330 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2331 return 1;
2332 } else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) {
2333 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], launch_data_get_string(resp));
2334 r = 1;
2335 } else if (launch_data_get_type(resp) != LAUNCH_DATA_OPAQUE) {
2336 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
2337 r = 1;
2338 }
2339
2340 launch_data_free(resp);
2341 launch_data_free(resp1);
2342
2343 return r;
2344 }
2345
2346 int
2347 umask_cmd(int argc, char *const argv[])
2348 {
2349 bool badargs = false;
2350 char *endptr;
2351 long m = 0;
2352 int64_t inval, outval;
2353
2354 if (argc == 2) {
2355 m = strtol(argv[1], &endptr, 8);
2356 if (*endptr != '\0' || m > 0777)
2357 badargs = true;
2358 }
2359
2360 if (argc > 2 || badargs) {
2361 fprintf(stderr, "usage: %s %s <mask>\n", getprogname(), argv[0]);
2362 return 1;
2363 }
2364
2365 inval = m;
2366
2367 if (vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_UMASK, argc == 2 ? &inval : NULL, &outval) == NULL) {
2368 if (argc == 1) {
2369 fprintf(stdout, "%o\n", (unsigned int)outval);
2370 }
2371 return 0;
2372 } else {
2373 return 1;
2374 }
2375 }
2376
2377 int
2378 submit_cmd(int argc, char *const argv[])
2379 {
2380 launch_data_t msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2381 launch_data_t job = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2382 launch_data_t resp, largv = launch_data_alloc(LAUNCH_DATA_ARRAY);
2383 int ch, i, r = 0;
2384
2385 launch_data_dict_insert(job, launch_data_new_bool(false), LAUNCH_JOBKEY_ONDEMAND);
2386
2387 while ((ch = getopt(argc, argv, "l:p:o:e:")) != -1) {
2388 switch (ch) {
2389 case 'l':
2390 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_LABEL);
2391 break;
2392 case 'p':
2393 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_PROGRAM);
2394 break;
2395 case 'o':
2396 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_STANDARDOUTPATH);
2397 break;
2398 case 'e':
2399 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_STANDARDERRORPATH);
2400 break;
2401 default:
2402 fprintf(stderr, "usage: %s submit ...\n", getprogname());
2403 return 1;
2404 }
2405 }
2406 argc -= optind;
2407 argv += optind;
2408
2409 for (i = 0; argv[i]; i++) {
2410 launch_data_array_append(largv, launch_data_new_string(argv[i]));
2411 }
2412
2413 launch_data_dict_insert(job, largv, LAUNCH_JOBKEY_PROGRAMARGUMENTS);
2414
2415 launch_data_dict_insert(msg, job, LAUNCH_KEY_SUBMITJOB);
2416
2417 resp = launch_msg(msg);
2418 launch_data_free(msg);
2419
2420 if (resp == NULL) {
2421 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2422 return 1;
2423 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
2424 errno = launch_data_get_errno(resp);
2425 if (errno) {
2426 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(errno));
2427 r = 1;
2428 }
2429 } else {
2430 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], "unknown response");
2431 }
2432
2433 launch_data_free(resp);
2434
2435 return r;
2436 }
2437
2438 int
2439 getrusage_cmd(int argc, char *const argv[])
2440 {
2441 launch_data_t resp, msg;
2442 bool badargs = false;
2443 int r = 0;
2444
2445 if (argc != 2)
2446 badargs = true;
2447 else if (strcmp(argv[1], "self") && strcmp(argv[1], "children"))
2448 badargs = true;
2449
2450 if (badargs) {
2451 fprintf(stderr, "usage: %s %s self | children\n", getprogname(), argv[0]);
2452 return 1;
2453 }
2454
2455 if (!strcmp(argv[1], "self")) {
2456 msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGESELF);
2457 } else {
2458 msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGECHILDREN);
2459 }
2460
2461 resp = launch_msg(msg);
2462 launch_data_free(msg);
2463
2464 if (resp == NULL) {
2465 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2466 return 1;
2467 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
2468 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(launch_data_get_errno(resp)));
2469 r = 1;
2470 } else if (launch_data_get_type(resp) == LAUNCH_DATA_OPAQUE) {
2471 struct rusage *rusage = launch_data_get_opaque(resp);
2472 fprintf(stdout, "\t%-10f\tuser time used\n",
2473 (double)rusage->ru_utime.tv_sec + (double)rusage->ru_utime.tv_usec / (double)1000000);
2474 fprintf(stdout, "\t%-10f\tsystem time used\n",
2475 (double)rusage->ru_stime.tv_sec + (double)rusage->ru_stime.tv_usec / (double)1000000);
2476 fprintf(stdout, "\t%-10ld\tmax resident set size\n", rusage->ru_maxrss);
2477 fprintf(stdout, "\t%-10ld\tshared text memory size\n", rusage->ru_ixrss);
2478 fprintf(stdout, "\t%-10ld\tunshared data size\n", rusage->ru_idrss);
2479 fprintf(stdout, "\t%-10ld\tunshared stack size\n", rusage->ru_isrss);
2480 fprintf(stdout, "\t%-10ld\tpage reclaims\n", rusage->ru_minflt);
2481 fprintf(stdout, "\t%-10ld\tpage faults\n", rusage->ru_majflt);
2482 fprintf(stdout, "\t%-10ld\tswaps\n", rusage->ru_nswap);
2483 fprintf(stdout, "\t%-10ld\tblock input operations\n", rusage->ru_inblock);
2484 fprintf(stdout, "\t%-10ld\tblock output operations\n", rusage->ru_oublock);
2485 fprintf(stdout, "\t%-10ld\tmessages sent\n", rusage->ru_msgsnd);
2486 fprintf(stdout, "\t%-10ld\tmessages received\n", rusage->ru_msgrcv);
2487 fprintf(stdout, "\t%-10ld\tsignals received\n", rusage->ru_nsignals);
2488 fprintf(stdout, "\t%-10ld\tvoluntary context switches\n", rusage->ru_nvcsw);
2489 fprintf(stdout, "\t%-10ld\tinvoluntary context switches\n", rusage->ru_nivcsw);
2490 } else {
2491 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
2492 r = 1;
2493 }
2494
2495 launch_data_free(resp);
2496
2497 return r;
2498 }
2499
2500 bool
2501 launch_data_array_append(launch_data_t a, launch_data_t o)
2502 {
2503 size_t offt = launch_data_array_get_count(a);
2504
2505 return launch_data_array_set_index(a, o, offt);
2506 }
2507
2508 mach_port_t
2509 str2bsport(const char *s)
2510 {
2511 bool getrootbs = strcmp(s, "/") == 0;
2512 mach_port_t last_bport, bport = bootstrap_port;
2513 task_t task = mach_task_self();
2514 kern_return_t result;
2515
2516 if (strcmp(s, "..") == 0 || getrootbs) {
2517 do {
2518 last_bport = bport;
2519 result = bootstrap_parent(last_bport, &bport);
2520
2521 if (result == BOOTSTRAP_NOT_PRIVILEGED) {
2522 fprintf(stderr, "Permission denied\n");
2523 return 1;
2524 } else if (result != BOOTSTRAP_SUCCESS) {
2525 fprintf(stderr, "bootstrap_parent() %d\n", result);
2526 return 1;
2527 }
2528 } while (getrootbs && last_bport != bport);
2529 } else {
2530 int pid = atoi(s);
2531
2532 result = task_for_pid(mach_task_self(), pid, &task);
2533
2534 if (result != KERN_SUCCESS) {
2535 fprintf(stderr, "task_for_pid() %s\n", mach_error_string(result));
2536 return 1;
2537 }
2538
2539 result = task_get_bootstrap_port(task, &bport);
2540
2541 if (result != KERN_SUCCESS) {
2542 fprintf(stderr, "Couldn't get bootstrap port: %s\n", mach_error_string(result));
2543 return 1;
2544 }
2545 }
2546
2547 return bport;
2548 }
2549
2550 int
2551 bsexec_cmd(int argc, char *const argv[])
2552 {
2553 kern_return_t result;
2554 mach_port_t bport;
2555
2556 if (argc < 3) {
2557 fprintf(stderr, "usage: %s bsexec <PID> prog...\n", getprogname());
2558 return 1;
2559 }
2560
2561 bport = str2bsport(argv[1]);
2562
2563 result = task_set_bootstrap_port(mach_task_self(), bport);
2564
2565 if (result != KERN_SUCCESS) {
2566 fprintf(stderr, "Couldn't switch to new bootstrap port: %s\n", mach_error_string(result));
2567 return 1;
2568 }
2569
2570 setgid(getgid());
2571 setuid(getuid());
2572
2573 if (fwexec((const char *const *)argv + 2, true) == -1) {
2574 fprintf(stderr, "%s bsexec failed: %s\n", getprogname(), strerror(errno));
2575 return 1;
2576 }
2577
2578 return 0;
2579 }
2580
2581 int
2582 bslist_cmd(int argc, char *const argv[])
2583 {
2584 kern_return_t result;
2585 mach_port_t bport = bootstrap_port;
2586 name_array_t service_names;
2587 mach_msg_type_number_t service_cnt, service_active_cnt;
2588 bootstrap_status_array_t service_actives;
2589 unsigned int i;
2590
2591 if (argc == 2)
2592 bport = str2bsport(argv[1]);
2593
2594 if (bport == MACH_PORT_NULL) {
2595 fprintf(stderr, "Invalid bootstrap port\n");
2596 return 1;
2597 }
2598
2599 result = bootstrap_info(bport, &service_names, &service_cnt, &service_actives, &service_active_cnt);
2600 if (result != BOOTSTRAP_SUCCESS) {
2601 fprintf(stderr, "bootstrap_info(): %d\n", result);
2602 return 1;
2603 }
2604
2605 #define bport_state(x) (((x) == BOOTSTRAP_STATUS_ACTIVE) ? "A" : ((x) == BOOTSTRAP_STATUS_ON_DEMAND) ? "D" : "I")
2606
2607 for (i = 0; i < service_cnt ; i++)
2608 fprintf(stdout, "%-3s%s\n", bport_state((service_actives[i])), service_names[i]);
2609
2610 return 0;
2611 }
2612
2613 bool
2614 is_legacy_mach_job(launch_data_t obj)
2615 {
2616 bool has_servicename = launch_data_dict_lookup(obj, MACHINIT_JOBKEY_SERVICENAME);
2617 bool has_command = launch_data_dict_lookup(obj, MACHINIT_JOBKEY_COMMAND);
2618 bool has_label = launch_data_dict_lookup(obj, LAUNCH_JOBKEY_LABEL);
2619
2620 return has_command && has_servicename && !has_label;
2621 }
2622
2623 void
2624 _log_launchctl_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test)
2625 {
2626 int saved_errno = errno;
2627 char buf[100];
2628 const char *file = strrchr(path, '/');
2629 char *rcs_rev_tmp = strchr(rcs_rev, ' ');
2630
2631 if (!file) {
2632 file = path;
2633 } else {
2634 file += 1;
2635 }
2636
2637 if (!rcs_rev_tmp) {
2638 strlcpy(buf, rcs_rev, sizeof(buf));
2639 } else {
2640 strlcpy(buf, rcs_rev_tmp + 1, sizeof(buf));
2641 rcs_rev_tmp = strchr(buf, ' ');
2642 if (rcs_rev_tmp)
2643 *rcs_rev_tmp = '\0';
2644 }
2645
2646 fprintf(stderr, "Bug: %s:%u (%s):%u: %s\n", file, line, buf, saved_errno, test);
2647 }
2648
2649 void
2650 loopback_setup_ipv4(void)
2651 {
2652 struct ifaliasreq ifra;
2653 struct ifreq ifr;
2654 int s;
2655
2656 memset(&ifr, 0, sizeof(ifr));
2657 strcpy(ifr.ifr_name, "lo0");
2658
2659 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
2660 return;
2661
2662 if (assumes(ioctl(s, SIOCGIFFLAGS, &ifr) != -1)) {
2663 ifr.ifr_flags |= IFF_UP;
2664 assumes(ioctl(s, SIOCSIFFLAGS, &ifr) != -1);
2665 }
2666
2667 memset(&ifra, 0, sizeof(ifra));
2668 strcpy(ifra.ifra_name, "lo0");
2669 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_family = AF_INET;
2670 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
2671 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_len = sizeof(struct sockaddr_in);
2672 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_family = AF_INET;
2673 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_addr.s_addr = htonl(IN_CLASSA_NET);
2674 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_len = sizeof(struct sockaddr_in);
2675
2676 assumes(ioctl(s, SIOCAIFADDR, &ifra) != -1);
2677
2678 assumes(close(s) == 0);
2679 }
2680
2681 void
2682 loopback_setup_ipv6(void)
2683 {
2684 struct in6_aliasreq ifra6;
2685 struct ifreq ifr;
2686 int s6;
2687
2688 memset(&ifr, 0, sizeof(ifr));
2689 strcpy(ifr.ifr_name, "lo0");
2690
2691 if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
2692 return;
2693
2694 memset(&ifr, 0, sizeof(ifr));
2695 strcpy(ifr.ifr_name, "lo0");
2696
2697 if (assumes(ioctl(s6, SIOCGIFFLAGS, &ifr) != -1)) {
2698 ifr.ifr_flags |= IFF_UP;
2699 assumes(ioctl(s6, SIOCSIFFLAGS, &ifr) != -1);
2700 }
2701
2702 memset(&ifra6, 0, sizeof(ifra6));
2703 strcpy(ifra6.ifra_name, "lo0");
2704
2705 ifra6.ifra_addr.sin6_family = AF_INET6;
2706 ifra6.ifra_addr.sin6_addr = in6addr_loopback;
2707 ifra6.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
2708 ifra6.ifra_prefixmask.sin6_family = AF_INET6;
2709 memset(&ifra6.ifra_prefixmask.sin6_addr, 0xff, sizeof(struct in6_addr));
2710 ifra6.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
2711 ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
2712 ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
2713
2714 assumes(ioctl(s6, SIOCAIFADDR_IN6, &ifra6) != -1);
2715
2716 assumes(close(s6) == 0);
2717 }
2718
2719 pid_t
2720 fwexec(const char *const *argv, bool _wait)
2721 {
2722 int wstatus;
2723 pid_t p;
2724
2725 switch ((p = fork())) {
2726 case -1:
2727 break;
2728 case 0:
2729 if (!_wait) {
2730 setsid();
2731 }
2732 execvp(argv[0], (char *const *)argv);
2733 _exit(EXIT_FAILURE);
2734 break;
2735 default:
2736 if (!_wait)
2737 return p;
2738 if (p == waitpid(p, &wstatus, 0)) {
2739 if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == EXIT_SUCCESS)
2740 return p;
2741 }
2742 break;
2743 }
2744
2745 return -1;
2746 }
2747
2748 void
2749 do_potential_fsck(void)
2750 {
2751 const char *safe_fsck_tool[] = { "fsck", "-fy", NULL };
2752 const char *fsck_tool[] = { "fsck", "-p", NULL };
2753 const char *remount_tool[] = { "mount", "-uw", "/", NULL };
2754 struct statfs sfs;
2755
2756 if (!assumes(statfs("/", &sfs) != -1)) {
2757 return;
2758 }
2759
2760 if (!(sfs.f_flags & MNT_RDONLY)) {
2761 return;
2762 }
2763
2764 if (!is_safeboot()) {
2765 #if 0
2766 /* We have disabled this block for now. We need to revisit this optimization after Leopard. */
2767 if (sfs.f_flags & MNT_JOURNALED) {
2768 goto out;
2769 }
2770 #endif
2771
2772 if (fwexec(fsck_tool, true) != -1) {
2773 goto out;
2774 }
2775 }
2776
2777 if (fwexec(safe_fsck_tool, true) != -1) {
2778 goto out;
2779 }
2780
2781 fprintf(stderr, "fsck failed!\n");
2782
2783 /* someday, we should keep booting read-only, but as of today, other sub-systems cannot handle that scenario */
2784 #if TARGET_OS_EMBEDDED
2785 const char *nvram_tool[] = { "/usr/sbin/nvram", "auto-boot=false", NULL };
2786 assumes(fwexec(nvram_tool, true) != -1);
2787 assumes(reboot(RB_AUTOBOOT) != -1);
2788 #else
2789 assumes(reboot(RB_HALT) != -1);
2790 #endif
2791
2792 return;
2793 out:
2794 /*
2795 * Once this is fixed:
2796 *
2797 * <rdar://problem/3948774> Mount flag updates should be possible with NULL as the forth argument to mount()
2798 *
2799 * We can then do this one system call instead of calling out a full blown process.
2800 *
2801 * assumes(mount(sfs.f_fstypename, "/", MNT_UPDATE, NULL) != -1);
2802 */
2803
2804 #if TARGET_OS_EMBEDDED
2805 if (path_check("/etc/fstab")) {
2806 const char *mount_tool[] = { "mount", "-vat", "nonfs", NULL };
2807 if (!assumes(fwexec(mount_tool, true) != -1)) {
2808 assumes(fwexec(nvram_tool, true) != -1);
2809 assumes(reboot(RB_AUTOBOOT) != -1);
2810 }
2811 } else
2812 #endif
2813 {
2814 assumes(fwexec(remount_tool, true) != -1);
2815 }
2816
2817 fix_bogus_file_metadata();
2818 }
2819
2820 void
2821 fix_bogus_file_metadata(void)
2822 {
2823 static const struct {
2824 const char *path;
2825 const uid_t owner;
2826 const gid_t group;
2827 const mode_t needed_bits;
2828 const mode_t bad_bits;
2829 } f[] = {
2830 { "/sbin/launchd", 0, 0, S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH, S_ISUID|S_ISGID|S_ISVTX|S_IWOTH },
2831 { _PATH_TMP, 0, 0, S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO, S_ISUID|S_ISGID },
2832 { _PATH_VARTMP, 0, 0, S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO, S_ISUID|S_ISGID },
2833 };
2834 struct stat sb;
2835 size_t i;
2836
2837 for (i = 0; i < (sizeof(f) / sizeof(f[0])); i++) {
2838 mode_t i_needed_bits;
2839 mode_t i_bad_bits;
2840 bool fix_mode = false;
2841 bool fix_id = false;
2842
2843 if (!assumes(stat(f[i].path, &sb) != -1)) {
2844 continue;
2845 }
2846
2847 i_needed_bits = ~sb.st_mode & f[i].needed_bits;
2848 i_bad_bits = sb.st_mode & f[i].bad_bits;
2849
2850 if (i_bad_bits) {
2851 fprintf(stderr, "Crucial filesystem check: Removing bogus mode bits 0%o on path: %s\n", i_bad_bits, f[i].path);
2852 fix_mode = true;
2853 }
2854 if (i_needed_bits) {
2855 fprintf(stderr, "Crucial filesystem check: Adding missing mode bits 0%o on path: %s\n", i_needed_bits, f[i].path);
2856 fix_mode = true;
2857 }
2858 if (sb.st_uid != f[i].owner) {
2859 fprintf(stderr, "Crucial filesystem check: Fixing bogus UID %u on path: %s\n", sb.st_uid, f[i].path);
2860 fix_id = true;
2861 }
2862 if (sb.st_gid != f[i].group) {
2863 fprintf(stderr, "Crucial filesystem check: Fixing bogus GID %u on path: %s\n", sb.st_gid, f[i].path);
2864 fix_id = true;
2865 }
2866
2867 if (fix_mode) {
2868 assumes(chmod(f[i].path, (sb.st_mode & ~i_bad_bits) | i_needed_bits) != -1);
2869 }
2870 if (fix_id) {
2871 assumes(chown(f[i].path, f[i].owner, f[i].group) != -1);
2872 }
2873 }
2874 }
2875
2876 bool
2877 path_check(const char *path)
2878 {
2879 struct stat sb;
2880
2881 if (stat(path, &sb) == 0)
2882 return true;
2883 return false;
2884 }
2885
2886 bool
2887 is_safeboot(void)
2888 {
2889 int sbmib[] = { CTL_KERN, KERN_SAFEBOOT };
2890 uint32_t sb = 0;
2891 size_t sbsz = sizeof(sb);
2892
2893 if (!assumes(sysctl(sbmib, 2, &sb, &sbsz, NULL, 0) == 0))
2894 return false;
2895
2896 return (bool)sb;
2897 }
2898
2899 bool
2900 is_netboot(void)
2901 {
2902 int nbmib[] = { CTL_KERN, KERN_NETBOOT };
2903 uint32_t nb = 0;
2904 size_t nbsz = sizeof(nb);
2905
2906 if (!assumes(sysctl(nbmib, 2, &nb, &nbsz, NULL, 0) == 0))
2907 return false;
2908
2909 return (bool)nb;
2910 }
2911
2912 void
2913 empty_dir(const char *thedir, struct stat *psb)
2914 {
2915 struct dirent *de;
2916 struct stat psb2;
2917 DIR *od;
2918 int currend_dir_fd;
2919
2920 if (!psb) {
2921 psb = &psb2;
2922 if (!assumes(lstat(thedir, psb) != -1)) {
2923 return;
2924 }
2925 }
2926
2927 if (!assumes((currend_dir_fd = open(".", 0)) != -1)) {
2928 return;
2929 }
2930
2931 if (!assumes(chdir(thedir) != -1)) {
2932 goto out;
2933 }
2934
2935 if (!assumes(od = opendir("."))) {
2936 goto out;
2937 }
2938
2939 while ((de = readdir(od))) {
2940 struct stat sb;
2941
2942 if (strcmp(de->d_name, ".") == 0) {
2943 continue;
2944 }
2945
2946 if (strcmp(de->d_name, "..") == 0) {
2947 continue;
2948 }
2949
2950 if (!assumes(lstat(de->d_name, &sb) != -1)) {
2951 continue;
2952 }
2953
2954 if (psb->st_dev != sb.st_dev) {
2955 assumes(unmount(de->d_name, MNT_FORCE) != -1);
2956
2957 /* Let's lstat() again to see if the unmount() worked and what was under it */
2958 if (!assumes(lstat(de->d_name, &sb) != -1)) {
2959 continue;
2960 }
2961
2962 if (!assumes(psb->st_dev == sb.st_dev)) {
2963 continue;
2964 }
2965 }
2966
2967 if (S_ISDIR(sb.st_mode)) {
2968 empty_dir(de->d_name, &sb);
2969 }
2970
2971 assumes(lchflags(de->d_name, 0) != -1);
2972 assumes(remove(de->d_name) != -1);
2973 }
2974
2975 assumes(closedir(od) != -1);
2976
2977 out:
2978 assumes(fchdir(currend_dir_fd) != -1);
2979 assumes(close(currend_dir_fd) != -1);
2980 }
2981
2982 int
2983 touch_file(const char *path, mode_t m)
2984 {
2985 int fd = open(path, O_CREAT, m);
2986
2987 if (fd == -1)
2988 return -1;
2989
2990 return close(fd);
2991 }
2992
2993 void
2994 apply_sysctls_from_file(const char *thefile)
2995 {
2996 const char *sysctl_tool[] = { "sysctl", "-w", NULL, NULL };
2997 size_t ln_len = 0;
2998 char *val, *tmpstr;
2999 FILE *sf;
3000
3001 if (!(sf = fopen(thefile, "r")))
3002 return;
3003
3004 while ((val = fgetln(sf, &ln_len))) {
3005 if (ln_len == 0) {
3006 continue;
3007 }
3008 if (!assumes((tmpstr = malloc(ln_len + 1)) != NULL)) {
3009 continue;
3010 }
3011 memcpy(tmpstr, val, ln_len);
3012 tmpstr[ln_len] = 0;
3013 val = tmpstr;
3014
3015 if (val[ln_len - 1] == '\n' || val[ln_len - 1] == '\r') {
3016 val[ln_len - 1] = '\0';
3017 }
3018
3019 while (*val && isspace(*val))
3020 val++;
3021 if (*val == '\0' || *val == '#') {
3022 goto skip_sysctl_tool;
3023 }
3024 sysctl_tool[2] = val;
3025 assumes(fwexec(sysctl_tool, true) != -1);
3026 skip_sysctl_tool:
3027 free(tmpstr);
3028 }
3029
3030 assumes(fclose(sf) == 0);
3031 }
3032
3033 void
3034 do_sysversion_sysctl(void)
3035 {
3036 int mib[] = { CTL_KERN, KERN_OSVERSION };
3037 CFDictionaryRef versdict;
3038 CFStringRef buildvers;
3039 char buf[1024];
3040 size_t bufsz = sizeof(buf);
3041
3042 /* <rdar://problem/4477682> ER: launchd should set kern.osversion very early in boot */
3043
3044 if (sysctl(mib, 2, buf, &bufsz, NULL, 0) == -1) {
3045 fprintf(stderr, "sysctl(): %s\n", strerror(errno));
3046 return;
3047 }
3048
3049 if (buf[0] != '\0') {
3050 return;
3051 }
3052
3053 if (!assumes((versdict = _CFCopySystemVersionDictionary()))) {
3054 return;
3055 }
3056
3057 if (assumes((buildvers = CFDictionaryGetValue(versdict, _kCFSystemVersionBuildVersionKey)))) {
3058 CFStringGetCString(buildvers, buf, sizeof(buf), kCFStringEncodingUTF8);
3059
3060 assumes(sysctl(mib, 2, NULL, 0, buf, strlen(buf) + 1) != -1);
3061 }
3062
3063 CFRelease(versdict);
3064 }
3065
3066 void
3067 do_application_firewall_magic(int sfd, launch_data_t thejob)
3068 {
3069 const char *prog = NULL, *partialprog = NULL;
3070 char *path, *pathtmp, **pathstmp;
3071 char *paths[100];
3072 launch_data_t tmp;
3073
3074 /*
3075 * Sigh...
3076 * <rdar://problem/4684434> setsockopt() with the executable path as the argument
3077 */
3078
3079 if ((tmp = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_PROGRAM))) {
3080 prog = launch_data_get_string(tmp);
3081 }
3082
3083 if (!prog) {
3084 if ((tmp = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_PROGRAMARGUMENTS))) {
3085 if ((tmp = launch_data_array_get_index(tmp, 0))) {
3086 if (assumes((partialprog = launch_data_get_string(tmp)) != NULL)) {
3087 if (partialprog[0] == '/') {
3088 prog = partialprog;
3089 }
3090 }
3091 }
3092 }
3093 }
3094
3095 if (!prog) {
3096 pathtmp = path = strdup(getenv("PATH"));
3097
3098 pathstmp = paths;
3099
3100 while ((*pathstmp = strsep(&pathtmp, ":"))) {
3101 if (**pathstmp != '\0') {
3102 pathstmp++;
3103 }
3104 }
3105
3106 free(path);
3107 pathtmp = alloca(MAXPATHLEN);
3108
3109 pathstmp = paths;
3110
3111 for (; *pathstmp; pathstmp++) {
3112 snprintf(pathtmp, MAXPATHLEN, "%s/%s", *pathstmp, partialprog);
3113 if (path_check(pathtmp)) {
3114 prog = pathtmp;
3115 break;
3116 }
3117 }
3118 }
3119
3120 if (assumes(prog != NULL)) {
3121 /* The networking team has asked us to ignore the failure of this API if errno == ENOPROTOOPT */
3122 assumes(setsockopt(sfd, SOL_SOCKET, SO_EXECPATH, prog, strlen(prog) + 1) != -1 || errno == ENOPROTOOPT);
3123 }
3124 }
3125
3126
3127 void
3128 preheat_page_cache_hack(void)
3129 {
3130 struct dirent *de;
3131 DIR *thedir;
3132
3133 /* Disable this hack for now */
3134 return;
3135
3136 if ((thedir = opendir("/etc/preheat_at_boot")) == NULL) {
3137 return;
3138 }
3139
3140 while ((de = readdir(thedir))) {
3141 struct stat sb;
3142 void *junkbuf;
3143 int fd;
3144
3145 if (de->d_name[0] == '.') {
3146 continue;
3147 }
3148
3149 if ((fd = open(de->d_name, O_RDONLY)) == -1) {
3150 continue;
3151 }
3152
3153 if (fstat(fd, &sb) != -1) {
3154 if ((sb.st_size < 10*1024*1024) && (junkbuf = malloc((size_t)sb.st_size)) != NULL) {
3155 assumes(read(fd, junkbuf, (size_t)sb.st_size) == (ssize_t)sb.st_size);
3156 free(junkbuf);
3157 }
3158 }
3159
3160 close(fd);
3161 }
3162
3163 closedir(thedir);
3164 }
3165
3166
3167 void
3168 do_bootroot_magic(void)
3169 {
3170 const char *kextcache_tool[] = { "kextcache", "-U", "/", NULL };
3171 CFTypeRef bootrootProp;
3172 io_service_t chosen;
3173 int wstatus;
3174 pid_t p;
3175
3176 chosen = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/chosen");
3177
3178 if (!assumes(chosen)) {
3179 return;
3180 }
3181
3182 bootrootProp = IORegistryEntryCreateCFProperty(chosen, CFSTR(kBootRootActiveKey), kCFAllocatorDefault, 0);
3183
3184 IOObjectRelease(chosen);
3185
3186 if (!bootrootProp) {
3187 return;
3188 }
3189
3190 CFRelease(bootrootProp);
3191
3192 if (!assumes((p = fwexec(kextcache_tool, false)) != -1)) {
3193 return;
3194 }
3195
3196 if (!assumes(waitpid(p, &wstatus, 0) != -1)) {
3197 return;
3198 }
3199
3200 if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == EX_OSFILE) {
3201 assumes(reboot(RB_AUTOBOOT) != -1);
3202 }
3203 }