1/*
2 * Copyright (c) 2005-2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_APACHE_LICENSE_HEADER_START@
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * @APPLE_APACHE_LICENSE_HEADER_END@
19 */
20
21#include "config.h"
22#include "launch.h"
23#include "launch_priv.h"
24#include "bootstrap.h"
25#include "vproc.h"
26#include "vproc_priv.h"
27#include "vproc_internal.h"
28#include "bootstrap_priv.h"
29#include "launch_internal.h"
30
31#include <CoreFoundation/CoreFoundation.h>
32#include <CoreFoundation/CFPriv.h>
33#include <CoreFoundation/CFLogUtilities.h>
34#include <TargetConditionals.h>
35#include <IOKit/IOKitLib.h>
36#include <NSSystemDirectories.h>
37#include <mach/mach.h>
38#include <mach-o/getsect.h>
39#include <sys/types.h>
40#include <sys/sysctl.h>
41#include <sys/time.h>
42#include <sys/sysctl.h>
43#include <sys/stat.h>
44#include <sys/socket.h>
45#include <sys/un.h>
46#include <sys/fcntl.h>
47#include <sys/event.h>
48#include <sys/resource.h>
49#include <sys/param.h>
50#include <sys/mount.h>
51#include <sys/reboot.h>
52#include <net/if.h>
53#include <netinet/in.h>
54#include <netinet/in_var.h>
55#include <netinet6/nd6.h>
56#include <unistd.h>
57#include <dirent.h>
58#include <libgen.h>
59#include <libinfo.h>
60#include <pwd.h>
61#include <stdio.h>
62#include <stdlib.h>
63#include <pwd.h>
64#include <grp.h>
65#include <netdb.h>
66#include <syslog.h>
67#include <glob.h>
68#include <readline/readline.h>
69#include <readline/history.h>
70#include <dns_sd.h>
71#include <paths.h>
72#include <utmpx.h>
73#include <bootfiles.h>
74#include <sysexits.h>
75#include <util.h>
76#include <spawn.h>
77#include <sys/syslimits.h>
78#include <fnmatch.h>
79#include <os/assumes.h>
80#include <dlfcn.h>
81#if HAVE_SYSTEMSTATS
82#include <systemstats/systemstats.h>
83#endif
84
85#if HAVE_LIBAUDITD
86#include <bsm/auditd_lib.h>
87#ifndef	AUDITD_PLIST_FILE
88#define	AUDITD_PLIST_FILE "/System/Library/LaunchDaemons/com.apple.auditd.plist"
89#endif
90#endif
91
92extern char **environ;
93
94#define LAUNCH_SECDIR _PATH_TMP "launch-XXXXXX"
95#define LAUNCH_ENV_KEEPCONTEXT	"LaunchKeepContext"
96#define LAUNCH_ENV_BOOTSTRAPPINGSYSTEM "LaunchBootstrappingSystem"
97
98#define CFTypeCheck(cf, type) (CFGetTypeID(cf) == type ## GetTypeID())
99#define CFReleaseIfNotNULL(cf) if (cf) CFRelease(cf);
100
101#if TARGET_OS_EMBEDDED
102#include <sys/kern_memorystatus.h>
103
104#define XPC_PLIST_CACHE "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib"
105#define XPC_PLIST_CACHE_KEY "LaunchDaemons"
106
107#if JETSAM_PRIORITY_REVISION
108#define READ_JETSAM_DEFAULTS 1
109#define JETSAM_PROP_DIR "/System/Library/LaunchDaemons"
110#define JETSAM_PROP_DIR_LENGTH (sizeof(JETSAM_PROP_DIR) - 1)
111#define JETSAM_PROP_PREFIX "com.apple.jetsamproperties."
112#define JETSAM_PROP_PREFIX_LENGTH (sizeof(JETSAM_PROP_PREFIX) - 1)
113#define JETSAM_PROP_SUFFIX ".plist"
114#define JETSAM_PROP_SUFFIX_LENGTH (sizeof(JETSAM_PROP_SUFFIX) - 1)
115#endif
116#endif
117
118struct load_unload_state {
119	launch_data_t pass1;
120	char *session_type;
121	bool editondisk:1, load:1, forceload:1;
122};
123
124static void launchctl_log(int level, const char *fmt, ...);
125static void launchctl_log_CFString(int level, CFStringRef string);
126static void myCFDictionaryApplyFunction(const void *key, const void *value, void *context);
127static CFTypeRef CFTypeCreateFromLaunchData(launch_data_t obj);
128static CFArrayRef CFArrayCreateFromLaunchArray(launch_data_t arr);
129static CFDictionaryRef CFDictionaryCreateFromLaunchDictionary(launch_data_t dict);
130static bool launch_data_array_append(launch_data_t a, launch_data_t o);
131static void insert_event(launch_data_t, const char *, const char *, launch_data_t);
132static void distill_jobs(launch_data_t);
133static void distill_config_file(launch_data_t);
134static void distill_fsevents(launch_data_t);
135static void sock_dict_cb(launch_data_t what, const char *key, void *context);
136static void sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob);
137static launch_data_t CF2launch_data(CFTypeRef);
138static launch_data_t read_plist_file(const char *file, bool editondisk, bool load);
139#if TARGET_OS_EMBEDDED
140static CFPropertyListRef GetPropertyListFromCache(void);
141static CFPropertyListRef CreateMyPropertyListFromCachedFile(const char *posixfile);
142static bool require_jobs_from_cache(void);
143#endif
144static CFPropertyListRef CreateMyPropertyListFromFile(const char *);
145static CFPropertyListRef CFPropertyListCreateFromFile(CFURLRef plistURL);
146static void WriteMyPropertyListToFile(CFPropertyListRef, const char *);
147static bool path_goodness_check(const char *path, bool forceload);
148static void readpath(const char *, struct load_unload_state *);
149static void readfile(const char *, struct load_unload_state *);
150static int _fd(int);
151static int demux_cmd(int argc, char *const argv[]);
152static void submit_job_pass(launch_data_t jobs);
153static void do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup);
154static mach_port_t str2bsport(const char *s);
155static void print_jobs(launch_data_t j, const char *key, void *context);
156static void print_obj(launch_data_t obj, const char *key, void *context);
157static bool str2lim(const char *buf, rlim_t *res);
158static const char *lim2str(rlim_t val, char *buf);
159static const char *num2name(int n);
160static ssize_t name2num(const char *n);
161static void unloadjob(launch_data_t job);
162static void print_key_value(launch_data_t obj, const char *key, void *context);
163static void print_launchd_env(launch_data_t obj, const char *key, void *context);
164static void loopback_setup_ipv4(void);
165static void loopback_setup_ipv6(void);
166static pid_t fwexec(const char *const *argv, int *wstatus);
167static void do_potential_fsck(void);
168static bool path_check(const char *path);
169static bool is_safeboot(void);
170static bool is_netboot(void);
171static void apply_sysctls_from_file(const char *thefile);
172static void empty_dir(const char *thedir, struct stat *psb);
173static int touch_file(const char *path, mode_t m);
174static void do_sysversion_sysctl(void);
175static void do_application_firewall_magic(int sfd, launch_data_t thejob);
176static void preheat_page_cache_hack(void);
177static void do_bootroot_magic(void);
178static void do_single_user_mode(bool);
179static bool do_single_user_mode2(void);
180static void do_crash_debug_mode(void);
181static bool do_crash_debug_mode2(void);
182static void read_launchd_conf(void);
183static bool job_disabled_logic(launch_data_t obj);
184static void fix_bogus_file_metadata(void);
185static void do_file_init(void) __attribute__((constructor));
186static void setup_system_context(void);
187static void handle_system_bootstrapper_crashes_separately(void);
188static void fatal_signal_handler(int sig, siginfo_t *si, void *uap);
189
190typedef enum {
191	BOOTCACHE_START = 1,
192	BOOTCACHE_TAG,
193	BOOTCACHE_STOP,
194} BootCache_action_t;
195
196static void do_BootCache_magic(BootCache_action_t what);
197
198static int bootstrap_cmd(int argc, char *const argv[]);
199static int load_and_unload_cmd(int argc, char *const argv[]);
200//static int reload_cmd(int argc, char *const argv[]);
201static int start_stop_remove_cmd(int argc, char *const argv[]);
202static int submit_cmd(int argc, char *const argv[]);
203static int list_cmd(int argc, char *const argv[]);
204
205static int setenv_cmd(int argc, char *const argv[]);
206static int unsetenv_cmd(int argc, char *const argv[]);
207static int getenv_and_export_cmd(int argc, char *const argv[]);
208static int wait4debugger_cmd(int argc, char *const argv[]);
209
210static int limit_cmd(int argc, char *const argv[]);
211static int stdio_cmd(int argc, char *const argv[]);
212static int fyi_cmd(int argc, char *const argv[]);
213static int logupdate_cmd(int argc, char *const argv[]);
214static int umask_cmd(int argc, char *const argv[]);
215static int getrusage_cmd(int argc, char *const argv[]);
216static int bsexec_cmd(int argc, char *const argv[]);
217static int _bslist_cmd(mach_port_t bport, unsigned int depth, bool show_job, bool local_only);
218static int bslist_cmd(int argc, char *const argv[]);
219static int _bstree_cmd(mach_port_t bsport, unsigned int depth, bool show_jobs);
220static int bstree_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)));
221static int managerpid_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)));
222static int manageruid_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)));
223static int managername_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)));
224static int asuser_cmd(int argc, char * const argv[]);
225static int exit_cmd(int argc, char *const argv[]) __attribute__((noreturn));
226static int help_cmd(int argc, char *const argv[]);
227
228static const struct {
229	const char *name;
230	int (*func)(int argc, char *const argv[]);
231	const char *desc;
232} cmds[] = {
233	{ "load",			load_and_unload_cmd,	"Load configuration files and/or directories" },
234	{ "unload",			load_and_unload_cmd,	"Unload configuration files and/or directories" },
235//	{ "reload",			reload_cmd,				"Reload configuration files and/or directories" },
236	{ "start",			start_stop_remove_cmd,	"Start specified job" },
237	{ "stop",			start_stop_remove_cmd,	"Stop specified job" },
238	{ "submit",			submit_cmd,				"Submit a job from the command line" },
239	{ "remove",			start_stop_remove_cmd,	"Remove specified job" },
240	{ "bootstrap",		bootstrap_cmd,			"Bootstrap launchd" },
241	{ "list",			list_cmd,				"List jobs and information about jobs" },
242	{ "setenv",			setenv_cmd,				"Set an environmental variable in launchd" },
243	{ "unsetenv",		unsetenv_cmd,			"Unset an environmental variable in launchd" },
244	{ "getenv",			getenv_and_export_cmd,	"Get an environmental variable from launchd" },
245	{ "export",			getenv_and_export_cmd,	"Export shell settings from launchd" },
246	{ "debug",			wait4debugger_cmd,		"Set the WaitForDebugger flag for the target job to true." },
247	{ "limit",			limit_cmd,				"View and adjust launchd resource limits" },
248	{ "stdout",			stdio_cmd,				"Redirect launchd's standard out to the given path" },
249	{ "stderr",			stdio_cmd,				"Redirect launchd's standard error to the given path" },
250	{ "shutdown",		fyi_cmd,				"Prepare for system shutdown" },
251	{ "singleuser",		fyi_cmd,				"Switch to single-user mode" },
252	{ "getrusage",		getrusage_cmd,			"Get resource usage statistics from launchd" },
253	{ "log",			logupdate_cmd,			"Adjust the logging level or mask of launchd" },
254	{ "umask",			umask_cmd,				"Change launchd's umask" },
255	{ "bsexec",			bsexec_cmd,				"Execute a process within a different Mach bootstrap subset" },
256	{ "bslist",			bslist_cmd,				"List Mach bootstrap services and optional servers" },
257	{ "bstree",			bstree_cmd,				"Show the entire Mach bootstrap tree. Requires root privileges." },
258	{ "managerpid",		managerpid_cmd,			"Print the PID of the launchd managing this Mach bootstrap." },
259	{ "manageruid",		manageruid_cmd,			"Print the UID of the launchd managing this Mach bootstrap." },
260	{ "managername",	managername_cmd,		"Print the name of this Mach bootstrap." },
261	{ "asuser",			asuser_cmd,				"Execute a subcommand in the given user's context." },
262	{ "exit",			exit_cmd,				"Exit the interactive invocation of launchctl" },
263	{ "quit",			exit_cmd,				"Quit the interactive invocation of launchctl" },
264	{ "help",			help_cmd,				"This help output" },
265};
266
267static bool _launchctl_istty;
268static bool _launchctl_verbose;
269static bool _launchctl_is_managed;
270static bool _launchctl_apple_internal;
271static bool _launchctl_system_context;
272static bool _launchctl_uid0_context;
273static bool _launchctl_system_bootstrap;
274static bool _launchctl_peruser_bootstrap;
275static bool _launchctl_verbose_boot = false;
276static bool _launchctl_startup_debugging = false;
277
278static bool _launchctl_overrides_db_changed = false;
279static CFMutableDictionaryRef _launchctl_overrides_db = NULL;
280
281static char *_launchctl_job_overrides_db_path;
282static char *_launchctl_managername = NULL;
283
284#if READ_JETSAM_DEFAULTS
285static CFDictionaryRef _launchctl_jetsam_defaults = NULL;
286static CFDictionaryRef _launchctl_jetsam_defaults_cached = NULL;
287#endif
288
289int
290main(int argc, char *const argv[])
291{
292	char *l;
293
294	if (getenv(LAUNCH_ENV_BOOTSTRAPPINGSYSTEM)) {
295		/* We're bootstrapping the install environment, so we can't talk to
296		 * mDNSResponder or opendirectoryd.
297		 *
298		 * See <rdar://problem/9877230>.
299		 */
300		si_search_module_set_flags("mdns", 1);
301		si_search_module_set_flags("ds", 1);
302	}
303
304	int64_t is_managed = 0;
305	(void)vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &is_managed);
306	_launchctl_is_managed = is_managed;
307
308	_launchctl_istty = isatty(STDIN_FILENO);
309	argc--, argv++;
310
311	if (argc > 0 && argv[0][0] == '-') {
312		char *flago;
313
314		for (flago = argv[0] + 1; *flago; flago++) {
315			switch (*flago) {
316			case 'v':
317				_launchctl_verbose = true;
318				break;
319			case 'u':
320				if (argc > 1) {
321					if (strncmp(argv[1], "root", sizeof("root")) == 0) {
322						_launchctl_uid0_context = true;
323					} else {
324						launchctl_log(LOG_ERR, "Unknown user: %s", argv[1]);
325						exit(EXIT_FAILURE);
326					}
327					argc--, argv++;
328				} else {
329					launchctl_log(LOG_ERR, "-u option requires an argument.");
330				}
331				break;
332			case '1':
333				_launchctl_system_context = true;
334				break;
335			default:
336				launchctl_log(LOG_ERR, "Unknown argument: '-%c'", *flago);
337				break;
338			}
339		}
340		argc--, argv++;
341	}
342
343	/* Running in the context of the root user's per-user launchd is only from
344	 * within that session.
345	 */
346	if (_launchctl_uid0_context) {
347		int64_t manager_uid = -1, manager_pid = -1;
348		(void)vproc_swap_integer(NULL, VPROC_GSK_MGR_UID, NULL, &manager_uid);
349		(void)vproc_swap_integer(NULL, VPROC_GSK_MGR_PID, NULL, &manager_pid);
350		if (manager_uid || manager_pid == 1) {
351			launchctl_log(LOG_ERR, "Running in the root user's per-user context is not supported outside of the root user's bootstrap.");
352			exit(EXIT_FAILURE);
353		}
354	} else if (!(_launchctl_system_context || _launchctl_uid0_context)) {
355		/* Running in the system context is implied when we're running as root
356		 * and not running as a bootstrapper.
357		 */
358		_launchctl_system_context = (!_launchctl_is_managed && getuid() == 0);
359	}
360
361	if (_launchctl_system_context) {
362		if (getuid() == 0) {
363			setup_system_context();
364		} else {
365			launchctl_log(LOG_ERR, "You must be root to run in the system context.");
366			exit(EXIT_FAILURE);
367		}
368	} else if (_launchctl_uid0_context) {
369		if (getuid() != 0) {
370			launchctl_log(LOG_ERR, "You must be root to run in the root user context.");
371			exit(EXIT_FAILURE);
372		}
373	}
374
375	if (!readline) {
376		launchctl_log(LOG_ERR, "missing library: readline");
377		exit(EXIT_FAILURE);
378	}
379
380	if (argc == 0) {
381		while ((l = readline(_launchctl_istty ? "launchd% " : NULL))) {
382			char *inputstring = l, *argv2[100], **ap = argv2;
383			int i = 0;
384
385			while ((*ap = strsep(&inputstring, " \t"))) {
386				if (**ap != '\0') {
387					ap++;
388					i++;
389				}
390			}
391
392			if (i > 0) {
393				demux_cmd(i, argv2);
394			}
395
396			free(l);
397		}
398
399		if (_launchctl_istty) {
400			fputc('\n', stdout);
401		}
402	}
403
404	if (argc > 0) {
405		exit(demux_cmd(argc, argv));
406	}
407
408	exit(EXIT_SUCCESS);
409}
410
411int
412demux_cmd(int argc, char *const argv[])
413{
414	size_t i;
415
416	optind = 1;
417	optreset = 1;
418
419	for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) {
420		if (!strcmp(cmds[i].name, argv[0])) {
421			return cmds[i].func(argc, argv);
422		}
423	}
424
425	launchctl_log(LOG_ERR, "%s: unknown subcommand \"%s\"", getprogname(), argv[0]);
426	return 1;
427}
428
429void
430launchctl_log(int level, const char *fmt, ...)
431{
432	va_list ap;
433	va_start(ap, fmt);
434
435	if (_launchctl_is_managed) {
436		vsyslog(level, fmt, ap);
437	} else {
438		char *buff = NULL;
439		(void)vasprintf(&buff, fmt, ap);
440
441		FILE *where = stdout;
442		if (level < LOG_NOTICE) {
443			where = stderr;
444		}
445
446		fprintf(where, "%s\n", buff);
447		free(buff);
448	}
449
450	va_end(ap);
451}
452
453void
454launchctl_log_CFString(int level, CFStringRef string)
455{
456	// Big enough. Don't feel like jumping through CF's hoops.
457	char *buff = malloc(4096);
458	(void)CFStringGetCString(string, buff, 4096, kCFStringEncodingUTF8);
459	launchctl_log(level, "%s", buff);
460	free(buff);
461}
462
463void
464read_launchd_conf(void)
465{
466#if !TARGET_OS_EMBEDDED
467	char s[1000], *c, *av[100];
468	const char *file;
469	size_t len;
470	int i;
471	FILE *f;
472
473	if (getppid() == 1) {
474		file = "/etc/launchd.conf";
475	} else {
476		file = "/etc/launchd-user.conf";
477	}
478
479	if (!(f = fopen(file, "r"))) {
480		return;
481	}
482
483	while ((c = fgets(s, (int) sizeof s, f))) {
484		len = strlen(c);
485		if (len && c[len - 1] == '\n') {
486			c[len - 1] = '\0';
487		}
488
489		i = 0;
490
491		while ((av[i] = strsep(&c, " \t"))) {
492			if (*(av[i]) != '\0') {
493				i++;
494			}
495		}
496
497		if (i > 0) {
498			demux_cmd(i, av);
499		}
500	}
501
502	fclose(f);
503#endif // !TARGET_OS_EMBEDDED
504}
505
506static CFPropertyListRef
507CFPropertyListCreateFromFile(CFURLRef plistURL)
508{
509	CFReadStreamRef plistReadStream = CFReadStreamCreateWithFile(NULL, plistURL);
510
511	CFErrorRef streamErr = NULL;
512	if (!CFReadStreamOpen(plistReadStream)) {
513		streamErr = CFReadStreamCopyError(plistReadStream);
514		CFStringRef errString = CFErrorCopyDescription(streamErr);
515
516		launchctl_log_CFString(LOG_ERR, errString);
517
518		CFRelease(errString);
519		CFRelease(streamErr);
520	}
521
522	CFPropertyListRef plist = NULL;
523	if (plistReadStream) {
524		CFStringRef errString = NULL;
525		CFPropertyListFormat plistFormat = 0;
526		plist = CFPropertyListCreateFromStream(NULL, plistReadStream, 0, kCFPropertyListImmutable, &plistFormat, &errString);
527		if (!plist) {
528			launchctl_log_CFString(LOG_ERR, errString);
529			CFRelease(errString);
530		}
531	}
532
533	CFReadStreamClose(plistReadStream);
534	CFRelease(plistReadStream);
535
536	return plist;
537}
538
539int
540unsetenv_cmd(int argc, char *const argv[])
541{
542	launch_data_t resp, tmp, msg;
543
544	if (argc != 2) {
545		launchctl_log(LOG_ERR, "%s usage: unsetenv <key>", getprogname());
546		return 1;
547	}
548
549	msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
550
551	tmp = launch_data_new_string(argv[1]);
552	launch_data_dict_insert(msg, tmp, LAUNCH_KEY_UNSETUSERENVIRONMENT);
553
554	resp = launch_msg(msg);
555
556	launch_data_free(msg);
557
558	if (resp) {
559		launch_data_free(resp);
560	} else {
561		launchctl_log(LOG_ERR, "launch_msg(\"%s\"): %s", LAUNCH_KEY_UNSETUSERENVIRONMENT, strerror(errno));
562	}
563
564	return 0;
565}
566
567int
568setenv_cmd(int argc, char *const argv[])
569{
570	launch_data_t resp, tmp, tmpv, msg;
571
572	if (argc != 3) {
573		launchctl_log(LOG_ERR, "%s usage: setenv <key> <value>", getprogname());
574		return 1;
575	}
576
577	msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
578	tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
579
580	tmpv = launch_data_new_string(argv[2]);
581	launch_data_dict_insert(tmp, tmpv, argv[1]);
582	launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETUSERENVIRONMENT);
583
584	resp = launch_msg(msg);
585	launch_data_free(msg);
586
587	if (resp) {
588		launch_data_free(resp);
589	} else {
590		launchctl_log(LOG_ERR, "launch_msg(\"%s\"): %s", LAUNCH_KEY_SETUSERENVIRONMENT, strerror(errno));
591	}
592
593	return 0;
594}
595
596void
597print_launchd_env(launch_data_t obj, const char *key, void *context)
598{
599	bool *is_csh = context;
600
601	/* XXX escape the double quotes */
602	if (*is_csh) {
603		launchctl_log(LOG_NOTICE, "setenv %s \"%s\";", key, launch_data_get_string(obj));
604	} else {
605		launchctl_log(LOG_NOTICE, "%s=\"%s\"; export %s;", key, launch_data_get_string(obj), key);
606	}
607}
608
609void
610print_key_value(launch_data_t obj, const char *key, void *context)
611{
612	const char *k = context;
613
614	if (!strcmp(key, k)) {
615		launchctl_log(LOG_NOTICE, "%s", launch_data_get_string(obj));
616	}
617}
618
619int
620getenv_and_export_cmd(int argc, char *const argv[])
621{
622	launch_data_t resp;
623	bool is_csh = false;
624	char *k;
625
626	if (!strcmp(argv[0], "export")) {
627		char *s = getenv("SHELL");
628		if (s) {
629			is_csh = strstr(s, "csh") ? true : false;
630		}
631	} else if (argc != 2) {
632		launchctl_log(LOG_ERR, "%s usage: getenv <key>", getprogname());
633		return 1;
634	}
635
636	k = argv[1];
637
638	if (vproc_swap_complex(NULL, VPROC_GSK_ENVIRONMENT, NULL, &resp) == NULL) {
639		if (!strcmp(argv[0], "export")) {
640			launch_data_dict_iterate(resp, print_launchd_env, &is_csh);
641		} else {
642			launch_data_dict_iterate(resp, print_key_value, k);
643		}
644		launch_data_free(resp);
645		return 0;
646	} else {
647		return 1;
648	}
649
650	return 0;
651}
652
653int
654wait4debugger_cmd(int argc, char * const argv[])
655{
656	if (argc != 3) {
657		launchctl_log(LOG_ERR, "%s usage: debug <label> <value>", argv[0]);
658		return 1;
659	}
660
661	int result = 1;
662	int64_t inval = 0;
663	if (strncmp(argv[2], "true", sizeof("true")) == 0) {
664		inval = 1;
665	} else if (strncmp(argv[2], "false", sizeof("false")) != 0) {
666		inval = atoi(argv[2]);
667		inval &= 1;
668	}
669
670	vproc_t vp = vprocmgr_lookup_vproc(argv[1]);
671	if (vp) {
672		vproc_err_t verr = vproc_swap_integer(vp, VPROC_GSK_WAITFORDEBUGGER, &inval, NULL);
673		if (verr) {
674			launchctl_log(LOG_ERR, "Failed to set WaitForDebugger flag on %s.", argv[1]);
675		} else {
676			result = 0;
677		}
678		vproc_release(vp);
679	}
680
681	return result;
682}
683
684void
685unloadjob(launch_data_t job)
686{
687	launch_data_t tmps;
688
689	tmps = launch_data_dict_lookup(job, LAUNCH_JOBKEY_LABEL);
690
691	if (!tmps) {
692		launchctl_log(LOG_ERR, "%s: Error: Missing Key: %s", getprogname(), LAUNCH_JOBKEY_LABEL);
693		return;
694	}
695
696	if (_vproc_send_signal_by_label(launch_data_get_string(tmps), VPROC_MAGIC_UNLOAD_SIGNAL) != NULL) {
697		launchctl_log(LOG_ERR, "%s: Error unloading: %s", getprogname(), launch_data_get_string(tmps));
698	}
699}
700
701#if READ_JETSAM_DEFAULTS
702
703static CFDictionaryRef
704read_jetsam_defaults_from_cache(void) {
705	CFPropertyListRef cache = GetPropertyListFromCache();
706	CFPropertyListRef defaults = NULL;
707	const void **keys = 0;
708	CFIndex count, i;
709
710	if (!cache) {
711		return NULL;
712	}
713
714	CFPropertyListRef cachefiles = CFDictionaryGetValue(cache, CFSTR(XPC_PLIST_CACHE_KEY));
715	if (!cachefiles) {
716		return NULL;
717	}
718
719	count = CFDictionaryGetCount(cachefiles);
720	keys = (const void **)malloc(sizeof(void *) * count);
721	if (!keys) {
722		return NULL;
723	}
724
725	CFDictionaryGetKeysAndValues(cachefiles, keys, NULL);
726	for (i = 0; i < count; i++) {
727		CFStringRef key = (CFStringRef)keys[i];
728		CFIndex key_length = CFStringGetLength(key);
729
730		if (key_length <= (CFIndex)(JETSAM_PROP_DIR_LENGTH + JETSAM_PROP_PREFIX_LENGTH + JETSAM_PROP_SUFFIX_LENGTH + 1)) {
731			continue;
732		}
733
734		if (CFStringCompareWithOptions(key, CFSTR(JETSAM_PROP_DIR "/" JETSAM_PROP_PREFIX),
735			CFRangeMake(0, JETSAM_PROP_DIR_LENGTH + JETSAM_PROP_PREFIX_LENGTH + 1), 0)) {
736			continue;
737		}
738
739		if (CFStringCompareWithOptions(key, CFSTR(JETSAM_PROP_SUFFIX),
740			CFRangeMake(key_length - JETSAM_PROP_SUFFIX_LENGTH, JETSAM_PROP_SUFFIX_LENGTH), 0)) {
741			continue;
742		}
743
744		defaults = CFDictionaryGetValue(cachefiles, key);
745		break;
746	}
747
748	free(keys);
749
750	return defaults;
751}
752
753static CFDictionaryRef
754read_jetsam_defaults_from_file(void) {
755	DIR *dirp;
756	struct dirent *dp;
757	CFDictionaryRef defaults = NULL;
758
759	dirp = opendir(JETSAM_PROP_DIR);
760	while ((dp = readdir(dirp)) != NULL) {
761		char *fullpath;
762
763		if (dp->d_namlen <= (JETSAM_PROP_PREFIX_LENGTH + JETSAM_PROP_SUFFIX_LENGTH)) {
764			continue;
765		}
766
767		if (strncmp(dp->d_name, JETSAM_PROP_PREFIX, JETSAM_PROP_PREFIX_LENGTH)) {
768			continue;
769		}
770
771		if (strncmp(dp->d_name + dp->d_namlen - JETSAM_PROP_SUFFIX_LENGTH, JETSAM_PROP_SUFFIX, JETSAM_PROP_SUFFIX_LENGTH)) {
772			continue;
773		}
774
775		if (-1 != asprintf(&fullpath, "%s/%s", JETSAM_PROP_DIR, dp->d_name)) {
776			defaults = (CFDictionaryRef)CreateMyPropertyListFromFile(fullpath);
777			free(fullpath);
778		}
779
780		break;
781	}
782
783	if (dirp) {
784		closedir(dirp);
785	}
786
787	return defaults;
788}
789
790static bool
791submit_cached_defaults(void) {
792	launch_data_t msg, resp;
793	const void **keys = NULL;
794	int i;
795
796	if (_launchctl_jetsam_defaults_cached == NULL) {
797        return false;
798	}
799
800	/* The dictionary to transmit */
801	CFMutableDictionaryRef payload_dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
802
803	/* Add a key to indicate that this is a special job */
804	CFBooleanRef ID = kCFBooleanTrue;
805	CFDictionaryAddValue(payload_dict, CFSTR(LAUNCH_JOBKEY_DEFAULTS), ID);
806
807 	CFMutableDictionaryRef defaults_dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
808
809	CFDictionaryAddValue(payload_dict, CFSTR(LAUNCHD_JOB_DEFAULTS), defaults_dict);
810
811	/* Compile appropriate launchd dictionary... */
812	CFIndex count = CFDictionaryGetCount(_launchctl_jetsam_defaults_cached);
813	keys = (const void **)malloc(sizeof(void *) * count);
814	if (!keys) {
815		goto exit;
816	}
817
818	CFDictionaryGetKeysAndValues(_launchctl_jetsam_defaults_cached, keys, NULL);
819
820	for (i = 0; i < count; i++) {
821		CFStringRef label = (CFStringRef)keys[i];
822
823		/* Get the defaults for the job */
824		CFDictionaryRef job_defaults_dict = CFDictionaryGetValue(_launchctl_jetsam_defaults_cached, label);
825		if (!(job_defaults_dict && CFTypeCheck(job_defaults_dict, CFDictionary))) {
826			continue;
827		}
828
829		/* Create a new dictionary to represent the job */
830		CFMutableDictionaryRef job_dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
831
832		/* Add the defaults */
833		CFDictionaryAddValue(job_dict, CFSTR(LAUNCH_JOBKEY_JETSAMPROPERTIES), job_defaults_dict);
834
835		/* Finally, add the result to the main dictionary */
836		CFDictionaryAddValue(defaults_dict, label, job_dict);
837
838		/* Cleanup */
839		CFRelease(job_dict);
840	}
841
842	/* Send the payload */
843	launch_data_t ldp = CF2launch_data(payload_dict);
844
845	msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
846	launch_data_dict_insert(msg, ldp, LAUNCH_KEY_SUBMITJOB);
847
848	resp = launch_msg(msg);
849	launch_data_free(msg);
850
851	launch_data_free(resp);
852
853exit:
854	CFRelease(defaults_dict);
855	CFRelease(payload_dict);
856
857	free(keys);
858
859	return true;
860}
861
862static boolean_t
863read_jetsam_defaults(void)
864{
865	/* Current supported version */
866	const int v = 3;
867
868	CFDictionaryRef jetsam_defaults = NULL;
869
870	if (require_jobs_from_cache()) {
871		jetsam_defaults = read_jetsam_defaults_from_cache();
872	} else {
873		jetsam_defaults = read_jetsam_defaults_from_file();
874	}
875
876	if (NULL == jetsam_defaults) {
877		launchctl_log(LOG_NOTICE, "%s: no jetsam property file found", getprogname());
878		return false;
879	}
880
881	/* Validate the version */
882	CFNumberRef defaults_vers = CFDictionaryGetValue(jetsam_defaults, CFSTR("Version"));
883	if (!(defaults_vers && CFTypeCheck(defaults_vers, CFNumber))) {
884		return false;
885	}
886
887	CFNumberRef supported_vers = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &v);
888	if (!(kCFCompareEqualTo == CFNumberCompare(defaults_vers, supported_vers, NULL ))) {
889		return false;
890	}
891
892	/* These defaults are merged within launchctl prior to submitting the job */
893	_launchctl_jetsam_defaults = CFDictionaryGetValue(jetsam_defaults, CFSTR(LAUNCHD_JOB_DEFAULTS));
894	if (!(_launchctl_jetsam_defaults && CFTypeCheck(_launchctl_jetsam_defaults, CFDictionary))) {
895		_launchctl_jetsam_defaults = NULL;
896		return false;
897	}
898
899	/* Cached defaults (applied by launchd) - parse and submit immediately as a fake job */
900	_launchctl_jetsam_defaults_cached = CFDictionaryGetValue(jetsam_defaults, CFSTR(LAUNCHD_JOB_DEFAULTS_CACHED));
901	if (!(_launchctl_jetsam_defaults_cached && CFTypeCheck(_launchctl_jetsam_defaults_cached, CFDictionary))) {
902		_launchctl_jetsam_defaults_cached = NULL;
903		return false;
904	}
905
906	submit_cached_defaults();
907
908	return true;
909}
910
911#endif /* READ_JETSAM_DEFAULTS */
912
913launch_data_t
914read_plist_file(const char *file, bool editondisk, bool load)
915{
916	CFPropertyListRef plist;
917	launch_data_t r = NULL;
918#if TARGET_OS_EMBEDDED
919	if (require_jobs_from_cache()) {
920		plist = CreateMyPropertyListFromCachedFile(file);
921	} else {
922		plist = CreateMyPropertyListFromFile(file);
923	}
924#else
925	plist = CreateMyPropertyListFromFile(file);
926#endif
927
928	if (NULL == plist) {
929		launchctl_log(LOG_ERR, "%s: no plist was returned for: %s", getprogname(), file);
930		return NULL;
931	}
932
933	CFStringRef label = CFDictionaryGetValue(plist, CFSTR(LAUNCH_JOBKEY_LABEL));
934	if (!(label && CFTypeCheck(label, CFString))) {
935		return NULL;
936	}
937
938	if (_launchctl_overrides_db) {
939		CFDictionaryRef overrides = CFDictionaryGetValue(_launchctl_overrides_db, label);
940		if (overrides && CFTypeCheck(overrides, CFDictionary)) {
941			CFBooleanRef disabled = CFDictionaryGetValue(overrides, CFSTR(LAUNCH_JOBKEY_DISABLED));
942			if (disabled && CFTypeCheck(disabled, CFBoolean)) {
943				CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED), disabled);
944			}
945		}
946	}
947
948	if (editondisk) {
949		if (_launchctl_overrides_db) {
950			CFMutableDictionaryRef job = (CFMutableDictionaryRef)CFDictionaryGetValue(_launchctl_overrides_db, label);
951			if (!job || !CFTypeCheck(job, CFDictionary)) {
952				job = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
953				CFDictionarySetValue(_launchctl_overrides_db, label, job);
954				CFRelease(job);
955			}
956
957			CFDictionarySetValue(job, CFSTR(LAUNCH_JOBKEY_DISABLED), load ? kCFBooleanFalse : kCFBooleanTrue);
958			CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED), load ? kCFBooleanFalse : kCFBooleanTrue);
959			_launchctl_overrides_db_changed = true;
960		} else {
961			if (load) {
962				CFDictionaryRemoveValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED));
963			} else {
964				CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_DISABLED), kCFBooleanTrue);
965			}
966			WriteMyPropertyListToFile(plist, file);
967		}
968	}
969
970#if READ_JETSAM_DEFAULTS
971	if (_launchctl_jetsam_defaults) {
972		CFDictionaryRef job_defaults_dict = CFDictionaryGetValue(_launchctl_jetsam_defaults, label);
973		if (job_defaults_dict) {
974			CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_JETSAMPROPERTIES), job_defaults_dict);
975		}
976	} else {
977		/* The plist is missing. Set a default memory limit, since the device will be otherwise unusable */
978		long default_limit = 0;
979		CFMutableDictionaryRef job_defaults_dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
980		CFNumberRef memory_limit = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &default_limit);
981		if (memory_limit) {
982			CFDictionaryAddValue(job_defaults_dict, CFSTR(LAUNCH_JOBKEY_JETSAMMEMORYLIMIT), memory_limit);
983			CFRelease(memory_limit);
984		}
985		CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR(LAUNCH_JOBKEY_JETSAMPROPERTIES), job_defaults_dict);
986		CFRelease(job_defaults_dict);
987	}
988#endif /* READ_JETSAM_DEFAULTS */
989
990	r = CF2launch_data(plist);
991
992	CFRelease(plist);
993
994	return r;
995}
996
997static bool
998sysctl_hw_streq(int mib_slot, const char *str)
999{
1000	char buf[1000];
1001	size_t bufsz = sizeof(buf);
1002	int mib[] = { CTL_HW, mib_slot };
1003
1004	if (sysctl(mib, 2, buf, &bufsz, NULL, 0) != -1) {
1005		if (strcmp(buf, str) == 0) {
1006			return true;
1007		}
1008	}
1009
1010	return false;
1011}
1012
1013static void
1014limitloadtohardware_iterator(launch_data_t val, const char *key, void *ctx)
1015{
1016	bool *result = ctx;
1017
1018	char name[128];
1019	(void)snprintf(name, sizeof(name), "hw.%s", key);
1020
1021	int mib[2];
1022	size_t sz = 2;
1023	if (*result != true && os_assumes_zero(sysctlnametomib(name, mib, &sz)) == 0) {
1024		if (launch_data_get_type(val) == LAUNCH_DATA_ARRAY) {
1025			size_t c = launch_data_array_get_count(val);
1026
1027			size_t i = 0;
1028			for (i = 0; i < c; i++) {
1029				launch_data_t oai = launch_data_array_get_index(val, i);
1030				if (sysctl_hw_streq(mib[1], launch_data_get_string(oai))) {
1031					*result = true;
1032					i = c;
1033				}
1034			}
1035		}
1036	}
1037}
1038
1039void
1040readfile(const char *what, struct load_unload_state *lus)
1041{
1042	char ourhostname[1024];
1043	launch_data_t tmpd, tmps, thejob, tmpa;
1044	bool job_disabled = false;
1045	size_t i, c;
1046
1047	gethostname(ourhostname, sizeof(ourhostname));
1048
1049	if (NULL == (thejob = read_plist_file(what, lus->editondisk, lus->load))) {
1050		launchctl_log(LOG_ERR, "%s: no plist was returned for: %s", getprogname(), what);
1051		return;
1052	}
1053
1054
1055	if (NULL == launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LABEL)) {
1056		launchctl_log(LOG_ERR, "%s: missing the Label key: %s", getprogname(), what);
1057		goto out_bad;
1058	}
1059
1060	if ((launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_PROGRAM) == NULL) &&
1061		(launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_PROGRAMARGUMENTS) == NULL)) {
1062		launchctl_log(LOG_ERR, "%s: neither a Program nor a ProgramArguments key was specified: %s", getprogname(), what);
1063		goto out_bad;
1064	}
1065
1066	if (NULL != (tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADFROMHOSTS))) {
1067		c = launch_data_array_get_count(tmpa);
1068
1069		for (i = 0; i < c; i++) {
1070			launch_data_t oai = launch_data_array_get_index(tmpa, i);
1071			if (!strcasecmp(ourhostname, launch_data_get_string(oai))) {
1072				goto out_bad;
1073			}
1074		}
1075	}
1076
1077	if (NULL != (tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOHOSTS))) {
1078		c = launch_data_array_get_count(tmpa);
1079
1080		for (i = 0; i < c; i++) {
1081			launch_data_t oai = launch_data_array_get_index(tmpa, i);
1082			if (!strcasecmp(ourhostname, launch_data_get_string(oai))) {
1083				break;
1084			}
1085		}
1086
1087		if (i == c) {
1088			goto out_bad;
1089		}
1090	}
1091
1092	if (NULL != (tmpd = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOHARDWARE))) {
1093		bool result = false;
1094		launch_data_dict_iterate(tmpd, limitloadtohardware_iterator, &result);
1095		if (!result) {
1096			goto out_bad;
1097		}
1098	}
1099
1100	if (NULL != (tmpd = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADFROMHARDWARE))) {
1101		bool result = false;
1102		launch_data_dict_iterate(tmpd, limitloadtohardware_iterator, &result);
1103		if (result) {
1104			goto out_bad;
1105		}
1106	}
1107
1108	/* If the manager is Aqua, the LimitLoadToSessionType should default to
1109	 * "Aqua".
1110	 *
1111	 * <rdar://problem/8297909>
1112	 */
1113	if (!_launchctl_managername) {
1114		if (vproc_swap_string(NULL, VPROC_GSK_MGR_NAME, NULL, &_launchctl_managername)) {
1115			if (bootstrap_port) {
1116				/* This is only an error if we are running with a neutered
1117				 * bootstrap port, otherwise we wouldn't expect this operating to
1118				 * succeed.
1119				 *
1120				 * <rdar://problem/10514286>
1121				 */
1122				launchctl_log(LOG_ERR, "Could not obtain manager name: ppid/bootstrap: %d/0x%x", getppid(), bootstrap_port);
1123			}
1124
1125			_launchctl_managername = "";
1126		}
1127	}
1128
1129	if (!lus->session_type) {
1130		if (strcmp(_launchctl_managername, "Aqua") == 0) {
1131			lus->session_type = "Aqua";
1132		}
1133	}
1134
1135	if (lus->session_type && !(tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE))) {
1136		tmpa = launch_data_new_string("Aqua");
1137		launch_data_dict_insert(thejob, tmpa, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE);
1138	}
1139
1140	if ((tmpa = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE))) {
1141		const char *allowed_session;
1142		bool skipjob = true;
1143
1144		/* My sincere apologies to anyone who has to deal with this
1145		 * LimitLoadToSessionType madness. It was like this when I got here, but
1146		 * I've knowingly made it worse, hopefully to the benefit of the end
1147		 * user.
1148		 *
1149		 * See <rdar://problem/8769211> and <rdar://problem/7114980>.
1150		 */
1151		if (!lus->session_type && launch_data_get_type(tmpa) == LAUNCH_DATA_STRING) {
1152			if (strcasecmp("System", _launchctl_managername) == 0 && strcasecmp("System", launch_data_get_string(tmpa)) == 0) {
1153				skipjob = false;
1154			}
1155		}
1156
1157		if (lus->session_type) switch (launch_data_get_type(tmpa)) {
1158		case LAUNCH_DATA_ARRAY:
1159			c = launch_data_array_get_count(tmpa);
1160			for (i = 0; i < c; i++) {
1161				tmps = launch_data_array_get_index(tmpa, i);
1162				allowed_session = launch_data_get_string(tmps);
1163				if (strcasecmp(lus->session_type, allowed_session) == 0) {
1164					skipjob = false;
1165					/* we have to do the following so job_reparent_hack() works within launchd */
1166					tmpa = launch_data_new_string(lus->session_type);
1167					launch_data_dict_insert(thejob, tmpa, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE);
1168					break;
1169				}
1170			}
1171			break;
1172		case LAUNCH_DATA_STRING:
1173			allowed_session = launch_data_get_string(tmpa);
1174			if (strcasecmp(lus->session_type, allowed_session) == 0) {
1175				skipjob = false;
1176			}
1177			break;
1178		default:
1179			break;
1180		}
1181
1182		if (skipjob) {
1183			goto out_bad;
1184		}
1185	}
1186
1187	if ((tmpd = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_DISABLED))) {
1188		job_disabled = job_disabled_logic(tmpd);
1189	}
1190
1191	if (lus->forceload) {
1192		job_disabled = false;
1193	}
1194
1195	if (job_disabled && lus->load) {
1196		goto out_bad;
1197	}
1198
1199	if (_launchctl_system_bootstrap || _launchctl_peruser_bootstrap) {
1200		uuid_t uuid;
1201		uuid_clear(uuid);
1202
1203		launch_data_t lduuid = launch_data_new_opaque(uuid, sizeof(uuid_t));
1204		launch_data_dict_insert(thejob, lduuid, LAUNCH_JOBKEY_SECURITYSESSIONUUID);
1205	}
1206
1207	launch_data_array_append(lus->pass1, thejob);
1208
1209	if (_launchctl_verbose) {
1210		launchctl_log(LOG_NOTICE, "Will load: %s", what);
1211	}
1212
1213	return;
1214out_bad:
1215	if (_launchctl_verbose) {
1216		launchctl_log(LOG_NOTICE, "Ignored: %s", what);
1217	}
1218	launch_data_free(thejob);
1219}
1220
1221static void
1222job_disabled_dict_logic(launch_data_t obj, const char *key, void *context)
1223{
1224	bool *r = context;
1225
1226	if (launch_data_get_type(obj) != LAUNCH_DATA_STRING) {
1227		return;
1228	}
1229
1230	if (strcasecmp(key, LAUNCH_JOBKEY_DISABLED_MACHINETYPE) == 0) {
1231		if (sysctl_hw_streq(HW_MACHINE, launch_data_get_string(obj))) {
1232			*r = true;
1233		}
1234	} else if (strcasecmp(key, LAUNCH_JOBKEY_DISABLED_MODELNAME) == 0) {
1235		if (sysctl_hw_streq(HW_MODEL, launch_data_get_string(obj))) {
1236			*r = true;
1237		}
1238	}
1239}
1240
1241bool
1242job_disabled_logic(launch_data_t obj)
1243{
1244	bool r = false;
1245
1246	switch (launch_data_get_type(obj)) {
1247		case LAUNCH_DATA_DICTIONARY:
1248			launch_data_dict_iterate(obj, job_disabled_dict_logic, &r);
1249			break;
1250		case LAUNCH_DATA_BOOL:
1251			r = launch_data_get_bool(obj);
1252			break;
1253		default:
1254			break;
1255	}
1256
1257	return r;
1258}
1259
1260bool
1261path_goodness_check(const char *path, bool forceload)
1262{
1263	struct stat sb;
1264
1265	if (stat(path, &sb) == -1) {
1266		launchctl_log(LOG_ERR, "%s: Couldn't stat(\"%s\"): %s", getprogname(), path, strerror(errno));
1267		return false;
1268	}
1269
1270	if (forceload) {
1271		return true;
1272	}
1273
1274	if (sb.st_mode & (S_IWOTH|S_IWGRP)) {
1275		launchctl_log(LOG_ERR, "%s: Dubious permissions on file (skipping): %s", getprogname(), path);
1276		return false;
1277	}
1278
1279	if (sb.st_uid != 0 && sb.st_uid != getuid()) {
1280		launchctl_log(LOG_ERR, "%s: Dubious ownership on file (skipping): %s", getprogname(), path);
1281		return false;
1282	}
1283
1284	if (!(S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))) {
1285		launchctl_log(LOG_ERR, "%s: Dubious path. Not a regular file or directory (skipping): %s", getprogname(), path);
1286		return false;
1287	}
1288
1289	if ((!S_ISDIR(sb.st_mode)) && (fnmatch("*.plist", path, FNM_CASEFOLD) == FNM_NOMATCH)) {
1290		launchctl_log(LOG_ERR, "%s: Dubious file. Not of type .plist (skipping): %s", getprogname(), path);
1291		return false;
1292	}
1293
1294	return true;
1295}
1296
1297void
1298readpath(const char *what, struct load_unload_state *lus)
1299{
1300	char buf[MAXPATHLEN];
1301	struct stat sb;
1302	struct dirent *de;
1303	DIR *d;
1304
1305	if (!path_goodness_check(what, lus->forceload)) {
1306		return;
1307	}
1308
1309	if (stat(what, &sb) == -1) {
1310		return;
1311	}
1312
1313	if (S_ISREG(sb.st_mode)) {
1314		readfile(what, lus);
1315	} else if (S_ISDIR(sb.st_mode)) {
1316		if ((d = opendir(what)) == NULL) {
1317			launchctl_log(LOG_ERR, "%s: opendir() failed to open the directory", getprogname());
1318			return;
1319		}
1320
1321		while ((de = readdir(d))) {
1322			if (de->d_name[0] == '.') {
1323				continue;
1324			}
1325			snprintf(buf, sizeof(buf), "%s/%s", what, de->d_name);
1326
1327			if (!path_goodness_check(buf, lus->forceload)) {
1328				continue;
1329			}
1330
1331			readfile(buf, lus);
1332		}
1333		closedir(d);
1334	}
1335}
1336
1337void
1338insert_event(launch_data_t job, const char *stream, const char *key, launch_data_t event)
1339{
1340	launch_data_t launchevents, streamdict;
1341
1342	launchevents = launch_data_dict_lookup(job, LAUNCH_JOBKEY_LAUNCHEVENTS);
1343	if (launchevents == NULL) {
1344		launchevents = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1345		launch_data_dict_insert(job, launchevents, LAUNCH_JOBKEY_LAUNCHEVENTS);
1346	}
1347
1348	streamdict = launch_data_dict_lookup(launchevents, stream);
1349	if (streamdict == NULL) {
1350		streamdict = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1351		launch_data_dict_insert(launchevents, streamdict, stream);
1352	}
1353
1354	launch_data_dict_insert(streamdict, event, key);
1355}
1356
1357struct distill_context {
1358	launch_data_t base;
1359	launch_data_t newsockdict;
1360};
1361
1362void
1363distill_jobs(launch_data_t jobs)
1364{
1365	size_t i, c = launch_data_array_get_count(jobs);
1366	launch_data_t job;
1367
1368	for (i = 0; i < c; i++) {
1369		job = launch_data_array_get_index(jobs, i);
1370		distill_config_file(job);
1371		distill_fsevents(job);
1372	}
1373}
1374
1375void
1376distill_config_file(launch_data_t id_plist)
1377{
1378	struct distill_context dc = { id_plist, NULL };
1379	launch_data_t tmp;
1380
1381	if ((tmp = launch_data_dict_lookup(dc.base, LAUNCH_JOBKEY_SOCKETS))) {
1382		dc.newsockdict = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1383		launch_data_dict_iterate(tmp, sock_dict_cb, &dc);
1384		launch_data_dict_insert(dc.base, dc.newsockdict, LAUNCH_JOBKEY_SOCKETS);
1385	}
1386}
1387
1388void
1389sock_dict_cb(launch_data_t what, const char *key, void *context)
1390{
1391	struct distill_context *dc = context;
1392	launch_data_t fdarray = launch_data_alloc(LAUNCH_DATA_ARRAY);
1393
1394	launch_data_dict_insert(dc->newsockdict, fdarray, key);
1395
1396	if (launch_data_get_type(what) == LAUNCH_DATA_DICTIONARY) {
1397		sock_dict_edit_entry(what, key, fdarray, dc->base);
1398	} else if (launch_data_get_type(what) == LAUNCH_DATA_ARRAY) {
1399		launch_data_t tmp;
1400		size_t i;
1401
1402		for (i = 0; i < launch_data_array_get_count(what); i++) {
1403			tmp = launch_data_array_get_index(what, i);
1404			sock_dict_edit_entry(tmp, key, fdarray, dc->base);
1405		}
1406	}
1407}
1408
1409void
1410sock_dict_edit_entry(launch_data_t tmp, const char *key, launch_data_t fdarray, launch_data_t thejob)
1411{
1412	launch_data_t a, val;
1413	int sfd, st = SOCK_STREAM;
1414	bool passive = true;
1415
1416	if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_TYPE))) {
1417		if (!strcasecmp(launch_data_get_string(val), "stream")) {
1418			st = SOCK_STREAM;
1419		} else if (!strcasecmp(launch_data_get_string(val), "dgram")) {
1420			st = SOCK_DGRAM;
1421		} else if (!strcasecmp(launch_data_get_string(val), "seqpacket")) {
1422			st = SOCK_SEQPACKET;
1423		}
1424	}
1425
1426	if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PASSIVE))) {
1427		passive = launch_data_get_bool(val);
1428	}
1429
1430	if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_SECUREWITHKEY))) {
1431		char secdir[] = LAUNCH_SECDIR, buf[1024];
1432		launch_data_t uenv = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES);
1433
1434		if (NULL == uenv) {
1435			uenv = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1436			launch_data_dict_insert(thejob, uenv, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES);
1437		}
1438
1439		mkdtemp(secdir);
1440
1441		sprintf(buf, "%s/%s", secdir, key);
1442
1443		a = launch_data_new_string(buf);
1444		launch_data_dict_insert(tmp, a, LAUNCH_JOBSOCKETKEY_PATHNAME);
1445		a = launch_data_new_string(buf);
1446		launch_data_dict_insert(uenv, a, launch_data_get_string(val));
1447	}
1448
1449	if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PATHNAME))) {
1450		struct sockaddr_un sun;
1451		mode_t sun_mode = 0;
1452		mode_t oldmask;
1453		bool setm = false;
1454
1455		memset(&sun, 0, sizeof(sun));
1456
1457		sun.sun_family = AF_UNIX;
1458
1459		strncpy(sun.sun_path, launch_data_get_string(val), sizeof(sun.sun_path));
1460
1461		if (posix_assumes_zero(sfd = _fd(socket(AF_UNIX, st, 0))) == -1) {
1462			return;
1463		}
1464
1465		if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PATHMODE))) {
1466			sun_mode = (mode_t)launch_data_get_integer(val);
1467			setm = true;
1468		}
1469
1470		if (passive) {
1471			if (unlink(sun.sun_path) == -1 && errno != ENOENT) {
1472				close(sfd);
1473				return;
1474			}
1475			oldmask = umask(S_IRWXG|S_IRWXO);
1476			if (bind(sfd, (struct sockaddr *)&sun, (socklen_t) sizeof sun) == -1) {
1477				close(sfd);
1478				umask(oldmask);
1479				return;
1480			}
1481			umask(oldmask);
1482			if (setm) {
1483				chmod(sun.sun_path, sun_mode);
1484			}
1485			if ((st == SOCK_STREAM || st == SOCK_SEQPACKET) && listen(sfd, -1) == -1) {
1486				close(sfd);
1487				return;
1488			}
1489		} else if (connect(sfd, (struct sockaddr *)&sun, (socklen_t) sizeof sun) == -1) {
1490			close(sfd);
1491			return;
1492		}
1493
1494		val = launch_data_new_fd(sfd);
1495		launch_data_array_append(fdarray, val);
1496	} else {
1497		launch_data_t rnames = NULL;
1498		const char *node = NULL, *serv = NULL, *mgroup = NULL;
1499		char servnbuf[50];
1500		struct addrinfo hints, *res0, *res;
1501		int gerr, sock_opt = 1;
1502
1503		memset(&hints, 0, sizeof(hints));
1504
1505		hints.ai_socktype = st;
1506		if (passive) {
1507			hints.ai_flags |= AI_PASSIVE;
1508		}
1509
1510		if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_NODENAME))) {
1511			node = launch_data_get_string(val);
1512		}
1513		if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_MULTICASTGROUP))) {
1514			mgroup = launch_data_get_string(val);
1515		}
1516		if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_SERVICENAME))) {
1517			if (LAUNCH_DATA_INTEGER == launch_data_get_type(val)) {
1518				sprintf(servnbuf, "%lld", launch_data_get_integer(val));
1519				serv = servnbuf;
1520			} else {
1521				serv = launch_data_get_string(val);
1522			}
1523		}
1524		if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_FAMILY))) {
1525			if (!strcasecmp("IPv4", launch_data_get_string(val))) {
1526				hints.ai_family = AF_INET;
1527			} else if (!strcasecmp("IPv6", launch_data_get_string(val))) {
1528				hints.ai_family = AF_INET6;
1529			}
1530		}
1531		if ((val = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_PROTOCOL))) {
1532			if (!strcasecmp("TCP", launch_data_get_string(val))) {
1533				hints.ai_protocol = IPPROTO_TCP;
1534			} else if (!strcasecmp("UDP", launch_data_get_string(val))) {
1535				hints.ai_protocol = IPPROTO_UDP;
1536			}
1537		}
1538		if ((rnames = launch_data_dict_lookup(tmp, LAUNCH_JOBSOCKETKEY_BONJOUR))) {
1539			if (LAUNCH_DATA_BOOL != launch_data_get_type(rnames) || launch_data_get_bool(rnames)) {
1540				launch_data_t newevent;
1541				char eventkey[100];
1542
1543				newevent = launch_data_copy(tmp);
1544				snprintf(eventkey, sizeof(eventkey), "com.apple.launchd.%s", key);
1545				insert_event(thejob, "com.apple.bonjour.registration", eventkey, newevent);
1546			}
1547		}
1548
1549		if ((gerr = getaddrinfo(node, serv, &hints, &res0)) != 0) {
1550			launchctl_log(LOG_ERR, "getaddrinfo(): %s", gai_strerror(gerr));
1551			return;
1552		}
1553
1554		for (res = res0; res; res = res->ai_next) {
1555			if ((sfd = _fd(socket(res->ai_family, res->ai_socktype, res->ai_protocol))) == -1) {
1556				launchctl_log(LOG_ERR, "socket(): %s", strerror(errno));
1557				return;
1558			}
1559
1560			do_application_firewall_magic(sfd, thejob);
1561
1562			if (hints.ai_flags & AI_PASSIVE) {
1563				if (AF_INET6 == res->ai_family && -1 == setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY,
1564							(void *)&sock_opt, (socklen_t) sizeof sock_opt)) {
1565					launchctl_log(LOG_ERR, "setsockopt(IPV6_V6ONLY): %m");
1566					return;
1567				}
1568				if (mgroup) {
1569					if (setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, (void *)&sock_opt, (socklen_t) sizeof sock_opt) == -1) {
1570						launchctl_log(LOG_ERR, "setsockopt(SO_REUSEPORT): %s", strerror(errno));
1571						return;
1572					}
1573				} else {
1574					if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt, (socklen_t) sizeof sock_opt) == -1) {
1575						launchctl_log(LOG_ERR, "setsockopt(SO_REUSEADDR): %s", strerror(errno));
1576						return;
1577					}
1578				}
1579				if (bind(sfd, res->ai_addr, res->ai_addrlen) == -1) {
1580					launchctl_log(LOG_ERR, "bind(): %s", strerror(errno));
1581					return;
1582				}
1583				/* The kernel may have dynamically assigned some part of the
1584				 * address. (The port being a common example.)
1585				 */
1586				if (getsockname(sfd, res->ai_addr, &res->ai_addrlen) == -1) {
1587					launchctl_log(LOG_ERR, "getsockname(): %s", strerror(errno));
1588					return;
1589				}
1590
1591				if (mgroup) {
1592					do_mgroup_join(sfd, res->ai_family, res->ai_socktype, res->ai_protocol, mgroup);
1593				}
1594				if ((res->ai_socktype == SOCK_STREAM || res->ai_socktype == SOCK_SEQPACKET) && listen(sfd, -1) == -1) {
1595					launchctl_log(LOG_ERR, "listen(): %s", strerror(errno));
1596					return;
1597				}
1598			} else {
1599				if (connect(sfd, res->ai_addr, res->ai_addrlen) == -1) {
1600					launchctl_log(LOG_ERR, "connect(): %s", strerror(errno));
1601					return;
1602				}
1603			}
1604			val = launch_data_new_fd(sfd);
1605			launch_data_array_append(fdarray, val);
1606		}
1607	}
1608}
1609
1610void
1611distill_fsevents(launch_data_t id_plist)
1612{
1613	launch_data_t copy, newevent;
1614	launch_data_t tmp, tmp2;
1615
1616	if ((tmp = launch_data_dict_lookup(id_plist, LAUNCH_JOBKEY_QUEUEDIRECTORIES))) {
1617		copy = launch_data_copy(tmp);
1618		(void)launch_data_dict_remove(id_plist, LAUNCH_JOBKEY_QUEUEDIRECTORIES);
1619
1620		newevent = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1621		launch_data_dict_insert(newevent, copy, LAUNCH_JOBKEY_QUEUEDIRECTORIES);
1622		insert_event(id_plist, "com.apple.fsevents.matching", "com.apple.launchd." LAUNCH_JOBKEY_QUEUEDIRECTORIES, newevent);
1623	}
1624
1625	if ((tmp = launch_data_dict_lookup(id_plist, LAUNCH_JOBKEY_WATCHPATHS))) {
1626		copy = launch_data_copy(tmp);
1627		(void)launch_data_dict_remove(id_plist, LAUNCH_JOBKEY_WATCHPATHS);
1628
1629		newevent = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1630		launch_data_dict_insert(newevent, copy, LAUNCH_JOBKEY_WATCHPATHS);
1631		insert_event(id_plist, "com.apple.fsevents.matching", "com.apple.launchd." LAUNCH_JOBKEY_WATCHPATHS, newevent);
1632	}
1633
1634	if ((tmp = launch_data_dict_lookup(id_plist, LAUNCH_JOBKEY_KEEPALIVE))) {
1635		if ((tmp2 = launch_data_dict_lookup(tmp, LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE))) {
1636			copy = launch_data_copy(tmp2);
1637			(void)launch_data_dict_remove(tmp, LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE);
1638
1639			newevent = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1640			launch_data_dict_insert(newevent, copy, LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE);
1641			insert_event(id_plist, "com.apple.fsevents.matching", "com.apple.launchd." LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE, newevent);
1642		}
1643	}
1644}
1645
1646void
1647do_mgroup_join(int fd, int family, int socktype, int protocol, const char *mgroup)
1648{
1649	struct addrinfo hints, *res0, *res;
1650	struct ip_mreq mreq;
1651	struct ipv6_mreq m6req;
1652	int gerr;
1653
1654	memset(&hints, 0, sizeof(hints));
1655
1656	hints.ai_flags |= AI_PASSIVE;
1657	hints.ai_family = family;
1658	hints.ai_socktype = socktype;
1659	hints.ai_protocol = protocol;
1660
1661	if ((gerr = getaddrinfo(mgroup, NULL, &hints, &res0)) != 0) {
1662		launchctl_log(LOG_ERR, "getaddrinfo(): %s", gai_strerror(gerr));
1663		return;
1664	}
1665
1666	for (res = res0; res; res = res->ai_next) {
1667		if (AF_INET == family) {
1668			memset(&mreq, 0, sizeof(mreq));
1669			mreq.imr_multiaddr = ((struct sockaddr_in *)res->ai_addr)->sin_addr;
1670			if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, (socklen_t) sizeof mreq) == -1) {
1671				launchctl_log(LOG_ERR, "setsockopt(IP_ADD_MEMBERSHIP): %s", strerror(errno));
1672				continue;
1673			}
1674			break;
1675		} else if (AF_INET6 == family) {
1676			memset(&m6req, 0, sizeof(m6req));
1677			m6req.ipv6mr_multiaddr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
1678			if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &m6req, (socklen_t) sizeof m6req) == -1) {
1679				launchctl_log(LOG_ERR, "setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno));
1680				continue;
1681			}
1682			break;
1683		} else {
1684			launchctl_log(LOG_ERR, "unknown family during multicast group bind!");
1685			break;
1686		}
1687	}
1688
1689	freeaddrinfo(res0);
1690}
1691
1692#pragma mark XPC Cache
1693
1694#if TARGET_OS_EMBEDDED
1695
1696CFPropertyListRef
1697GetPropertyListFromCache(void)
1698{
1699	static CFPropertyListRef propertyList;
1700	CFDataRef cacheData;
1701	CFErrorRef error;
1702
1703	if (!propertyList) {
1704		uint8_t *data = NULL;
1705		unsigned long sz = 0;
1706
1707		void *handle = dlopen(XPC_PLIST_CACHE, RTLD_NOW);
1708
1709		if (handle) {
1710			void *fnptr = dlsym(handle, "__xpcd_cache");
1711
1712			if (fnptr) {
1713				Dl_info image_info;
1714
1715				int rv = dladdr(fnptr, &image_info);
1716				if (rv != 0) {
1717					data = getsectiondata(image_info.dli_fbase, "__TEXT", "__xpcd_cache", &sz);
1718				} else {
1719					launchctl_log(LOG_ERR, "cache loading failed: failed to find address of __xpcd_cache symbol.");
1720				}
1721			} else {
1722				launchctl_log(LOG_ERR, "cache loading failed: failed to find __xpcd_cache symbol in cache.");
1723			}
1724		} else {
1725			launchctl_log(LOG_ERR, "cache loading failed: dlopen returned %s.", dlerror());
1726		}
1727
1728		if (data) {
1729			cacheData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, data, sz, kCFAllocatorNull);
1730			if (cacheData) {
1731				propertyList = CFPropertyListCreateWithData(kCFAllocatorDefault, cacheData, kCFPropertyListMutableContainersAndLeaves, NULL, &error);
1732				CFRelease(cacheData);
1733			} else {
1734				launchctl_log(LOG_ERR, "cache loading failed: unable to create data out of memory region.");
1735			}
1736		} else {
1737			launchctl_log(LOG_ERR, "cache loading failed: no cache data found in __TEXT,__xpcd_cache segment.");
1738		}
1739	}
1740
1741	return propertyList;
1742}
1743
1744CFPropertyListRef
1745CreateMyPropertyListFromCachedFile(const char *posixfile)
1746{
1747	CFPropertyListRef cache = GetPropertyListFromCache();
1748	CFPropertyListRef job = NULL;
1749
1750	if (cache) {
1751		CFPropertyListRef jobs = CFDictionaryGetValue(cache, CFSTR(XPC_PLIST_CACHE_KEY));
1752
1753		if (jobs) {
1754			CFStringRef key = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, posixfile, kCFStringEncodingUTF8, kCFAllocatorNull);
1755
1756			if (key) {
1757				job = CFDictionaryGetValue(jobs, key);
1758				CFRelease(key);
1759			}
1760		}
1761	}
1762
1763	if (job) {
1764		CFRetain(job);
1765	}
1766	return job;
1767}
1768
1769bool
1770require_jobs_from_cache(void)
1771{
1772	char buf[1024];
1773	size_t len;
1774	char *ptr;
1775	unsigned long val;
1776	bool cs_disabled = false;
1777	len = sizeof(buf);
1778
1779	if (sysctlbyname("kern.bootargs", buf, &len, NULL, 0) == 0) {
1780		ptr = strnstr(buf, "cs_enforcement_disable=", len);
1781		if (ptr != NULL) {
1782			val = strtoul(ptr + strlen("cs_enforcement_disable="), NULL, 10);
1783			cs_disabled = (val != 0);
1784		}
1785		ptr = strnstr(buf, "launchctl_enforce_codesign=", len);
1786		if (ptr != NULL) {
1787			char *endptr = NULL;
1788			char *startptr = ptr + strlen("launchctl_enforce_codesign=");
1789			val = strtoul(startptr, &endptr, 10);
1790			cs_disabled = (val == 0 && startptr != endptr);
1791		}
1792	}
1793
1794	return !cs_disabled;
1795}
1796
1797#endif
1798
1799#pragma mark File-based Property Lists
1800
1801CFPropertyListRef
1802CreateMyPropertyListFromFile(const char *posixfile)
1803{
1804	CFPropertyListRef propertyList;
1805	CFStringRef       errorString;
1806	CFDataRef         resourceData;
1807	SInt32            errorCode;
1808	CFURLRef          fileURL;
1809
1810	fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false);
1811	if (!fileURL) {
1812		launchctl_log(LOG_ERR, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed", getprogname(), posixfile);
1813	}
1814	if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode)) {
1815		launchctl_log(LOG_ERR, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d", getprogname(), posixfile, (int)errorCode);
1816	}
1817
1818	propertyList = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resourceData, kCFPropertyListMutableContainersAndLeaves, &errorString);
1819	if (fileURL) {
1820		CFRelease(fileURL);
1821	}
1822
1823	if (resourceData) {
1824		CFRelease(resourceData);
1825	}
1826
1827	return propertyList;
1828}
1829
1830void
1831WriteMyPropertyListToFile(CFPropertyListRef plist, const char *posixfile)
1832{
1833	CFDataRef	resourceData;
1834	CFURLRef	fileURL;
1835	SInt32		errorCode;
1836
1837	fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)posixfile, strlen(posixfile), false);
1838	if (!fileURL) {
1839		launchctl_log(LOG_ERR, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed", getprogname(), posixfile);
1840	}
1841	resourceData = CFPropertyListCreateXMLData(kCFAllocatorDefault, plist);
1842	if (resourceData == NULL) {
1843		launchctl_log(LOG_ERR, "%s: CFPropertyListCreateXMLData(%s) failed", getprogname(), posixfile);
1844	}
1845	if (!CFURLWriteDataAndPropertiesToResource(fileURL, resourceData, NULL, &errorCode)) {
1846		launchctl_log(LOG_ERR, "%s: CFURLWriteDataAndPropertiesToResource(%s) failed: %d", getprogname(), posixfile, (int)errorCode);
1847	}
1848
1849	if (resourceData) {
1850		CFRelease(resourceData);
1851	}
1852}
1853
1854static inline Boolean
1855_is_launch_data_t(launch_data_t obj)
1856{
1857	Boolean result = true;
1858
1859	switch (launch_data_get_type(obj)) {
1860		case LAUNCH_DATA_STRING		: break;
1861		case LAUNCH_DATA_INTEGER	: break;
1862		case LAUNCH_DATA_REAL		: break;
1863		case LAUNCH_DATA_BOOL		: break;
1864		case LAUNCH_DATA_ARRAY		: break;
1865		case LAUNCH_DATA_DICTIONARY	: break;
1866		case LAUNCH_DATA_FD 		: break;
1867		case LAUNCH_DATA_MACHPORT	: break;
1868		default						: result = false;
1869	}
1870
1871	return result;
1872}
1873
1874static void
1875_launch_data_iterate(launch_data_t obj, const char *key, CFMutableDictionaryRef dict)
1876{
1877	if (obj && _is_launch_data_t(obj)) {
1878		CFStringRef cfKey = CFStringCreateWithCString(NULL, key, kCFStringEncodingUTF8);
1879		CFTypeRef cfVal = CFTypeCreateFromLaunchData(obj);
1880
1881		if (cfVal) {
1882			CFDictionarySetValue(dict, cfKey, cfVal);
1883			CFRelease(cfVal);
1884		}
1885		CFRelease(cfKey);
1886	}
1887}
1888
1889static CFTypeRef
1890CFTypeCreateFromLaunchData(launch_data_t obj)
1891{
1892	CFTypeRef cfObj = NULL;
1893
1894	switch (launch_data_get_type(obj)) {
1895	case LAUNCH_DATA_STRING: {
1896		const char *str = launch_data_get_string(obj);
1897		cfObj = CFStringCreateWithCString(NULL, str, kCFStringEncodingUTF8);
1898		break;
1899	}
1900	case LAUNCH_DATA_INTEGER: {
1901		long long integer = launch_data_get_integer(obj);
1902		cfObj = CFNumberCreate(NULL, kCFNumberLongLongType, &integer);
1903		break;
1904	}
1905	case LAUNCH_DATA_REAL: {
1906		double real = launch_data_get_real(obj);
1907		cfObj = CFNumberCreate(NULL, kCFNumberDoubleType, &real);
1908		break;
1909	}
1910	case LAUNCH_DATA_BOOL: {
1911		bool yesno = launch_data_get_bool(obj);
1912		cfObj = yesno ? kCFBooleanTrue : kCFBooleanFalse;
1913		break;
1914	}
1915	case LAUNCH_DATA_ARRAY: {
1916		cfObj = (CFTypeRef)CFArrayCreateFromLaunchArray(obj);
1917		break;
1918	}
1919	case LAUNCH_DATA_DICTIONARY: {
1920		cfObj = (CFTypeRef)CFDictionaryCreateFromLaunchDictionary(obj);
1921		break;
1922	}
1923	case LAUNCH_DATA_FD: {
1924		int fd = launch_data_get_fd(obj);
1925		cfObj = CFNumberCreate(NULL, kCFNumberIntType, &fd);
1926		break;
1927	}
1928	case LAUNCH_DATA_MACHPORT: {
1929		mach_port_t port = launch_data_get_machport(obj);
1930		cfObj = CFNumberCreate(NULL, kCFNumberIntType, &port);
1931		break;
1932	}
1933	default:
1934		break;
1935	}
1936
1937	return cfObj;
1938}
1939
1940#pragma mark CFArray
1941static CFArrayRef
1942CFArrayCreateFromLaunchArray(launch_data_t arr)
1943{
1944	CFArrayRef result = NULL;
1945	CFMutableArrayRef mutResult = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1946
1947	if (launch_data_get_type(arr) == LAUNCH_DATA_ARRAY) {
1948		unsigned int count = launch_data_array_get_count(arr);
1949		unsigned int i = 0;
1950
1951		for (i = 0; i < count; i++) {
1952			launch_data_t launch_obj = launch_data_array_get_index(arr, i);
1953			CFTypeRef obj = CFTypeCreateFromLaunchData(launch_obj);
1954
1955			if (obj) {
1956				CFArrayAppendValue(mutResult, obj);
1957				CFRelease(obj);
1958			}
1959		}
1960
1961		result = CFArrayCreateCopy(NULL, mutResult);
1962	}
1963
1964	if (mutResult) {
1965		CFRelease(mutResult);
1966	}
1967	return result;
1968}
1969
1970#pragma mark CFDictionary / CFPropertyList
1971static CFDictionaryRef
1972CFDictionaryCreateFromLaunchDictionary(launch_data_t dict)
1973{
1974	CFDictionaryRef result = NULL;
1975
1976	if (launch_data_get_type(dict) == LAUNCH_DATA_DICTIONARY) {
1977		CFMutableDictionaryRef mutResult = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1978
1979		launch_data_dict_iterate(dict, (void (*)(launch_data_t, const char *, void *))_launch_data_iterate, mutResult);
1980
1981		result = CFDictionaryCreateCopy(NULL, mutResult);
1982		CFRelease(mutResult);
1983	}
1984
1985	return result;
1986}
1987
1988void
1989myCFDictionaryApplyFunction(const void *key, const void *value, void *context)
1990{
1991	launch_data_t ik, iw, where = context;
1992
1993	ik = CF2launch_data(key);
1994	iw = CF2launch_data(value);
1995
1996	launch_data_dict_insert(where, iw, launch_data_get_string(ik));
1997	launch_data_free(ik);
1998}
1999
2000launch_data_t
2001CF2launch_data(CFTypeRef cfr)
2002{
2003	launch_data_t r;
2004	CFTypeID cft = CFGetTypeID(cfr);
2005
2006	if (cft == CFStringGetTypeID()) {
2007		char buf[4096];
2008		CFStringGetCString(cfr, buf, sizeof(buf), kCFStringEncodingUTF8);
2009		r = launch_data_alloc(LAUNCH_DATA_STRING);
2010		launch_data_set_string(r, buf);
2011	} else if (cft == CFBooleanGetTypeID()) {
2012		r = launch_data_alloc(LAUNCH_DATA_BOOL);
2013		launch_data_set_bool(r, CFBooleanGetValue(cfr));
2014	} else if (cft == CFArrayGetTypeID()) {
2015		CFIndex i, ac = CFArrayGetCount(cfr);
2016		r = launch_data_alloc(LAUNCH_DATA_ARRAY);
2017		for (i = 0; i < ac; i++) {
2018			CFTypeRef v = CFArrayGetValueAtIndex(cfr, i);
2019			if (v) {
2020				launch_data_t iv = CF2launch_data(v);
2021				launch_data_array_set_index(r, iv, i);
2022			}
2023		}
2024	} else if (cft == CFDictionaryGetTypeID()) {
2025		r = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2026		CFDictionaryApplyFunction(cfr, myCFDictionaryApplyFunction, r);
2027	} else if (cft == CFDataGetTypeID()) {
2028		r = launch_data_alloc(LAUNCH_DATA_OPAQUE);
2029		launch_data_set_opaque(r, CFDataGetBytePtr(cfr), CFDataGetLength(cfr));
2030	} else if (cft == CFNumberGetTypeID()) {
2031		long long n;
2032		double d;
2033		CFNumberType cfnt = CFNumberGetType(cfr);
2034		switch (cfnt) {
2035		case kCFNumberSInt8Type:
2036		case kCFNumberSInt16Type:
2037		case kCFNumberSInt32Type:
2038		case kCFNumberSInt64Type:
2039		case kCFNumberCharType:
2040		case kCFNumberShortType:
2041		case kCFNumberIntType:
2042		case kCFNumberLongType:
2043		case kCFNumberLongLongType:
2044			CFNumberGetValue(cfr, kCFNumberLongLongType, &n);
2045			r = launch_data_alloc(LAUNCH_DATA_INTEGER);
2046			launch_data_set_integer(r, n);
2047			break;
2048		case kCFNumberFloat32Type:
2049		case kCFNumberFloat64Type:
2050		case kCFNumberFloatType:
2051		case kCFNumberDoubleType:
2052			CFNumberGetValue(cfr, kCFNumberDoubleType, &d);
2053			r = launch_data_alloc(LAUNCH_DATA_REAL);
2054			launch_data_set_real(r, d);
2055			break;
2056		default:
2057			r = NULL;
2058			break;
2059		}
2060	} else {
2061		r = NULL;
2062	}
2063	return r;
2064}
2065
2066int
2067help_cmd(int argc, char *const argv[])
2068{
2069	size_t i, l, cmdwidth = 0;
2070
2071	int level = LOG_NOTICE;
2072	if (argc == 0 || argv == NULL) {
2073		level = LOG_ERR;
2074	}
2075
2076	launchctl_log(level, "usage: %s <subcommand>", getprogname());
2077
2078	for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) {
2079		l = strlen(cmds[i].name);
2080		if (l > cmdwidth) {
2081			cmdwidth = l;
2082		}
2083	}
2084
2085	for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++) {
2086		launchctl_log(level, "\t%-*s\t%s", (int)cmdwidth, cmds[i].name, cmds[i].desc);
2087	}
2088
2089	return 0;
2090}
2091
2092int
2093exit_cmd(int argc __attribute__((unused)), char *const argv[] __attribute__((unused)))
2094{
2095	exit(0);
2096}
2097
2098int
2099_fd(int fd)
2100{
2101	if (fd >= 0)
2102		fcntl(fd, F_SETFD, 1);
2103	return fd;
2104}
2105
2106void
2107do_single_user_mode(bool sflag)
2108{
2109	if (sflag) {
2110		while (!do_single_user_mode2()) {
2111			sleep(1);
2112		}
2113	}
2114}
2115
2116bool
2117do_single_user_mode2(void)
2118{
2119	bool runcom_fsck = true; /* should_fsck(); */
2120	int wstatus;
2121	int fd;
2122	pid_t p;
2123
2124	switch ((p = fork())) {
2125	case -1:
2126		syslog(LOG_ERR, "can't fork single-user shell, trying again: %m");
2127		return false;
2128	case 0:
2129		break;
2130	default:
2131		(void)os_assumes_zero(waitpid(p, &wstatus, 0));
2132		if (WIFEXITED(wstatus)) {
2133			if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
2134				return true;
2135			} else {
2136				launchctl_log(LOG_NOTICE, "single user mode: exit status: %d", WEXITSTATUS(wstatus));
2137			}
2138		} else {
2139			launchctl_log(LOG_NOTICE, "single user mode shell: %s", strsignal(WTERMSIG(wstatus)));
2140		}
2141		return false;
2142	}
2143
2144	revoke(_PATH_CONSOLE);
2145	if (posix_assumes_zero((fd = open(_PATH_CONSOLE, O_RDWR))) == -1) {
2146		_exit(EXIT_FAILURE);
2147	}
2148	if (posix_assumes_zero(login_tty(fd)) == -1) {
2149		_exit(EXIT_FAILURE);
2150	}
2151
2152	mach_timespec_t wt = { 5, 0 };
2153	IOKitWaitQuiet(kIOMasterPortDefault, &wt); /* This will hopefully return after all the kexts have shut up. */
2154
2155	setenv("TERM", "vt100", 1);
2156	if (runcom_fsck) {
2157		fprintf(stdout, "Singleuser boot -- fsck not done\n");
2158		fprintf(stdout, "Root device is mounted read-only\n");
2159		fprintf(stdout, "If you want to make modifications to files:\n");
2160		fprintf(stdout, "\t/sbin/fsck -fy\n\t/sbin/mount -uw /\n");
2161		fprintf(stdout, "If you wish to boot the system:\n");
2162		fprintf(stdout, "\texit\n");
2163		fflush(stdout);
2164	}
2165
2166	execl(_PATH_BSHELL, "-sh", NULL);
2167	fprintf(stderr, "can't exec %s for single user: %m\n", _PATH_BSHELL);
2168	_exit(EXIT_FAILURE);
2169}
2170
2171void
2172do_crash_debug_mode(void)
2173{
2174	while (!do_crash_debug_mode2()) {
2175		sleep(1);
2176	}
2177}
2178
2179bool
2180do_crash_debug_mode2(void)
2181{
2182	int wstatus;
2183	int fd;
2184	pid_t p;
2185
2186	switch ((p = fork())) {
2187	case -1:
2188		syslog(LOG_ERR, "can't fork crash debug shell, trying again: %m");
2189		return false;
2190	case 0:
2191		break;
2192	default:
2193		(void)os_assumes_zero(waitpid(p, &wstatus, 0));
2194		if (WIFEXITED(wstatus)) {
2195			if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
2196				return true;
2197			} else {
2198				launchctl_log(LOG_NOTICE, "crash debug mode: exit status: %d", WEXITSTATUS(wstatus));
2199			}
2200		} else {
2201			launchctl_log(LOG_NOTICE, "crash debug mode shell: %s", strsignal(WTERMSIG(wstatus)));
2202		}
2203		return false;
2204	}
2205
2206	revoke(_PATH_CONSOLE);
2207	if (posix_assumes_zero((fd = open(_PATH_CONSOLE, O_RDWR))) == -1) {
2208		_exit(EXIT_FAILURE);
2209	}
2210	if (posix_assumes_zero(login_tty(fd)) == -1) {
2211		_exit(EXIT_FAILURE);
2212	}
2213
2214	/* The idea is to wait until all the kexts have quiesced to prevent a bunch
2215	 * of log messages from being slammed onto the console prompt. It mostly
2216	 * works.
2217	 */
2218	mach_timespec_t wt = { 5, 0 };
2219	IOKitWaitQuiet(kIOMasterPortDefault, &wt);
2220
2221	setenv("TERM", "vt100", 1);
2222	fprintf(stdout, "Entering boot-time debugging mode...\n");
2223	fprintf(stdout, "The system bootstrapper process has crashed. To debug:\n");
2224	fprintf(stdout, "\tgdb attach %i\n", getppid());
2225	fprintf(stdout, "You can try booting the system with:\n");
2226	fprintf(stdout, "\tlaunchctl load -S System -D All\n");
2227
2228	execl(_PATH_BSHELL, "-sh", NULL);
2229	fprintf(stderr, "can't exec %s for crash debug: %m\n", _PATH_BSHELL);
2230	_exit(EXIT_FAILURE);
2231}
2232
2233static void
2234exit_at_sigterm(int sig)
2235{
2236	if (sig == SIGTERM) {
2237		_exit(EXIT_SUCCESS);
2238	}
2239}
2240
2241void
2242fatal_signal_handler(int sig __attribute__((unused)), siginfo_t *si __attribute__((unused)), void *uap __attribute__((unused)))
2243{
2244	do_crash_debug_mode();
2245}
2246
2247void
2248handle_system_bootstrapper_crashes_separately(void)
2249{
2250	if (!_launchctl_startup_debugging) {
2251		return;
2252	}
2253
2254	fprintf(stdout, "com.apple.launchctl.System\t\t\t*** Handling system bootstrapper crashes separately. ***\n");
2255	struct sigaction fsa;
2256
2257	fsa.sa_sigaction = fatal_signal_handler;
2258	fsa.sa_flags = SA_SIGINFO;
2259	sigemptyset(&fsa.sa_mask);
2260
2261	(void)posix_assumes_zero(sigaction(SIGILL, &fsa, NULL));
2262	(void)posix_assumes_zero(sigaction(SIGFPE, &fsa, NULL));
2263	(void)posix_assumes_zero(sigaction(SIGBUS, &fsa, NULL));
2264	(void)posix_assumes_zero(sigaction(SIGTRAP, &fsa, NULL));
2265	(void)posix_assumes_zero(sigaction(SIGABRT, &fsa, NULL));
2266}
2267
2268#if TARGET_OS_EMBEDDED
2269static void
2270init_data_protection(void)
2271{
2272	if (path_check("/usr/libexec/init_data_protection")) {
2273		const char *init_cp[] = { "/usr/libexec/init_data_protection", NULL };
2274		if (fwexec(init_cp, NULL) == -1) {
2275			launchctl_log(LOG_ERR, "Couldn't init content protection: %d: %s", errno, strerror(errno));
2276			(void)reboot(RB_HALT);
2277
2278			_exit(EXIT_FAILURE);
2279		}
2280	}
2281}
2282#endif
2283
2284static void
2285system_specific_bootstrap(bool sflag)
2286{
2287	int hnmib[] = { CTL_KERN, KERN_HOSTNAME };
2288	struct kevent kev;
2289	int kq;
2290#if HAVE_LIBAUDITD
2291	launch_data_t lda, ldb;
2292#endif
2293
2294	handle_system_bootstrapper_crashes_separately();
2295
2296	// Disable Libinfo lookups to mdns and ds while bootstrapping (8698260)
2297	si_search_module_set_flags("mdns", 1);
2298	si_search_module_set_flags("ds", 1);
2299
2300	/* rc.cdrom's hack to load the system means that we're not the real system
2301	 * bootstrapper. So we set this environment variable, and if the real
2302	 * bootstrapper detects it, it will disable lookups to mDNSResponder and
2303	 * opendirectoryd to prevent deadlocks at boot.
2304	 *
2305	 * See <rdar://problem/9877230>.
2306	 */
2307	(void)setenv(LAUNCH_ENV_BOOTSTRAPPINGSYSTEM, "1", 1);
2308
2309	do_sysversion_sysctl();
2310
2311	do_single_user_mode(sflag);
2312
2313	(void)posix_assumes_zero(kq = kqueue());
2314	EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, 60, 0);
2315	(void)posix_assumes_zero(kevent(kq, &kev, 1, NULL, 0, NULL));
2316
2317	__OS_COMPILETIME_ASSERT__(SIG_ERR == (typeof(SIG_ERR))-1);
2318	EV_SET(&kev, SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, 0);
2319	(void)posix_assumes_zero(kevent(kq, &kev, 1, NULL, 0, NULL));
2320	(void)posix_assumes_zero(signal(SIGTERM, SIG_IGN));
2321	(void)posix_assumes_zero(sysctl(hnmib, 2, NULL, NULL, "localhost", sizeof("localhost")));
2322
2323	loopback_setup_ipv4();
2324	loopback_setup_ipv6();
2325
2326	apply_sysctls_from_file("/etc/sysctl.conf");
2327
2328#if TARGET_OS_EMBEDDED
2329	if (path_check("/etc/rc.boot")) {
2330		const char *rcboot_tool[] = { "/etc/rc.boot", NULL };
2331
2332		(void)posix_assumes_zero(signal(SIGTERM, exit_at_sigterm));
2333		(void)posix_assumes_zero(fwexec(rcboot_tool, NULL));
2334	}
2335#endif
2336
2337	if (path_check("/etc/rc.cdrom")) {
2338		const char *rccdrom_tool[] = { _PATH_BSHELL, "/etc/rc.cdrom", "multiuser", NULL };
2339
2340		/* The bootstrapper should always be killable during install-time. This
2341		 * is a special case for /etc/rc.cdrom, which runs a process and never
2342		 * exits.
2343		 *
2344		 * <rdar://problem/6103485>
2345		 */
2346		(void)posix_assumes_zero(signal(SIGTERM, exit_at_sigterm));
2347		(void)posix_assumes_zero(fwexec(rccdrom_tool, NULL));
2348		(void)reboot(RB_HALT);
2349		_exit(EXIT_FAILURE);
2350	} else if (is_netboot()) {
2351		const char *rcnetboot_tool[] = { _PATH_BSHELL, "/etc/rc.netboot", "init", NULL };
2352		if (posix_assumes_zero(fwexec(rcnetboot_tool, NULL)) == -1) {
2353			(void)reboot(RB_HALT);
2354			_exit(EXIT_FAILURE);
2355		}
2356	} else {
2357		do_potential_fsck();
2358	}
2359
2360#if TARGET_OS_EMBEDDED
2361	if (path_check("/usr/libexec/tzinit")) {
2362		const char *tzinit_tool[] = { "/usr/libexec/tzinit", NULL };
2363		(void)posix_assumes_zero(fwexec(tzinit_tool, NULL));
2364	}
2365#endif
2366
2367#if TARGET_OS_EMBEDDED
2368	if (path_check("/usr/libexec/FinishRestoreFromBackup")) {
2369		const char *finish_restore[] = { "/usr/libexec/FinishRestoreFromBackup", NULL };
2370		if (fwexec(finish_restore, NULL) == -1) {
2371			launchctl_log(LOG_ERR, "Couldn't finish restore: %d: %s", errno, strerror(errno));
2372			(void)reboot(RB_HALT);
2373
2374			_exit(EXIT_FAILURE);
2375		}
2376	}
2377#endif
2378
2379	if (path_check("/usr/libexec/cc_fips_test")) {
2380		const char *fips_tool[] = { "/usr/libexec/cc_fips_test", "-P", NULL };
2381		if (fwexec(fips_tool, NULL) == -1) {
2382			launchctl_log(LOG_ERR, "FIPS self check failure: %d: %s", errno, strerror(errno));
2383			(void)reboot(RB_HALT);
2384
2385			_exit(EXIT_FAILURE);
2386		}
2387	}
2388
2389	if (path_check("/etc/rc.server")) {
2390		const char *rcserver_tool[] = { _PATH_BSHELL, "/etc/rc.server", NULL };
2391		(void)posix_assumes_zero(fwexec(rcserver_tool, NULL));
2392	}
2393
2394	read_launchd_conf();
2395
2396	if (path_check("/var/account/acct")) {
2397		(void)posix_assumes_zero(acct("/var/account/acct"));
2398	}
2399
2400#if !TARGET_OS_EMBEDDED
2401	if (path_check("/etc/fstab")) {
2402		const char *mount_tool[] = { "mount", "-vat", "nonfs", NULL };
2403		(void)posix_assumes_zero(fwexec(mount_tool, NULL));
2404	}
2405#endif
2406
2407	if (path_check("/etc/rc.installer_cleanup")) {
2408		const char *rccleanup_tool[] = { _PATH_BSHELL, "/etc/rc.installer_cleanup", "multiuser", NULL };
2409		(void)posix_assumes_zero(fwexec(rccleanup_tool, NULL));
2410	}
2411
2412	if (path_check("/etc/rc.deferred_install")) {
2413		int status = 0;
2414		const char *deferredinstall_tool[] = { _PATH_BSHELL, "/etc/rc.deferred_install", NULL };
2415		if (posix_assumes_zero(fwexec(deferredinstall_tool, &status)) == 0) {
2416			if (WEXITSTATUS(status) == EXIT_SUCCESS) {
2417				if (_launchctl_apple_internal) {
2418					launchctl_log(LOG_NOTICE, "Deferred install script completed successfully. Rebooting in 3 seconds...");
2419					sleep(3);
2420				}
2421
2422				(void)remove(deferredinstall_tool[1]);
2423				(void)reboot(RB_AUTOBOOT);
2424				exit(EXIT_FAILURE);
2425			} else {
2426				launchctl_log(LOG_NOTICE, "Deferred install script exited with status %i. Continuing boot and hoping it'll work...", WEXITSTATUS(status));
2427				(void)remove(deferredinstall_tool[1]);
2428			}
2429		}
2430	}
2431
2432	empty_dir(_PATH_VARRUN, NULL);
2433	empty_dir(_PATH_TMP, NULL);
2434	(void)remove(_PATH_NOLOGIN);
2435
2436	if (path_check("/usr/libexec/dirhelper")) {
2437		const char *dirhelper_tool[] = { "/usr/libexec/dirhelper", "-machineBoot", NULL };
2438		(void)posix_assumes_zero(fwexec(dirhelper_tool, NULL));
2439	}
2440
2441	(void)posix_assumes_zero(touch_file(_PATH_UTMPX, DEFFILEMODE));
2442#if !TARGET_OS_EMBEDDED
2443	(void)posix_assumes_zero(touch_file(_PATH_VARRUN "/.systemStarterRunning", DEFFILEMODE));
2444#endif
2445
2446#if HAVE_LIBAUDITD
2447	/* Only start auditing if not "Disabled" in auditd plist. */
2448	if ((lda = read_plist_file(AUDITD_PLIST_FILE, false, false)) != NULL && ((ldb = launch_data_dict_lookup(lda, LAUNCH_JOBKEY_DISABLED)) == NULL || job_disabled_logic(ldb) == false)) {
2449		(void)os_assumes_zero(audit_quick_start());
2450		launch_data_free(lda);
2451	}
2452#else
2453	if (path_check("/etc/security/rc.audit")) {
2454		const char *audit_tool[] = { _PATH_BSHELL, "/etc/security/rc.audit", NULL };
2455		(void)posix_assumes_zero(fwexec(audit_tool, NULL));
2456	}
2457#endif
2458
2459#if HAVE_SYSTEMSTATS
2460	systemstats_boot();
2461#endif
2462
2463	do_BootCache_magic(BOOTCACHE_START);
2464
2465	preheat_page_cache_hack();
2466
2467	_vproc_set_global_on_demand(true);
2468
2469	char *load_launchd_items[] = { "load", "-D", "all", NULL };
2470	int load_launchd_items_cnt = 3;
2471
2472	if (is_safeboot()) {
2473		load_launchd_items[2] = "system";
2474	}
2475
2476	(void)posix_assumes_zero(load_and_unload_cmd(load_launchd_items_cnt, load_launchd_items));
2477
2478	/* See <rdar://problem/5066316>. */
2479	if (!_launchctl_apple_internal) {
2480		mach_timespec_t w = { 5, 0 };
2481		IOKitWaitQuiet(kIOMasterPortDefault, &w);
2482	}
2483
2484	do_BootCache_magic(BOOTCACHE_TAG);
2485
2486	do_bootroot_magic();
2487
2488	_vproc_set_global_on_demand(false);
2489
2490	(void)posix_assumes_zero(kevent(kq, NULL, 0, &kev, 1, NULL));
2491
2492	/* warmd now handles cutting off the BootCache. We just kick it off. */
2493	(void)close(kq);
2494}
2495
2496void
2497do_BootCache_magic(BootCache_action_t what)
2498{
2499	const char *bcc_tool[] = { "/usr/sbin/BootCacheControl", NULL, NULL };
2500
2501	if (is_safeboot() || !path_check(bcc_tool[0])) {
2502		return;
2503	}
2504
2505	switch (what) {
2506	case BOOTCACHE_START:
2507		bcc_tool[1] = "start";
2508		break;
2509	case BOOTCACHE_TAG:
2510		bcc_tool[1] = "tag";
2511		break;
2512	case BOOTCACHE_STOP:
2513		bcc_tool[1] = "stop";
2514		break;
2515	}
2516
2517	fwexec(bcc_tool, NULL);
2518}
2519
2520int
2521bootstrap_cmd(int argc, char *const argv[])
2522{
2523	char *session = NULL;
2524	bool sflag = false;
2525	int ch;
2526
2527	while ((ch = getopt(argc, argv, "sS:")) != -1) {
2528		switch (ch) {
2529		case 's':
2530			sflag = true;
2531			break;
2532		case 'S':
2533			session = optarg;
2534			break;
2535		case '?':
2536		default:
2537			break;
2538		}
2539	}
2540
2541	optind = 1;
2542	optreset = 1;
2543
2544	if (!session) {
2545		launchctl_log(LOG_ERR, "usage: %s bootstrap [-s] -S <session-type>", getprogname());
2546		return 1;
2547	}
2548
2549	if (strcasecmp(session, "System") == 0) {
2550		_launchctl_system_bootstrap = true;
2551		system_specific_bootstrap(sflag);
2552	} else {
2553		char *load_launchd_items[] = {
2554			"load",
2555			"-S",
2556			session,
2557			"-D",
2558			"all",
2559			NULL,
2560			NULL,
2561			NULL,
2562		};
2563		size_t the_argc = 5;
2564
2565		bool bootstrap_login_items = false;
2566		if (strcasecmp(session, VPROCMGR_SESSION_AQUA) == 0) {
2567			bootstrap_login_items = true;
2568		} else if (strcasecmp(session, VPROCMGR_SESSION_BACKGROUND) == 0
2569				   || strcasecmp(session, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
2570			/* If we're bootstrapping either the LoginWindow or Background
2571			 * sessions, then we only load items from /System and /Library. We
2572			 * do not attempt to load anything from a user's home directory, as
2573			 * it might not be available at this time.
2574			 */
2575			load_launchd_items[4] = "system";
2576			if (!is_safeboot()) {
2577				load_launchd_items[5] = "-D";
2578				load_launchd_items[6] = "local";
2579				the_argc += 2;
2580			}
2581
2582			if (strcasecmp(session, VPROCMGR_SESSION_BACKGROUND) == 0) {
2583				/* This is to force a bootstrapped job to inherit its security
2584				 * session from the launchd that it resides in.
2585				 */
2586				_launchctl_peruser_bootstrap = true;
2587				read_launchd_conf();
2588			}
2589		}
2590
2591		if (is_safeboot()) {
2592			load_launchd_items[4] = "system";
2593		}
2594
2595		int result = load_and_unload_cmd(the_argc, load_launchd_items);
2596		if (result) {
2597			syslog(LOG_ERR, "Could not bootstrap session: %s", session);
2598			return 1;
2599		}
2600
2601		/* This will tell launchd to start listening on MachServices again. When
2602		 * bootstrapping, launchd ignores requests from everyone but the
2603		 * bootstrapper (us), so this unsets the "weird bootstrap" mode.
2604		 */
2605		int64_t junk = 0;
2606		vproc_err_t verr = vproc_swap_integer(NULL, VPROC_GSK_WEIRD_BOOTSTRAP, &junk, NULL);
2607		if (!verr) {
2608#if !TARGET_OS_EMBEDDED
2609			if (bootstrap_login_items) {
2610				void *smf = dlopen("/System/Library/Frameworks/ServiceManagement.framework/Versions/A/ServiceManagement", 0);
2611				if (smf) {
2612					void (*_SMLoginItemBootstrapItemsFunc)(void) = dlsym(smf, "_SMLoginItemBootstrapItems");
2613					if (_SMLoginItemBootstrapItemsFunc) {
2614						_SMLoginItemBootstrapItemsFunc();
2615					} else {
2616						launchctl_log(LOG_ERR, "Could not find login item bootstrap function. LoginItems will be unavailable.");
2617					}
2618				} else {
2619					launchctl_log(LOG_ERR, "Failed to open ServiceManagement framework. LoginItems will be unavailable.");
2620				}
2621			}
2622#endif
2623		} else if (bootstrap_login_items) {
2624			launchctl_log(LOG_ERR, "Failed to unset weird bootstrap. LoginItems will be unavailable.");
2625		}
2626	}
2627
2628	return 0;
2629}
2630
2631int
2632load_and_unload_cmd(int argc, char *const argv[])
2633{
2634	NSSearchPathEnumerationState es = 0;
2635	char nspath[PATH_MAX * 2]; /* safe side, we need to append */
2636	bool badopts = false;
2637	struct load_unload_state lus;
2638	size_t i;
2639	int ch;
2640
2641	memset(&lus, 0, sizeof(lus));
2642
2643	if (strcmp(argv[0], "load") == 0) {
2644		lus.load = true;
2645	}
2646
2647	while ((ch = getopt(argc, argv, "wFS:D:")) != -1) {
2648		switch (ch) {
2649		case 'w':
2650			lus.editondisk = true;
2651			break;
2652		case 'F':
2653			lus.forceload = true;
2654			break;
2655		case 'S':
2656			lus.session_type = optarg;
2657			break;
2658		case 'D':
2659			if (strcasecmp(optarg, "all") == 0) {
2660				es |= NSAllDomainsMask;
2661			} else if (strcasecmp(optarg, "user") == 0) {
2662				es |= NSUserDomainMask;
2663			} else if (strcasecmp(optarg, "local") == 0) {
2664				es |= NSLocalDomainMask;
2665			} else if (strcasecmp(optarg, "network") == 0) {
2666				es |= NSNetworkDomainMask;
2667			} else if (strcasecmp(optarg, "system") == 0) {
2668				es |= NSSystemDomainMask;
2669			} else {
2670				badopts = true;
2671			}
2672			break;
2673		case '?':
2674		default:
2675			badopts = true;
2676			break;
2677		}
2678	}
2679	argc -= optind;
2680	argv += optind;
2681
2682	if (lus.session_type == NULL) {
2683		es &= ~NSUserDomainMask;
2684	}
2685
2686	if (argc == 0 && es == 0) {
2687		badopts = true;
2688	}
2689
2690	if (badopts) {
2691		launchctl_log(LOG_ERR, "usage: %s load [-wF] [-D <user|local|network|system|all>] paths...", getprogname());
2692		return 1;
2693	}
2694
2695	int dbfd = -1;
2696	vproc_err_t verr = vproc_swap_string(NULL, VPROC_GSK_JOB_OVERRIDES_DB, NULL, &_launchctl_job_overrides_db_path);
2697	if (verr) {
2698		if (bootstrap_port) {
2699			launchctl_log(LOG_ERR, "Could not get location of job overrides database: ppid/bootstrap: %d/0x%x", getppid(), bootstrap_port);
2700		}
2701	} else {
2702		dbfd = open(_launchctl_job_overrides_db_path, O_RDONLY | O_EXLOCK | O_CREAT, S_IRUSR | S_IWUSR);
2703		if (dbfd != -1) {
2704			_launchctl_overrides_db = (CFMutableDictionaryRef)CreateMyPropertyListFromFile(_launchctl_job_overrides_db_path);
2705			if (!_launchctl_overrides_db) {
2706				_launchctl_overrides_db = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2707			}
2708		} else if (errno != EROFS) {
2709			launchctl_log(LOG_ERR, "Could not open job overrides database at: %s: %d: %s", _launchctl_job_overrides_db_path, errno, strerror(errno));
2710		}
2711	}
2712
2713#if READ_JETSAM_DEFAULTS
2714	if (!read_jetsam_defaults()) {
2715		launchctl_log(LOG_NOTICE, "Failed to read jetsam defaults; no process limits applied");
2716	}
2717#endif
2718
2719	/* Only one pass! */
2720	lus.pass1 = launch_data_alloc(LAUNCH_DATA_ARRAY);
2721
2722	es = NSStartSearchPathEnumeration(NSLibraryDirectory, es);
2723
2724	while ((es = NSGetNextSearchPathEnumeration(es, nspath))) {
2725		if (lus.session_type) {
2726			strcat(nspath, "/LaunchAgents");
2727		} else {
2728			strcat(nspath, "/LaunchDaemons");
2729		}
2730
2731		bool should_glob = true;
2732
2733#if TARGET_OS_EMBEDDED
2734		if (require_jobs_from_cache()) {
2735			CFDictionaryRef cache = GetPropertyListFromCache();
2736			if (cache) {
2737				CFDictionaryRef launchdJobs = CFDictionaryGetValue(cache, CFSTR(XPC_PLIST_CACHE_KEY));
2738				if (launchdJobs) {
2739					CFIndex sz = CFDictionaryGetCount(launchdJobs);
2740
2741					CFStringRef *keys = malloc(sz * sizeof(CFStringRef));
2742					CFDictionaryGetKeysAndValues(launchdJobs, (const void**)keys, NULL);
2743
2744					for (i=0; i < (size_t)sz; i++) {
2745						char path[PATH_MAX];
2746						if (CFStringGetCString(keys[i], path, PATH_MAX, kCFStringEncodingUTF8) && (strncmp(path, nspath, strlen(nspath)) == 0)) {
2747							readpath(path, &lus);
2748						}
2749					}
2750				}
2751			}
2752
2753			should_glob = false;
2754		}
2755#endif
2756
2757		if (should_glob) {
2758			glob_t g;
2759
2760			if (glob(nspath, GLOB_TILDE|GLOB_NOSORT, NULL, &g) == 0) {
2761				for (i = 0; i < g.gl_pathc; i++) {
2762					readpath(g.gl_pathv[i], &lus);
2763				}
2764				globfree(&g);
2765			}
2766		}
2767	}
2768
2769	for (i = 0; i < (size_t)argc; i++) {
2770		readpath(argv[i], &lus);
2771	}
2772
2773	if (launch_data_array_get_count(lus.pass1) == 0) {
2774		if (!_launchctl_is_managed) {
2775			launchctl_log(LOG_ERR, "nothing found to %s", lus.load ? "load" : "unload");
2776		}
2777		launch_data_free(lus.pass1);
2778		return _launchctl_is_managed ? 0 : 1;
2779	}
2780
2781	if (lus.load) {
2782		distill_jobs(lus.pass1);
2783		submit_job_pass(lus.pass1);
2784	} else {
2785		for (i = 0; i < launch_data_array_get_count(lus.pass1); i++) {
2786			unloadjob(launch_data_array_get_index(lus.pass1, i));
2787		}
2788	}
2789
2790	if (_launchctl_overrides_db_changed) {
2791		WriteMyPropertyListToFile(_launchctl_overrides_db, _launchctl_job_overrides_db_path);
2792	}
2793
2794	flock(dbfd, LOCK_UN);
2795	close(dbfd);
2796	return 0;
2797}
2798
2799void
2800submit_job_pass(launch_data_t jobs)
2801{
2802	launch_data_t msg, resp;
2803	size_t i;
2804	int e;
2805
2806	if (launch_data_array_get_count(jobs) == 0)
2807		return;
2808
2809	msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2810
2811	launch_data_dict_insert(msg, jobs, LAUNCH_KEY_SUBMITJOB);
2812
2813	resp = launch_msg(msg);
2814
2815	if (resp) {
2816		switch (launch_data_get_type(resp)) {
2817		case LAUNCH_DATA_ERRNO:
2818			if ((e = launch_data_get_errno(resp)))
2819				launchctl_log(LOG_ERR, "%s", strerror(e));
2820			break;
2821		case LAUNCH_DATA_ARRAY:
2822			for (i = 0; i < launch_data_array_get_count(jobs); i++) {
2823				launch_data_t obatind = launch_data_array_get_index(resp, i);
2824				launch_data_t jatind = launch_data_array_get_index(jobs, i);
2825				const char *lab4job = launch_data_get_string(launch_data_dict_lookup(jatind, LAUNCH_JOBKEY_LABEL));
2826				if (LAUNCH_DATA_ERRNO == launch_data_get_type(obatind)) {
2827					e = launch_data_get_errno(obatind);
2828					switch (e) {
2829					case EEXIST:
2830						launchctl_log(LOG_ERR, "%s: %s", lab4job, "Already loaded");
2831						break;
2832					case ESRCH:
2833						launchctl_log(LOG_ERR, "%s: %s", lab4job, "Not loaded");
2834						break;
2835					case ENEEDAUTH:
2836						launchctl_log(LOG_ERR, "%s: %s", lab4job, "Could not set security session");
2837					default:
2838						launchctl_log(LOG_ERR, "%s: %s", lab4job, strerror(e));
2839					case 0:
2840						break;
2841					}
2842				}
2843			}
2844			break;
2845		default:
2846			launchctl_log(LOG_ERR, "unknown respose from launchd!");
2847			break;
2848		}
2849		launch_data_free(resp);
2850	} else {
2851		launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno));
2852	}
2853
2854	launch_data_free(msg);
2855}
2856
2857int
2858start_stop_remove_cmd(int argc, char *const argv[])
2859{
2860	launch_data_t resp, msg;
2861	const char *lmsgcmd = LAUNCH_KEY_STOPJOB;
2862	int e, r = 0;
2863
2864	if (0 == strcmp(argv[0], "start"))
2865		lmsgcmd = LAUNCH_KEY_STARTJOB;
2866
2867	if (0 == strcmp(argv[0], "remove"))
2868		lmsgcmd = LAUNCH_KEY_REMOVEJOB;
2869
2870	if (argc != 2) {
2871		launchctl_log(LOG_ERR, "usage: %s %s <job label>", getprogname(), argv[0]);
2872		return 1;
2873	}
2874
2875	msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2876	launch_data_dict_insert(msg, launch_data_new_string(argv[1]), lmsgcmd);
2877
2878	resp = launch_msg(msg);
2879	launch_data_free(msg);
2880
2881	if (resp == NULL) {
2882		launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno));
2883		return 1;
2884	} else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
2885		if ((e = launch_data_get_errno(resp))) {
2886			launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], strerror(e));
2887			r = 1;
2888		}
2889	} else {
2890		launchctl_log(LOG_ERR, "%s %s returned unknown response", getprogname(), argv[0]);
2891		r = 1;
2892	}
2893
2894	launch_data_free(resp);
2895	return r;
2896}
2897
2898void
2899print_jobs(launch_data_t j, const char *key __attribute__((unused)), void *context __attribute__((unused)))
2900{
2901	static size_t depth = 0;
2902	launch_data_t lo = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LABEL);
2903	launch_data_t pido = launch_data_dict_lookup(j, LAUNCH_JOBKEY_PID);
2904	launch_data_t stato = launch_data_dict_lookup(j, LAUNCH_JOBKEY_LASTEXITSTATUS);
2905	const char *label = launch_data_get_string(lo);
2906	size_t i;
2907
2908	if (pido) {
2909		fprintf(stdout, "%lld\t-\t%s\n", launch_data_get_integer(pido), label);
2910	} else if (stato) {
2911		int wstatus = (int)launch_data_get_integer(stato);
2912		if (WIFEXITED(wstatus)) {
2913			fprintf(stdout, "-\t%d\t%s\n", WEXITSTATUS(wstatus), label);
2914		} else if (WIFSIGNALED(wstatus)) {
2915			fprintf(stdout, "-\t-%d\t%s\n", WTERMSIG(wstatus), label);
2916		} else {
2917			fprintf(stdout, "-\t???\t%s\n", label);
2918		}
2919	} else {
2920		fprintf(stdout, "-\t-\t%s\n", label);
2921	}
2922	for (i = 0; i < depth; i++) {
2923		fprintf(stdout, "\t");
2924	}
2925}
2926
2927void
2928print_obj(launch_data_t obj, const char *key, void *context __attribute__((unused)))
2929{
2930	static size_t indent = 0;
2931	size_t i, c;
2932
2933	for (i = 0; i < indent; i++) {
2934		fprintf(stdout, "\t");
2935	}
2936
2937	if (key) {
2938		fprintf(stdout, "\"%s\" = ", key);
2939	}
2940
2941	switch (launch_data_get_type(obj)) {
2942	case LAUNCH_DATA_STRING:
2943		fprintf(stdout, "\"%s\";\n", launch_data_get_string(obj));
2944		break;
2945	case LAUNCH_DATA_INTEGER:
2946		fprintf(stdout, "%lld;\n", launch_data_get_integer(obj));
2947		break;
2948	case LAUNCH_DATA_REAL:
2949		fprintf(stdout, "%f;\n", launch_data_get_real(obj));
2950		break;
2951	case LAUNCH_DATA_BOOL:
2952		fprintf(stdout, "%s;\n", launch_data_get_bool(obj) ? "true" : "false");
2953		break;
2954	case LAUNCH_DATA_ARRAY:
2955		c = launch_data_array_get_count(obj);
2956		fprintf(stdout, "(\n");
2957		indent++;
2958		for (i = 0; i < c; i++) {
2959			print_obj(launch_data_array_get_index(obj, i), NULL, NULL);
2960		}
2961		indent--;
2962		for (i = 0; i < indent; i++) {
2963			fprintf(stdout, "\t");
2964		}
2965		fprintf(stdout, ");\n");
2966		break;
2967	case LAUNCH_DATA_DICTIONARY:
2968		fprintf(stdout, "{\n");
2969		indent++;
2970		launch_data_dict_iterate(obj, print_obj, NULL);
2971		indent--;
2972		for (i = 0; i < indent; i++) {
2973			fprintf(stdout, "\t");
2974		}
2975		fprintf(stdout, "};\n");
2976		break;
2977	case LAUNCH_DATA_FD:
2978		fprintf(stdout, "file-descriptor-object;\n");
2979		break;
2980	case LAUNCH_DATA_MACHPORT:
2981		fprintf(stdout, "mach-port-object;\n");
2982		break;
2983	default:
2984		fprintf(stdout, "???;\n");
2985		break;
2986	}
2987}
2988
2989int
2990list_cmd(int argc, char *const argv[])
2991{
2992	if (_launchctl_is_managed) {
2993		/* This output is meant for a command line, so don't print anything if
2994		 * we're managed by launchd.
2995		 */
2996		return 1;
2997	}
2998
2999	launch_data_t resp, msg = NULL;
3000	int r = 0;
3001
3002	bool plist_output = false;
3003	char *label = NULL;
3004	if (argc > 3) {
3005		launchctl_log(LOG_ERR, "usage: %s list [-x] [label]", getprogname());
3006		return 1;
3007	} else if (argc >= 2) {
3008		plist_output = (strncmp(argv[1], "-x", sizeof("-x")) == 0);
3009		label = plist_output ? argv[2] : argv[1];
3010	}
3011
3012	if (label) {
3013		msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
3014		launch_data_dict_insert(msg, launch_data_new_string(label), LAUNCH_KEY_GETJOB);
3015
3016		resp = launch_msg(msg);
3017		launch_data_free(msg);
3018
3019		if (resp == NULL) {
3020			launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno));
3021			r = 1;
3022		} else if (launch_data_get_type(resp) == LAUNCH_DATA_DICTIONARY) {
3023			if (plist_output) {
3024				CFDictionaryRef respDict = CFDictionaryCreateFromLaunchDictionary(resp);
3025				CFStringRef plistStr = NULL;
3026				if (respDict) {
3027					CFDataRef plistData = CFPropertyListCreateXMLData(NULL, (CFPropertyListRef)respDict);
3028					CFRelease(respDict);
3029					if (plistData) {
3030						plistStr = CFStringCreateWithBytes(NULL, CFDataGetBytePtr(plistData), CFDataGetLength(plistData), kCFStringEncodingUTF8, false);
3031						CFRelease(plistData);
3032					} else {
3033						r = 1;
3034					}
3035				} else {
3036					r = 1;
3037				}
3038
3039				if (plistStr) {
3040					launchctl_log_CFString(LOG_NOTICE, plistStr);
3041					CFRelease(plistStr);
3042					r = 0;
3043				}
3044			} else {
3045				print_obj(resp, NULL, NULL);
3046				r = 0;
3047			}
3048			launch_data_free(resp);
3049		} else {
3050			launchctl_log(LOG_ERR, "%s %s returned unknown response", getprogname(), argv[0]);
3051			r = 1;
3052			launch_data_free(resp);
3053		}
3054	} else if (vproc_swap_complex(NULL, VPROC_GSK_ALLJOBS, NULL, &resp) == NULL) {
3055		fprintf(stdout, "PID\tStatus\tLabel\n");
3056		launch_data_dict_iterate(resp, print_jobs, NULL);
3057		launch_data_free(resp);
3058
3059		r = 0;
3060	}
3061
3062	return r;
3063}
3064
3065int
3066stdio_cmd(int argc __attribute__((unused)), char *const argv[])
3067{
3068	launchctl_log(LOG_ERR, "%s %s: This sub-command no longer does anything", getprogname(), argv[0]);
3069	return 1;
3070}
3071
3072int
3073fyi_cmd(int argc, char *const argv[])
3074{
3075	launch_data_t resp, msg;
3076	const char *lmsgk = NULL;
3077	int e, r = 0;
3078
3079	if (argc != 1) {
3080		launchctl_log(LOG_ERR, "usage: %s %s", getprogname(), argv[0]);
3081		return 1;
3082	}
3083
3084	if (!strcmp(argv[0], "shutdown")) {
3085		lmsgk = LAUNCH_KEY_SHUTDOWN;
3086	} else if (!strcmp(argv[0], "singleuser")) {
3087		lmsgk = LAUNCH_KEY_SINGLEUSER;
3088	} else {
3089		return 1;
3090	}
3091
3092	msg = launch_data_new_string(lmsgk);
3093	resp = launch_msg(msg);
3094	launch_data_free(msg);
3095
3096	if (resp == NULL) {
3097		launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno));
3098		return 1;
3099	} else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
3100		if ((e = launch_data_get_errno(resp))) {
3101			launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], strerror(e));
3102			r = 1;
3103		}
3104	} else {
3105		launchctl_log(LOG_ERR, "%s %s returned unknown response", getprogname(), argv[0]);
3106		r = 1;
3107	}
3108
3109	launch_data_free(resp);
3110
3111	return r;
3112}
3113
3114int
3115logupdate_cmd(int argc, char *const argv[])
3116{
3117	int64_t inval, outval;
3118	bool badargs = false, maskmode = false, onlymode = false, levelmode = false;
3119	static const struct {
3120		const char *name;
3121		int level;
3122	} logtbl[] = {
3123		{ "debug",	LOG_DEBUG },
3124		{ "info",	LOG_INFO },
3125		{ "notice",	LOG_NOTICE },
3126		{ "warning",	LOG_WARNING },
3127		{ "error",	LOG_ERR },
3128		{ "critical",	LOG_CRIT },
3129		{ "alert",	LOG_ALERT },
3130		{ "emergency",	LOG_EMERG },
3131	};
3132	size_t i, j, logtblsz = sizeof logtbl / sizeof logtbl[0];
3133	int m = 0;
3134
3135	if (argc >= 2) {
3136		if (!strcmp(argv[1], "mask"))
3137			maskmode = true;
3138		else if (!strcmp(argv[1], "only"))
3139			onlymode = true;
3140		else if (!strcmp(argv[1], "level"))
3141			levelmode = true;
3142		else
3143			badargs = true;
3144	}
3145
3146	if (maskmode)
3147		m = LOG_UPTO(LOG_DEBUG);
3148
3149	if (argc > 2 && (maskmode || onlymode)) {
3150		for (i = 2; i < (size_t)argc; i++) {
3151			for (j = 0; j < logtblsz; j++) {
3152				if (!strcmp(argv[i], logtbl[j].name)) {
3153					if (maskmode)
3154						m &= ~(LOG_MASK(logtbl[j].level));
3155					else
3156						m |= LOG_MASK(logtbl[j].level);
3157					break;
3158				}
3159			}
3160			if (j == logtblsz) {
3161				badargs = true;
3162				break;
3163			}
3164		}
3165	} else if (argc > 2 && levelmode) {
3166		for (j = 0; j < logtblsz; j++) {
3167			if (!strcmp(argv[2], logtbl[j].name)) {
3168				m = LOG_UPTO(logtbl[j].level);
3169				break;
3170			}
3171		}
3172		if (j == logtblsz)
3173			badargs = true;
3174	} else if (argc != 1) {
3175		badargs = true;
3176	}
3177
3178	if (badargs) {
3179		launchctl_log(LOG_ERR, "usage: %s [[mask loglevels...] | [only loglevels...] [level loglevel]]", getprogname());
3180		return 1;
3181	}
3182
3183	inval = m;
3184
3185	if (vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_LOG_MASK, argc != 1 ? &inval : NULL, &outval) == NULL) {
3186		if (argc == 1) {
3187			for (j = 0; j < logtblsz; j++) {
3188				if (outval & LOG_MASK(logtbl[j].level)) {
3189					launchctl_log(LOG_NOTICE, "%s ", logtbl[j].name);
3190				}
3191			}
3192			launchctl_log(LOG_NOTICE, "");
3193		}
3194		return 0;
3195	} else {
3196		return 1;
3197	}
3198}
3199
3200static const struct {
3201	const char *name;
3202	int lim;
3203} limlookup[] = {
3204	{ "cpu",	RLIMIT_CPU },
3205	{ "filesize",	RLIMIT_FSIZE },
3206	{ "data",	RLIMIT_DATA },
3207	{ "stack",	RLIMIT_STACK },
3208	{ "core",	RLIMIT_CORE },
3209	{ "rss", 	RLIMIT_RSS },
3210	{ "memlock",	RLIMIT_MEMLOCK },
3211	{ "maxproc",	RLIMIT_NPROC },
3212	{ "maxfiles",	RLIMIT_NOFILE }
3213};
3214
3215static const size_t limlookupcnt = sizeof limlookup / sizeof limlookup[0];
3216
3217ssize_t
3218name2num(const char *n)
3219{
3220	size_t i;
3221
3222	for (i = 0; i < limlookupcnt; i++) {
3223		if (!strcmp(limlookup[i].name, n)) {
3224			return limlookup[i].lim;
3225		}
3226	}
3227	return -1;
3228}
3229
3230const char *
3231num2name(int n)
3232{
3233	size_t i;
3234
3235	for (i = 0; i < limlookupcnt; i++) {
3236		if (limlookup[i].lim == n)
3237			return limlookup[i].name;
3238	}
3239	return NULL;
3240}
3241
3242const char *
3243lim2str(rlim_t val, char *buf)
3244{
3245	if (val == RLIM_INFINITY)
3246		strcpy(buf, "unlimited");
3247	else
3248		sprintf(buf, "%lld", val);
3249	return buf;
3250}
3251
3252bool
3253str2lim(const char *buf, rlim_t *res)
3254{
3255	char *endptr;
3256	*res = strtoll(buf, &endptr, 10);
3257	if (!strcmp(buf, "unlimited")) {
3258		*res = RLIM_INFINITY;
3259		return false;
3260	} else if (*endptr == '\0') {
3261		 return false;
3262	}
3263	return true;
3264}
3265
3266int
3267limit_cmd(int argc, char *const argv[])
3268{
3269	char slimstr[100];
3270	char hlimstr[100];
3271	struct rlimit *lmts = NULL;
3272	launch_data_t resp, resp1 = NULL, msg, tmp;
3273	int r = 0;
3274	size_t i, lsz = -1;
3275	ssize_t which = 0;
3276	rlim_t slim = -1, hlim = -1;
3277	bool badargs = false;
3278
3279	if (argc > 4)
3280		badargs = true;
3281
3282	if (argc >= 3 && str2lim(argv[2], &slim))
3283		badargs = true;
3284	else
3285		hlim = slim;
3286
3287	if (argc == 4 && str2lim(argv[3], &hlim))
3288		badargs = true;
3289
3290	if (argc >= 2 && -1 == (which = name2num(argv[1])))
3291		badargs = true;
3292
3293	if (badargs) {
3294		launchctl_log(LOG_ERR, "usage: %s %s [", getprogname(), argv[0]);
3295		for (i = 0; i < limlookupcnt; i++)
3296			launchctl_log(LOG_ERR, "%s %s", limlookup[i].name, (i + 1) == limlookupcnt ? "" : "| ");
3297		launchctl_log(LOG_ERR, "[both | soft hard]]");
3298		return 1;
3299	}
3300
3301	msg = launch_data_new_string(LAUNCH_KEY_GETRESOURCELIMITS);
3302	resp = launch_msg(msg);
3303	launch_data_free(msg);
3304
3305	if (resp == NULL) {
3306		launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno));
3307		return 1;
3308	} else if (launch_data_get_type(resp) == LAUNCH_DATA_OPAQUE) {
3309		lmts = launch_data_get_opaque(resp);
3310		lsz = launch_data_get_opaque_size(resp);
3311		if (argc <= 2) {
3312			for (i = 0; i < (lsz / sizeof(struct rlimit)); i++) {
3313				if (argc == 2 && (size_t)which != i)
3314					continue;
3315				launchctl_log(LOG_NOTICE, "\t%-12s%-15s%-15s", num2name((int)i),
3316						lim2str(lmts[i].rlim_cur, slimstr),
3317						lim2str(lmts[i].rlim_max, hlimstr));
3318			}
3319		}
3320	} else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) {
3321		launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], launch_data_get_string(resp));
3322		r = 1;
3323	} else {
3324		launchctl_log(LOG_ERR, "%s %s returned unknown response", getprogname(), argv[0]);
3325		r = 1;
3326	}
3327
3328	if (argc <= 2 || r != 0) {
3329		launch_data_free(resp);
3330		return r;
3331	} else {
3332		resp1 = resp;
3333	}
3334
3335	lmts[which].rlim_cur = slim;
3336	lmts[which].rlim_max = hlim;
3337
3338	bool maxfiles_exceeded = false;
3339	if (strncmp(argv[1], "maxfiles", sizeof("maxfiles")) == 0) {
3340		if (argc > 2) {
3341			maxfiles_exceeded = (strncmp(argv[2], "unlimited", sizeof("unlimited")) == 0);
3342		}
3343
3344		if (argc > 3) {
3345			maxfiles_exceeded = (maxfiles_exceeded || strncmp(argv[3], "unlimited", sizeof("unlimited")) == 0);
3346		}
3347
3348		if (maxfiles_exceeded) {
3349			launchctl_log(LOG_ERR, "Neither the hard nor soft limit for \"maxfiles\" can be unlimited. Please use a numeric parameter for both.");
3350			return 1;
3351		}
3352	}
3353
3354	msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
3355	tmp = launch_data_new_opaque(lmts, lsz);
3356	launch_data_dict_insert(msg, tmp, LAUNCH_KEY_SETRESOURCELIMITS);
3357	resp = launch_msg(msg);
3358	launch_data_free(msg);
3359
3360	if (resp == NULL) {
3361		launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno));
3362		return 1;
3363	} else if (launch_data_get_type(resp) == LAUNCH_DATA_STRING) {
3364		launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], launch_data_get_string(resp));
3365		r = 1;
3366	} else if (launch_data_get_type(resp) != LAUNCH_DATA_OPAQUE) {
3367		launchctl_log(LOG_ERR, "%s %s returned unknown response", getprogname(), argv[0]);
3368		r = 1;
3369	}
3370
3371	launch_data_free(resp);
3372	launch_data_free(resp1);
3373
3374	return r;
3375}
3376
3377int
3378umask_cmd(int argc, char *const argv[])
3379{
3380	bool badargs = false;
3381	char *endptr;
3382	long m = 0;
3383	int64_t inval, outval;
3384
3385	if (argc == 2) {
3386		m = strtol(argv[1], &endptr, 8);
3387		if (*endptr != '\0' || m > 0777)
3388			badargs = true;
3389	}
3390
3391	if (argc > 2 || badargs) {
3392		launchctl_log(LOG_ERR, "usage: %s %s <mask>", getprogname(), argv[0]);
3393		return 1;
3394	}
3395
3396	inval = m;
3397
3398	if (vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_UMASK, argc == 2 ? &inval : NULL, &outval) == NULL) {
3399		if (argc == 1) {
3400			launchctl_log(LOG_NOTICE, "%o", (unsigned int)outval);
3401		}
3402		return 0;
3403	} else {
3404		return 1;
3405	}
3406}
3407
3408void
3409setup_system_context(void)
3410{
3411	if (getenv(LAUNCHD_SOCKET_ENV)) {
3412		return;
3413	}
3414
3415	if (getenv(LAUNCH_ENV_KEEPCONTEXT)) {
3416		return;
3417	}
3418
3419	if (geteuid() != 0) {
3420		launchctl_log(LOG_ERR, "You must be the root user to perform this operation.");
3421		return;
3422	}
3423
3424	/* Use the system launchd's socket. */
3425	setenv("__USE_SYSTEM_LAUNCHD", "1", 0);
3426
3427	/* Put ourselves in the system launchd's bootstrap. */
3428	mach_port_t rootbs = str2bsport("/");
3429	mach_port_deallocate(mach_task_self(), bootstrap_port);
3430	task_set_bootstrap_port(mach_task_self(), rootbs);
3431	bootstrap_port = rootbs;
3432}
3433
3434int
3435submit_cmd(int argc, char *const argv[])
3436{
3437	launch_data_t msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
3438	launch_data_t job = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
3439	launch_data_t resp, largv = launch_data_alloc(LAUNCH_DATA_ARRAY);
3440	int ch, i, r = 0;
3441
3442	launch_data_dict_insert(job, launch_data_new_bool(false), LAUNCH_JOBKEY_ONDEMAND);
3443
3444	while ((ch = getopt(argc, argv, "l:p:o:e:")) != -1) {
3445		switch (ch) {
3446		case 'l':
3447			launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_LABEL);
3448			break;
3449		case 'p':
3450			launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_PROGRAM);
3451			break;
3452		case 'o':
3453			launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_STANDARDOUTPATH);
3454			break;
3455		case 'e':
3456			launch_data_dict_insert(job, launch_data_new_string(optarg), LAUNCH_JOBKEY_STANDARDERRORPATH);
3457			break;
3458		default:
3459			launchctl_log(LOG_ERR, "usage: %s submit ...", getprogname());
3460			return 1;
3461		}
3462	}
3463	argc -= optind;
3464	argv += optind;
3465
3466	for (i = 0; argv[i]; i++) {
3467		launch_data_array_append(largv, launch_data_new_string(argv[i]));
3468	}
3469
3470	launch_data_dict_insert(job, largv, LAUNCH_JOBKEY_PROGRAMARGUMENTS);
3471
3472	launch_data_dict_insert(msg, job, LAUNCH_KEY_SUBMITJOB);
3473
3474	resp = launch_msg(msg);
3475	launch_data_free(msg);
3476
3477	if (resp == NULL) {
3478		launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno));
3479		return 1;
3480	} else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
3481		errno = launch_data_get_errno(resp);
3482		if (errno) {
3483			launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], strerror(errno));
3484			r = 1;
3485		}
3486	} else {
3487		launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], "unknown response");
3488	}
3489
3490	launch_data_free(resp);
3491
3492	return r;
3493}
3494
3495int
3496getrusage_cmd(int argc, char *const argv[])
3497{
3498	launch_data_t resp, msg;
3499	bool badargs = false;
3500	int r = 0;
3501
3502	if (argc != 2)
3503		badargs = true;
3504	else if (strcmp(argv[1], "self") && strcmp(argv[1], "children"))
3505		badargs = true;
3506
3507	if (badargs) {
3508		launchctl_log(LOG_ERR, "usage: %s %s self | children", getprogname(), argv[0]);
3509		return 1;
3510	}
3511
3512	if (!strcmp(argv[1], "self")) {
3513		msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGESELF);
3514	} else {
3515		msg = launch_data_new_string(LAUNCH_KEY_GETRUSAGECHILDREN);
3516	}
3517
3518	resp = launch_msg(msg);
3519	launch_data_free(msg);
3520
3521	if (resp == NULL) {
3522		launchctl_log(LOG_ERR, "launch_msg(): %s", strerror(errno));
3523		return 1;
3524	} else if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) {
3525		launchctl_log(LOG_ERR, "%s %s error: %s", getprogname(), argv[0], strerror(launch_data_get_errno(resp)));
3526		r = 1;
3527	} else if (launch_data_get_type(resp) == LAUNCH_DATA_OPAQUE) {
3528		struct rusage *rusage = launch_data_get_opaque(resp);
3529		launchctl_log(LOG_NOTICE, "\t%-10f\tuser time used",
3530				(double)rusage->ru_utime.tv_sec + (double)rusage->ru_utime.tv_usec / (double)1000000);
3531		launchctl_log(LOG_NOTICE, "\t%-10f\tsystem time used",
3532				(double)rusage->ru_stime.tv_sec + (double)rusage->ru_stime.tv_usec / (double)1000000);
3533		launchctl_log(LOG_NOTICE, "\t%-10ld\tmax resident set size", rusage->ru_maxrss);
3534		launchctl_log(LOG_NOTICE, "\t%-10ld\tshared text memory size", rusage->ru_ixrss);
3535		launchctl_log(LOG_NOTICE, "\t%-10ld\tunshared data size", rusage->ru_idrss);
3536		launchctl_log(LOG_NOTICE, "\t%-10ld\tunshared stack size", rusage->ru_isrss);
3537		launchctl_log(LOG_NOTICE, "\t%-10ld\tpage reclaims", rusage->ru_minflt);
3538		launchctl_log(LOG_NOTICE, "\t%-10ld\tpage faults", rusage->ru_majflt);
3539		launchctl_log(LOG_NOTICE, "\t%-10ld\tswaps", rusage->ru_nswap);
3540		launchctl_log(LOG_NOTICE, "\t%-10ld\tblock input operations", rusage->ru_inblock);
3541		launchctl_log(LOG_NOTICE, "\t%-10ld\tblock output operations", rusage->ru_oublock);
3542		launchctl_log(LOG_NOTICE, "\t%-10ld\tmessages sent", rusage->ru_msgsnd);
3543		launchctl_log(LOG_NOTICE, "\t%-10ld\tmessages received", rusage->ru_msgrcv);
3544		launchctl_log(LOG_NOTICE, "\t%-10ld\tsignals received", rusage->ru_nsignals);
3545		launchctl_log(LOG_NOTICE, "\t%-10ld\tvoluntary context switches", rusage->ru_nvcsw);
3546		launchctl_log(LOG_NOTICE, "\t%-10ld\tinvoluntary context switches", rusage->ru_nivcsw);
3547	} else {
3548		launchctl_log(LOG_ERR, "%s %s returned unknown response", getprogname(), argv[0]);
3549		r = 1;
3550	}
3551
3552	launch_data_free(resp);
3553
3554	return r;
3555}
3556
3557bool
3558launch_data_array_append(launch_data_t a, launch_data_t o)
3559{
3560	size_t offt = launch_data_array_get_count(a);
3561
3562	return launch_data_array_set_index(a, o, offt);
3563}
3564
3565mach_port_t
3566str2bsport(const char *s)
3567{
3568	bool getrootbs = strcmp(s, "/") == 0;
3569	mach_port_t last_bport, bport = bootstrap_port;
3570	task_t task = mach_task_self();
3571	kern_return_t result;
3572
3573	if (strcmp(s, "..") == 0 || getrootbs) {
3574		do {
3575			last_bport = bport;
3576			result = bootstrap_parent(last_bport, &bport);
3577
3578			if (result == BOOTSTRAP_NOT_PRIVILEGED) {
3579				launchctl_log(LOG_ERR, "Permission denied");
3580				return 1;
3581			} else if (result != BOOTSTRAP_SUCCESS) {
3582				launchctl_log(LOG_ERR, "bootstrap_parent() %d", result);
3583				return 1;
3584			}
3585		} while (getrootbs && last_bport != bport);
3586	} else if (strcmp(s, "0") == 0 || strcmp(s, "NULL") == 0) {
3587		bport = MACH_PORT_NULL;
3588	} else {
3589		int pid = atoi(s);
3590
3591		result = task_for_pid(mach_task_self(), pid, &task);
3592
3593		if (result != KERN_SUCCESS) {
3594			launchctl_log(LOG_ERR, "task_for_pid() %s", mach_error_string(result));
3595			return 1;
3596		}
3597
3598		result = task_get_bootstrap_port(task, &bport);
3599
3600		if (result != KERN_SUCCESS) {
3601			launchctl_log(LOG_ERR, "Couldn't get bootstrap port: %s", mach_error_string(result));
3602			return 1;
3603		}
3604	}
3605
3606	return bport;
3607}
3608
3609int
3610bsexec_cmd(int argc, char *const argv[])
3611{
3612	kern_return_t result;
3613	mach_port_t bport;
3614
3615	if (argc < 3) {
3616		launchctl_log(LOG_ERR, "usage: %s bsexec <PID> prog...", getprogname());
3617		return 1;
3618	}
3619
3620	bport = str2bsport(argv[1]);
3621
3622	result = task_set_bootstrap_port(mach_task_self(), bport);
3623
3624	if (result != KERN_SUCCESS) {
3625		launchctl_log(LOG_ERR, "Couldn't switch to new bootstrap port: %s", mach_error_string(result));
3626		return 1;
3627	}
3628
3629	setgid(getgid());
3630	setuid(getuid());
3631
3632	setenv(LAUNCH_ENV_KEEPCONTEXT, "1", 1);
3633	if (fwexec((const char *const *)argv + 2, NULL) == -1) {
3634		launchctl_log(LOG_ERR, "%s bsexec failed: %s", getprogname(), strerror(errno));
3635		return 1;
3636	}
3637
3638	return 0;
3639}
3640
3641int
3642_bslist_cmd(mach_port_t bport, unsigned int depth, bool show_job, bool local_only)
3643{
3644	kern_return_t result;
3645	name_array_t service_names;
3646	name_array_t service_jobs;
3647	mach_msg_type_number_t service_cnt, service_jobs_cnt, service_active_cnt;
3648	bootstrap_status_array_t service_actives;
3649	unsigned int i;
3650
3651	if (bport == MACH_PORT_NULL) {
3652		launchctl_log(LOG_ERR, "Invalid bootstrap port");
3653		return 1;
3654	}
3655
3656	uint64_t flags = 0;
3657	flags |= local_only ? BOOTSTRAP_FORCE_LOCAL : 0;
3658	result = bootstrap_info(bport, &service_names, &service_cnt, &service_jobs, &service_jobs_cnt, &service_actives, &service_active_cnt, flags);
3659	if (result != BOOTSTRAP_SUCCESS) {
3660		launchctl_log(LOG_ERR, "bootstrap_info(): %d", result);
3661		return 1;
3662	}
3663
3664#define bport_state(x)	(((x) == BOOTSTRAP_STATUS_ACTIVE) ? "A" : ((x) == BOOTSTRAP_STATUS_ON_DEMAND) ? "D" : "I")
3665
3666	for (i = 0; i < service_cnt ; i++) {
3667		if (!show_job) {
3668			fprintf(stdout, "%*s%-3s%s\n", depth, "", bport_state((service_actives[i])), service_names[i]);
3669		} else {
3670			fprintf(stdout, "%*s%-3s%s (%s)\n", depth, "", bport_state((service_actives[i])), service_names[i], service_jobs[i]);
3671		}
3672	}
3673
3674	return 0;
3675}
3676
3677int
3678bslist_cmd(int argc, char *const argv[])
3679{
3680	if (_launchctl_is_managed) {
3681		/* This output is meant for a command line, so don't print anything if
3682		 * we're managed by launchd.
3683		 */
3684		return 1;
3685	}
3686
3687	mach_port_t bport = bootstrap_port;
3688	bool show_jobs = false;
3689	if (argc > 2 && strcmp(argv[2], "-j") == 0) {
3690		show_jobs = true;
3691	}
3692
3693	if (argc > 1) {
3694		if (show_jobs) {
3695			bport = str2bsport(argv[1]);
3696		} else if (strcmp(argv[1], "-j") == 0) {
3697			show_jobs = true;
3698		}
3699	}
3700
3701	if (bport == MACH_PORT_NULL) {
3702		launchctl_log(LOG_ERR, "Invalid bootstrap port");
3703		return 1;
3704	}
3705
3706	return _bslist_cmd(bport, 0, show_jobs, false);
3707}
3708
3709int
3710_bstree_cmd(mach_port_t bsport, unsigned int depth, bool show_jobs)
3711{
3712	if (bsport == MACH_PORT_NULL) {
3713		launchctl_log(LOG_ERR, "No root port!");
3714		return 1;
3715	}
3716
3717	mach_port_array_t child_ports = NULL;
3718	name_array_t child_names = NULL;
3719	bootstrap_property_array_t child_props = NULL;
3720	unsigned int cnt = 0;
3721
3722	kern_return_t kr = bootstrap_lookup_children(bsport, &child_ports, &child_names, &child_props, (mach_msg_type_number_t *)&cnt);
3723	if (kr != BOOTSTRAP_SUCCESS && kr != BOOTSTRAP_NO_CHILDREN) {
3724		if (kr == BOOTSTRAP_NOT_PRIVILEGED) {
3725			launchctl_log(LOG_ERR, "You must be root to perform this operation.");
3726		} else {
3727			launchctl_log(LOG_ERR, "bootstrap_lookup_children(): %d", kr);
3728		}
3729
3730		return 1;
3731	}
3732
3733	unsigned int i = 0;
3734	_bslist_cmd(bsport, depth, show_jobs, true);
3735
3736	for (i = 0; i < cnt; i++) {
3737		char *type = NULL;
3738		if (child_props[i] & BOOTSTRAP_PROPERTY_PERUSER) {
3739			type = "Per-user";
3740		} else if (child_props[i] & BOOTSTRAP_PROPERTY_EXPLICITSUBSET) {
3741			type = "Explicit Subset";
3742		} else if (child_props[i] & BOOTSTRAP_PROPERTY_IMPLICITSUBSET) {
3743			type = "Implicit Subset";
3744		} else if (child_props[i] & BOOTSTRAP_PROPERTY_MOVEDSUBSET) {
3745			type = "Moved Subset";
3746		} else if (child_props[i] & BOOTSTRAP_PROPERTY_XPC_SINGLETON) {
3747			type = "XPC Singleton Domain";
3748		} else if (child_props[i] & BOOTSTRAP_PROPERTY_XPC_DOMAIN) {
3749			type = "XPC Private Domain";
3750		} else {
3751			type = "Unknown";
3752		}
3753
3754		fprintf(stdout, "%*s%s (%s)/\n", depth, "", child_names[i], type);
3755		if (child_ports[i] != MACH_PORT_NULL) {
3756			_bstree_cmd(child_ports[i], depth + 4, show_jobs);
3757		}
3758	}
3759
3760	return 0;
3761}
3762
3763int
3764bstree_cmd(int argc, char * const argv[])
3765{
3766	if (_launchctl_is_managed) {
3767		/* This output is meant for a command line, so don't print anything if
3768		 * we're managed by launchd.
3769		 */
3770		return 1;
3771	}
3772
3773	bool show_jobs = false;
3774	if (geteuid() != 0) {
3775		launchctl_log(LOG_ERR, "You must be root to perform this operation.");
3776		return 1;
3777	} else {
3778		if (argc == 2 && strcmp(argv[1], "-j") == 0) {
3779			show_jobs = true;
3780		}
3781		fprintf(stdout, "System/\n");
3782	}
3783
3784	return _bstree_cmd(str2bsport("/"), 4, show_jobs);
3785}
3786
3787int
3788managerpid_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)))
3789{
3790	int64_t manager_pid = 0;
3791	vproc_err_t verr = vproc_swap_integer(NULL, VPROC_GSK_MGR_PID, NULL, (int64_t *)&manager_pid);
3792	if (verr) {
3793		launchctl_log(LOG_NOTICE, "Unknown job manager!");
3794		return 1;
3795	}
3796
3797	launchctl_log(LOG_NOTICE, "%d", (pid_t)manager_pid);
3798	return 0;
3799}
3800
3801int
3802manageruid_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)))
3803{
3804	int64_t manager_uid = 0;
3805	vproc_err_t verr = vproc_swap_integer(NULL, VPROC_GSK_MGR_UID, NULL, (int64_t *)&manager_uid);
3806	if (verr) {
3807		launchctl_log(LOG_NOTICE, "Unknown job manager!");
3808		return 1;
3809	}
3810
3811	launchctl_log(LOG_NOTICE, "%lli", manager_uid);
3812	return 0;
3813}
3814
3815int
3816managername_cmd(int argc __attribute__((unused)), char * const argv[] __attribute__((unused)))
3817{
3818	char *manager_name = NULL;
3819	vproc_err_t verr = vproc_swap_string(NULL, VPROC_GSK_MGR_NAME, NULL, &manager_name);
3820	if (verr) {
3821		launchctl_log(LOG_NOTICE, "Unknown job manager!");
3822		return 1;
3823	}
3824
3825	launchctl_log(LOG_NOTICE, "%s", manager_name);
3826	free(manager_name);
3827
3828	return 0;
3829}
3830
3831int
3832asuser_cmd(int argc, char * const argv[])
3833{
3834	/* This code plays fast and loose with Mach ports. Do NOT use it as any sort
3835	 * of reference for port handling. Or really anything else in this file.
3836	 */
3837	uid_t req_uid = (uid_t)-2;
3838	if (argc > 2) {
3839		req_uid = atoi(argv[1]);
3840		if (req_uid == (uid_t)-2) {
3841			launchctl_log(LOG_ERR, "You cannot run a command nobody.");
3842			return 1;
3843		}
3844	} else {
3845		launchctl_log(LOG_ERR, "Usage: launchctl asuser <UID> <command> [arguments...].");
3846		return 1;
3847	}
3848
3849	if (geteuid() != 0) {
3850		launchctl_log(LOG_ERR, "You must be root to run a command as another user.");
3851		return 1;
3852	}
3853
3854	mach_port_t rbs = MACH_PORT_NULL;
3855	kern_return_t kr = bootstrap_get_root(bootstrap_port, &rbs);
3856	if (kr != BOOTSTRAP_SUCCESS) {
3857		launchctl_log(LOG_ERR, "bootstrap_get_root(): %u", kr);
3858		return 1;
3859	}
3860
3861	mach_port_t bp = MACH_PORT_NULL;
3862	kr = bootstrap_look_up_per_user(rbs, NULL, req_uid, &bp);
3863	if (kr != BOOTSTRAP_SUCCESS) {
3864		launchctl_log(LOG_ERR, "bootstrap_look_up_per_user(): %u", kr);
3865		return 1;
3866	}
3867
3868	bootstrap_port = bp;
3869	kr = task_set_bootstrap_port(mach_task_self(), bp);
3870	if (kr != KERN_SUCCESS) {
3871		launchctl_log(LOG_ERR, "task_set_bootstrap_port(): 0x%x: %s", kr, mach_error_string(kr));
3872		return 1;
3873	}
3874
3875	name_t sockpath;
3876	sockpath[0] = 0;
3877	kr = _vprocmgr_getsocket(sockpath);
3878	if (kr != BOOTSTRAP_SUCCESS) {
3879		launchctl_log(LOG_ERR, "_vprocmgr_getsocket(): %u", kr);
3880		return 1;
3881	}
3882
3883	setenv(LAUNCHD_SOCKET_ENV, sockpath, 1);
3884	setenv(LAUNCH_ENV_KEEPCONTEXT, "1", 1);
3885	if (fwexec((const char *const *)argv + 2, NULL) == -1) {
3886		launchctl_log(LOG_ERR, "Couldn't spawn command: %s", argv[2]);
3887		return 1;
3888	}
3889
3890	return 0;
3891}
3892
3893void
3894loopback_setup_ipv4(void)
3895{
3896	struct ifaliasreq ifra;
3897	struct ifreq ifr;
3898	int s;
3899
3900	memset(&ifr, 0, sizeof(ifr));
3901	strcpy(ifr.ifr_name, "lo0");
3902
3903	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
3904		return;
3905
3906	if (posix_assumes_zero(ioctl(s, SIOCGIFFLAGS, &ifr)) != -1) {
3907		ifr.ifr_flags |= IFF_UP;
3908		(void)posix_assumes_zero(ioctl(s, SIOCSIFFLAGS, &ifr));
3909	}
3910
3911	memset(&ifra, 0, sizeof(ifra));
3912	strcpy(ifra.ifra_name, "lo0");
3913	((struct sockaddr_in *)&ifra.ifra_addr)->sin_family = AF_INET;
3914	((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
3915	((struct sockaddr_in *)&ifra.ifra_addr)->sin_len = sizeof(struct sockaddr_in);
3916	((struct sockaddr_in *)&ifra.ifra_mask)->sin_family = AF_INET;
3917	((struct sockaddr_in *)&ifra.ifra_mask)->sin_addr.s_addr = htonl(IN_CLASSA_NET);
3918	((struct sockaddr_in *)&ifra.ifra_mask)->sin_len = sizeof(struct sockaddr_in);
3919
3920	(void)posix_assumes_zero(ioctl(s, SIOCAIFADDR, &ifra));
3921	(void)close(s);
3922}
3923
3924void
3925loopback_setup_ipv6(void)
3926{
3927	struct in6_aliasreq ifra6;
3928	struct ifreq ifr;
3929	int s6;
3930
3931	memset(&ifr, 0, sizeof(ifr));
3932	strcpy(ifr.ifr_name, "lo0");
3933
3934	if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
3935		return;
3936
3937	memset(&ifr, 0, sizeof(ifr));
3938	strcpy(ifr.ifr_name, "lo0");
3939
3940	if (posix_assumes_zero(ioctl(s6, SIOCGIFFLAGS, &ifr)) != -1) {
3941		ifr.ifr_flags |= IFF_UP;
3942		(void)posix_assumes_zero(ioctl(s6, SIOCSIFFLAGS, &ifr));
3943	}
3944
3945	memset(&ifra6, 0, sizeof(ifra6));
3946	strcpy(ifra6.ifra_name, "lo0");
3947
3948	ifra6.ifra_addr.sin6_family = AF_INET6;
3949	ifra6.ifra_addr.sin6_addr = in6addr_loopback;
3950	ifra6.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
3951	ifra6.ifra_prefixmask.sin6_family = AF_INET6;
3952	memset(&ifra6.ifra_prefixmask.sin6_addr, 0xff, sizeof(struct in6_addr));
3953	ifra6.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
3954	ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
3955	ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
3956
3957	if (ioctl(s6, SIOCAIFADDR_IN6, &ifra6) == -1 && errno != EEXIST) {
3958		(void)os_assumes_zero(errno);
3959	}
3960
3961	(void)close(s6);
3962}
3963
3964pid_t
3965fwexec(const char *const *argv, int *wstatus)
3966{
3967	int wstatus2;
3968	pid_t p;
3969
3970	/* We'd use posix_spawnp(), but we want to workaround: 6288899 */
3971	if ((p = vfork()) == -1) {
3972		return -1;
3973	} else if (p == 0) {
3974		execvp(argv[0], (char *const *)argv);
3975		_exit(EXIT_FAILURE);
3976	}
3977
3978	if (waitpid(p, wstatus ? wstatus : &wstatus2, 0) == -1) {
3979		return -1;
3980	}
3981
3982	if (wstatus) {
3983		return p;
3984	} else if (WIFEXITED(wstatus2) && WEXITSTATUS(wstatus2) == EXIT_SUCCESS) {
3985		return p;
3986	}
3987
3988	return -1;
3989}
3990
3991void
3992do_potential_fsck(void)
3993{
3994	/* XXX: This whole function's logic needs to be redone. */
3995
3996	const char *safe_fsck_tool[] = { "fsck", "-fy", NULL };
3997	const char *fsck_tool[] = { "fsck", "-q", NULL };
3998	const char *remount_tool[] = { "mount", "-uw", "/", NULL };
3999#if TARGET_OS_EMBEDDED
4000	const char *nvram_tool[] = { "/usr/sbin/nvram", "auto-boot=false", NULL };
4001#endif /* TARGET_OS_EMBEDDED */
4002	struct statfs sfs;
4003	int status = 0;
4004
4005	if (posix_assumes_zero(statfs("/", &sfs)) == -1) {
4006		return;
4007	}
4008
4009	if (!(sfs.f_flags & MNT_RDONLY)) {
4010		return;
4011	}
4012
4013	if (!is_safeboot()) {
4014#if 0
4015		/* We have disabled this block for now. We need to revisit this optimization after Leopard. */
4016		if (sfs.f_flags & MNT_JOURNALED) {
4017			goto out;
4018		}
4019#endif
4020		launchctl_log(LOG_NOTICE, "Running fsck on the boot volume...");
4021		if (fwexec(fsck_tool, &status) != -1) {
4022			if (WEXITSTATUS(status) != 0) {
4023				launchctl_log(LOG_NOTICE, "fsck exited with status: %d", WEXITSTATUS(status));
4024			} else {
4025				goto out;
4026			}
4027		} else {
4028			launchctl_log(LOG_NOTICE, "fwexec(): %d: %s", errno, strerror(errno));
4029		}
4030	}
4031
4032	launchctl_log(LOG_NOTICE, "Running safe fsck on the boot volume...");
4033	if (fwexec(safe_fsck_tool, &status) != -1) {
4034		if (WEXITSTATUS(status) != 0) {
4035			launchctl_log(LOG_NOTICE, "Safe fsck exited with status: %d", WEXITSTATUS(status));
4036		} else {
4037			goto out;
4038		}
4039	} else {
4040		launchctl_log(LOG_NOTICE, "fwexec(): %d: %s", errno, strerror(errno));
4041	}
4042
4043	/* someday, we should keep booting read-only, but as of today, other sub-systems cannot handle that scenario */
4044#if TARGET_OS_EMBEDDED
4045	launchctl_log(LOG_NOTICE, "fsck failed! Booting into restore mode...");
4046	(void)posix_assumes_zero(fwexec(nvram_tool, NULL));
4047	(void)reboot(RB_AUTOBOOT);
4048#else
4049	launchctl_log(LOG_NOTICE, "fsck failed! Shutting down in 3 seconds.");
4050	sleep(3);
4051	(void)reboot(RB_HALT);
4052#endif
4053
4054	return;
4055out:
4056
4057#if TARGET_OS_EMBEDDED
4058	/* Once we've validated the root filesystem, kick off any
4059	 * tasks needed for data protection before we mount other file
4060	 * systems.
4061	 */
4062	init_data_protection();
4063#endif
4064
4065	/*
4066	 * Once this is fixed:
4067	 *
4068	 * <rdar://problem/3948774> Mount flag updates should be possible with NULL as the forth argument to mount()
4069	 *
4070	 * We can then do this one system call instead of calling out a full blown process.
4071	 *
4072	 * assumes(mount(sfs.f_fstypename, "/", MNT_UPDATE, NULL) != -1);
4073	 */
4074#if TARGET_OS_EMBEDDED
4075	if (path_check("/etc/fstab")) {
4076		const char *mount_tool[] = { "mount", "-vat", "nonfs", NULL };
4077		if (posix_assumes_zero(fwexec(mount_tool, NULL)) == -1) {
4078			(void)fwexec(nvram_tool, NULL);
4079			(void)reboot(RB_AUTOBOOT);
4080		}
4081	} else
4082#endif
4083	{
4084		(void)posix_assumes_zero(fwexec(remount_tool, NULL));
4085	}
4086
4087	fix_bogus_file_metadata();
4088}
4089
4090void
4091fix_bogus_file_metadata(void)
4092{
4093	// Don't do any of this on embedded: <rdar://problem/13212363>
4094#if !TARGET_OS_EMBEDDED
4095	static const struct {
4096		const char *path;
4097		const uid_t owner;
4098		const gid_t group;
4099		const mode_t needed_bits;
4100		const mode_t bad_bits;
4101		const bool create;
4102	} f[] = {
4103		{ "/sbin/launchd", 0, 0, S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH, S_ISUID|S_ISGID|S_ISVTX|S_IWOTH, false },
4104		{ _PATH_TMP, 0, 0, S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO, S_ISUID|S_ISGID, true },
4105		{ _PATH_VARTMP, 0, 0, S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO, S_ISUID|S_ISGID, true },
4106		{ "/var/folders", 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_ISUID | S_ISGID, true },
4107		{ LAUNCHD_DB_PREFIX, 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IWGRP | S_IWOTH, true },
4108		{ LAUNCHD_DB_PREFIX "/com.apple.launchd", 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IWGRP | S_IWOTH, true },
4109		// Fixing <rdar://problem/7571633>.
4110		{ _PATH_VARDB, 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IWGRP | S_IWOTH | S_ISUID | S_ISGID, true },
4111		{ _PATH_VARDB "mds/", 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IWGRP | S_IWOTH | S_ISUID | S_ISGID, true },
4112		// Similar fix for <rdar://problem/6550172>.
4113		{ "/Library/StartupItems", 0, 0, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IWGRP | S_IWOTH | S_ISUID | S_ISGID, true },
4114	};
4115	struct stat sb;
4116	size_t i;
4117
4118	for (i = 0; i < (sizeof(f) / sizeof(f[0])); i++) {
4119		mode_t i_needed_bits;
4120		mode_t i_bad_bits;
4121		bool fix_mode = false;
4122		bool fix_id = false;
4123
4124		if (stat(f[i].path, &sb) == -1) {
4125			launchctl_log(LOG_NOTICE, "Crucial filesystem check: Path not present: %s. %s", f[i].path, f[i].create ? "Will create." : "");
4126			if (f[i].create) {
4127				if (posix_assumes_zero(mkdir(f[i].path, f[i].needed_bits)) == -1) {
4128					continue;
4129				} else if (posix_assumes_zero(stat(f[i].path, &sb)) == -1) {
4130					continue;
4131				}
4132			} else {
4133				continue;
4134			}
4135		}
4136
4137		i_needed_bits = ~sb.st_mode & f[i].needed_bits;
4138		i_bad_bits = sb.st_mode & f[i].bad_bits;
4139
4140		if (i_bad_bits) {
4141			launchctl_log(LOG_ERR, "Crucial filesystem check: Removing bogus mode bits 0%o on path: %s", i_bad_bits, f[i].path);
4142			fix_mode = true;
4143		}
4144		if (i_needed_bits) {
4145			launchctl_log(LOG_ERR, "Crucial filesystem check: Adding missing mode bits 0%o on path: %s", i_needed_bits, f[i].path);
4146			fix_mode = true;
4147		}
4148		if (sb.st_uid != f[i].owner) {
4149			launchctl_log(LOG_ERR, "Crucial filesystem check: Fixing bogus UID %u on path: %s", sb.st_uid, f[i].path);
4150			fix_id = true;
4151		}
4152		if (sb.st_gid != f[i].group) {
4153			launchctl_log(LOG_ERR, "Crucial filesystem check: Fixing bogus GID %u on path: %s", sb.st_gid, f[i].path);
4154			fix_id = true;
4155		}
4156
4157		if (fix_mode) {
4158			(void)posix_assumes_zero(chmod(f[i].path, (sb.st_mode & ~i_bad_bits) | i_needed_bits));
4159		}
4160		if (fix_id) {
4161			(void)posix_assumes_zero(chown(f[i].path, f[i].owner, f[i].group));
4162		}
4163	}
4164#endif
4165}
4166
4167
4168bool
4169path_check(const char *path)
4170{
4171	struct stat sb;
4172
4173	if (stat(path, &sb) == 0)
4174		return true;
4175	return false;
4176}
4177
4178bool
4179is_safeboot(void)
4180{
4181	int sbmib[] = { CTL_KERN, KERN_SAFEBOOT };
4182	uint32_t sb = 0;
4183	size_t sbsz = sizeof(sb);
4184
4185	if (posix_assumes_zero(sysctl(sbmib, 2, &sb, &sbsz, NULL, 0)) == -1) {
4186		return false;
4187	}
4188
4189	return (bool)sb;
4190}
4191
4192bool
4193is_netboot(void)
4194{
4195	int nbmib[] = { CTL_KERN, KERN_NETBOOT };
4196	uint32_t nb = 0;
4197	size_t nbsz = sizeof(nb);
4198
4199	if (posix_assumes_zero(sysctl(nbmib, 2, &nb, &nbsz, NULL, 0)) == -1) {
4200		return false;
4201	}
4202
4203	return (bool)nb;
4204}
4205
4206void
4207empty_dir(const char *thedir, struct stat *psb)
4208{
4209	struct dirent *de;
4210	struct stat psb2;
4211	DIR *od;
4212	int currend_dir_fd;
4213
4214	if (!psb) {
4215		psb = &psb2;
4216		if (posix_assumes_zero(lstat(thedir, psb)) == -1) {
4217			return;
4218		}
4219	}
4220
4221	if (posix_assumes_zero(currend_dir_fd = open(".", 0)) == -1) {
4222		return;
4223	}
4224
4225	if (posix_assumes_zero(chdir(thedir)) == -1) {
4226		goto out;
4227	}
4228
4229	if (!(od = opendir("."))) {
4230		(void)os_assumes_zero(errno);
4231		goto out;
4232	}
4233
4234	while ((de = readdir(od))) {
4235		struct stat sb;
4236
4237		if (strcmp(de->d_name, ".") == 0) {
4238			continue;
4239		}
4240
4241		if (strcmp(de->d_name, "..") == 0) {
4242			continue;
4243		}
4244
4245		if (posix_assumes_zero(lstat(de->d_name, &sb)) == -1) {
4246			continue;
4247		}
4248
4249		if (psb->st_dev != sb.st_dev) {
4250			(void)posix_assumes_zero(unmount(de->d_name, MNT_FORCE));
4251
4252			/* Let's lstat() again to see if the unmount() worked and what was
4253			 * under it.
4254			 */
4255			if (posix_assumes_zero(lstat(de->d_name, &sb)) == -1) {
4256				continue;
4257			}
4258
4259			if (os_assumes(psb->st_dev == sb.st_dev)) {
4260				continue;
4261			}
4262		}
4263
4264		if (S_ISDIR(sb.st_mode)) {
4265			empty_dir(de->d_name, &sb);
4266		}
4267
4268		(void)posix_assumes_zero(lchflags(de->d_name, 0));
4269		(void)posix_assumes_zero(remove(de->d_name));
4270	}
4271
4272	(void)closedir(od);
4273
4274out:
4275	(void)posix_assumes_zero(fchdir(currend_dir_fd));
4276	(void)posix_assumes_zero(close(currend_dir_fd));
4277}
4278
4279int
4280touch_file(const char *path, mode_t m)
4281{
4282	int fd = open(path, O_CREAT, m);
4283
4284	if (fd == -1)
4285		return -1;
4286
4287	return close(fd);
4288}
4289
4290void
4291apply_sysctls_from_file(const char *thefile)
4292{
4293	const char *sysctl_tool[] = { "sysctl", "-w", NULL, NULL };
4294	size_t ln_len = 0;
4295	char *val, *tmpstr;
4296	FILE *sf;
4297
4298	if (!(sf = fopen(thefile, "r")))
4299		return;
4300
4301	while ((val = fgetln(sf, &ln_len))) {
4302		if (ln_len == 0) {
4303			continue;
4304		}
4305		if (!(tmpstr = malloc(ln_len + 1))) {
4306			(void)os_assumes_zero(errno);
4307			continue;
4308		}
4309		memcpy(tmpstr, val, ln_len);
4310		tmpstr[ln_len] = 0;
4311		val = tmpstr;
4312
4313		if (val[ln_len - 1] == '\n' || val[ln_len - 1] == '\r') {
4314			val[ln_len - 1] = '\0';
4315		}
4316
4317		while (*val && isspace(*val))
4318			val++;
4319		if (*val == '\0' || *val == '#') {
4320			goto skip_sysctl_tool;
4321		}
4322		sysctl_tool[2] = val;
4323		(void)posix_assumes_zero(fwexec(sysctl_tool, NULL));
4324skip_sysctl_tool:
4325		free(tmpstr);
4326	}
4327
4328	(void)fclose(sf);
4329}
4330
4331static CFStringRef
4332copySystemBuildVersion(void)
4333{
4334    CFStringRef build = NULL;
4335    const char path[] = "/System/Library/CoreServices/SystemVersion.plist";
4336    CFURLRef plistURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (const uint8_t *)path, sizeof(path) - 1, false);
4337
4338	CFPropertyListRef plist = NULL;
4339    if (plistURL && (plist = CFPropertyListCreateFromFile(plistURL))) {
4340		if (CFTypeCheck(plist, CFDictionary)) {
4341			build = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)plist, _kCFSystemVersionBuildVersionKey);
4342			if (build && CFTypeCheck(build, CFString)) {
4343				CFRetain(build);
4344			} else {
4345				build = CFSTR("99Z999");
4346			}
4347		}
4348
4349		CFRelease(plist);
4350    } else {
4351		build = CFSTR("99Z999");
4352	}
4353
4354	if (plistURL) {
4355		CFRelease(plistURL);
4356	}
4357
4358    return build;
4359}
4360
4361void
4362do_sysversion_sysctl(void)
4363{
4364	int mib[] = { CTL_KERN, KERN_OSVERSION };
4365	CFStringRef buildvers;
4366	char buf[1024];
4367	size_t bufsz = sizeof(buf);
4368
4369	/* <rdar://problem/4477682> ER: launchd should set kern.osversion very early in boot */
4370
4371	if (sysctl(mib, 2, buf, &bufsz, NULL, 0) == -1) {
4372		launchctl_log(LOG_ERR, "sysctl(): %s", strerror(errno));
4373		return;
4374	}
4375
4376	if (buf[0] != '\0') {
4377		return;
4378	}
4379
4380	buildvers = copySystemBuildVersion();
4381	if (buildvers) {
4382		CFStringGetCString(buildvers, buf, sizeof(buf), kCFStringEncodingUTF8);
4383		(void)posix_assumes_zero(sysctl(mib, 2, NULL, 0, buf, strlen(buf) + 1));
4384	}
4385
4386	CFRelease(buildvers);
4387}
4388
4389void
4390do_application_firewall_magic(int sfd, launch_data_t thejob)
4391{
4392	const char *prog = NULL, *partialprog = NULL;
4393	char *path, *pathtmp, **pathstmp;
4394	char *paths[100];
4395	launch_data_t tmp;
4396
4397	/*
4398	 * Sigh...
4399	 * <rdar://problem/4684434> setsockopt() with the executable path as the argument
4400	 */
4401
4402	if ((tmp = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_PROGRAM))) {
4403		prog = launch_data_get_string(tmp);
4404	}
4405
4406	if (!prog) {
4407		if ((tmp = launch_data_dict_lookup(thejob, LAUNCH_JOBKEY_PROGRAMARGUMENTS))) {
4408			if ((tmp = launch_data_array_get_index(tmp, 0))) {
4409				if ((partialprog = launch_data_get_string(tmp))) {
4410					if (partialprog[0] == '/') {
4411						prog = partialprog;
4412					}
4413				}
4414			}
4415		}
4416	}
4417
4418	if (!prog) {
4419		pathtmp = path = strdup(getenv("PATH"));
4420
4421		pathstmp = paths;
4422
4423		while ((*pathstmp = strsep(&pathtmp, ":"))) {
4424			if (**pathstmp != '\0') {
4425				pathstmp++;
4426			}
4427		}
4428
4429		free(path);
4430		pathtmp = alloca(MAXPATHLEN);
4431
4432		pathstmp = paths;
4433
4434		for (; *pathstmp; pathstmp++) {
4435			snprintf(pathtmp, MAXPATHLEN, "%s/%s", *pathstmp, partialprog);
4436			if (path_check(pathtmp)) {
4437				prog = pathtmp;
4438				break;
4439			}
4440		}
4441	}
4442
4443	if (prog != NULL) {
4444		/* The networking team has asked us to ignore the failure of this API if
4445		 * errno == ENOPROTOOPT.
4446		 */
4447		if (setsockopt(sfd, SOL_SOCKET, SO_EXECPATH, prog, (socklen_t)(strlen(prog) + 1)) == -1 && errno != ENOPROTOOPT) {
4448			(void)os_assumes_zero(errno);
4449		}
4450	}
4451}
4452
4453
4454void
4455preheat_page_cache_hack(void)
4456{
4457	struct dirent *de;
4458	DIR *thedir;
4459
4460	/* Disable this hack for now */
4461	return;
4462
4463	if ((thedir = opendir("/etc/preheat_at_boot")) == NULL) {
4464		return;
4465	}
4466
4467	while ((de = readdir(thedir))) {
4468		struct stat sb;
4469		void *junkbuf;
4470		int fd;
4471
4472		if (de->d_name[0] == '.') {
4473			continue;
4474		}
4475
4476		if ((fd = open(de->d_name, O_RDONLY)) == -1) {
4477			continue;
4478		}
4479
4480		if (fstat(fd, &sb) != -1) {
4481			if ((sb.st_size < 10*1024*1024) && (junkbuf = malloc((size_t)sb.st_size)) != NULL) {
4482				ssize_t n = read(fd, junkbuf, (size_t)sb.st_size);
4483				if (posix_assumes_zero(n) != -1 && n != (ssize_t)sb.st_size) {
4484					(void)os_assumes_zero(n);
4485				}
4486				free(junkbuf);
4487			}
4488		}
4489
4490		close(fd);
4491	}
4492
4493	closedir(thedir);
4494}
4495
4496void
4497do_bootroot_magic(void)
4498{
4499	const char *kextcache_tool[] = { "kextcache", "-U", "/", NULL };
4500	CFTypeRef bootrootProp;
4501	io_service_t chosen;
4502	int wstatus;
4503	pid_t p;
4504
4505	chosen = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/chosen");
4506
4507	if (!os_assumes(chosen)) {
4508		return;
4509	}
4510
4511	bootrootProp = IORegistryEntryCreateCFProperty(chosen, CFSTR(kBootRootActiveKey), kCFAllocatorDefault, 0);
4512
4513	IOObjectRelease(chosen);
4514
4515	if (!bootrootProp) {
4516		return;
4517	}
4518
4519	CFRelease(bootrootProp);
4520
4521	if (posix_assumes_zero(p = fwexec(kextcache_tool, &wstatus)) == -1) {
4522		return;
4523	}
4524
4525	if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == EX_OSFILE) {
4526		(void)reboot(RB_AUTOBOOT);
4527	}
4528}
4529
4530void
4531do_file_init(void)
4532{
4533	struct stat sb;
4534
4535	if (stat("/AppleInternal", &sb) == 0 && stat("/var/db/disableAppleInternal", &sb) == -1) {
4536		_launchctl_apple_internal = true;
4537	}
4538
4539	char bootargs[128];
4540	size_t len = sizeof(bootargs);
4541	int r = sysctlbyname("kern.bootargs", bootargs, &len, NULL, 0);
4542	if (r == 0 && (strnstr(bootargs, "-v", len) != NULL || strnstr(bootargs, "-s", len))) {
4543		_launchctl_verbose_boot = true;
4544	}
4545
4546	if (stat("/var/db/.launchd_shutdown_debugging", &sb) == 0 && _launchctl_verbose_boot) {
4547		_launchctl_startup_debugging = true;
4548	}
4549}
4550