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