]> git.saurik.com Git - apple/launchd.git/blob - launchd/src/launchctl.c
a3b78d2fadd4bb5327edf274c91be49eb1005024
[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: 1.88 $";
22
23 #include <CoreFoundation/CoreFoundation.h>
24 #include <CoreFoundation/CFPriv.h>
25 #include <NSSystemDirectories.h>
26 #include <mach/mach.h>
27 #include <sys/types.h>
28 #include <sys/sysctl.h>
29 #include <sys/time.h>
30 #include <sys/sysctl.h>
31 #include <sys/stat.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34 #include <sys/fcntl.h>
35 #include <sys/event.h>
36 #include <sys/resource.h>
37 #include <sys/param.h>
38 #include <sys/mount.h>
39 #include <sys/reboot.h>
40 #include <net/if.h>
41 #include <netinet/in.h>
42 #include <netinet/in_var.h>
43 #include <netinet6/nd6.h>
44 #include <unistd.h>
45 #include <dirent.h>
46 #include <libgen.h>
47 #include <pwd.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <pwd.h>
51 #include <grp.h>
52 #include <netdb.h>
53 #include <syslog.h>
54 #include <glob.h>
55 #include <readline/readline.h>
56 #include <readline/history.h>
57 #include <dns_sd.h>
58 #include <paths.h>
59 #include <utmp.h>
60 #include <utmpx.h>
61
62 #include "bootstrap_public.h"
63 #include "bootstrap_private.h"
64 #include "launch.h"
65 #include "launch_priv.h"
66
67 #define LAUNCH_SECDIR "/tmp/launch-XXXXXX"
68
69 #define MACHINIT_JOBKEY_ONDEMAND "OnDemand"
70 #define MACHINIT_JOBKEY_SERVICENAME "ServiceName"
71 #define MACHINIT_JOBKEY_COMMAND "Command"
72 #define MACHINIT_JOBKEY_SERVERPORT "ServerPort"
73 #define MACHINIT_JOBKEY_SERVICEPORT "ServicePort"
74
75 #define assumes(e) \
76 (__builtin_expect(!(e), 0) ? _log_launchctl_bug(__rcs_file_version__, __FILE__, __LINE__, #e), false : true)
77
78
79 struct load_unload_state {
80 launch_data_t pass0;
81 launch_data_t pass1;
82 launch_data_t pass2;
83 char *session_type;
84 unsigned int editondisk:1, load:1, forceload:1, __pad:29;
85 };
86
87 static void myCFDictionaryApplyFunction(const void *key, const void *value, void *context);
88 static bool launch_data_array_append(launch_data_t a, launch_data_t o);
89 static void distill_jobs(launch_data_t);
90 static void distill_config_file(launch_data_t);
91 static void sock_dict_cb(launch_data_t what, const char *key, void *context);
92 static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob);
93 static launch_data_t CF2launch_data(CFTypeRef);
94 static launch_data_t read_plist_file(const char *file, bool editondisk, bool load);
95 static CFPropertyListRef CreateMyPropertyListFromFile(const char *);
96 static void WriteMyPropertyListToFile(CFPropertyListRef, const char *);
97 static bool path_goodness_check(const char *path, bool forceload);
98 static void readpath(const char *, struct load_unload_state *);
99 static void readfile(const char *, struct load_unload_state *);
100 static int _fd(int);
101 static int demux_cmd(int argc, char *const argv[]);
102 static launch_data_t do_rendezvous_magic(const struct addrinfo *res, const char *serv);
103 static void submit_job_pass(launch_data_t jobs);
104 static void submit_mach_jobs(launch_data_t jobs);
105 static void let_go_of_mach_jobs(launch_data_t jobs);
106 static void do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup);
107 static mach_port_t str2bsport(const char *s);
108 static void print_jobs(launch_data_t j);
109 static void print_obj(launch_data_t obj, const char *key, void *context);
110 static bool is_legacy_mach_job(launch_data_t obj);
111 static bool delay_to_second_pass(launch_data_t o);
112 static void delay_to_second_pass2(launch_data_t o, const char *key, void *context);
113 static bool str2lim(const char *buf, rlim_t *res);
114 static const char *lim2str(rlim_t val, char *buf);
115 static const char *num2name(int n);
116 static ssize_t name2num(const char *n);
117 static void unloadjob(launch_data_t job);
118 static void print_key_value(launch_data_t obj, const char *key, void *context);
119 static void print_launchd_env(launch_data_t obj, const char *key, void *context);
120 static void _log_launchctl_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test);
121 static void loopback_setup_ipv4(void);
122 static void loopback_setup_ipv6(void);
123 static pid_t fwexec(const char *const *argv, bool _wait);
124 static void do_potential_fsck(void);
125 static bool path_check(const char *path);
126 static bool is_safeboot(void);
127 static bool is_netboot(void);
128 static void apply_func_to_dir(const char *thedir, void (*thefunc)(const char *));
129 static void apply_sysctls_from_file(const char *thefile);
130 static void empty_dir(const char *path);
131 static int touch_file(const char *path, mode_t m);
132 static void do_sysversion_sysctl(void);
133 static void workaround4465949(void);
134
135 static int bootstrap_cmd(int argc, char *const argv[]);
136 static int load_and_unload_cmd(int argc, char *const argv[]);
137 //static int reload_cmd(int argc, char *const argv[]);
138 static int start_stop_remove_cmd(int argc, char *const argv[]);
139 static int submit_cmd(int argc, char *const argv[]);
140 static int list_cmd(int argc, char *const argv[]);
141
142 static int setenv_cmd(int argc, char *const argv[]);
143 static int unsetenv_cmd(int argc, char *const argv[]);
144 static int getenv_and_export_cmd(int argc, char *const argv[]);
145
146 static int limit_cmd(int argc, char *const argv[]);
147 static int stdio_cmd(int argc, char *const argv[]);
148 static int fyi_cmd(int argc, char *const argv[]);
149 static int logupdate_cmd(int argc, char *const argv[]);
150 static int umask_cmd(int argc, char *const argv[]);
151 static int getrusage_cmd(int argc, char *const argv[]);
152 static int bsexec_cmd(int argc, char *const argv[]);
153 static int bslist_cmd(int argc, char *const argv[]);
154
155 static int exit_cmd(int argc, char *const argv[]) __attribute__((noreturn));
156 static int help_cmd(int argc, char *const argv[]);
157
158 static const struct {
159 const char *name;
160 int (*func)(int argc, char *const argv[]);
161 const char *desc;
162 } cmds[] = {
163 { "load", load_and_unload_cmd, "Load configuration files and/or directories" },
164 { "unload", load_and_unload_cmd, "Unload configuration files and/or directories" },
165 // { "reload", reload_cmd, "Reload configuration files and/or directories" },
166 { "start", start_stop_remove_cmd, "Start specified job" },
167 { "stop", start_stop_remove_cmd, "Stop specified job" },
168 { "submit", submit_cmd, "Submit a job from the command line" },
169 { "remove", start_stop_remove_cmd, "Remove specified job" },
170 { "bootstrap", bootstrap_cmd, "Bootstrap launchd" },
171 { "list", list_cmd, "List jobs and information about jobs" },
172 { "setenv", setenv_cmd, "Set an environmental variable in launchd" },
173 { "unsetenv", unsetenv_cmd, "Unset an environmental variable in launchd" },
174 { "getenv", getenv_and_export_cmd, "Get an environmental variable from launchd" },
175 { "export", getenv_and_export_cmd, "Export shell settings from launchd" },
176 { "limit", limit_cmd, "View and adjust launchd resource limits" },
177 { "stdout", stdio_cmd, "Redirect launchd's standard out to the given path" },
178 { "stderr", stdio_cmd, "Redirect launchd's standard error to the given path" },
179 { "shutdown", fyi_cmd, "Prepare for system shutdown" },
180 { "singleuser", fyi_cmd, "Switch to single-user mode" },
181 { "reloadttys", fyi_cmd, "Reload /etc/ttys" },
182 { "getrusage", getrusage_cmd, "Get resource usage statistics from launchd" },
183 { "log", logupdate_cmd, "Adjust the logging level or mask of launchd" },
184 { "umask", umask_cmd, "Change launchd's umask" },
185 { "bsexec", bsexec_cmd, "Execute a process within a different Mach bootstrap subset" },
186 { "bslist", bslist_cmd, "List Mach bootstrap services and optional servers" },
187 { "exit", exit_cmd, "Exit the interactive invocation of launchctl" },
188 { "quit", exit_cmd, "Quit the interactive invocation of launchctl" },
189 { "help", help_cmd, "This help output" },
190 };
191
192 static bool istty = false;
193 static bool verbose = false;
194
195 int
196 main(int argc, char *const argv[])
197 {
198 char *l;
199
200 do_sysversion_sysctl();
201
202 istty = isatty(STDIN_FILENO);
203
204 argc--, argv++;
205
206 if (argc > 0 && argv[0][0] == '-') {
207 char *flago;
208
209 for (flago = argv[0] + 1; *flago; flago++) {
210 switch (*flago) {
211 case 'v':
212 verbose = true;
213 break;
214 default:
215 fprintf(stderr, "Unknown argument: '-%c'\n", *flago);
216 break;
217 }
218 }
219 argc--, argv++;
220 }
221
222 if (NULL == readline) {
223 fprintf(stderr, "missing library: readline\n");
224 exit(EXIT_FAILURE);
225 }
226
227 if (argc == 0) {
228 while ((l = readline(istty ? "launchd% " : NULL))) {
229 char *inputstring = l, *argv2[100], **ap = argv2;
230 int i = 0;
231
232 while ((*ap = strsep(&inputstring, " \t"))) {
233 if (**ap != '\0') {
234 ap++;
235 i++;
236 }
237 }
238
239 if (i > 0)
240 demux_cmd(i, argv2);
241
242 free(l);
243 }
244
245 if (istty) {
246 fputc('\n', stdout);
247 }
248 }
249
250 if (argc > 0) {
251 exit(demux_cmd(argc, argv));
252 }
253
254 exit(EXIT_SUCCESS);
255 }
256
257 int
258 demux_cmd(int argc, char *const argv[])
259 {
260 size_t i;
261
262 optind = 1;
263 optreset = 1;
264
265 for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) {
266 if (!strcmp(cmds[i].name, argv[0]))
267 return cmds[i].func(argc, argv);
268 }
269
270 fprintf(stderr, "%s: unknown subcommand \"%s\"\n", getprogname(), argv[0]);
271 return 1;
272 }
273
274 int
275 unsetenv_cmd(int argc, char *const argv[])
276 {
277 launch_data_t resp, tmp, msg;
278
279 if (argc != 2) {
280 fprintf(stderr, "%s usage: unsetenv <key>\n", getprogname());
281 return 1;
282 }
283
284 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
285
286 tmp = launch_data_new_string(argv[1]);
287 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_UNSETUSERENVIRONMENT);
288
289 resp = launch_msg(msg);
290
291 launch_data_free(msg);
292
293 if (resp) {
294 launch_data_free(resp);
295 } else {
296 fprintf(stderr, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_UNSETUSERENVIRONMENT, strerror(errno));
297 }
298
299 return 0;
300 }
301
302 int
303 setenv_cmd(int argc, char *const argv[])
304 {
305 launch_data_t resp, tmp, tmpv, msg;
306
307 if (argc != 3) {
308 fprintf(stderr, "%s usage: setenv <key> <value>\n", getprogname());
309 return 1;
310 }
311
312 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
313 tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
314
315 tmpv = launch_data_new_string(argv[2]);
316 launch_data_dict_insert(tmp, tmpv, argv[1]);
317 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETUSERENVIRONMENT);
318
319 resp = launch_msg(msg);
320 launch_data_free(msg);
321
322 if (resp) {
323 launch_data_free(resp);
324 } else {
325 fprintf(stderr, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_SETUSERENVIRONMENT, strerror(errno));
326 }
327
328 return 0;
329 }
330
331 void
332 print_launchd_env(launch_data_t obj, const char *key, void *context)
333 {
334 bool *is_csh = context;
335
336 /* XXX escape the double quotes */
337 if (*is_csh)
338 fprintf(stdout, "setenv %s \"%s\";\n", key, launch_data_get_string(obj));
339 else
340 fprintf(stdout, "%s=\"%s\"; export %s;\n", key, launch_data_get_string(obj), key);
341 }
342
343 void
344 print_key_value(launch_data_t obj, const char *key, void *context)
345 {
346 const char *k = context;
347
348 if (!strcmp(key, k))
349 fprintf(stdout, "%s\n", launch_data_get_string(obj));
350 }
351
352 int
353 getenv_and_export_cmd(int argc, char *const argv[] __attribute__((unused)))
354 {
355 launch_data_t resp, msg;
356 bool is_csh = false;
357 char *k;
358
359 if (!strcmp(argv[0], "export")) {
360 char *s = getenv("SHELL");
361 if (s)
362 is_csh = strstr(s, "csh") ? true : false;
363 } else if (argc != 2) {
364 fprintf(stderr, "%s usage: getenv <key>\n", getprogname());
365 return 1;
366 }
367
368 k = argv[1];
369
370 msg = launch_data_new_string(LAUNCH_KEY_GETUSERENVIRONMENT);
371
372 resp = launch_msg(msg);
373 launch_data_free(msg);
374
375 if (resp) {
376 if (!strcmp(argv[0], "export"))
377 launch_data_dict_iterate(resp, print_launchd_env, &is_csh);
378 else
379 launch_data_dict_iterate(resp, print_key_value, k);
380 launch_data_free(resp);
381 } else {
382 fprintf(stderr, "launch_msg(\"" LAUNCH_KEY_GETUSERENVIRONMENT "\"): %s\n", strerror(errno));
383 }
384 return 0;
385 }
386
387 void
388 unloadjob(launch_data_t job)
389 {
390 launch_data_t resp, tmp, tmps, msg;
391 int e;
392
393 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
394 tmp = launch_data_alloc(LAUNCH_DATA_STRING);
395 tmps = launch_data_dict_lookup(job, LAUNCH_JOBKEY_LABEL);
396
397 if (!tmps) {
398 fprintf(stderr, "%s: Error: Missing Key: %s\n", getprogname(), LAUNCH_JOBKEY_LABEL);
399 return;
400 }
401
402 launch_data_set_string(tmp, launch_data_get_string(tmps));
403 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_REMOVEJOB);
404 resp = launch_msg(msg);
405 launch_data_free(msg);
406 if (!resp) {
407 fprintf(stderr, "%s: Error: launch_msg(): %s\n", getprogname(), strerror(errno));
408 return;
409 }
410 if (LAUNCH_DATA_ERRNO == launch_data_get_type(resp)) {
411 if ((e = launch_data_get_errno(resp)))
412 fprintf(stderr, "%s\n", strerror(e));
413 }
414 launch_data_free(resp);
415 }
416
417 launch_data_t
418 read_plist_file(const char *file, bool editondisk, bool load)
419 {
420 CFPropertyListRef plist = CreateMyPropertyListFromFile(file);
421 launch_data_t r = NULL;
422
423 if (NULL == plist) {
424 fprintf(stderr, "%s: no plist was returned for: %s\n", getprogname(), file);
425 return NULL;
426 }
427
428 if (editondisk) {
429 if (load)
430 CFDictionaryRemoveValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED));
431 else
432 CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED), kCFBooleanTrue);
433 WriteMyPropertyListToFile(plist, file);
434 }
435
436 r = CF2launch_data(plist);
437
438 CFRelease(plist);
439
440 return r;
441 }
442
443 void
444 delay_to_second_pass2(launch_data_t o, const char *key, void *context)
445 {
446 bool *res = context;
447 size_t i;
448
449 if (key && 0 == strcmp(key, LAUNCH_JOBSOCKETKEY_BONJOUR)) {
450 *res = true;
451 return;
452 }
453
454 switch (launch_data_get_type(o)) {
455 case LAUNCH_DATA_DICTIONARY:
456 launch_data_dict_iterate(o, delay_to_second_pass2, context);
457 break;
458 case LAUNCH_DATA_ARRAY:
459 for (i = 0; i < launch_data_array_get_count(o); i++)
460 delay_to_second_pass2(launch_data_array_get_index(o, i), NULL, context);
461 break;
462 default:
463 break;
464 }
465 }
466
467 bool
468 delay_to_second_pass(launch_data_t o)
469 {
470 bool res = false;
471
472 launch_data_t socks = launch_data_dict_lookup(o, LAUNCH_JOBKEY_SOCKETS);
473
474 if (NULL == socks)
475 return false;
476
477 delay_to_second_pass2(socks, NULL, &res);
478
479 return res;
480 }
481
482 void
483 readfile(const char *what, struct load_unload_state *lus)
484 {
485 char ourhostname[1024];
486 launch_data_t tmpd, tmps, thejob, tmpa;
487 bool job_disabled = false;
488 size_t i, c;
489
490 gethostname(ourhostname, sizeof(ourhostname));
491
492 if (NULL == (thejob = read_plist_file(what, lus->editondisk, lus->load))) {
493 fprintf(stderr, "%s: no plist was returned for: %s\n", getprogname(), what);
494 return;
495 }
496
497 if (is_legacy_mach_job(thejob)) {
498 fprintf(stderr, "%s: Please convert the following to launchd: %s\n", getprogname(), what);
499 launch_data_array_append(lus->pass0, thejob);
500 return;
501 }
502
503 if (NULL == launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LABEL)) {
504 fprintf(stderr, "%s: missing the Label key: %s\n", getprogname(), what);
505 goto out_bad;
506 }
507
508 if (NULL != (tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADFROMHOSTS))) {
509 c = launch_data_array_get_count(tmpa);
510
511 for (i = 0; i < c; i++) {
512 launch_data_t oai = launch_data_array_get_index(tmpa, i);
513 if (!strcasecmp(ourhostname, launch_data_get_string(oai)))
514 goto out_bad;
515 }
516 }
517
518 if (NULL != (tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOHOSTS))) {
519 c = launch_data_array_get_count(tmpa);
520
521 for (i = 0; i < c; i++) {
522 launch_data_t oai = launch_data_array_get_index(tmpa, i);
523 if (!strcasecmp(ourhostname, launch_data_get_string(oai)))
524 break;
525 }
526
527 if (i == c)
528 goto out_bad;
529 }
530
531 if ((tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE))) {
532 const char *allowed_session;
533 bool skipjob = true;
534
535 if (lus->session_type) switch (launch_data_get_type(tmpa)) {
536 case LAUNCH_DATA_ARRAY:
537 c = launch_data_array_get_count(tmpa);
538 for (i = 0; i < c; i++) {
539 tmps = launch_data_array_get_index(tmpa, i);
540 allowed_session = launch_data_get_string(tmps);
541 if (strcasecmp(lus->session_type, allowed_session) == 0) {
542 skipjob = false;
543 break;
544 }
545 }
546 break;
547 case LAUNCH_DATA_STRING:
548 allowed_session = launch_data_get_string(tmpa);
549 if (strcasecmp(lus->session_type, allowed_session) == 0) {
550 skipjob = false;
551 }
552 break;
553 default:
554 break;
555 }
556
557 if (skipjob) {
558 goto out_bad;
559 }
560 }
561
562 if ((tmpd = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_DISABLED)))
563 job_disabled = launch_data_get_bool(tmpd);
564
565 if (lus->forceload)
566 job_disabled = false;
567
568 if (job_disabled && lus->load)
569 goto out_bad;
570
571 if (delay_to_second_pass(thejob))
572 launch_data_array_append(lus->pass2, thejob);
573 else
574 launch_data_array_append(lus->pass1, thejob);
575
576 if (verbose)
577 fprintf(stdout, "Will load: %s\n", what);
578
579 return;
580 out_bad:
581 if (verbose)
582 fprintf(stdout, "Ignored: %s\n", what);
583 launch_data_free(thejob);
584 }
585
586 bool
587 path_goodness_check(const char *path, bool forceload)
588 {
589 struct stat sb;
590
591 if (stat(path, &sb) == -1) {
592 fprintf(stderr, "%s: Couldn't stat(\"%s\"): %s\n", getprogname(), path, strerror(errno));
593 return false;
594 }
595
596 if (forceload)
597 return true;
598
599 if (sb.st_mode & (S_IWOTH|S_IWGRP)) {
600 fprintf(stderr, "%s: Dubious permissions on file (skipping): %s\n", getprogname(), path);
601 return false;
602 }
603
604 if (sb.st_uid != 0 && sb.st_uid != getuid()) {
605 fprintf(stderr, "%s: Dubious ownership on file (skipping): %s\n", getprogname(), path);
606 return false;
607 }
608
609 if (!(S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))) {
610 fprintf(stderr, "%s: Dubious path. Not a regular file or directory (skipping): %s\n", getprogname(), path);
611 return false;
612 }
613
614 return true;
615 }
616
617 void
618 readpath(const char *what, struct load_unload_state *lus)
619 {
620 char buf[MAXPATHLEN];
621 struct stat sb;
622 struct dirent *de;
623 DIR *d;
624
625 if (!path_goodness_check(what, lus->forceload))
626 return;
627
628 if (stat(what, &sb) == -1)
629 return;
630
631 if (S_ISREG(sb.st_mode)) {
632 readfile(what, lus);
633 } else if (S_ISDIR(sb.st_mode)) {
634 if ((d = opendir(what)) == NULL) {
635 fprintf(stderr, "%s: opendir() failed to open the directory\n", getprogname());
636 return;
637 }
638
639 while ((de = readdir(d))) {
640 if ((de->d_name[0] == '.'))
641 continue;
642 snprintf(buf, sizeof(buf), "%s/%s", what, de->d_name);
643
644 if (!path_goodness_check(buf, lus->forceload))
645 continue;
646
647 readfile(buf, lus);
648 }
649 closedir(d);
650 }
651 }
652
653 struct distill_context {
654 launch_data_t base;
655 launch_data_t newsockdict;
656 };
657
658 void
659 distill_jobs(launch_data_t jobs)
660 {
661 size_t i, c = launch_data_array_get_count(jobs);
662
663 for (i = 0; i < c; i++)
664 distill_config_file(launch_data_array_get_index(jobs, i));
665 }
666
667 void
668 distill_config_file(launch_data_t id_plist)
669 {
670 struct distill_context dc = { id_plist, NULL };
671 launch_data_t tmp;
672
673 if ((tmp = launch_data_dict_lookup(dc.base, LAUNCH_JOBKEY_SOCKETS))) {
674 dc.newsockdict = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
675 launch_data_dict_iterate(tmp, sock_dict_cb, &dc);
676 launch_data_dict_insert(dc.base, dc.newsockdict, LAUNCH_JOBKEY_SOCKETS);
677 }
678 }
679
680 void
681 sock_dict_cb(launch_data_t what, const char *key, void *context)
682 {
683 struct distill_context *dc = context;
684 launch_data_t fdarray = launch_data_alloc(LAUNCH_DATA_ARRAY);
685
686 launch_data_dict_insert(dc->newsockdict, fdarray, key);
687
688 if (launch_data_get_type(what) == LAUNCH_DATA_DICTIONARY) {
689 sock_dict_edit_entry(what, key, fdarray, dc->base);
690 } else if (launch_data_get_type(what) == LAUNCH_DATA_ARRAY) {
691 launch_data_t tmp;
692 size_t i;
693
694 for (i = 0; i < launch_data_array_get_count(what); i++) {
695 tmp = launch_data_array_get_index(what, i);
696 sock_dict_edit_entry(tmp, key, fdarray, dc->base);
697 }
698 }
699 }
700
701 void
702 sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob)
703 {
704 launch_data_t a, val;
705 int sfd, st = SOCK_STREAM;
706 bool passive = true;
707
708 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_TYPE))) {
709 if (!strcasecmp(launch_data_get_string(val), "stream")) {
710 st = SOCK_STREAM;
711 } else if (!strcasecmp(launch_data_get_string(val), "dgram")) {
712 st = SOCK_DGRAM;
713 } else if (!strcasecmp(launch_data_get_string(val), "seqpacket")) {
714 st = SOCK_SEQPACKET;
715 }
716 }
717
718 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PASSIVE)))
719 passive = launch_data_get_bool(val);
720
721 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_SECUREWITHKEY))) {
722 char secdir[] = LAUNCH_SECDIR, buf[1024];
723 launch_data_t uenv = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES);
724
725 if (NULL == uenv) {
726 uenv = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
727 launch_data_dict_insert(thejob, uenv, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES);
728 }
729
730 mkdtemp(secdir);
731
732 sprintf(buf, "%s/%s", secdir, key);
733
734 a = launch_data_new_string(buf);
735 launch_data_dict_insert(tmp, a, LAUNCH_JOBSOCKETKEY_PATHNAME);
736 a = launch_data_new_string(buf);
737 launch_data_dict_insert(uenv, a, launch_data_get_string(val));
738 }
739
740 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PATHNAME))) {
741 struct sockaddr_un sun;
742 mode_t sun_mode = 0;
743 mode_t oldmask;
744 bool setm = false;
745
746 memset(&sun, 0, sizeof(sun));
747
748 sun.sun_family = AF_UNIX;
749
750 strncpy(sun.sun_path, launch_data_get_string(val), sizeof(sun.sun_path));
751
752 if ((sfd = _fd(socket(AF_UNIX, st, 0))) == -1)
753 return;
754
755 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PATHMODE))) {
756 sun_mode = (mode_t)launch_data_get_integer(val);
757 setm = true;
758 }
759
760 if (passive) {
761 if (unlink(sun.sun_path) == -1 && errno != ENOENT) {
762 close(sfd);
763 return;
764 }
765 oldmask = umask(S_IRWXG|S_IRWXO);
766 if (bind(sfd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
767 close(sfd);
768 umask(oldmask);
769 return;
770 }
771 umask(oldmask);
772 if (setm) {
773 chmod(sun.sun_path, sun_mode);
774 }
775 if ((st == SOCK_STREAM || st == SOCK_SEQPACKET)
776 && listen(sfd, SOMAXCONN) == -1) {
777 close(sfd);
778 return;
779 }
780 } else if (connect(sfd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
781 close(sfd);
782 return;
783 }
784
785 val = launch_data_new_fd(sfd);
786 launch_data_array_append(fdarray, val);
787 } else {
788 launch_data_t rnames = NULL;
789 const char *node = NULL, *serv = NULL, *mgroup = NULL;
790 char servnbuf[50];
791 struct addrinfo hints, *res0, *res;
792 int gerr, sock_opt = 1;
793 bool rendezvous = false;
794
795 memset(&hints, 0, sizeof(hints));
796
797 hints.ai_socktype = st;
798 if (passive)
799 hints.ai_flags |= AI_PASSIVE;
800
801 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_NODENAME)))
802 node = launch_data_get_string(val);
803 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_MULTICASTGROUP)))
804 mgroup = launch_data_get_string(val);
805 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_SERVICENAME))) {
806 if (LAUNCH_DATA_INTEGER == launch_data_get_type(val)) {
807 sprintf(servnbuf, "%lld", launch_data_get_integer(val));
808 serv = servnbuf;
809 } else {
810 serv = launch_data_get_string(val);
811 }
812 }
813 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_FAMILY))) {
814 if (!strcasecmp("IPv4", launch_data_get_string(val)))
815 hints.ai_family = AF_INET;
816 else if (!strcasecmp("IPv6", launch_data_get_string(val)))
817 hints.ai_family = AF_INET6;
818 }
819 if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PROTOCOL))) {
820 if (!strcasecmp("TCP", launch_data_get_string(val)))
821 hints.ai_protocol = IPPROTO_TCP;
822 }
823 if ((rnames = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_BONJOUR))) {
824 rendezvous = true;
825 if (LAUNCH_DATA_BOOL == launch_data_get_type(rnames)) {
826 rendezvous = launch_data_get_bool(rnames);
827 rnames = NULL;
828 }
829 }
830
831 if ((gerr = getaddrinfo(node, serv, &hints, &res0)) != 0) {
832 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(gerr));
833 return;
834 }
835
836 for (res = res0; res; res = res->ai_next) {
837 launch_data_t rvs_fd = NULL;
838 if ((sfd = _fd(socket(res->ai_family, res->ai_socktype, res->ai_protocol))) == -1) {
839 fprintf(stderr, "socket(): %s\n", strerror(errno));
840 return;
841 }
842 if (hints.ai_flags & AI_PASSIVE) {
843 if (AF_INET6 == res->ai_family && -1 == setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY,
844 (void *)&sock_opt, sizeof(sock_opt))) {
845 fprintf(stderr, "setsockopt(IPV6_V6ONLY): %m");
846 return;
847 }
848 if (mgroup) {
849 if (setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, (void *)&sock_opt, sizeof(sock_opt)) == -1) {
850 fprintf(stderr, "setsockopt(SO_REUSEPORT): %s\n", strerror(errno));
851 return;
852 }
853 } else {
854 if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt, sizeof(sock_opt)) == -1) {
855 fprintf(stderr, "setsockopt(SO_REUSEADDR): %s\n", strerror(errno));
856 return;
857 }
858 }
859 if (bind(sfd, res->ai_addr, res->ai_addrlen) == -1) {
860 fprintf(stderr, "bind(): %s\n", strerror(errno));
861 return;
862 }
863
864 if (mgroup) {
865 do_mgroup_join(sfd, res->ai_family, res->ai_socktype, res->ai_protocol, mgroup);
866 }
867 if ((res->ai_socktype == SOCK_STREAM || res->ai_socktype == SOCK_SEQPACKET)
868 && listen(sfd, SOMAXCONN) == -1) {
869 fprintf(stderr, "listen(): %s\n", strerror(errno));
870 return;
871 }
872 if (rendezvous && (res->ai_family == AF_INET || res->ai_family == AF_INET6) &&
873 (res->ai_socktype == SOCK_STREAM || res->ai_socktype == SOCK_DGRAM)) {
874 launch_data_t rvs_fds = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_BONJOURFDS);
875 if (NULL == rvs_fds) {
876 rvs_fds = launch_data_alloc(LAUNCH_DATA_ARRAY);
877 launch_data_dict_insert(thejob, rvs_fds, LAUNCH_JOBKEY_BONJOURFDS);
878 }
879 if (NULL == rnames) {
880 rvs_fd = do_rendezvous_magic(res, serv);
881 if (rvs_fd)
882 launch_data_array_append(rvs_fds, rvs_fd);
883 } else if (LAUNCH_DATA_STRING == launch_data_get_type(rnames)) {
884 rvs_fd = do_rendezvous_magic(res, launch_data_get_string(rnames));
885 if (rvs_fd)
886 launch_data_array_append(rvs_fds, rvs_fd);
887 } else if (LAUNCH_DATA_ARRAY == launch_data_get_type(rnames)) {
888 size_t rn_i, rn_ac = launch_data_array_get_count(rnames);
889
890 for (rn_i = 0; rn_i < rn_ac; rn_i++) {
891 launch_data_t rn_tmp = launch_data_array_get_index(rnames, rn_i);
892
893 rvs_fd = do_rendezvous_magic(res, launch_data_get_string(rn_tmp));
894 if (rvs_fd)
895 launch_data_array_append(rvs_fds, rvs_fd);
896 }
897 }
898 }
899 } else {
900 if (connect(sfd, res->ai_addr, res->ai_addrlen) == -1) {
901 fprintf(stderr, "connect(): %s\n", strerror(errno));
902 return;
903 }
904 }
905 val = launch_data_new_fd(sfd);
906 if (rvs_fd) {
907 /* <rdar://problem/3964648> Launchd should not register the same service more than once */
908 /* <rdar://problem/3965154> Switch to DNSServiceRegisterAddrInfo() */
909 rendezvous = false;
910 }
911 launch_data_array_append(fdarray, val);
912 }
913 }
914 }
915
916 void
917 do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup)
918 {
919 struct addrinfo hints, *res0, *res;
920 struct ip_mreq mreq;
921 struct ipv6_mreq m6req;
922 int gerr;
923
924 memset(&hints, 0, sizeof(hints));
925
926 hints.ai_flags |= AI_PASSIVE;
927 hints.ai_family = family;
928 hints.ai_socktype = socktype;
929 hints.ai_protocol = protocol;
930
931 if ((gerr = getaddrinfo(mgroup, NULL, &hints, &res0)) != 0) {
932 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(gerr));
933 return;
934 }
935
936 for (res = res0; res; res = res->ai_next) {
937 if (AF_INET == family) {
938 memset(&mreq, 0, sizeof(mreq));
939 mreq.imr_multiaddr = ((struct sockaddr_in *)res->ai_addr)->sin_addr;
940 if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) {
941 fprintf(stderr, "setsockopt(IP_ADD_MEMBERSHIP): %s\n", strerror(errno));
942 continue;
943 }
944 break;
945 } else if (AF_INET6 == family) {
946 memset(&m6req, 0, sizeof(m6req));
947 m6req.ipv6mr_multiaddr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
948 if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &m6req, sizeof(m6req)) == -1) {
949 fprintf(stderr, "setsockopt(IPV6_JOIN_GROUP): %s\n", strerror(errno));
950 continue;
951 }
952 break;
953 } else {
954 fprintf(stderr, "unknown family during multicast group bind!\n");
955 break;
956 }
957 }
958
959 freeaddrinfo(res0);
960 }
961
962
963 launch_data_t
964 do_rendezvous_magic(const struct addrinfo *res, const char *serv)
965 {
966 struct stat sb;
967 DNSServiceRef service;
968 DNSServiceErrorType error;
969 char rvs_buf[200];
970 short port;
971 static int statres = 1;
972
973 if (1 == statres)
974 statres = stat("/usr/sbin/mDNSResponder", &sb);
975
976 if (-1 == statres)
977 return NULL;
978
979 sprintf(rvs_buf, "_%s._%s.", serv, res->ai_socktype == SOCK_STREAM ? "tcp" : "udp");
980
981 if (res->ai_family == AF_INET)
982 port = ((struct sockaddr_in *)res->ai_addr)->sin_port;
983 else
984 port = ((struct sockaddr_in6 *)res->ai_addr)->sin6_port;
985
986 error = DNSServiceRegister(&service, 0, 0, NULL, rvs_buf, NULL, NULL, port, 0, NULL, NULL, NULL);
987
988 if (error == kDNSServiceErr_NoError)
989 return launch_data_new_fd(DNSServiceRefSockFD(service));
990
991 fprintf(stderr, "DNSServiceRegister(\"%s\"): %d\n", serv, error);
992 return NULL;
993 }
994
995 CFPropertyListRef
996 CreateMyPropertyListFromFile(const char *posixfile)
997 {
998 CFPropertyListRef propertyList;
999 CFStringRef errorString;
1000 CFDataRef resourceData;
1001 SInt32 errorCode;
1002 CFURLRef fileURL;
1003
1004 fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false);
1005 if (!fileURL)
1006 fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile);
1007 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode))
1008 fprintf(stderr, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d\n", getprogname(), posixfile, (int)errorCode);
1009 propertyList = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resourceData, kCFPropertyListMutableContainers, &errorString);
1010 if (!propertyList)
1011 fprintf(stderr, "%s: propertyList is NULL\n", getprogname());
1012
1013 return propertyList;
1014 }
1015
1016 void
1017 WriteMyPropertyListToFile(CFPropertyListRef plist, const char *posixfile)
1018 {
1019 CFDataRef resourceData;
1020 CFURLRef fileURL;
1021 SInt32 errorCode;
1022
1023 fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false);
1024 if (!fileURL)
1025 fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile);
1026 resourceData = CFPropertyListCreateXMLData(kCFAllocatorDefault, plist);
1027 if (resourceData == NULL)
1028 fprintf(stderr, "%s: CFPropertyListCreateXMLData(%s) failed", getprogname(), posixfile);
1029 if (!CFURLWriteDataAndPropertiesToResource(fileURL, resourceData, NULL, &errorCode))
1030 fprintf(stderr, "%s: CFURLWriteDataAndPropertiesToResource(%s) failed: %d\n", getprogname(), posixfile, (int)errorCode);
1031 }
1032
1033 void
1034 myCFDictionaryApplyFunction(const void *key, const void *value, void *context)
1035 {
1036 launch_data_t ik, iw, where = context;
1037
1038 ik = CF2launch_data(key);
1039 iw = CF2launch_data(value);
1040
1041 launch_data_dict_insert(where, iw, launch_data_get_string(ik));
1042 launch_data_free(ik);
1043 }
1044
1045 launch_data_t
1046 CF2launch_data(CFTypeRef cfr)
1047 {
1048 launch_data_t r;
1049 CFTypeID cft = CFGetTypeID(cfr);
1050
1051 if (cft == CFStringGetTypeID()) {
1052 char buf[4096];
1053 CFStringGetCString(cfr, buf, sizeof(buf), kCFStringEncodingUTF8);
1054 r = launch_data_alloc(LAUNCH_DATA_STRING);
1055 launch_data_set_string(r, buf);
1056 } else if (cft == CFBooleanGetTypeID()) {
1057 r = launch_data_alloc(LAUNCH_DATA_BOOL);
1058 launch_data_set_bool(r, CFBooleanGetValue(cfr));
1059 } else if (cft == CFArrayGetTypeID()) {
1060 CFIndex i, ac = CFArrayGetCount(cfr);
1061 r = launch_data_alloc(LAUNCH_DATA_ARRAY);
1062 for (i = 0; i < ac; i++) {
1063 CFTypeRef v = CFArrayGetValueAtIndex(cfr, i);
1064 if (v) {
1065 launch_data_t iv = CF2launch_data(v);
1066 launch_data_array_set_index(r, iv, i);
1067 }
1068 }
1069 } else if (cft == CFDictionaryGetTypeID()) {
1070 r = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1071 CFDictionaryApplyFunction(cfr, myCFDictionaryApplyFunction, r);
1072 } else if (cft == CFDataGetTypeID()) {
1073 r = launch_data_alloc(LAUNCH_DATA_ARRAY);
1074 launch_data_set_opaque(r, CFDataGetBytePtr(cfr), CFDataGetLength(cfr));
1075 } else if (cft == CFNumberGetTypeID()) {
1076 long long n;
1077 double d;
1078 CFNumberType cfnt = CFNumberGetType(cfr);
1079 switch (cfnt) {
1080 case kCFNumberSInt8Type:
1081 case kCFNumberSInt16Type:
1082 case kCFNumberSInt32Type:
1083 case kCFNumberSInt64Type:
1084 case kCFNumberCharType:
1085 case kCFNumberShortType:
1086 case kCFNumberIntType:
1087 case kCFNumberLongType:
1088 case kCFNumberLongLongType:
1089 CFNumberGetValue(cfr, kCFNumberLongLongType, &n);
1090 r = launch_data_alloc(LAUNCH_DATA_INTEGER);
1091 launch_data_set_integer(r, n);
1092 break;
1093 case kCFNumberFloat32Type:
1094 case kCFNumberFloat64Type:
1095 case kCFNumberFloatType:
1096 case kCFNumberDoubleType:
1097 CFNumberGetValue(cfr, kCFNumberDoubleType, &d);
1098 r = launch_data_alloc(LAUNCH_DATA_REAL);
1099 launch_data_set_real(r, d);
1100 break;
1101 default:
1102 r = NULL;
1103 break;
1104 }
1105 } else {
1106 r = NULL;
1107 }
1108 return r;
1109 }
1110
1111 int
1112 help_cmd(int argc, char *const argv[])
1113 {
1114 FILE *where = stdout;
1115 int l, cmdwidth = 0;
1116 size_t i;
1117
1118 if (argc == 0 || argv == NULL)
1119 where = stderr;
1120
1121 fprintf(where, "usage: %s <subcommand>\n", getprogname());
1122
1123 for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) {
1124 l = strlen(cmds[i].name);
1125 if (l > cmdwidth)
1126 cmdwidth = l;
1127 }
1128
1129 for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++)
1130 fprintf(where, "\t%-*s\t%s\n", cmdwidth, cmds[i].name, cmds[i].desc);
1131
1132 return 0;
1133 }
1134
1135 int
1136 exit_cmd(int argc __attribute__((unused)), char *const argv[] __attribute__((unused)))
1137 {
1138 exit(0);
1139 }
1140
1141 int
1142 _fd(int fd)
1143 {
1144 if (fd >= 0)
1145 fcntl(fd, F_SETFD, 1);
1146 return fd;
1147 }
1148
1149 int
1150 bootstrap_cmd(int argc __attribute__((unused)), char *const argv[] __attribute__((unused)))
1151 {
1152 int memmib[] = { CTL_HW, HW_MEMSIZE };
1153 int mvnmib[] = { CTL_KERN, KERN_MAXVNODES };
1154 int hnmib[] = { CTL_KERN, KERN_HOSTNAME };
1155 uint64_t mem = 0;
1156 uint32_t mvn;
1157 size_t memsz = sizeof(mem);
1158 struct group *tfp_gr;
1159
1160 if (assumes((tfp_gr = getgrnam("procview")) != NULL)) {
1161 int tfp_r_mib[3] = { CTL_KERN, KERN_TFP, KERN_TFP_READ_GROUP };
1162 gid_t tfp_r_gid = tfp_gr->gr_gid;
1163 assumes(sysctl(tfp_r_mib, 3, NULL, NULL, &tfp_r_gid, sizeof(tfp_r_gid)) != -1);
1164 }
1165
1166 if (assumes((tfp_gr = getgrnam("procmod")) != NULL)) {
1167 int tfp_rw_mib[3] = { CTL_KERN, KERN_TFP, KERN_TFP_RW_GROUP };
1168 gid_t tfp_rw_gid = tfp_gr->gr_gid;
1169 assumes(sysctl(tfp_rw_mib, 3, NULL, NULL, &tfp_rw_gid, sizeof(tfp_rw_gid)) != -1);
1170 }
1171
1172 if (assumes(sysctl(memmib, 2, &mem, &memsz, NULL, 0) != -1)) {
1173 mvn = mem / (64 * 1024) + 1024;
1174 assumes(sysctl(mvnmib, 2, NULL, NULL, &mvn, sizeof(mvn)) != -1);
1175 }
1176 assumes(sysctl(hnmib, 2, NULL, NULL, "localhost", sizeof("localhost")) != -1);
1177
1178 loopback_setup_ipv4();
1179 loopback_setup_ipv6();
1180
1181 apply_sysctls_from_file("/etc/sysctl-macosxserver.conf");
1182 apply_sysctls_from_file("/etc/sysctl.conf");
1183
1184 if (path_check("/System/Installation") && path_check("/etc/rc.cdrom")) {
1185 const char *rccdrom_tool[] = { _PATH_BSHELL, "/etc/rc.cdrom", "multiuser", NULL };
1186 assumes(fwexec(rccdrom_tool, true) != -1);
1187 assumes(reboot(RB_HALT) != -1);
1188 _exit(EXIT_FAILURE);
1189 } else if (is_netboot()) {
1190 const char *rcnetboot_tool[] = { _PATH_BSHELL, "/etc/rc.netboot", "init", NULL };
1191 if (!assumes(fwexec(rcnetboot_tool, true) != -1)) {
1192 assumes(reboot(RB_HALT) != -1);
1193 _exit(EXIT_FAILURE);
1194 }
1195 } else {
1196 do_potential_fsck();
1197 }
1198
1199 if (path_check("/var/account/acct")) {
1200 assumes(acct("/var/account/acct") != -1);
1201 }
1202
1203 if (path_check("/etc/fstab")) {
1204 const char *mount_tool[] = { "mount", "-vat", "nonfs", NULL };
1205 assumes(fwexec(mount_tool, true) != -1);
1206 }
1207
1208 if (path_check("/etc/rc.installer_cleanup")) {
1209 const char *rccleanup_tool[] = { _PATH_BSHELL, "/etc/rc.installer_cleanup", "multiuser", NULL };
1210 assumes(fwexec(rccleanup_tool, true) != -1);
1211 }
1212
1213 apply_func_to_dir(_PATH_VARRUN, empty_dir);
1214 apply_func_to_dir(_PATH_TMP, empty_dir);
1215 remove(_PATH_NOLOGIN);
1216
1217 // XXX --> RMRF_ITEMS="/var/tmp/folders.*
1218
1219 // 775 www:www /var/run/davlocks 4489695
1220 // 775 root:daemon /var/run/StartupItems
1221
1222 assumes(touch_file(_PATH_UTMP, DEFFILEMODE) != -1);
1223 assumes(touch_file(_PATH_UTMPX, DEFFILEMODE) != -1);
1224 assumes(touch_file(_PATH_VARRUN "/.systemStarterRunning", DEFFILEMODE) != -1);
1225
1226 if (!path_check("/var/db/netinfo/local.nidb.migrated") && !path_check("/var/db/netinfo/local.nidb")) {
1227 const char *create_nidb_tool[] = { "/usr/libexec/create_nidb", NULL };
1228
1229 fprintf(stderr, "NetInfo database missing. Creating...\n");
1230
1231 mkdir("/var/db/netinfo", ACCESSPERMS);
1232 remove("/var/db/.AppleSetupDone");
1233 assumes(fwexec(create_nidb_tool, true) != -1);
1234 }
1235
1236 if (path_check("/etc/security/rc.audit")) {
1237 const char *audit_tool[] = { _PATH_BSHELL, "/etc/security/rc.audit", NULL };
1238 assumes(fwexec(audit_tool, true) != -1);
1239 }
1240
1241 if (path_check("/Library/Preferences/com.apple.sharing.firewall.plist")) {
1242 const char *fw_tool[] = { "/usr/libexec/FirewallTool", NULL };
1243 assumes(fwexec(fw_tool, true) != -1);
1244 }
1245
1246 const char *bcc_tool[] = { "BootCacheControl", "start", NULL };
1247 assumes(fwexec(bcc_tool, true) != -1);
1248
1249 char *load_launchd_items[] = { "load", "-D", "all", "/etc/mach_init.d", NULL };
1250 if (is_safeboot())
1251 load_launchd_items[2] = "system";
1252 assumes(load_and_unload_cmd(4, load_launchd_items) == 0);
1253
1254 const char *bcc_tag_tool[] = { "BootCacheControl", "tag", NULL };
1255 assumes(fwexec(bcc_tag_tool, true) != -1);
1256
1257 const char *SystemStarter_tool[] = { "SystemStarter", NULL };
1258 assumes(fwexec(SystemStarter_tool, false) != -1);
1259
1260 workaround4465949();
1261
1262 if (path_check("/etc/rc.local")) {
1263 const char *rc_local_tool[] = { _PATH_BSHELL, "/etc/rc.local", NULL };
1264 assumes(fwexec(rc_local_tool, false) != -1);
1265 }
1266
1267 return 0;
1268 }
1269
1270 void
1271 workaround4465949(void)
1272 {
1273 const char *pbs_tool[] = { "/System/Library/CoreServices/pbs", NULL };
1274 const char *lca_tool[] = { "/System/Library/CoreServices/Language Chooser.app/Contents/MacOS/Language Chooser", NULL};
1275 char *const reloadttys_argv[] = { "reloadttys", NULL };
1276 int wstatus;
1277 pid_t pbs_p;
1278
1279 if (path_check("/System/Library/LaunchDaemons/com.apple.loginwindow.plist")) {
1280 return;
1281 }
1282
1283 if (path_check(pbs_tool[0]) && path_check(lca_tool[0]) &&
1284 !path_check("/var/db/.AppleSetupDone") &&
1285 path_check("/var/db/.RunLanguageChooserToo")) {
1286 if (assumes((pbs_p = fwexec(pbs_tool, false)) != -1)) {
1287 assumes(fwexec(lca_tool, true) != -1);
1288 assumes(kill(pbs_p, SIGTERM) != -1);
1289 assumes(waitpid(pbs_p, &wstatus, 0) != -1);
1290 }
1291 }
1292
1293 assumes(fyi_cmd(1, reloadttys_argv) == 0);
1294 }
1295
1296 int
1297 load_and_unload_cmd(int argc, char *const argv[])
1298 {
1299 NSSearchPathEnumerationState es = 0;
1300 char nspath[PATH_MAX * 2]; /* safe side, we need to append */
1301 bool badopts = false;
1302 struct load_unload_state lus;
1303 size_t i;
1304 int ch;
1305
1306 memset(&lus, 0, sizeof(lus));
1307
1308 if (!strcmp(argv[0], "load"))
1309 lus.load = true;
1310
1311 while ((ch = getopt(argc, argv, "wFS:D:")) != -1) {
1312 switch (ch) {
1313 case 'w':
1314 lus.editondisk = true;
1315 break;
1316 case 'F':
1317 lus.forceload = true;
1318 break;
1319 case 'S':
1320 lus.session_type = optarg;
1321 break;
1322 case 'D':
1323 if (strcasecmp(optarg, "all") == 0) {
1324 es |= NSAllDomainsMask;
1325 } else if (strcasecmp(optarg, "user") == 0) {
1326 es |= NSUserDomainMask;
1327 } else if (strcasecmp(optarg, "local") == 0) {
1328 es |= NSLocalDomainMask;
1329 } else if (strcasecmp(optarg, "network") == 0) {
1330 es |= NSNetworkDomainMask;
1331 } else if (strcasecmp(optarg, "system") == 0) {
1332 es |= NSSystemDomainMask;
1333 } else {
1334 badopts = true;
1335 }
1336 break;
1337 case '?':
1338 default:
1339 badopts = true;
1340 break;
1341 }
1342 }
1343 argc -= optind;
1344 argv += optind;
1345
1346 if (lus.session_type == NULL)
1347 es &= ~NSUserDomainMask;
1348
1349 if (argc == 0 && es == 0)
1350 badopts = true;
1351
1352 if (badopts) {
1353 fprintf(stderr, "usage: %s load [-wF] [-D <user|local|network|system|all>] paths...\n", getprogname());
1354 return 1;
1355 }
1356
1357 /* I wish I didn't need to do three passes, but I need to load mDNSResponder and use it too.
1358 * And loading legacy mach init jobs is extra fun.
1359 *
1360 * In later versions of launchd, I hope to load everything in the first pass,
1361 * then do the Bonjour magic on the jobs that need it, and reload them, but for now,
1362 * I haven't thought through the various complexities of reloading jobs, and therefore
1363 * launchd doesn't have reload support right now.
1364 */
1365
1366 lus.pass0 = launch_data_alloc(LAUNCH_DATA_ARRAY);
1367 lus.pass1 = launch_data_alloc(LAUNCH_DATA_ARRAY);
1368 lus.pass2 = launch_data_alloc(LAUNCH_DATA_ARRAY);
1369
1370 es = NSStartSearchPathEnumeration(NSLibraryDirectory, es);
1371
1372 while ((es = NSGetNextSearchPathEnumeration(es, nspath))) {
1373 glob_t g;
1374
1375 if (lus.session_type) {
1376 strcat(nspath, "/LaunchAgents");
1377 } else {
1378 strcat(nspath, "/LaunchDaemons");
1379 }
1380
1381 if (glob(nspath, GLOB_TILDE|GLOB_NOSORT, NULL, &g) == 0) {
1382 for (i = 0; i < g.gl_pathc; i++) {
1383 readpath(g.gl_pathv[i], &lus);
1384 }
1385 globfree(&g);
1386 }
1387 }
1388
1389 for (i = 0; i < (size_t)argc; i++)
1390 readpath(argv[i], &lus);
1391
1392 if (launch_data_array_get_count(lus.pass0) == 0 &&
1393 launch_data_array_get_count(lus.pass1) == 0 &&
1394 launch_data_array_get_count(lus.pass2) == 0) {
1395 fprintf(stderr, "nothing found to %s\n", lus.load ? "load" : "unload");
1396 launch_data_free(lus.pass0);
1397 launch_data_free(lus.pass1);
1398 launch_data_free(lus.pass2);
1399 return 1;
1400 }
1401
1402 if (lus.load) {
1403 distill_jobs(lus.pass1);
1404 submit_mach_jobs(lus.pass0);
1405 submit_job_pass(lus.pass1);
1406 let_go_of_mach_jobs(lus.pass0);
1407 distill_jobs(lus.pass2);
1408 submit_job_pass(lus.pass2);
1409 } else {
1410 for (i = 0; i < launch_data_array_get_count(lus.pass1); i++)
1411 unloadjob(launch_data_array_get_index(lus.pass1, i));
1412 for (i = 0; i < launch_data_array_get_count(lus.pass2); i++)
1413 unloadjob(launch_data_array_get_index(lus.pass2, i));
1414 }
1415
1416 return 0;
1417 }
1418
1419 void
1420 submit_mach_jobs(launch_data_t jobs)
1421 {
1422 size_t i, c;
1423
1424 c = launch_data_array_get_count(jobs);
1425
1426 for (i = 0; i < c; i++) {
1427 launch_data_t tmp, oai = launch_data_array_get_index(jobs, i);
1428 const char *sn = NULL, *cmd = NULL;
1429 bool d = true;
1430 mach_port_t msr, msv;
1431 kern_return_t kr;
1432 uid_t u = getuid();
1433
1434 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_ONDEMAND)))
1435 d = launch_data_get_bool(tmp);
1436 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVICENAME)))
1437 sn = launch_data_get_string(tmp);
1438 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_COMMAND)))
1439 cmd = launch_data_get_string(tmp);
1440
1441 if ((kr = bootstrap_create_server(bootstrap_port, (char *)cmd, u, d, &msr)) != KERN_SUCCESS) {
1442 fprintf(stderr, "%s: bootstrap_create_server(): %d\n", getprogname(), kr);
1443 continue;
1444 }
1445 if ((kr = bootstrap_create_service(msr, (char*)sn, &msv)) != KERN_SUCCESS) {
1446 fprintf(stderr, "%s: bootstrap_create_service(): %d\n", getprogname(), kr);
1447 mach_port_destroy(mach_task_self(), msr);
1448 continue;
1449 }
1450 launch_data_dict_insert(oai, launch_data_new_machport(msr), MACHINIT_JOBKEY_SERVERPORT);
1451 launch_data_dict_insert(oai, launch_data_new_machport(msv), MACHINIT_JOBKEY_SERVICEPORT);
1452 }
1453 }
1454
1455 void
1456 let_go_of_mach_jobs(launch_data_t jobs)
1457 {
1458 size_t i, c = launch_data_array_get_count(jobs);
1459
1460 for (i = 0; i < c; i++) {
1461 launch_data_t tmp, oai = launch_data_array_get_index(jobs, i);
1462 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVICEPORT))) {
1463 mach_port_destroy(mach_task_self(), launch_data_get_machport(tmp));
1464 } else {
1465 fprintf(stderr, "%s: ack! missing service port!\n", getprogname());
1466 }
1467 if ((tmp = launch_data_dict_lookup(oai, MACHINIT_JOBKEY_SERVERPORT))) {
1468 mach_port_destroy(mach_task_self(), launch_data_get_machport(tmp));
1469 } else {
1470 fprintf(stderr, "%s: ack! missing server port!\n", getprogname());
1471 }
1472 }
1473 }
1474
1475 void
1476 submit_job_pass(launch_data_t jobs)
1477 {
1478 launch_data_t msg, resp;
1479 size_t i;
1480 int e;
1481
1482 if (launch_data_array_get_count(jobs) == 0)
1483 return;
1484
1485 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1486
1487 launch_data_dict_insert(msg, jobs, LAUNCH_KEY_SUBMITJOB);
1488
1489 resp = launch_msg(msg);
1490
1491 if (resp) {
1492 switch (launch_data_get_type(resp)) {
1493 case LAUNCH_DATA_ERRNO:
1494 if ((e = launch_data_get_errno(resp)))
1495 fprintf(stderr, "%s\n", strerror(e));
1496 break;
1497 case LAUNCH_DATA_ARRAY:
1498 for (i = 0; i < launch_data_array_get_count(jobs); i++) {
1499 launch_data_t obatind = launch_data_array_get_index(resp, i);
1500 launch_data_t jatind = launch_data_array_get_index(jobs, i);
1501 const char *lab4job = launch_data_get_string(launch_data_dict_lookup(jatind, LAUNCH_JOBKEY_LABEL));
1502 if (LAUNCH_DATA_ERRNO == launch_data_get_type(obatind)) {
1503 e = launch_data_get_errno(obatind);
1504 switch (e) {
1505 case EEXIST:
1506 fprintf(stderr, "%s: %s\n", lab4job, "Already loaded");
1507 break;
1508 case ESRCH:
1509 fprintf(stderr, "%s: %s\n", lab4job, "Not loaded");
1510 break;
1511 default:
1512 fprintf(stderr, "%s: %s\n", lab4job, strerror(e));
1513 case 0:
1514 break;
1515 }
1516 }
1517 }
1518 break;
1519 default:
1520 fprintf(stderr, "unknown respose from launchd!\n");
1521 break;
1522 }
1523 launch_data_free(resp);
1524 } else {
1525 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1526 }
1527
1528 launch_data_free(msg);
1529 }
1530
1531 int
1532 start_stop_remove_cmd(int argc, char *const argv[])
1533 {
1534 launch_data_t resp, msg;
1535 const char *lmsgcmd = LAUNCH_KEY_STOPJOB;
1536 int e, r = 0;
1537
1538 if (0 == strcmp(argv[0], "start"))
1539 lmsgcmd = LAUNCH_KEY_STARTJOB;
1540
1541 if (0 == strcmp(argv[0], "remove"))
1542 lmsgcmd = LAUNCH_KEY_REMOVEJOB;
1543
1544 if (argc != 2) {
1545 fprintf(stderr, "usage: %s %s <job label>\n", getprogname(), argv[0]);
1546 return 1;
1547 }
1548
1549 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1550 launch_data_dict_insert(msg, launch_data_new_string(argv[1]), lmsgcmd);
1551
1552 resp = launch_msg(msg);
1553 launch_data_free(msg);
1554
1555 if (resp == NULL) {
1556 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1557 return 1;
1558 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
1559 if ((e = launch_data_get_errno(resp))) {
1560 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e));
1561 r = 1;
1562 }
1563 } else {
1564 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
1565 r = 1;
1566 }
1567
1568 launch_data_free(resp);
1569 return r;
1570 }
1571
1572 void
1573 print_jobs(launch_data_t j)
1574 {
1575 static size_t depth = 0;
1576 launch_data_t lo = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LABEL);
1577 launch_data_t pido = launch_data_dict_lookup(j, LAUNCH_JOBKEY_PID);
1578 launch_data_t stato = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LASTEXITSTATUS);
1579 launch_data_t sjobs = launch_data_dict_lookup(j, LAUNCH_JOBKEY_SUBJOBS);
1580 const char *label = launch_data_get_string(lo);
1581 size_t i, c;
1582
1583 if (pido) {
1584 fprintf(stdout, "%lld\t-\t", launch_data_get_integer(pido));
1585 } else if (stato) {
1586 int wstatus = (int)launch_data_get_integer(stato);
1587 if (WIFEXITED(wstatus)) {
1588 fprintf(stdout, "-\t%d\t", WEXITSTATUS(wstatus));
1589 } else if (WIFSIGNALED(wstatus)) {
1590 fprintf(stdout, "-\t-%d\t", WTERMSIG(wstatus));
1591 } else {
1592 fprintf(stdout, "-\t???\t");
1593 }
1594 } else {
1595 fprintf(stdout, "-\t-\t");
1596 }
1597 for (i = 0; i < depth; i++)
1598 fprintf(stdout, "\t");
1599
1600 fprintf(stdout, "%s\n", label);
1601
1602 if (sjobs) {
1603 launch_data_t oai;
1604
1605 c = launch_data_array_get_count(sjobs);
1606
1607 depth++;
1608 for (i = 0; i < c; i++) {
1609 oai = launch_data_array_get_index(sjobs, i);
1610 print_jobs(oai);
1611 }
1612 depth--;
1613 }
1614 }
1615
1616 void
1617 print_obj(launch_data_t obj, const char *key, void *context __attribute__((unused)))
1618 {
1619 static size_t indent = 0;
1620 size_t i, c;
1621
1622 for (i = 0; i < indent; i++)
1623 fprintf(stdout, "\t");
1624
1625 if (key)
1626 fprintf(stdout, "\"%s\" = ", key);
1627
1628 switch (launch_data_get_type(obj)) {
1629 case LAUNCH_DATA_STRING:
1630 fprintf(stdout, "\"%s\";\n", launch_data_get_string(obj));
1631 break;
1632 case LAUNCH_DATA_INTEGER:
1633 fprintf(stdout, "%lld;\n", launch_data_get_integer(obj));
1634 break;
1635 case LAUNCH_DATA_REAL:
1636 fprintf(stdout, "%f;\n", launch_data_get_real(obj));
1637 break;
1638 case LAUNCH_DATA_BOOL:
1639 fprintf(stdout, "%s;\n", launch_data_get_bool(obj) ? "true" : "false");
1640 break;
1641 case LAUNCH_DATA_ARRAY:
1642 c = launch_data_array_get_count(obj);
1643 fprintf(stdout, "(\n");
1644 indent++;
1645 for (i = 0; i < c; i++)
1646 print_obj(launch_data_array_get_index(obj, i), NULL, NULL);
1647 indent--;
1648 for (i = 0; i < indent; i++)
1649 fprintf(stdout, "\t");
1650 fprintf(stdout, ");\n");
1651 break;
1652 case LAUNCH_DATA_DICTIONARY:
1653 fprintf(stdout, "{\n");
1654 indent++;
1655 launch_data_dict_iterate(obj, print_obj, NULL);
1656 indent--;
1657 for (i = 0; i < indent; i++)
1658 fprintf(stdout, "\t");
1659 fprintf(stdout, "};\n");
1660 break;
1661 case LAUNCH_DATA_FD:
1662 fprintf(stdout, "file-descriptor-object;\n");
1663 break;
1664 case LAUNCH_DATA_MACHPORT:
1665 fprintf(stdout, "mach-port-object;\n");
1666 break;
1667 default:
1668 fprintf(stdout, "???;\n");
1669 break;
1670 }
1671 }
1672
1673 int
1674 list_cmd(int argc, char *const argv[])
1675 {
1676 launch_data_t resp, msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1677 int r = 0;
1678
1679 if (argc > 2) {
1680 fprintf(stderr, "usage: %s list [label]\n", getprogname());
1681 return 1;
1682 } else if (argc == 2) {
1683 launch_data_dict_insert(msg, launch_data_new_string(argv[1]), LAUNCH_KEY_GETJOB);
1684 } else {
1685 launch_data_dict_insert(msg, launch_data_new_string(""), LAUNCH_KEY_GETJOB);
1686 }
1687
1688 resp = launch_msg(msg);
1689 launch_data_free(msg);
1690
1691 if (resp == NULL) {
1692 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1693 return 1;
1694 } else if (launch_data_get_type(resp) == LAUNCH_DATA_DICTIONARY) {
1695 if (argc == 1) {
1696 fprintf(stdout, "PID\tStatus\tLabel\n");
1697 print_jobs(resp);
1698 } else {
1699 print_obj(resp, NULL, NULL);
1700 }
1701 } else {
1702 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
1703 r = 1;
1704 }
1705
1706 launch_data_free(resp);
1707
1708 return r;
1709 }
1710
1711 int
1712 stdio_cmd(int argc, char *const argv[])
1713 {
1714 launch_data_t resp, msg, tmp;
1715 int e, fd = -1, r = 0;
1716
1717 if (argc != 2) {
1718 fprintf(stderr, "usage: %s %s <path>\n", getprogname(), argv[0]);
1719 return 1;
1720 }
1721
1722 fd = open(argv[1], O_CREAT|O_APPEND|O_WRONLY|O_NOCTTY, DEFFILEMODE);
1723
1724 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1725
1726 if (fd == -1) {
1727 tmp = launch_data_new_string(argv[1]);
1728 } else {
1729 tmp = launch_data_new_fd(fd);
1730 }
1731
1732 if (!strcmp(argv[0], "stdout")) {
1733 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETSTDOUT);
1734 } else {
1735 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETSTDERR);
1736 }
1737
1738 resp = launch_msg(msg);
1739 launch_data_free(msg);
1740
1741 if (resp == NULL) {
1742 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1743 return 1;
1744 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
1745 if ((e = launch_data_get_errno(resp))) {
1746 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e));
1747 r = 1;
1748 }
1749 } else {
1750 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
1751 r = 1;
1752 }
1753
1754 if (fd != -1)
1755 close(fd);
1756
1757 launch_data_free(resp);
1758
1759 return r;
1760 }
1761
1762 int
1763 fyi_cmd(int argc, char *const argv[])
1764 {
1765 launch_data_t resp, msg;
1766 const char *lmsgk = LAUNCH_KEY_RELOADTTYS;
1767 int e, r = 0;
1768
1769 if (argc != 1) {
1770 fprintf(stderr, "usage: %s %s\n", getprogname(), argv[0]);
1771 return 1;
1772 }
1773
1774 if (!strcmp(argv[0], "shutdown")) {
1775 lmsgk = LAUNCH_KEY_SHUTDOWN;
1776 } else if (!strcmp(argv[0], "singleuser")) {
1777 lmsgk = LAUNCH_KEY_SINGLEUSER;
1778 }
1779
1780 msg = launch_data_new_string(lmsgk);
1781 resp = launch_msg(msg);
1782 launch_data_free(msg);
1783
1784 if (resp == NULL) {
1785 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1786 return 1;
1787 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
1788 if ((e = launch_data_get_errno(resp))) {
1789 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e));
1790 r = 1;
1791 }
1792 } else {
1793 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
1794 r = 1;
1795 }
1796
1797 launch_data_free(resp);
1798
1799 return r;
1800 }
1801
1802 int
1803 logupdate_cmd(int argc, char *const argv[])
1804 {
1805 launch_data_t resp, msg;
1806 int e, i, j, r = 0, m = 0;
1807 bool badargs = false, maskmode = false, onlymode = false, levelmode = false;
1808 const char *whichcmd = LAUNCH_KEY_SETLOGMASK;
1809 static const struct {
1810 const char *name;
1811 int level;
1812 } logtbl[] = {
1813 { "debug", LOG_DEBUG },
1814 { "info", LOG_INFO },
1815 { "notice", LOG_NOTICE },
1816 { "warning", LOG_WARNING },
1817 { "error", LOG_ERR },
1818 { "critical", LOG_CRIT },
1819 { "alert", LOG_ALERT },
1820 { "emergency", LOG_EMERG },
1821 };
1822 int logtblsz = sizeof logtbl / sizeof logtbl[0];
1823
1824 if (argc >= 2) {
1825 if (!strcmp(argv[1], "mask"))
1826 maskmode = true;
1827 else if (!strcmp(argv[1], "only"))
1828 onlymode = true;
1829 else if (!strcmp(argv[1], "level"))
1830 levelmode = true;
1831 else
1832 badargs = true;
1833 }
1834
1835 if (maskmode)
1836 m = LOG_UPTO(LOG_DEBUG);
1837
1838 if (argc > 2 && (maskmode || onlymode)) {
1839 for (i = 2; i < argc; i++) {
1840 for (j = 0; j < logtblsz; j++) {
1841 if (!strcmp(argv[i], logtbl[j].name)) {
1842 if (maskmode)
1843 m &= ~(LOG_MASK(logtbl[j].level));
1844 else
1845 m |= LOG_MASK(logtbl[j].level);
1846 break;
1847 }
1848 }
1849 if (j == logtblsz) {
1850 badargs = true;
1851 break;
1852 }
1853 }
1854 } else if (argc > 2 && levelmode) {
1855 for (j = 0; j < logtblsz; j++) {
1856 if (!strcmp(argv[2], logtbl[j].name)) {
1857 m = LOG_UPTO(logtbl[j].level);
1858 break;
1859 }
1860 }
1861 if (j == logtblsz)
1862 badargs = true;
1863 } else if (argc == 1) {
1864 whichcmd = LAUNCH_KEY_GETLOGMASK;
1865 } else {
1866 badargs = true;
1867 }
1868
1869 if (badargs) {
1870 fprintf(stderr, "usage: %s [[mask loglevels...] | [only loglevels...] [level loglevel]]\n", getprogname());
1871 return 1;
1872 }
1873
1874 if (whichcmd == LAUNCH_KEY_SETLOGMASK) {
1875 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1876 launch_data_dict_insert(msg, launch_data_new_integer(m), whichcmd);
1877 } else {
1878 msg = launch_data_new_string(whichcmd);
1879 }
1880
1881 resp = launch_msg(msg);
1882 launch_data_free(msg);
1883
1884 if (resp == NULL) {
1885 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
1886 return 1;
1887 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
1888 if ((e = launch_data_get_errno(resp))) {
1889 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(e));
1890 r = 1;
1891 }
1892 } else if (launch_data_get_type(resp) == LAUNCH_DATA_INTEGER) {
1893 if (whichcmd == LAUNCH_KEY_GETLOGMASK) {
1894 m = launch_data_get_integer(resp);
1895 for (j = 0; j < logtblsz; j++) {
1896 if (m & LOG_MASK(logtbl[j].level))
1897 fprintf(stdout, "%s ", logtbl[j].name);
1898 }
1899 fprintf(stdout, "\n");
1900 }
1901 } else {
1902 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
1903 r = 1;
1904 }
1905
1906 launch_data_free(resp);
1907
1908 return r;
1909 }
1910
1911 static const struct {
1912 const char *name;
1913 int lim;
1914 } limlookup[] = {
1915 { "cpu", RLIMIT_CPU },
1916 { "filesize", RLIMIT_FSIZE },
1917 { "data", RLIMIT_DATA },
1918 { "stack", RLIMIT_STACK },
1919 { "core", RLIMIT_CORE },
1920 { "rss", RLIMIT_RSS },
1921 { "memlock", RLIMIT_MEMLOCK },
1922 { "maxproc", RLIMIT_NPROC },
1923 { "maxfiles", RLIMIT_NOFILE }
1924 };
1925
1926 static const size_t limlookupcnt = sizeof limlookup / sizeof limlookup[0];
1927
1928 ssize_t
1929 name2num(const char *n)
1930 {
1931 size_t i;
1932
1933 for (i = 0; i < limlookupcnt; i++) {
1934 if (!strcmp(limlookup[i].name, n)) {
1935 return limlookup[i].lim;
1936 }
1937 }
1938 return -1;
1939 }
1940
1941 const char *
1942 num2name(int n)
1943 {
1944 size_t i;
1945
1946 for (i = 0; i < limlookupcnt; i++) {
1947 if (limlookup[i].lim == n)
1948 return limlookup[i].name;
1949 }
1950 return NULL;
1951 }
1952
1953 const char *
1954 lim2str(rlim_t val, char *buf)
1955 {
1956 if (val == RLIM_INFINITY)
1957 strcpy(buf, "unlimited");
1958 else
1959 sprintf(buf, "%lld", val);
1960 return buf;
1961 }
1962
1963 bool
1964 str2lim(const char *buf, rlim_t *res)
1965 {
1966 char *endptr;
1967 *res = strtoll(buf, &endptr, 10);
1968 if (!strcmp(buf, "unlimited")) {
1969 *res = RLIM_INFINITY;
1970 return false;
1971 } else if (*endptr == '\0') {
1972 return false;
1973 }
1974 return true;
1975 }
1976
1977 int
1978 limit_cmd(int argc __attribute__((unused)), char *const argv[])
1979 {
1980 char slimstr[100];
1981 char hlimstr[100];
1982 struct rlimit *lmts = NULL;
1983 launch_data_t resp, resp1 = NULL, msg, tmp;
1984 int r = 0;
1985 size_t i, lsz = -1;
1986 ssize_t which = 0;
1987 rlim_t slim = -1, hlim = -1;
1988 bool badargs = false;
1989
1990 if (argc > 4)
1991 badargs = true;
1992
1993 if (argc >= 3 && str2lim(argv[2], &slim))
1994 badargs = true;
1995 else
1996 hlim = slim;
1997
1998 if (argc == 4 && str2lim(argv[3], &hlim))
1999 badargs = true;
2000
2001 if (argc >= 2 && -1 == (which = name2num(argv[1])))
2002 badargs = true;
2003
2004 if (badargs) {
2005 fprintf(stderr, "usage: %s %s [", getprogname(), argv[0]);
2006 for (i = 0; i < limlookupcnt; i++)
2007 fprintf(stderr, "%s %s", limlookup[i].name, (i + 1) == limlookupcnt ? "" : "| ");
2008 fprintf(stderr, "[both | soft hard]]\n");
2009 return 1;
2010 }
2011
2012 msg = launch_data_new_string(LAUNCH_KEY_GETRESOURCELIMITS);
2013 resp = launch_msg(msg);
2014 launch_data_free(msg);
2015
2016 if (resp == NULL) {
2017 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2018 return 1;
2019 } else if (launch_data_get_type(resp) == LAUNCH_DATA_OPAQUE) {
2020 lmts = launch_data_get_opaque(resp);
2021 lsz = launch_data_get_opaque_size(resp);
2022 if (argc <= 2) {
2023 for (i = 0; i < (lsz / sizeof(struct rlimit)); i++) {
2024 if (argc == 2 && (size_t)which != i)
2025 continue;
2026 fprintf(stdout, "\t%-12s%-15s%-15s\n", num2name(i),
2027 lim2str(lmts[i].rlim_cur, slimstr),
2028 lim2str(lmts[i].rlim_max, hlimstr));
2029 }
2030 }
2031 } else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) {
2032 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], launch_data_get_string(resp));
2033 r = 1;
2034 } else {
2035 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
2036 r = 1;
2037 }
2038
2039 if (argc <= 2 || r != 0) {
2040 launch_data_free(resp);
2041 return r;
2042 } else {
2043 resp1 = resp;
2044 }
2045
2046 lmts[which].rlim_cur = slim;
2047 lmts[which].rlim_max = hlim;
2048
2049 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2050 tmp = launch_data_new_opaque(lmts, lsz);
2051 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETRESOURCELIMITS);
2052 resp = launch_msg(msg);
2053 launch_data_free(msg);
2054
2055 if (resp == NULL) {
2056 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2057 return 1;
2058 } else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) {
2059 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], launch_data_get_string(resp));
2060 r = 1;
2061 } else if (launch_data_get_type(resp) != LAUNCH_DATA_OPAQUE) {
2062 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
2063 r = 1;
2064 }
2065
2066 launch_data_free(resp);
2067 launch_data_free(resp1);
2068
2069 return r;
2070 }
2071
2072 int
2073 umask_cmd(int argc, char *const argv[])
2074 {
2075 launch_data_t resp, msg;
2076 bool badargs = false;
2077 char *endptr;
2078 long m = 0;
2079 int r = 0;
2080
2081 if (argc == 2) {
2082 m = strtol(argv[1], &endptr, 8);
2083 if (*endptr != '\0' || m > 0777)
2084 badargs = true;
2085 }
2086
2087 if (argc > 2 || badargs) {
2088 fprintf(stderr, "usage: %s %s <mask>\n", getprogname(), argv[0]);
2089 return 1;
2090 }
2091
2092
2093 if (argc == 1) {
2094 msg = launch_data_new_string(LAUNCH_KEY_GETUMASK);
2095 } else {
2096 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2097 launch_data_dict_insert(msg, launch_data_new_integer(m), LAUNCH_KEY_SETUMASK);
2098 }
2099 resp = launch_msg(msg);
2100 launch_data_free(msg);
2101
2102 if (resp == NULL) {
2103 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2104 return 1;
2105 } else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) {
2106 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], launch_data_get_string(resp));
2107 r = 1;
2108 } else if (launch_data_get_type(resp) != LAUNCH_DATA_INTEGER) {
2109 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
2110 r = 1;
2111 } else if (argc == 1) {
2112 fprintf(stdout, "%o\n", (unsigned int)launch_data_get_integer(resp));
2113 }
2114
2115 launch_data_free(resp);
2116
2117 return r;
2118 }
2119
2120 int
2121 submit_cmd(int argc, char *const argv[])
2122 {
2123 launch_data_t msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2124 launch_data_t job = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2125 launch_data_t resp, largv = launch_data_alloc(LAUNCH_DATA_ARRAY);
2126 int ch, i, r = 0;
2127
2128 launch_data_dict_insert(job, launch_data_new_bool(false), LAUNCH_JOBKEY_ONDEMAND);
2129
2130 while ((ch = getopt(argc, argv, "l:p:o:e:")) != -1) {
2131 switch (ch) {
2132 case 'l':
2133 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_LABEL);
2134 break;
2135 case 'p':
2136 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_PROGRAM);
2137 break;
2138 case 'o':
2139 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_STANDARDOUTPATH);
2140 break;
2141 case 'e':
2142 launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_STANDARDERRORPATH);
2143 break;
2144 default:
2145 fprintf(stderr, "usage: %s submit ...\n", getprogname());
2146 return 1;
2147 }
2148 }
2149 argc -= optind;
2150 argv += optind;
2151
2152 for (i = 0; argv[i]; i++) {
2153 launch_data_array_append(largv, launch_data_new_string(argv[i]));
2154 }
2155
2156 launch_data_dict_insert(job, largv, LAUNCH_JOBKEY_PROGRAMARGUMENTS);
2157
2158 launch_data_dict_insert(msg, job, LAUNCH_KEY_SUBMITJOB);
2159
2160 resp = launch_msg(msg);
2161 launch_data_free(msg);
2162
2163 if (resp == NULL) {
2164 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2165 return 1;
2166 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
2167 errno = launch_data_get_errno(resp);
2168 if (errno) {
2169 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(errno));
2170 r = 1;
2171 }
2172 } else {
2173 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], "unknown response");
2174 }
2175
2176 launch_data_free(resp);
2177
2178 return r;
2179 }
2180
2181 int
2182 getrusage_cmd(int argc, char *const argv[])
2183 {
2184 launch_data_t resp, msg;
2185 bool badargs = false;
2186 int r = 0;
2187
2188 if (argc != 2)
2189 badargs = true;
2190 else if (strcmp(argv[1], "self") && strcmp(argv[1], "children"))
2191 badargs = true;
2192
2193 if (badargs) {
2194 fprintf(stderr, "usage: %s %s self | children\n", getprogname(), argv[0]);
2195 return 1;
2196 }
2197
2198 if (!strcmp(argv[1], "self")) {
2199 msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGESELF);
2200 } else {
2201 msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGECHILDREN);
2202 }
2203
2204 resp = launch_msg(msg);
2205 launch_data_free(msg);
2206
2207 if (resp == NULL) {
2208 fprintf(stderr, "launch_msg(): %s\n", strerror(errno));
2209 return 1;
2210 } else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
2211 fprintf(stderr, "%s %s error: %s\n", getprogname(), argv[0], strerror(launch_data_get_errno(resp)));
2212 r = 1;
2213 } else if (launch_data_get_type(resp) == LAUNCH_DATA_OPAQUE) {
2214 struct rusage *rusage = launch_data_get_opaque(resp);
2215 fprintf(stdout, "\t%-10f\tuser time used\n",
2216 (double)rusage->ru_utime.tv_sec + (double)rusage->ru_utime.tv_usec / (double)1000000);
2217 fprintf(stdout, "\t%-10f\tsystem time used\n",
2218 (double)rusage->ru_stime.tv_sec + (double)rusage->ru_stime.tv_usec / (double)1000000);
2219 fprintf(stdout, "\t%-10ld\tmax resident set size\n", rusage->ru_maxrss);
2220 fprintf(stdout, "\t%-10ld\tshared text memory size\n", rusage->ru_ixrss);
2221 fprintf(stdout, "\t%-10ld\tunshared data size\n", rusage->ru_idrss);
2222 fprintf(stdout, "\t%-10ld\tunshared stack size\n", rusage->ru_isrss);
2223 fprintf(stdout, "\t%-10ld\tpage reclaims\n", rusage->ru_minflt);
2224 fprintf(stdout, "\t%-10ld\tpage faults\n", rusage->ru_majflt);
2225 fprintf(stdout, "\t%-10ld\tswaps\n", rusage->ru_nswap);
2226 fprintf(stdout, "\t%-10ld\tblock input operations\n", rusage->ru_inblock);
2227 fprintf(stdout, "\t%-10ld\tblock output operations\n", rusage->ru_oublock);
2228 fprintf(stdout, "\t%-10ld\tmessages sent\n", rusage->ru_msgsnd);
2229 fprintf(stdout, "\t%-10ld\tmessages received\n", rusage->ru_msgrcv);
2230 fprintf(stdout, "\t%-10ld\tsignals received\n", rusage->ru_nsignals);
2231 fprintf(stdout, "\t%-10ld\tvoluntary context switches\n", rusage->ru_nvcsw);
2232 fprintf(stdout, "\t%-10ld\tinvoluntary context switches\n", rusage->ru_nivcsw);
2233 } else {
2234 fprintf(stderr, "%s %s returned unknown response\n", getprogname(), argv[0]);
2235 r = 1;
2236 }
2237
2238 launch_data_free(resp);
2239
2240 return r;
2241 }
2242
2243 bool
2244 launch_data_array_append(launch_data_t a, launch_data_t o)
2245 {
2246 size_t offt = launch_data_array_get_count(a);
2247
2248 return launch_data_array_set_index(a, o, offt);
2249 }
2250
2251 mach_port_t
2252 str2bsport(const char *s)
2253 {
2254 bool getrootbs = strcmp(s, "/") == 0;
2255 mach_port_t last_bport, bport = bootstrap_port;
2256 task_t task = mach_task_self();
2257 kern_return_t result;
2258
2259 if (strcmp(s, "..") == 0 || getrootbs) {
2260 do {
2261 last_bport = bport;
2262 result = bootstrap_parent(last_bport, &bport);
2263
2264 if (result == BOOTSTRAP_NOT_PRIVILEGED) {
2265 fprintf(stderr, "Permission denied\n");
2266 return 1;
2267 } else if (result != BOOTSTRAP_SUCCESS) {
2268 fprintf(stderr, "bootstrap_parent() %d\n", result);
2269 return 1;
2270 }
2271 } while (getrootbs && last_bport != bport);
2272 } else {
2273 int pid = atoi(s);
2274
2275 result = task_for_pid(mach_task_self(), pid, &task);
2276
2277 if (result != KERN_SUCCESS) {
2278 fprintf(stderr, "task_for_pid() %s\n", mach_error_string(result));
2279 return 1;
2280 }
2281
2282 result = task_get_bootstrap_port(task, &bport);
2283
2284 if (result != KERN_SUCCESS) {
2285 fprintf(stderr, "Couldn't get bootstrap port: %s\n", mach_error_string(result));
2286 return 1;
2287 }
2288 }
2289
2290 return bport;
2291 }
2292
2293 int
2294 bsexec_cmd(int argc, char *const argv[])
2295 {
2296 kern_return_t result;
2297 mach_port_t bport;
2298
2299 if (argc < 3) {
2300 fprintf(stderr, "usage: %s bsexec <PID> prog...\n", getprogname());
2301 return 1;
2302 }
2303
2304 bport = str2bsport(argv[1]);
2305
2306 result = task_set_bootstrap_port(mach_task_self(), bport);
2307
2308 if (result != KERN_SUCCESS) {
2309 fprintf(stderr, "Couldn't switch to new bootstrap port: %s\n", mach_error_string(result));
2310 return 1;
2311 }
2312
2313 setgid(getgid());
2314 setuid(getuid());
2315
2316 execvp(argv[2], argv + 2);
2317 fprintf(stderr, "execvp(): %s\n", strerror(errno));
2318 return 1;
2319 }
2320
2321 int
2322 bslist_cmd(int argc, char *const argv[])
2323 {
2324 kern_return_t result;
2325 mach_port_t bport = bootstrap_port;
2326 name_array_t service_names;
2327 mach_msg_type_number_t service_cnt, service_active_cnt;
2328 bootstrap_status_array_t service_actives;
2329 unsigned int i;
2330
2331 if (argc == 2)
2332 bport = str2bsport(argv[1]);
2333
2334 if (bport == MACH_PORT_NULL) {
2335 fprintf(stderr, "Invalid bootstrap port\n");
2336 return 1;
2337 }
2338
2339 result = bootstrap_info(bport, &service_names, &service_cnt, &service_actives, &service_active_cnt);
2340 if (result != BOOTSTRAP_SUCCESS) {
2341 fprintf(stderr, "bootstrap_info(): %d\n", result);
2342 return 1;
2343 }
2344
2345 #define bport_state(x) (((x) == BOOTSTRAP_STATUS_ACTIVE) ? "A" : ((x) == BOOTSTRAP_STATUS_ON_DEMAND) ? "D" : "I")
2346
2347 for (i = 0; i < service_cnt ; i++)
2348 fprintf(stdout, "%-3s%s\n", bport_state((service_actives[i])), service_names[i]);
2349
2350 return 0;
2351 }
2352
2353 bool
2354 is_legacy_mach_job(launch_data_t obj)
2355 {
2356 bool has_servicename = launch_data_dict_lookup(obj, MACHINIT_JOBKEY_SERVICENAME);
2357 bool has_command = launch_data_dict_lookup(obj, MACHINIT_JOBKEY_COMMAND);
2358 bool has_label = launch_data_dict_lookup(obj, LAUNCH_JOBKEY_LABEL);
2359
2360 return has_command && has_servicename && !has_label;
2361 }
2362
2363 void
2364 _log_launchctl_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test)
2365 {
2366 int saved_errno = errno;
2367 char buf[100];
2368 const char *file = strrchr(path, '/');
2369 char *rcs_rev_tmp = strchr(rcs_rev, ' ');
2370
2371 if (!file) {
2372 file = path;
2373 } else {
2374 file += 1;
2375 }
2376
2377 if (!rcs_rev_tmp) {
2378 strlcpy(buf, rcs_rev, sizeof(buf));
2379 } else {
2380 strlcpy(buf, rcs_rev_tmp + 1, sizeof(buf));
2381 rcs_rev_tmp = strchr(buf, ' ');
2382 if (rcs_rev_tmp)
2383 *rcs_rev_tmp = '\0';
2384 }
2385
2386 fprintf(stderr, "Bug: %s:%u (%s):%u: %s\n", file, line, buf, saved_errno, test);
2387 }
2388
2389 void
2390 loopback_setup_ipv4(void)
2391 {
2392 struct ifaliasreq ifra;
2393 struct ifreq ifr;
2394 int s;
2395
2396 memset(&ifr, 0, sizeof(ifr));
2397 strcpy(ifr.ifr_name, "lo0");
2398
2399 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
2400 return;
2401
2402 if (assumes(ioctl(s, SIOCGIFFLAGS, &ifr) != -1)) {
2403 ifr.ifr_flags |= IFF_UP;
2404 assumes(ioctl(s, SIOCSIFFLAGS, &ifr) != -1);
2405 }
2406
2407 memset(&ifra, 0, sizeof(ifra));
2408 strcpy(ifra.ifra_name, "lo0");
2409 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_family = AF_INET;
2410 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
2411 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_len = sizeof(struct sockaddr_in);
2412 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_family = AF_INET;
2413 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_addr.s_addr = htonl(IN_CLASSA_NET);
2414 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_len = sizeof(struct sockaddr_in);
2415
2416 assumes(ioctl(s, SIOCAIFADDR, &ifra) != -1);
2417
2418 assumes(close(s) == 0);
2419 }
2420
2421 void
2422 loopback_setup_ipv6(void)
2423 {
2424 struct in6_aliasreq ifra6;
2425 struct ifreq ifr;
2426 int s6;
2427
2428 memset(&ifr, 0, sizeof(ifr));
2429 strcpy(ifr.ifr_name, "lo0");
2430
2431 if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
2432 return;
2433
2434 memset(&ifr, 0, sizeof(ifr));
2435 strcpy(ifr.ifr_name, "lo0");
2436
2437 if (assumes(ioctl(s6, SIOCGIFFLAGS, &ifr) != -1)) {
2438 ifr.ifr_flags |= IFF_UP;
2439 assumes(ioctl(s6, SIOCSIFFLAGS, &ifr) != -1);
2440 }
2441
2442 memset(&ifra6, 0, sizeof(ifra6));
2443 strcpy(ifra6.ifra_name, "lo0");
2444
2445 ifra6.ifra_addr.sin6_family = AF_INET6;
2446 ifra6.ifra_addr.sin6_addr = in6addr_loopback;
2447 ifra6.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
2448 ifra6.ifra_prefixmask.sin6_family = AF_INET6;
2449 memset(&ifra6.ifra_prefixmask.sin6_addr, 0xff, sizeof(struct in6_addr));
2450 ifra6.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
2451 ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
2452 ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
2453
2454 assumes(ioctl(s6, SIOCAIFADDR_IN6, &ifra6) != -1);
2455
2456 assumes(close(s6) == 0);
2457 }
2458
2459 pid_t
2460 fwexec(const char *const *argv, bool _wait)
2461 {
2462 int wstatus;
2463 pid_t p;
2464
2465 switch ((p = fork())) {
2466 case -1:
2467 break;
2468 case 0:
2469 setsid();
2470 execvp(argv[0], (char *const *)argv);
2471 _exit(EXIT_FAILURE);
2472 break;
2473 default:
2474 if (!_wait)
2475 return p;
2476 if (p == waitpid(p, &wstatus, 0)) {
2477 if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == EXIT_SUCCESS)
2478 return p;
2479 }
2480 break;
2481 }
2482
2483 return -1;
2484 }
2485
2486 void
2487 do_potential_fsck(void)
2488 {
2489 const char *safe_fsck_tool[] = { "fsck", "-fy", NULL };
2490 const char *fsck_tool[] = { "fsck", "-p", NULL };
2491 const char *remount_tool[] = { "mount", "-uw", "/", NULL };
2492 struct statfs sfs;
2493
2494 if (assumes(statfs("/", &sfs) != -1)) {
2495 if (!(sfs.f_flags & MNT_RDONLY)) {
2496 fprintf(stdout, "Root file system is read-write, skipping fsck.\n");
2497 return;
2498 }
2499 }
2500
2501 if (!is_safeboot()) {
2502 if (fwexec(fsck_tool, true) != -1)
2503 goto out;
2504 }
2505
2506 if (fwexec(safe_fsck_tool, true) != -1) {
2507 goto out;
2508 }
2509
2510 fprintf(stderr, "fsck failed! Leaving the root file system read-only...\n");
2511
2512 return;
2513 out:
2514 assumes(fwexec(remount_tool, true) != -1);
2515 }
2516
2517 bool
2518 path_check(const char *path)
2519 {
2520 struct stat sb;
2521
2522 if (stat(path, &sb) == 0)
2523 return true;
2524 return false;
2525 }
2526
2527 bool
2528 is_safeboot(void)
2529 {
2530 int sbmib[] = { CTL_KERN, KERN_SAFEBOOT };
2531 uint32_t sb = 0;
2532 size_t sbsz = sizeof(sb);
2533
2534 if (!assumes(sysctl(sbmib, 2, &sb, &sbsz, NULL, 0) == 0))
2535 return false;
2536
2537 return (bool)sb;
2538 }
2539
2540 bool
2541 is_netboot(void)
2542 {
2543 int nbmib[] = { CTL_KERN, KERN_NETBOOT };
2544 uint32_t nb = 0;
2545 size_t nbsz = sizeof(nb);
2546
2547 if (!assumes(sysctl(nbmib, 2, &nb, &nbsz, NULL, 0) == 0))
2548 return false;
2549
2550 return (bool)nb;
2551 }
2552
2553 void
2554 apply_func_to_dir(const char *thedir, void (*thefunc)(const char *))
2555 {
2556 struct dirent *de;
2557 DIR *od;
2558 int currend_dir_fd;
2559
2560 if (!assumes((currend_dir_fd = open(".", 0)) != -1))
2561 return;
2562
2563 if (!assumes(chdir(thedir) != -1))
2564 goto out;
2565
2566 if (!assumes(od = opendir(".")))
2567 goto out;
2568
2569 while ((de = readdir(od))) {
2570 struct stat sb;
2571
2572 if (strcmp(de->d_name, ".") == 0)
2573 continue;
2574 if (strcmp(de->d_name, "..") == 0)
2575 continue;
2576
2577 if (assumes(stat(de->d_name, &sb) != -1)) {
2578 if (S_ISDIR(sb.st_mode))
2579 apply_func_to_dir(de->d_name, thefunc);
2580 thefunc(de->d_name);
2581 }
2582 }
2583
2584 assumes(closedir(od) != -1);
2585
2586 out:
2587 assumes(fchdir(currend_dir_fd) != -1);
2588 assumes(close(currend_dir_fd) != -1);
2589 }
2590
2591 void
2592 empty_dir(const char *path)
2593 {
2594 assumes(chflags(path, 0) != -1);
2595 assumes(remove(path) != -1);
2596 }
2597
2598 int
2599 touch_file(const char *path, mode_t m)
2600 {
2601 int fd = open(path, O_CREAT, m);
2602
2603 if (fd == -1)
2604 return -1;
2605
2606 return close(fd);
2607 }
2608
2609 void
2610 apply_sysctls_from_file(const char *thefile)
2611 {
2612 const char *sysctl_tool[] = { "sysctl", "-w", NULL, NULL };
2613 size_t ln_len = 0;
2614 char *val, *tmpstr;
2615 FILE *sf;
2616
2617 if (!(sf = fopen(thefile, "r")))
2618 return;
2619
2620 while ((val = fgetln(sf, &ln_len))) {
2621 if (ln_len == 0)
2622 continue;
2623 if (!assumes((tmpstr = malloc(ln_len + 1)) != NULL))
2624 continue;
2625 memcpy(tmpstr, val, ln_len);
2626 tmpstr[ln_len] = 0;
2627 val = tmpstr;
2628
2629 while (*val && isspace(*val))
2630 val++;
2631 if (*val == '\0' || *val == '#')
2632 goto skip_sysctl_tool;
2633 sysctl_tool[2] = val;
2634 assumes(fwexec(sysctl_tool, true) != -1);
2635 skip_sysctl_tool:
2636 free(tmpstr);
2637 }
2638
2639 assumes(fclose(sf) == 0);
2640 }
2641
2642 void
2643 do_sysversion_sysctl(void)
2644 {
2645 int mib[] = { CTL_KERN, KERN_OSVERSION };
2646 CFDictionaryRef versdict;
2647 CFStringRef buildvers;
2648 char buf[1024];
2649 size_t bufsz = sizeof(buf);
2650
2651 /* <rdar://problem/4477682> ER: launchd should set kern.osversion very early in boot */
2652
2653 if (getuid() != 0)
2654 return;
2655
2656 if (sysctl(mib, 2, buf, &bufsz, NULL, 0) == -1) {
2657 fprintf(stderr, "sysctl(): %s\n", strerror(errno));
2658 return;
2659 }
2660
2661 if (buf[0] != '\0')
2662 return;
2663
2664 versdict = _CFCopySystemVersionDictionary();
2665 buildvers = CFDictionaryGetValue(versdict, _kCFSystemVersionBuildVersionKey);
2666 CFStringGetCString(buildvers, buf, sizeof(buf), kCFStringEncodingUTF8);
2667
2668 if (sysctl(mib, 2, NULL, 0, buf, strlen(buf) + 1) == -1) {
2669 fprintf(stderr, "sysctl(): %s\n", strerror(errno));
2670 }
2671
2672 CFRelease(versdict);
2673 }