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