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