2 * Copyright (c) 2005-2010 Apple Inc. All rights reserved.
4 * @APPLE_APACHE_LICENSE_HEADER_START@
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
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * @APPLE_APACHE_LICENSE_HEADER_END@
21 static const char *const __rcs_file_version__
= "$Revision: 24957 $";
25 #include "launch_priv.h"
26 #include "bootstrap.h"
28 #include "vproc_priv.h"
29 #include "vproc_internal.h"
30 #include "bootstrap_priv.h"
31 #include "launch_internal.h"
33 #include <CoreFoundation/CoreFoundation.h>
34 #include <CoreFoundation/CFPriv.h>
35 #include <CoreFoundation/CFLogUtilities.h>
36 #include <TargetConditionals.h>
37 #include <IOKit/IOKitLib.h>
38 #include <NSSystemDirectories.h>
39 #include <mach/mach.h>
40 #include <sys/types.h>
41 #include <sys/sysctl.h>
43 #include <sys/sysctl.h>
45 #include <sys/socket.h>
47 /* This is just so it's easy for me to compile launchctl without buildit. */
48 #define SO_EXECPATH 0x1085
51 #include <sys/fcntl.h>
52 #include <sys/event.h>
53 #include <sys/resource.h>
54 #include <sys/param.h>
55 #include <sys/mount.h>
56 #include <sys/reboot.h>
58 #include <netinet/in.h>
59 #include <netinet/in_var.h>
60 #include <netinet6/nd6.h>
73 #include <readline/readline.h>
74 #include <readline/history.h>
78 #include <bootfiles.h>
82 #include <sys/syslimits.h>
86 #include <bsm/auditd_lib.h>
87 #ifndef AUDITD_PLIST_FILE
88 #define AUDITD_PLIST_FILE "/System/Library/LaunchDaemons/com.apple.auditd.plist"
92 extern char **environ
;
95 #define LAUNCH_SECDIR _PATH_TMP "launch-XXXXXX"
96 #define LAUNCH_ENV_KEEPCONTEXT "LaunchKeepContext"
98 #define MACHINIT_JOBKEY_ONDEMAND "OnDemand"
99 #define MACHINIT_JOBKEY_SERVICENAME "ServiceName"
100 #define MACHINIT_JOBKEY_COMMAND "Command"
101 #define MACHINIT_JOBKEY_SERVERPORT "ServerPort"
102 #define MACHINIT_JOBKEY_SERVICEPORT "ServicePort"
105 (__builtin_expect(!(e), 0) ? _log_launchctl_bug(__rcs_file_version__, __FILE__, __LINE__, #e), false : true)
107 #define CFTypeCheck(cf, type) (CFGetTypeID(cf) == type ## GetTypeID())
109 struct load_unload_state
{
113 bool editondisk
:1, load
:1, forceload
:1;
116 static void myCFDictionaryApplyFunction(const void *key
, const void *value
, void *context
);
117 static void job_override(CFTypeRef key
, CFTypeRef val
, CFMutableDictionaryRef job
);
118 static CFTypeRef
CFTypeCreateFromLaunchData(launch_data_t obj
);
119 static CFArrayRef
CFArrayCreateFromLaunchArray(launch_data_t arr
);
120 static CFDictionaryRef
CFDictionaryCreateFromLaunchDictionary(launch_data_t dict
);
121 static bool launch_data_array_append(launch_data_t a
, launch_data_t o
);
122 static void distill_jobs(launch_data_t
);
123 static void distill_config_file(launch_data_t
);
124 static void sock_dict_cb(launch_data_t what
, const char *key
, void *context
);
125 static void sock_dict_edit_entry(launch_data_t tmp
, const char *key
, launch_data_t fdarray
, launch_data_t thejob
);
126 static launch_data_t
CF2launch_data(CFTypeRef
);
127 static launch_data_t
read_plist_file(const char *file
, bool editondisk
, bool load
);
128 static CFPropertyListRef
CreateMyPropertyListFromFile(const char *);
129 static CFPropertyListRef
CFPropertyListCreateFromFile(CFURLRef plistURL
);
130 static void WriteMyPropertyListToFile(CFPropertyListRef
, const char *);
131 static bool path_goodness_check(const char *path
, bool forceload
);
132 static void readpath(const char *, struct load_unload_state
*);
133 static void readfile(const char *, struct load_unload_state
*);
135 static int demux_cmd(int argc
, char *const argv
[]);
136 static launch_data_t
do_rendezvous_magic(const struct addrinfo
*res
, const char *serv
);
137 static void submit_job_pass(launch_data_t jobs
);
138 static void do_mgroup_join(int fd
, int family
, int socktype
, int protocol
, const char *mgroup
);
139 static mach_port_t
str2bsport(const char *s
);
140 static void print_jobs(launch_data_t j
, const char *key
, void *context
);
141 static void print_obj(launch_data_t obj
, const char *key
, void *context
);
142 static bool delay_to_second_pass(launch_data_t o
);
143 static void delay_to_second_pass2(launch_data_t o
, const char *key
, void *context
);
144 static bool str2lim(const char *buf
, rlim_t
*res
);
145 static const char *lim2str(rlim_t val
, char *buf
);
146 static const char *num2name(int n
);
147 static ssize_t
name2num(const char *n
);
148 static void unloadjob(launch_data_t job
);
149 static void print_key_value(launch_data_t obj
, const char *key
, void *context
);
150 static void print_launchd_env(launch_data_t obj
, const char *key
, void *context
);
151 static void _log_launchctl_bug(const char *rcs_rev
, const char *path
, unsigned int line
, const char *test
);
152 static void loopback_setup_ipv4(void);
153 static void loopback_setup_ipv6(void);
154 static pid_t
fwexec(const char *const *argv
, int *wstatus
);
155 static void do_potential_fsck(void);
156 static bool path_check(const char *path
);
157 static bool is_safeboot(void);
158 static bool is_netboot(void);
159 static void apply_sysctls_from_file(const char *thefile
);
160 static void empty_dir(const char *thedir
, struct stat
*psb
);
161 static int touch_file(const char *path
, mode_t m
);
162 static void do_sysversion_sysctl(void);
163 static void do_application_firewall_magic(int sfd
, launch_data_t thejob
);
164 static void preheat_page_cache_hack(void);
165 static void do_bootroot_magic(void);
166 static void do_single_user_mode(bool);
167 static bool do_single_user_mode2(void);
168 static void do_crash_debug_mode(void);
169 static bool do_crash_debug_mode2(void);
170 static void read_launchd_conf(void);
171 static void read_environment_dot_plist(void);
172 static bool job_disabled_logic(launch_data_t obj
);
173 static void fix_bogus_file_metadata(void);
174 static void do_file_init(void) __attribute__((constructor
));
175 static void setup_system_context(void);
176 static void handle_system_bootstrapper_crashes_separately(void);
177 static void fatal_signal_handler(int sig
, siginfo_t
*si
, void *uap
);
183 } BootCache_action_t
;
185 static void do_BootCache_magic(BootCache_action_t what
);
187 static int bootstrap_cmd(int argc
, char *const argv
[]);
188 static int load_and_unload_cmd(int argc
, char *const argv
[]);
189 //static int reload_cmd(int argc, char *const argv[]);
190 static int start_stop_remove_cmd(int argc
, char *const argv
[]);
191 static int submit_cmd(int argc
, char *const argv
[]);
192 static int list_cmd(int argc
, char *const argv
[]);
194 static int setenv_cmd(int argc
, char *const argv
[]);
195 static int unsetenv_cmd(int argc
, char *const argv
[]);
196 static int getenv_and_export_cmd(int argc
, char *const argv
[]);
197 static int wait4debugger_cmd(int argc
, char *const argv
[]);
199 static int limit_cmd(int argc
, char *const argv
[]);
200 static int stdio_cmd(int argc
, char *const argv
[]);
201 static int fyi_cmd(int argc
, char *const argv
[]);
202 static int logupdate_cmd(int argc
, char *const argv
[]);
203 static int umask_cmd(int argc
, char *const argv
[]);
204 static int getrusage_cmd(int argc
, char *const argv
[]);
205 static int bsexec_cmd(int argc
, char *const argv
[]);
206 static int _bslist_cmd(mach_port_t bport
, unsigned int depth
, bool show_job
, bool local_only
);
207 static int bslist_cmd(int argc
, char *const argv
[]);
208 static int _bstree_cmd(mach_port_t bsport
, unsigned int depth
, bool show_jobs
);
209 static int bstree_cmd(int argc
__attribute__((unused
)), char * const argv
[] __attribute__((unused
)));
210 static int managerpid_cmd(int argc
__attribute__((unused
)), char * const argv
[] __attribute__((unused
)));
211 static int manageruid_cmd(int argc
__attribute__((unused
)), char * const argv
[] __attribute__((unused
)));
212 static int managername_cmd(int argc
__attribute__((unused
)), char * const argv
[] __attribute__((unused
)));
213 static int asuser_cmd(int argc
, char * const argv
[]);
214 static int exit_cmd(int argc
, char *const argv
[]) __attribute__((noreturn
));
215 static int help_cmd(int argc
, char *const argv
[]);
217 static const struct {
219 int (*func
)(int argc
, char *const argv
[]);
222 { "load", load_and_unload_cmd
, "Load configuration files and/or directories" },
223 { "unload", load_and_unload_cmd
, "Unload configuration files and/or directories" },
224 // { "reload", reload_cmd, "Reload configuration files and/or directories" },
225 { "start", start_stop_remove_cmd
, "Start specified job" },
226 { "stop", start_stop_remove_cmd
, "Stop specified job" },
227 { "submit", submit_cmd
, "Submit a job from the command line" },
228 { "remove", start_stop_remove_cmd
, "Remove specified job" },
229 { "bootstrap", bootstrap_cmd
, "Bootstrap launchd" },
230 { "list", list_cmd
, "List jobs and information about jobs" },
231 { "setenv", setenv_cmd
, "Set an environmental variable in launchd" },
232 { "unsetenv", unsetenv_cmd
, "Unset an environmental variable in launchd" },
233 { "getenv", getenv_and_export_cmd
, "Get an environmental variable from launchd" },
234 { "export", getenv_and_export_cmd
, "Export shell settings from launchd" },
235 { "debug", wait4debugger_cmd
, "Set the WaitForDebugger flag for the target job to true." },
236 { "limit", limit_cmd
, "View and adjust launchd resource limits" },
237 { "stdout", stdio_cmd
, "Redirect launchd's standard out to the given path" },
238 { "stderr", stdio_cmd
, "Redirect launchd's standard error to the given path" },
239 { "shutdown", fyi_cmd
, "Prepare for system shutdown" },
240 { "singleuser", fyi_cmd
, "Switch to single-user mode" },
241 { "getrusage", getrusage_cmd
, "Get resource usage statistics from launchd" },
242 { "log", logupdate_cmd
, "Adjust the logging level or mask of launchd" },
243 { "umask", umask_cmd
, "Change launchd's umask" },
244 { "bsexec", bsexec_cmd
, "Execute a process within a different Mach bootstrap subset" },
245 { "bslist", bslist_cmd
, "List Mach bootstrap services and optional servers" },
246 { "bstree", bstree_cmd
, "Show the entire Mach bootstrap tree. Requires root privileges." },
247 { "managerpid", managerpid_cmd
, "Print the PID of the launchd managing this Mach bootstrap." },
248 { "manageruid", manageruid_cmd
, "Print the UID of the launchd managing this Mach bootstrap." },
249 { "managername", managername_cmd
, "Print the name of this Mach bootstrap." },
250 { "asuser", asuser_cmd
, "Execute a subcommand in the given user's context." },
251 { "exit", exit_cmd
, "Exit the interactive invocation of launchctl" },
252 { "quit", exit_cmd
, "Quit the interactive invocation of launchctl" },
253 { "help", help_cmd
, "This help output" },
258 static bool is_managed
;
259 static bool do_apple_internal_magic
;
260 static bool system_context
;
261 static bool rootuser_context
;
262 static bool bootstrapping_system
;
263 static bool bootstrapping_peruser
;
264 static bool g_verbose_boot
= false;
265 static bool g_startup_debugging
= false;
267 static bool g_job_overrides_db_has_changed
= false;
268 static CFMutableDictionaryRef g_job_overrides_db
= NULL
;
269 static char g_job_overrides_db_path
[PATH_MAX
];
272 static bool g_job_cache_db_has_changed
= false;
273 static launch_data_t g_job_cache_db
= NULL
;
274 static char g_job_cache_db_path
[PATH_MAX
];
278 main(int argc
, char *const argv
[])
280 int64_t is_managed_val
= 0;
283 if (vproc_swap_integer(NULL
, VPROC_GSK_IS_MANAGED
, NULL
, &is_managed_val
) == NULL
&& is_managed_val
) {
287 istty
= isatty(STDIN_FILENO
);
290 if (argc
> 0 && argv
[0][0] == '-') {
293 for (flago
= argv
[0] + 1; *flago
; flago
++) {
300 if (strncmp(argv
[1], "root", sizeof("root")) == 0) {
301 rootuser_context
= true;
303 fprintf(stderr
, "Unknown user: %s\n", argv
[1]);
308 fprintf(stderr
, "-u option requires an argument.\n");
312 system_context
= true;
315 fprintf(stderr
, "Unknown argument: '-%c'\n", *flago
);
322 /* Running in the context of the root user's per-user launchd is only supported ... well
323 * in the root user's per-user context. I know it's confusing. I'm genuinely sorry.
325 if (rootuser_context
) {
326 int64_t manager_uid
= -1, manager_pid
= -1;
327 if (vproc_swap_integer(NULL
, VPROC_GSK_MGR_UID
, NULL
, &manager_uid
) == NULL
) {
328 if (vproc_swap_integer(NULL
, VPROC_GSK_MGR_PID
, NULL
, &manager_pid
) == NULL
) {
329 if (manager_uid
|| manager_pid
== 1) {
330 fprintf(stderr
, "Running in the root user's per-user context is not supported outside of the root user's bootstrap.\n");
335 } else if (!(system_context
|| rootuser_context
)) {
336 /* Running in the system context is implied when we're running as root and not running as a bootstrapper. */
337 system_context
= (!is_managed
&& getuid() == 0);
340 if (system_context
) {
342 setup_system_context();
344 fprintf(stderr
, "You must be root to run in the system context.\n");
347 } else if (rootuser_context
) {
349 fprintf(stderr
, "You must be root to run in the root user context.\n");
354 if (NULL
== readline
) {
355 fprintf(stderr
, "missing library: readline\n");
360 while ((l
= readline(istty
? "launchd% " : NULL
))) {
361 char *inputstring
= l
, *argv2
[100], **ap
= argv2
;
364 while ((*ap
= strsep(&inputstring
, " \t"))) {
384 exit(demux_cmd(argc
, argv
));
391 demux_cmd(int argc
, char *const argv
[])
398 for (i
= 0; i
< (sizeof cmds
/ sizeof cmds
[0]); i
++) {
399 if (!strcmp(cmds
[i
].name
, argv
[0])) {
400 return cmds
[i
].func(argc
, argv
);
404 fprintf(stderr
, "%s: unknown subcommand \"%s\"\n", getprogname(), argv
[0]);
409 read_launchd_conf(void)
411 char s
[1000], *c
, *av
[100];
417 if (getppid() == 1) {
418 file
= "/etc/launchd.conf";
420 file
= "/etc/launchd-user.conf";
423 if (!(f
= fopen(file
, "r"))) {
427 while ((c
= fgets(s
, (int) sizeof s
, f
))) {
429 if (len
&& c
[len
- 1] == '\n') {
435 while ((av
[i
] = strsep(&c
, " \t"))) {
436 if (*(av
[i
]) != '\0') {
449 CFPropertyListRef
CFPropertyListCreateFromFile(CFURLRef plistURL
)
451 CFReadStreamRef plistReadStream
= CFReadStreamCreateWithFile(NULL
, plistURL
);
453 CFErrorRef streamErr
= NULL
;
454 if (!CFReadStreamOpen(plistReadStream
)) {
455 streamErr
= CFReadStreamCopyError(plistReadStream
);
456 CFStringRef errString
= CFErrorCopyDescription(streamErr
);
460 CFRelease(errString
);
461 CFRelease(streamErr
);
464 CFPropertyListRef plist
= NULL
;
465 if (plistReadStream
) {
466 CFStringRef errString
= NULL
;
467 CFPropertyListFormat plistFormat
= 0;
468 plist
= CFPropertyListCreateFromStream(NULL
, plistReadStream
, 0, kCFPropertyListImmutable
, &plistFormat
, &errString
);
471 CFRelease(errString
);
475 CFReadStreamClose(plistReadStream
);
476 CFRelease(plistReadStream
);
481 #define CFReleaseIfNotNULL(cf) if (cf) CFRelease(cf);
483 read_environment_dot_plist(void)
485 CFStringRef plistPath
= NULL
;
486 CFURLRef plistURL
= NULL
;
487 CFDictionaryRef envPlist
= NULL
;
488 launch_data_t req
= NULL
, launch_env_dict
= NULL
, resp
= NULL
;
490 char plist_path_str
[PATH_MAX
];
491 plist_path_str
[PATH_MAX
- 1] = 0;
492 snprintf(plist_path_str
, sizeof(plist_path_str
), "%s/.MacOSX/environment.plist", getenv("HOME"));
495 if (stat(plist_path_str
, &sb
) == -1) {
499 plistPath
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%s"), plist_path_str
);
500 if (!assumes(plistPath
!= NULL
)) {
504 plistURL
= CFURLCreateWithFileSystemPath(NULL
, plistPath
, kCFURLPOSIXPathStyle
, false);
505 if (!assumes(plistURL
!= NULL
)) {
509 envPlist
= (CFDictionaryRef
)CFPropertyListCreateFromFile(plistURL
);
510 if (!assumes(envPlist
!= NULL
)) {
514 launch_env_dict
= CF2launch_data(envPlist
);
515 if (!assumes(launch_env_dict
!= NULL
)) {
519 req
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
520 if (!assumes(req
!= NULL
)) {
524 launch_data_dict_insert(req
, launch_env_dict
, LAUNCH_KEY_SETUSERENVIRONMENT
);
525 resp
= launch_msg(req
);
526 if (!assumes(resp
!= NULL
)) {
530 if (!assumes(launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
)) {
534 (void)assumes(launch_data_get_errno(resp
) == 0);
536 CFReleaseIfNotNULL(plistPath
);
537 CFReleaseIfNotNULL(plistURL
);
538 CFReleaseIfNotNULL(envPlist
);
540 launch_data_free(req
);
544 launch_data_free(resp
);
549 unsetenv_cmd(int argc
, char *const argv
[])
551 launch_data_t resp
, tmp
, msg
;
554 fprintf(stderr
, "%s usage: unsetenv <key>\n", getprogname());
558 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
560 tmp
= launch_data_new_string(argv
[1]);
561 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_UNSETUSERENVIRONMENT
);
563 resp
= launch_msg(msg
);
565 launch_data_free(msg
);
568 launch_data_free(resp
);
570 fprintf(stderr
, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_UNSETUSERENVIRONMENT
, strerror(errno
));
577 setenv_cmd(int argc
, char *const argv
[])
579 launch_data_t resp
, tmp
, tmpv
, msg
;
582 fprintf(stderr
, "%s usage: setenv <key> <value>\n", getprogname());
586 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
587 tmp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
589 tmpv
= launch_data_new_string(argv
[2]);
590 launch_data_dict_insert(tmp
, tmpv
, argv
[1]);
591 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_SETUSERENVIRONMENT
);
593 resp
= launch_msg(msg
);
594 launch_data_free(msg
);
597 launch_data_free(resp
);
599 fprintf(stderr
, "launch_msg(\"%s\"): %s\n", LAUNCH_KEY_SETUSERENVIRONMENT
, strerror(errno
));
606 print_launchd_env(launch_data_t obj
, const char *key
, void *context
)
608 bool *is_csh
= context
;
610 /* XXX escape the double quotes */
612 fprintf(stdout
, "setenv %s \"%s\";\n", key
, launch_data_get_string(obj
));
614 fprintf(stdout
, "%s=\"%s\"; export %s;\n", key
, launch_data_get_string(obj
), key
);
619 print_key_value(launch_data_t obj
, const char *key
, void *context
)
621 const char *k
= context
;
623 if (!strcmp(key
, k
)) {
624 fprintf(stdout
, "%s\n", launch_data_get_string(obj
));
629 getenv_and_export_cmd(int argc
, char *const argv
[])
635 if (!strcmp(argv
[0], "export")) {
636 char *s
= getenv("SHELL");
638 is_csh
= strstr(s
, "csh") ? true : false;
640 } else if (argc
!= 2) {
641 fprintf(stderr
, "%s usage: getenv <key>\n", getprogname());
647 if (vproc_swap_complex(NULL
, VPROC_GSK_ENVIRONMENT
, NULL
, &resp
) == NULL
) {
648 if (!strcmp(argv
[0], "export")) {
649 launch_data_dict_iterate(resp
, print_launchd_env
, &is_csh
);
651 launch_data_dict_iterate(resp
, print_key_value
, k
);
653 launch_data_free(resp
);
663 wait4debugger_cmd(int argc
, char * const argv
[])
666 fprintf(stderr
, "%s usage: debug <label> <value>\n", argv
[0]);
672 if (strncmp(argv
[2], "true", sizeof("true")) == 0) {
674 } else if (strncmp(argv
[2], "false", sizeof("false")) != 0) {
675 inval
= atoi(argv
[2]);
679 vproc_t vp
= vprocmgr_lookup_vproc(argv
[1]);
681 vproc_err_t verr
= vproc_swap_integer(vp
, VPROC_GSK_WAITFORDEBUGGER
, &inval
, NULL
);
683 fprintf(stderr
, "Failed to set WaitForDebugger flag on %s.\n", argv
[1]);
694 unloadjob(launch_data_t job
)
698 tmps
= launch_data_dict_lookup(job
, LAUNCH_JOBKEY_LABEL
);
701 fprintf(stderr
, "%s: Error: Missing Key: %s\n", getprogname(), LAUNCH_JOBKEY_LABEL
);
705 if (_vproc_send_signal_by_label(launch_data_get_string(tmps
), VPROC_MAGIC_UNLOAD_SIGNAL
) != NULL
) {
706 fprintf(stderr
, "%s: Error unloading: %s\n", getprogname(), launch_data_get_string(tmps
));
711 job_override(CFTypeRef key
, CFTypeRef val
, CFMutableDictionaryRef job
)
713 if (!CFTypeCheck(key
, CFString
)) {
716 if (CFStringCompare(key
, CFSTR(LAUNCH_JOBKEY_LABEL
), kCFCompareCaseInsensitive
) == 0) {
720 CFDictionarySetValue(job
, key
, val
);
724 read_plist_file(const char *file
, bool editondisk
, bool load
)
726 CFPropertyListRef plist
= CreateMyPropertyListFromFile(file
);
727 launch_data_t r
= NULL
;
730 fprintf(stderr
, "%s: no plist was returned for: %s\n", getprogname(), file
);
734 CFStringRef label
= CFDictionaryGetValue(plist
, CFSTR(LAUNCH_JOBKEY_LABEL
));
735 if (g_job_overrides_db
&& label
&& CFTypeCheck(label
, CFString
)) {
736 CFDictionaryRef overrides
= CFDictionaryGetValue(g_job_overrides_db
, label
);
737 if (overrides
&& CFTypeCheck(overrides
, CFDictionary
)) {
738 CFDictionaryApplyFunction(overrides
, (CFDictionaryApplierFunction
)job_override
, (void *)plist
);
743 if (g_job_overrides_db
) {
744 CFMutableDictionaryRef job
= (CFMutableDictionaryRef
)CFDictionaryGetValue(g_job_overrides_db
, label
);
745 if (!job
|| !CFTypeCheck(job
, CFDictionary
)) {
746 job
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
747 CFDictionarySetValue(g_job_overrides_db
, label
, job
);
751 CFDictionarySetValue(job
, CFSTR(LAUNCH_JOBKEY_DISABLED
), load
? kCFBooleanFalse
: kCFBooleanTrue
);
752 CFDictionarySetValue((CFMutableDictionaryRef
)plist
, CFSTR(LAUNCH_JOBKEY_DISABLED
), load
? kCFBooleanFalse
: kCFBooleanTrue
);
753 g_job_overrides_db_has_changed
= true;
756 CFDictionaryRemoveValue((CFMutableDictionaryRef
)plist
, CFSTR(LAUNCH_JOBKEY_DISABLED
));
758 CFDictionarySetValue((CFMutableDictionaryRef
)plist
, CFSTR(LAUNCH_JOBKEY_DISABLED
), kCFBooleanTrue
);
760 WriteMyPropertyListToFile(plist
, file
);
764 r
= CF2launch_data(plist
);
772 delay_to_second_pass2(launch_data_t o
, const char *key
, void *context
)
777 if (key
&& 0 == strcmp(key
, LAUNCH_JOBSOCKETKEY_BONJOUR
)) {
782 switch (launch_data_get_type(o
)) {
783 case LAUNCH_DATA_DICTIONARY
:
784 launch_data_dict_iterate(o
, delay_to_second_pass2
, context
);
786 case LAUNCH_DATA_ARRAY
:
787 for (i
= 0; i
< launch_data_array_get_count(o
); i
++) {
788 delay_to_second_pass2(launch_data_array_get_index(o
, i
), NULL
, context
);
797 delay_to_second_pass(launch_data_t o
)
801 launch_data_t socks
= launch_data_dict_lookup(o
, LAUNCH_JOBKEY_SOCKETS
);
807 delay_to_second_pass2(socks
, NULL
, &res
);
813 sysctl_hw_streq(int mib_slot
, const char *str
)
816 size_t bufsz
= sizeof(buf
);
817 int mib
[] = { CTL_HW
, mib_slot
};
819 if (sysctl(mib
, 2, buf
, &bufsz
, NULL
, 0) != -1) {
820 if (strcmp(buf
, str
) == 0) {
829 limitloadtohardware_iterator(launch_data_t val
, const char *key
, void *ctx
)
834 (void)snprintf(name
, sizeof(name
), "hw.%s", key
);
838 if (*result
!= true && assumes(sysctlnametomib(name
, mib
, &sz
) != -1)) {
839 if (launch_data_get_type(val
) == LAUNCH_DATA_ARRAY
) {
840 size_t c
= launch_data_array_get_count(val
);
843 for (i
= 0; i
< c
; i
++) {
844 launch_data_t oai
= launch_data_array_get_index(val
, i
);
845 if (sysctl_hw_streq(mib
[1], launch_data_get_string(oai
))) {
855 readfile(const char *what
, struct load_unload_state
*lus
)
857 char ourhostname
[1024];
858 launch_data_t tmpd
, tmps
, thejob
, tmpa
;
859 bool job_disabled
= false;
862 gethostname(ourhostname
, sizeof(ourhostname
));
864 if (NULL
== (thejob
= read_plist_file(what
, lus
->editondisk
, lus
->load
))) {
865 fprintf(stderr
, "%s: no plist was returned for: %s\n", getprogname(), what
);
870 if (NULL
== launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_LABEL
)) {
871 fprintf(stderr
, "%s: missing the Label key: %s\n", getprogname(), what
);
875 if ((launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_PROGRAM
) == NULL
) &&
876 (launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
) == NULL
)) {
877 fprintf(stderr
, "%s: neither a Program nor a ProgramArguments key was specified: %s", getprogname(), what
);
881 if (NULL
!= (tmpa
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_LIMITLOADFROMHOSTS
))) {
882 c
= launch_data_array_get_count(tmpa
);
884 for (i
= 0; i
< c
; i
++) {
885 launch_data_t oai
= launch_data_array_get_index(tmpa
, i
);
886 if (!strcasecmp(ourhostname
, launch_data_get_string(oai
))) {
892 if (NULL
!= (tmpa
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_LIMITLOADTOHOSTS
))) {
893 c
= launch_data_array_get_count(tmpa
);
895 for (i
= 0; i
< c
; i
++) {
896 launch_data_t oai
= launch_data_array_get_index(tmpa
, i
);
897 if (!strcasecmp(ourhostname
, launch_data_get_string(oai
))) {
907 if (NULL
!= (tmpd
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_LIMITLOADTOHARDWARE
))) {
909 launch_data_dict_iterate(tmpd
, limitloadtohardware_iterator
, &result
);
915 if (NULL
!= (tmpd
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_LIMITLOADFROMHARDWARE
))) {
917 launch_data_dict_iterate(tmpd
, limitloadtohardware_iterator
, &result
);
923 // if the manager is Aqua, the LimitLoadToSessionType should default to 'Aqua'
924 // fixes <rdar://problem/8297909>
925 char *manager
= "Bogus";
926 vproc_swap_string(NULL
, VPROC_GSK_MGR_NAME
, NULL
, &manager
);
927 if (!lus
->session_type
) {
928 if (strcmp(manager
, "Aqua") == 0) {
929 lus
->session_type
= "Aqua";
933 if (lus
->session_type
&& !(tmpa
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE
))) {
934 tmpa
= launch_data_new_string("Aqua");
935 launch_data_dict_insert(thejob
, tmpa
, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE
);
938 if ((tmpa
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE
))) {
939 const char *allowed_session
;
942 /* My sincere apologies to anyone who has to deal with this
943 * LimitLoadToSessionType madness. It was like this when I got here, but
944 * I've knowingly made it worse, hopefully to the benefit of the end
947 * See <rdar://problem/8769211> and <rdar://problem/7114980>.
949 if (!lus
->session_type
&& launch_data_get_type(tmpa
) == LAUNCH_DATA_STRING
) {
950 if (strcasecmp("System", manager
) == 0 && strcasecmp("System", launch_data_get_string(tmpa
)) == 0) {
955 if (lus
->session_type
) switch (launch_data_get_type(tmpa
)) {
956 case LAUNCH_DATA_ARRAY
:
957 c
= launch_data_array_get_count(tmpa
);
958 for (i
= 0; i
< c
; i
++) {
959 tmps
= launch_data_array_get_index(tmpa
, i
);
960 allowed_session
= launch_data_get_string(tmps
);
961 if (strcasecmp(lus
->session_type
, allowed_session
) == 0) {
963 /* we have to do the following so job_reparent_hack() works within launchd */
964 tmpa
= launch_data_new_string(lus
->session_type
);
965 launch_data_dict_insert(thejob
, tmpa
, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE
);
970 case LAUNCH_DATA_STRING
:
971 allowed_session
= launch_data_get_string(tmpa
);
972 if (strcasecmp(lus
->session_type
, allowed_session
) == 0) {
985 if ((tmpd
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_DISABLED
))) {
986 job_disabled
= job_disabled_logic(tmpd
);
989 if (lus
->forceload
) {
990 job_disabled
= false;
993 if (job_disabled
&& lus
->load
) {
997 if (bootstrapping_system
|| bootstrapping_peruser
) {
1001 launch_data_t uuid_d
= launch_data_new_opaque(uuid
, sizeof(uuid_t
));
1002 launch_data_dict_insert(thejob
, uuid_d
, LAUNCH_JOBKEY_SECURITYSESSIONUUID
);
1005 if (delay_to_second_pass(thejob
)) {
1006 launch_data_array_append(lus
->pass2
, thejob
);
1008 launch_data_array_append(lus
->pass1
, thejob
);
1012 fprintf(stdout
, "Will load: %s\n", what
);
1018 fprintf(stdout
, "Ignored: %s\n", what
);
1020 launch_data_free(thejob
);
1024 job_disabled_dict_logic(launch_data_t obj
, const char *key
, void *context
)
1028 if (launch_data_get_type(obj
) != LAUNCH_DATA_STRING
) {
1032 if (strcasecmp(key
, LAUNCH_JOBKEY_DISABLED_MACHINETYPE
) == 0) {
1033 if (sysctl_hw_streq(HW_MACHINE
, launch_data_get_string(obj
))) {
1036 } else if (strcasecmp(key
, LAUNCH_JOBKEY_DISABLED_MODELNAME
) == 0) {
1037 if (sysctl_hw_streq(HW_MODEL
, launch_data_get_string(obj
))) {
1044 job_disabled_logic(launch_data_t obj
)
1048 switch (launch_data_get_type(obj
)) {
1049 case LAUNCH_DATA_DICTIONARY
:
1050 launch_data_dict_iterate(obj
, job_disabled_dict_logic
, &r
);
1052 case LAUNCH_DATA_BOOL
:
1053 r
= launch_data_get_bool(obj
);
1063 path_goodness_check(const char *path
, bool forceload
)
1067 if (stat(path
, &sb
) == -1) {
1068 fprintf(stderr
, "%s: Couldn't stat(\"%s\"): %s\n", getprogname(), path
, strerror(errno
));
1076 if (sb
.st_mode
& (S_IWOTH
|S_IWGRP
)) {
1077 fprintf(stderr
, "%s: Dubious permissions on file (skipping): %s\n", getprogname(), path
);
1081 if (sb
.st_uid
!= 0 && sb
.st_uid
!= getuid()) {
1082 fprintf(stderr
, "%s: Dubious ownership on file (skipping): %s\n", getprogname(), path
);
1086 if (!(S_ISREG(sb
.st_mode
) || S_ISDIR(sb
.st_mode
))) {
1087 fprintf(stderr
, "%s: Dubious path. Not a regular file or directory (skipping): %s\n", getprogname(), path
);
1091 if ((!S_ISDIR(sb
.st_mode
)) && (fnmatch("*.plist", path
, FNM_CASEFOLD
) == FNM_NOMATCH
)) {
1092 fprintf(stderr
, "%s: Dubious file. Not of type .plist (skipping): %s\n", getprogname(), path
);
1100 readpath(const char *what
, struct load_unload_state
*lus
)
1102 char buf
[MAXPATHLEN
];
1107 if (!path_goodness_check(what
, lus
->forceload
)) {
1111 if (stat(what
, &sb
) == -1) {
1115 if (S_ISREG(sb
.st_mode
)) {
1116 readfile(what
, lus
);
1117 } else if (S_ISDIR(sb
.st_mode
)) {
1118 if ((d
= opendir(what
)) == NULL
) {
1119 fprintf(stderr
, "%s: opendir() failed to open the directory\n", getprogname());
1123 while ((de
= readdir(d
))) {
1124 if ((de
->d_name
[0] == '.')) {
1127 snprintf(buf
, sizeof(buf
), "%s/%s", what
, de
->d_name
);
1129 if (!path_goodness_check(buf
, lus
->forceload
)) {
1139 struct distill_context
{
1141 launch_data_t newsockdict
;
1145 distill_jobs(launch_data_t jobs
)
1147 size_t i
, c
= launch_data_array_get_count(jobs
);
1149 for (i
= 0; i
< c
; i
++)
1150 distill_config_file(launch_data_array_get_index(jobs
, i
));
1154 distill_config_file(launch_data_t id_plist
)
1156 struct distill_context dc
= { id_plist
, NULL
};
1159 if ((tmp
= launch_data_dict_lookup(dc
.base
, LAUNCH_JOBKEY_SOCKETS
))) {
1160 dc
.newsockdict
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1161 launch_data_dict_iterate(tmp
, sock_dict_cb
, &dc
);
1162 launch_data_dict_insert(dc
.base
, dc
.newsockdict
, LAUNCH_JOBKEY_SOCKETS
);
1167 sock_dict_cb(launch_data_t what
, const char *key
, void *context
)
1169 struct distill_context
*dc
= context
;
1170 launch_data_t fdarray
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
1172 launch_data_dict_insert(dc
->newsockdict
, fdarray
, key
);
1174 if (launch_data_get_type(what
) == LAUNCH_DATA_DICTIONARY
) {
1175 sock_dict_edit_entry(what
, key
, fdarray
, dc
->base
);
1176 } else if (launch_data_get_type(what
) == LAUNCH_DATA_ARRAY
) {
1180 for (i
= 0; i
< launch_data_array_get_count(what
); i
++) {
1181 tmp
= launch_data_array_get_index(what
, i
);
1182 sock_dict_edit_entry(tmp
, key
, fdarray
, dc
->base
);
1188 sock_dict_edit_entry(launch_data_t tmp
, const char *key
, launch_data_t fdarray
, launch_data_t thejob
)
1190 launch_data_t a
, val
;
1191 int sfd
, st
= SOCK_STREAM
;
1192 bool passive
= true;
1194 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_TYPE
))) {
1195 if (!strcasecmp(launch_data_get_string(val
), "stream")) {
1197 } else if (!strcasecmp(launch_data_get_string(val
), "dgram")) {
1199 } else if (!strcasecmp(launch_data_get_string(val
), "seqpacket")) {
1200 st
= SOCK_SEQPACKET
;
1204 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_PASSIVE
))) {
1205 passive
= launch_data_get_bool(val
);
1208 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_SECUREWITHKEY
))) {
1209 char secdir
[] = LAUNCH_SECDIR
, buf
[1024];
1210 launch_data_t uenv
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
);
1213 uenv
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1214 launch_data_dict_insert(thejob
, uenv
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
);
1219 sprintf(buf
, "%s/%s", secdir
, key
);
1221 a
= launch_data_new_string(buf
);
1222 launch_data_dict_insert(tmp
, a
, LAUNCH_JOBSOCKETKEY_PATHNAME
);
1223 a
= launch_data_new_string(buf
);
1224 launch_data_dict_insert(uenv
, a
, launch_data_get_string(val
));
1227 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_PATHNAME
))) {
1228 struct sockaddr_un sun
;
1229 mode_t sun_mode
= 0;
1233 memset(&sun
, 0, sizeof(sun
));
1235 sun
.sun_family
= AF_UNIX
;
1237 strncpy(sun
.sun_path
, launch_data_get_string(val
), sizeof(sun
.sun_path
));
1239 if ((sfd
= _fd(socket(AF_UNIX
, st
, 0))) == -1) {
1243 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_PATHMODE
))) {
1244 sun_mode
= (mode_t
)launch_data_get_integer(val
);
1249 if (unlink(sun
.sun_path
) == -1 && errno
!= ENOENT
) {
1253 oldmask
= umask(S_IRWXG
|S_IRWXO
);
1254 if (bind(sfd
, (struct sockaddr
*)&sun
, (socklen_t
) sizeof sun
) == -1) {
1261 chmod(sun
.sun_path
, sun_mode
);
1263 if ((st
== SOCK_STREAM
|| st
== SOCK_SEQPACKET
) && listen(sfd
, -1) == -1) {
1267 } else if (connect(sfd
, (struct sockaddr
*)&sun
, (socklen_t
) sizeof sun
) == -1) {
1272 val
= launch_data_new_fd(sfd
);
1273 launch_data_array_append(fdarray
, val
);
1275 launch_data_t rnames
= NULL
;
1276 const char *node
= NULL
, *serv
= NULL
, *mgroup
= NULL
;
1278 struct addrinfo hints
, *res0
, *res
;
1279 int gerr
, sock_opt
= 1;
1280 bool rendezvous
= false;
1282 memset(&hints
, 0, sizeof(hints
));
1284 hints
.ai_socktype
= st
;
1286 hints
.ai_flags
|= AI_PASSIVE
;
1289 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_NODENAME
))) {
1290 node
= launch_data_get_string(val
);
1292 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_MULTICASTGROUP
))) {
1293 mgroup
= launch_data_get_string(val
);
1295 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_SERVICENAME
))) {
1296 if (LAUNCH_DATA_INTEGER
== launch_data_get_type(val
)) {
1297 sprintf(servnbuf
, "%lld", launch_data_get_integer(val
));
1300 serv
= launch_data_get_string(val
);
1303 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_FAMILY
))) {
1304 if (!strcasecmp("IPv4", launch_data_get_string(val
))) {
1305 hints
.ai_family
= AF_INET
;
1306 } else if (!strcasecmp("IPv6", launch_data_get_string(val
))) {
1307 hints
.ai_family
= AF_INET6
;
1310 if ((val
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_PROTOCOL
))) {
1311 if (!strcasecmp("TCP", launch_data_get_string(val
))) {
1312 hints
.ai_protocol
= IPPROTO_TCP
;
1313 } else if (!strcasecmp("UDP", launch_data_get_string(val
))) {
1314 hints
.ai_protocol
= IPPROTO_UDP
;
1317 if ((rnames
= launch_data_dict_lookup(tmp
, LAUNCH_JOBSOCKETKEY_BONJOUR
))) {
1319 if (LAUNCH_DATA_BOOL
== launch_data_get_type(rnames
)) {
1320 rendezvous
= launch_data_get_bool(rnames
);
1325 if ((gerr
= getaddrinfo(node
, serv
, &hints
, &res0
)) != 0) {
1326 fprintf(stderr
, "getaddrinfo(): %s\n", gai_strerror(gerr
));
1330 for (res
= res0
; res
; res
= res
->ai_next
) {
1331 launch_data_t rvs_fd
= NULL
;
1332 if ((sfd
= _fd(socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
))) == -1) {
1333 fprintf(stderr
, "socket(): %s\n", strerror(errno
));
1337 do_application_firewall_magic(sfd
, thejob
);
1339 if (hints
.ai_flags
& AI_PASSIVE
) {
1340 if (AF_INET6
== res
->ai_family
&& -1 == setsockopt(sfd
, IPPROTO_IPV6
, IPV6_V6ONLY
,
1341 (void *)&sock_opt
, (socklen_t
) sizeof sock_opt
)) {
1342 fprintf(stderr
, "setsockopt(IPV6_V6ONLY): %m");
1346 if (setsockopt(sfd
, SOL_SOCKET
, SO_REUSEPORT
, (void *)&sock_opt
, (socklen_t
) sizeof sock_opt
) == -1) {
1347 fprintf(stderr
, "setsockopt(SO_REUSEPORT): %s\n", strerror(errno
));
1351 if (setsockopt(sfd
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&sock_opt
, (socklen_t
) sizeof sock_opt
) == -1) {
1352 fprintf(stderr
, "setsockopt(SO_REUSEADDR): %s\n", strerror(errno
));
1356 if (bind(sfd
, res
->ai_addr
, res
->ai_addrlen
) == -1) {
1357 fprintf(stderr
, "bind(): %s\n", strerror(errno
));
1360 /* The kernel may have dynamically assigned some part of the
1361 * address. (The port being a common example.)
1363 if (getsockname(sfd
, res
->ai_addr
, &res
->ai_addrlen
) == -1) {
1364 fprintf(stderr
, "getsockname(): %s\n", strerror(errno
));
1369 do_mgroup_join(sfd
, res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
, mgroup
);
1371 if ((res
->ai_socktype
== SOCK_STREAM
|| res
->ai_socktype
== SOCK_SEQPACKET
) && listen(sfd
, -1) == -1) {
1372 fprintf(stderr
, "listen(): %s\n", strerror(errno
));
1375 if (rendezvous
&& (res
->ai_family
== AF_INET
|| res
->ai_family
== AF_INET6
) &&
1376 (res
->ai_socktype
== SOCK_STREAM
|| res
->ai_socktype
== SOCK_DGRAM
)) {
1377 launch_data_t rvs_fds
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_BONJOURFDS
);
1378 if (NULL
== rvs_fds
) {
1379 rvs_fds
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
1380 launch_data_dict_insert(thejob
, rvs_fds
, LAUNCH_JOBKEY_BONJOURFDS
);
1382 if (NULL
== rnames
) {
1383 rvs_fd
= do_rendezvous_magic(res
, serv
);
1385 launch_data_array_append(rvs_fds
, rvs_fd
);
1387 } else if (LAUNCH_DATA_STRING
== launch_data_get_type(rnames
)) {
1388 rvs_fd
= do_rendezvous_magic(res
, launch_data_get_string(rnames
));
1390 launch_data_array_append(rvs_fds
, rvs_fd
);
1392 } else if (LAUNCH_DATA_ARRAY
== launch_data_get_type(rnames
)) {
1393 size_t rn_i
, rn_ac
= launch_data_array_get_count(rnames
);
1395 for (rn_i
= 0; rn_i
< rn_ac
; rn_i
++) {
1396 launch_data_t rn_tmp
= launch_data_array_get_index(rnames
, rn_i
);
1398 rvs_fd
= do_rendezvous_magic(res
, launch_data_get_string(rn_tmp
));
1400 launch_data_array_append(rvs_fds
, rvs_fd
);
1406 if (connect(sfd
, res
->ai_addr
, res
->ai_addrlen
) == -1) {
1407 fprintf(stderr
, "connect(): %s\n", strerror(errno
));
1411 val
= launch_data_new_fd(sfd
);
1413 /* <rdar://problem/3964648> Launchd should not register the same service more than once */
1414 /* <rdar://problem/3965154> Switch to DNSServiceRegisterAddrInfo() */
1417 launch_data_array_append(fdarray
, val
);
1423 do_mgroup_join(int fd
, int family
, int socktype
, int protocol
, const char *mgroup
)
1425 struct addrinfo hints
, *res0
, *res
;
1426 struct ip_mreq mreq
;
1427 struct ipv6_mreq m6req
;
1430 memset(&hints
, 0, sizeof(hints
));
1432 hints
.ai_flags
|= AI_PASSIVE
;
1433 hints
.ai_family
= family
;
1434 hints
.ai_socktype
= socktype
;
1435 hints
.ai_protocol
= protocol
;
1437 if ((gerr
= getaddrinfo(mgroup
, NULL
, &hints
, &res0
)) != 0) {
1438 fprintf(stderr
, "getaddrinfo(): %s\n", gai_strerror(gerr
));
1442 for (res
= res0
; res
; res
= res
->ai_next
) {
1443 if (AF_INET
== family
) {
1444 memset(&mreq
, 0, sizeof(mreq
));
1445 mreq
.imr_multiaddr
= ((struct sockaddr_in
*)res
->ai_addr
)->sin_addr
;
1446 if (setsockopt(fd
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, &mreq
, (socklen_t
) sizeof mreq
) == -1) {
1447 fprintf(stderr
, "setsockopt(IP_ADD_MEMBERSHIP): %s\n", strerror(errno
));
1451 } else if (AF_INET6
== family
) {
1452 memset(&m6req
, 0, sizeof(m6req
));
1453 m6req
.ipv6mr_multiaddr
= ((struct sockaddr_in6
*)res
->ai_addr
)->sin6_addr
;
1454 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_JOIN_GROUP
, &m6req
, (socklen_t
) sizeof m6req
) == -1) {
1455 fprintf(stderr
, "setsockopt(IPV6_JOIN_GROUP): %s\n", strerror(errno
));
1460 fprintf(stderr
, "unknown family during multicast group bind!\n");
1470 do_rendezvous_magic(const struct addrinfo
*res
, const char *serv
)
1473 DNSServiceRef service
;
1474 DNSServiceErrorType error
;
1477 static int statres
= 1;
1480 statres
= stat("/usr/sbin/mDNSResponder", &sb
);
1483 if (-1 == statres
) {
1487 sprintf(rvs_buf
, "_%s._%s.", serv
, res
->ai_socktype
== SOCK_STREAM
? "tcp" : "udp");
1489 if (res
->ai_family
== AF_INET
) {
1490 port
= ((struct sockaddr_in
*)res
->ai_addr
)->sin_port
;
1492 port
= ((struct sockaddr_in6
*)res
->ai_addr
)->sin6_port
;
1495 error
= DNSServiceRegister(&service
, 0, 0, NULL
, rvs_buf
, NULL
, NULL
, port
, 0, NULL
, NULL
, NULL
);
1497 if (error
== kDNSServiceErr_NoError
) {
1498 return launch_data_new_fd(DNSServiceRefSockFD(service
));
1501 fprintf(stderr
, "DNSServiceRegister(\"%s\"): %d\n", serv
, error
);
1506 CreateMyPropertyListFromFile(const char *posixfile
)
1508 CFPropertyListRef propertyList
;
1509 CFStringRef errorString
;
1510 CFDataRef resourceData
;
1514 fileURL
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
, (const UInt8
*)posixfile
, strlen(posixfile
), false);
1516 fprintf(stderr
, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile
);
1518 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault
, fileURL
, &resourceData
, NULL
, NULL
, &errorCode
)) {
1519 fprintf(stderr
, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d\n", getprogname(), posixfile
, (int)errorCode
);
1522 propertyList
= CFPropertyListCreateFromXMLData(kCFAllocatorDefault
, resourceData
, kCFPropertyListMutableContainersAndLeaves
, &errorString
);
1528 CFRelease(resourceData
);
1531 return propertyList
;
1535 WriteMyPropertyListToFile(CFPropertyListRef plist
, const char *posixfile
)
1537 CFDataRef resourceData
;
1541 fileURL
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
, (const UInt8
*)posixfile
, strlen(posixfile
), false);
1543 fprintf(stderr
, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), posixfile
);
1545 resourceData
= CFPropertyListCreateXMLData(kCFAllocatorDefault
, plist
);
1546 if (resourceData
== NULL
) {
1547 fprintf(stderr
, "%s: CFPropertyListCreateXMLData(%s) failed", getprogname(), posixfile
);
1549 if (!CFURLWriteDataAndPropertiesToResource(fileURL
, resourceData
, NULL
, &errorCode
)) {
1550 fprintf(stderr
, "%s: CFURLWriteDataAndPropertiesToResource(%s) failed: %d\n", getprogname(), posixfile
, (int)errorCode
);
1554 CFRelease(resourceData
);
1558 static inline Boolean
__is_launch_data_t(launch_data_t obj
)
1560 Boolean result
= true;
1562 switch (launch_data_get_type(obj
)) {
1563 case LAUNCH_DATA_STRING
: break;
1564 case LAUNCH_DATA_INTEGER
: break;
1565 case LAUNCH_DATA_REAL
: break;
1566 case LAUNCH_DATA_BOOL
: break;
1567 case LAUNCH_DATA_ARRAY
: break;
1568 case LAUNCH_DATA_DICTIONARY
: break;
1569 case LAUNCH_DATA_FD
: break;
1570 case LAUNCH_DATA_MACHPORT
: break;
1571 default : result
= false;
1577 static void __launch_data_iterate(launch_data_t obj
, const char *key
, CFMutableDictionaryRef dict
)
1579 if (obj
&& __is_launch_data_t(obj
)) {
1580 CFStringRef cfKey
= CFStringCreateWithCString(NULL
, key
, kCFStringEncodingUTF8
);
1581 CFTypeRef cfVal
= CFTypeCreateFromLaunchData(obj
);
1584 CFDictionarySetValue(dict
, cfKey
, cfVal
);
1591 static CFTypeRef
CFTypeCreateFromLaunchData(launch_data_t obj
)
1593 CFTypeRef cfObj
= NULL
;
1595 switch (launch_data_get_type(obj
)) {
1596 case LAUNCH_DATA_STRING
:
1598 const char *str
= launch_data_get_string(obj
);
1599 cfObj
= CFStringCreateWithCString(NULL
, str
, kCFStringEncodingUTF8
);
1603 case LAUNCH_DATA_INTEGER
:
1605 long long integer
= launch_data_get_integer(obj
);
1606 cfObj
= CFNumberCreate(NULL
, kCFNumberLongLongType
, &integer
);
1610 case LAUNCH_DATA_REAL
:
1612 double real
= launch_data_get_real(obj
);
1613 cfObj
= CFNumberCreate(NULL
, kCFNumberDoubleType
, &real
);
1617 case LAUNCH_DATA_BOOL
:
1619 bool yesno
= launch_data_get_bool(obj
);
1620 cfObj
= yesno
? kCFBooleanTrue
: kCFBooleanFalse
;
1624 case LAUNCH_DATA_ARRAY
:
1626 cfObj
= (CFTypeRef
)CFArrayCreateFromLaunchArray(obj
);
1630 case LAUNCH_DATA_DICTIONARY
:
1632 cfObj
= (CFTypeRef
)CFDictionaryCreateFromLaunchDictionary(obj
);
1636 case LAUNCH_DATA_FD
:
1638 int fd
= launch_data_get_fd(obj
);
1639 cfObj
= CFNumberCreate(NULL
, kCFNumberIntType
, &fd
);
1643 case LAUNCH_DATA_MACHPORT
:
1645 mach_port_t port
= launch_data_get_machport(obj
);
1646 cfObj
= CFNumberCreate(NULL
, kCFNumberIntType
, &port
);
1656 #pragma mark CFArray
1657 CFArrayRef
CFArrayCreateFromLaunchArray(launch_data_t arr
)
1659 CFArrayRef result
= NULL
;
1660 CFMutableArrayRef mutResult
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1662 if (launch_data_get_type(arr
) == LAUNCH_DATA_ARRAY
) {
1663 unsigned int count
= launch_data_array_get_count(arr
);
1666 for (i
= 0; i
< count
; i
++) {
1667 launch_data_t launch_obj
= launch_data_array_get_index(arr
, i
);
1668 CFTypeRef obj
= CFTypeCreateFromLaunchData(launch_obj
);
1671 CFArrayAppendValue(mutResult
, obj
);
1676 result
= CFArrayCreateCopy(NULL
, mutResult
);
1680 CFRelease(mutResult
);
1685 #pragma mark CFDictionary / CFPropertyList
1686 static CFDictionaryRef
CFDictionaryCreateFromLaunchDictionary(launch_data_t dict
)
1688 CFDictionaryRef result
= NULL
;
1690 if (launch_data_get_type(dict
) == LAUNCH_DATA_DICTIONARY
) {
1691 CFMutableDictionaryRef mutResult
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1693 launch_data_dict_iterate(dict
, (void (*)(launch_data_t
, const char *, void *))__launch_data_iterate
, mutResult
);
1695 result
= CFDictionaryCreateCopy(NULL
, mutResult
);
1696 CFRelease(mutResult
);
1703 myCFDictionaryApplyFunction(const void *key
, const void *value
, void *context
)
1705 launch_data_t ik
, iw
, where
= context
;
1707 ik
= CF2launch_data(key
);
1708 iw
= CF2launch_data(value
);
1710 launch_data_dict_insert(where
, iw
, launch_data_get_string(ik
));
1711 launch_data_free(ik
);
1715 CF2launch_data(CFTypeRef cfr
)
1718 CFTypeID cft
= CFGetTypeID(cfr
);
1720 if (cft
== CFStringGetTypeID()) {
1722 CFStringGetCString(cfr
, buf
, sizeof(buf
), kCFStringEncodingUTF8
);
1723 r
= launch_data_alloc(LAUNCH_DATA_STRING
);
1724 launch_data_set_string(r
, buf
);
1725 } else if (cft
== CFBooleanGetTypeID()) {
1726 r
= launch_data_alloc(LAUNCH_DATA_BOOL
);
1727 launch_data_set_bool(r
, CFBooleanGetValue(cfr
));
1728 } else if (cft
== CFArrayGetTypeID()) {
1729 CFIndex i
, ac
= CFArrayGetCount(cfr
);
1730 r
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
1731 for (i
= 0; i
< ac
; i
++) {
1732 CFTypeRef v
= CFArrayGetValueAtIndex(cfr
, i
);
1734 launch_data_t iv
= CF2launch_data(v
);
1735 launch_data_array_set_index(r
, iv
, i
);
1738 } else if (cft
== CFDictionaryGetTypeID()) {
1739 r
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1740 CFDictionaryApplyFunction(cfr
, myCFDictionaryApplyFunction
, r
);
1741 } else if (cft
== CFDataGetTypeID()) {
1742 r
= launch_data_alloc(LAUNCH_DATA_OPAQUE
);
1743 launch_data_set_opaque(r
, CFDataGetBytePtr(cfr
), CFDataGetLength(cfr
));
1744 } else if (cft
== CFNumberGetTypeID()) {
1747 CFNumberType cfnt
= CFNumberGetType(cfr
);
1749 case kCFNumberSInt8Type
:
1750 case kCFNumberSInt16Type
:
1751 case kCFNumberSInt32Type
:
1752 case kCFNumberSInt64Type
:
1753 case kCFNumberCharType
:
1754 case kCFNumberShortType
:
1755 case kCFNumberIntType
:
1756 case kCFNumberLongType
:
1757 case kCFNumberLongLongType
:
1758 CFNumberGetValue(cfr
, kCFNumberLongLongType
, &n
);
1759 r
= launch_data_alloc(LAUNCH_DATA_INTEGER
);
1760 launch_data_set_integer(r
, n
);
1762 case kCFNumberFloat32Type
:
1763 case kCFNumberFloat64Type
:
1764 case kCFNumberFloatType
:
1765 case kCFNumberDoubleType
:
1766 CFNumberGetValue(cfr
, kCFNumberDoubleType
, &d
);
1767 r
= launch_data_alloc(LAUNCH_DATA_REAL
);
1768 launch_data_set_real(r
, d
);
1781 help_cmd(int argc
, char *const argv
[])
1783 FILE *where
= stdout
;
1784 size_t i
, l
, cmdwidth
= 0;
1786 if (argc
== 0 || argv
== NULL
)
1789 fprintf(where
, "usage: %s <subcommand>\n", getprogname());
1791 for (i
= 0; i
< (sizeof cmds
/ sizeof cmds
[0]); i
++) {
1792 l
= strlen(cmds
[i
].name
);
1798 for (i
= 0; i
< (sizeof cmds
/ sizeof cmds
[0]); i
++) {
1799 fprintf(where
, "\t%-*s\t%s\n", (int)cmdwidth
, cmds
[i
].name
, cmds
[i
].desc
);
1806 exit_cmd(int argc
__attribute__((unused
)), char *const argv
[] __attribute__((unused
)))
1815 fcntl(fd
, F_SETFD
, 1);
1820 do_single_user_mode(bool sflag
)
1823 while (!do_single_user_mode2()) {
1830 do_single_user_mode2(void)
1832 bool runcom_fsck
= true; /* should_fsck(); */
1837 switch ((p
= fork())) {
1839 syslog(LOG_ERR
, "can't fork single-user shell, trying again: %m");
1844 (void)assumes(waitpid(p
, &wstatus
, 0) != -1);
1845 if (WIFEXITED(wstatus
)) {
1846 if (WEXITSTATUS(wstatus
) == EXIT_SUCCESS
) {
1849 fprintf(stdout
, "single user mode: exit status: %d\n", WEXITSTATUS(wstatus
));
1852 fprintf(stdout
, "single user mode shell: %s\n", strsignal(WTERMSIG(wstatus
)));
1857 revoke(_PATH_CONSOLE
);
1858 if (!assumes((fd
= open(_PATH_CONSOLE
, O_RDWR
)) != -1)) {
1859 _exit(EXIT_FAILURE
);
1861 if (!assumes(login_tty(fd
) != -1)) {
1862 _exit(EXIT_FAILURE
);
1865 mach_timespec_t wt
= { 5, 0 };
1866 IOKitWaitQuiet(kIOMasterPortDefault
, &wt
); /* This will hopefully return after all the kexts have shut up. */
1868 setenv("TERM", "vt100", 1);
1870 fprintf(stdout
, "Singleuser boot -- fsck not done\n");
1871 fprintf(stdout
, "Root device is mounted read-only\n\n");
1872 fprintf(stdout
, "If you want to make modifications to files:\n");
1873 fprintf(stdout
, "\t/sbin/fsck -fy\n\t/sbin/mount -uw /\n\n");
1874 fprintf(stdout
, "If you wish to boot the system:\n");
1875 fprintf(stdout
, "\texit\n\n");
1879 execl(_PATH_BSHELL
, "-sh", NULL
);
1880 syslog(LOG_ERR
, "can't exec %s for single user: %m", _PATH_BSHELL
);
1881 _exit(EXIT_FAILURE
);
1885 do_crash_debug_mode(void)
1887 while (!do_crash_debug_mode2()) {
1893 do_crash_debug_mode2(void)
1899 switch ((p
= fork())) {
1901 syslog(LOG_ERR
, "can't fork crash debug shell, trying again: %m");
1906 (void)assumes(waitpid(p
, &wstatus
, 0) != -1);
1907 if (WIFEXITED(wstatus
)) {
1908 if (WEXITSTATUS(wstatus
) == EXIT_SUCCESS
) {
1911 fprintf(stdout
, "crash debug mode: exit status: %d\n", WEXITSTATUS(wstatus
));
1914 fprintf(stdout
, "crash debug mode shell: %s\n", strsignal(WTERMSIG(wstatus
)));
1919 revoke(_PATH_CONSOLE
);
1920 if (!assumes((fd
= open(_PATH_CONSOLE
, O_RDWR
)) != -1)) {
1921 _exit(EXIT_FAILURE
);
1923 if (!assumes(login_tty(fd
) != -1)) {
1924 _exit(EXIT_FAILURE
);
1927 mach_timespec_t wt
= { 5, 0 };
1928 IOKitWaitQuiet(kIOMasterPortDefault
, &wt
); /* This will hopefully return after all the kexts have shut up. */
1930 setenv("TERM", "vt100", 1);
1931 fprintf(stdout
, "Entering boot-time debugging mode...\n");
1932 fprintf(stdout
, "The system bootstrapper process has crashed. To debug:\n");
1933 fprintf(stdout
, "\tgdb attach %i\n", getppid());
1934 fprintf(stdout
, "You can try booting the system with:\n");
1935 fprintf(stdout
, "\tlaunchctl load -S System -D All\n\n");
1937 execl(_PATH_BSHELL
, "-sh", NULL
);
1938 syslog(LOG_ERR
, "can't exec %s for crash debug: %m", _PATH_BSHELL
);
1939 _exit(EXIT_FAILURE
);
1943 exit_at_sigterm(int sig
)
1945 if (sig
== SIGTERM
) {
1946 _exit(EXIT_SUCCESS
);
1951 fatal_signal_handler(int sig
__attribute__((unused
)), siginfo_t
*si
__attribute__((unused
)), void *uap
__attribute__((unused
)))
1953 do_crash_debug_mode();
1957 handle_system_bootstrapper_crashes_separately(void)
1959 if (!g_startup_debugging
) {
1963 fprintf(stdout
, "com.apple.launchctl.System\t\t\t*** Handling system bootstrapper crashes separately. ***\n");
1964 struct sigaction fsa
;
1966 fsa
.sa_sigaction
= fatal_signal_handler
;
1967 fsa
.sa_flags
= SA_SIGINFO
;
1968 sigemptyset(&fsa
.sa_mask
);
1970 (void)assumes(sigaction(SIGILL
, &fsa
, NULL
) != -1);
1971 (void)assumes(sigaction(SIGFPE
, &fsa
, NULL
) != -1);
1972 (void)assumes(sigaction(SIGBUS
, &fsa
, NULL
) != -1);
1973 (void)assumes(sigaction(SIGSEGV
, &fsa
, NULL
) != -1);
1974 (void)assumes(sigaction(SIGTRAP
, &fsa
, NULL
) != -1);
1975 (void)assumes(sigaction(SIGABRT
, &fsa
, NULL
) != -1);
1979 system_specific_bootstrap(bool sflag
)
1981 int hnmib
[] = { CTL_KERN
, KERN_HOSTNAME
};
1985 launch_data_t lda
, ldb
;
1988 handle_system_bootstrapper_crashes_separately();
1990 // Disable Libinfo lookups to mdns and ds while bootstrapping (8698260)
1991 si_search_module_set_flags("mdns", 1);
1992 si_search_module_set_flags("ds", 1);
1994 do_sysversion_sysctl();
1996 do_single_user_mode(sflag
);
1998 (void)assumes((kq
= kqueue()) != -1);
2000 EV_SET(&kev
, 0, EVFILT_TIMER
, EV_ADD
|EV_ONESHOT
, NOTE_SECONDS
, 60, 0);
2001 (void)assumes(kevent(kq
, &kev
, 1, NULL
, 0, NULL
) != -1);
2003 EV_SET(&kev
, SIGTERM
, EVFILT_SIGNAL
, EV_ADD
, 0, 0, 0);
2004 (void)assumes(kevent(kq
, &kev
, 1, NULL
, 0, NULL
) != -1);
2005 (void)assumes(signal(SIGTERM
, SIG_IGN
) != SIG_ERR
);
2007 (void)assumes(sysctl(hnmib
, 2, NULL
, NULL
, "localhost", sizeof("localhost")) != -1);
2009 loopback_setup_ipv4();
2010 loopback_setup_ipv6();
2012 apply_sysctls_from_file("/etc/sysctl.conf");
2014 #if TARGET_OS_EMBEDDED
2015 if (path_check("/etc/rc.boot")) {
2016 const char *rcboot_tool
[] = { "/etc/rc.boot", NULL
};
2018 (void)assumes(signal(SIGTERM
, exit_at_sigterm
) != SIG_ERR
);
2019 (void)assumes(fwexec(rcboot_tool
, NULL
) != -1);
2023 if (path_check("/etc/rc.cdrom")) {
2024 const char *rccdrom_tool
[] = { _PATH_BSHELL
, "/etc/rc.cdrom", "multiuser", NULL
};
2026 /* The bootstrapper should always be killable during install-time (rdar://problem/6103485).
2027 * This is a special case for /etc/rc.cdrom, which runs a process and never exits.
2029 (void)assumes(signal(SIGTERM
, exit_at_sigterm
) != SIG_ERR
);
2030 (void)assumes(fwexec(rccdrom_tool
, NULL
) != -1);
2031 (void)assumes(reboot(RB_HALT
) != -1);
2032 _exit(EXIT_FAILURE
);
2033 } else if (is_netboot()) {
2034 const char *rcnetboot_tool
[] = { _PATH_BSHELL
, "/etc/rc.netboot", "init", NULL
};
2035 if (!assumes(fwexec(rcnetboot_tool
, NULL
) != -1)) {
2036 (void)assumes(reboot(RB_HALT
) != -1);
2037 _exit(EXIT_FAILURE
);
2040 do_potential_fsck();
2043 #if TARGET_OS_EMBEDDED
2044 if (path_check("/usr/libexec/cc_fips_test")) {
2045 const char *fips_tool
[] = { "/usr/libexec/cc_fips_test", "-P", NULL
};
2046 if (fwexec(fips_tool
, NULL
) == -1) {
2047 printf("FIPS self check failure\n");
2048 (void)assumes(reboot(RB_HALT
) != -1);
2049 _exit(EXIT_FAILURE
);
2054 if (path_check("/etc/rc.server")) {
2055 const char *rcserver_tool
[] = { _PATH_BSHELL
, "/etc/rc.server", NULL
};
2056 (void)assumes(fwexec(rcserver_tool
, NULL
) != -1);
2059 read_launchd_conf();
2061 if (path_check("/var/account/acct")) {
2062 (void)assumes(acct("/var/account/acct") != -1);
2065 #if !TARGET_OS_EMBEDDED
2066 if (path_check("/etc/fstab")) {
2067 const char *mount_tool
[] = { "mount", "-vat", "nonfs", NULL
};
2068 (void)assumes(fwexec(mount_tool
, NULL
) != -1);
2072 if (path_check("/etc/rc.installer_cleanup")) {
2073 const char *rccleanup_tool
[] = { _PATH_BSHELL
, "/etc/rc.installer_cleanup", "multiuser", NULL
};
2074 (void)assumes(fwexec(rccleanup_tool
, NULL
) != -1);
2077 if (path_check("/etc/rc.deferred_install")) {
2079 const char *deferredinstall_tool
[] = { _PATH_BSHELL
, "/etc/rc.deferred_install", NULL
};
2080 if (assumes(fwexec(deferredinstall_tool
, &status
) != -1)) {
2081 if (WEXITSTATUS(status
) == EXIT_SUCCESS
) {
2082 if (do_apple_internal_magic
) {
2083 fprintf(stdout
, "Deferred install script completed successfully. Rebooting in 3 seconds...\n");
2087 (void)assumes(remove(deferredinstall_tool
[1]) != -1);
2088 (void)assumes(reboot(RB_AUTOBOOT
) != -1);
2091 fprintf(stdout
, "Deferred install script exited with status %i. Continuing boot and hoping it'll work...\n", WEXITSTATUS(status
));
2092 (void)assumes(remove(deferredinstall_tool
[1]) != -1);
2097 empty_dir(_PATH_VARRUN
, NULL
);
2098 empty_dir(_PATH_TMP
, NULL
);
2099 remove(_PATH_NOLOGIN
);
2101 if (path_check("/usr/libexec/dirhelper")) {
2102 const char *dirhelper_tool
[] = { "/usr/libexec/dirhelper", "-machineBoot", NULL
};
2103 (void)assumes(fwexec(dirhelper_tool
, NULL
) != -1);
2106 (void)assumes(touch_file(_PATH_UTMPX
, DEFFILEMODE
) != -1);
2107 #if !TARGET_OS_EMBEDDED
2108 (void)assumes(touch_file(_PATH_VARRUN
"/.systemStarterRunning", DEFFILEMODE
) != -1);
2113 * Only start auditing if not "Disabled" in auditd plist.
2115 if ((lda
= read_plist_file(AUDITD_PLIST_FILE
, false, false)) != NULL
&&
2116 ((ldb
= launch_data_dict_lookup(lda
, LAUNCH_JOBKEY_DISABLED
)) == NULL
||
2117 job_disabled_logic(ldb
) == false))
2119 (void)assumes(audit_quick_start() == 0);
2120 launch_data_free(lda
);
2123 if (path_check("/etc/security/rc.audit")) {
2124 const char *audit_tool
[] = { _PATH_BSHELL
, "/etc/security/rc.audit", NULL
};
2125 (void)assumes(fwexec(audit_tool
, NULL
) != -1);
2129 do_BootCache_magic(BOOTCACHE_START
);
2131 preheat_page_cache_hack();
2133 _vproc_set_global_on_demand(true);
2135 char *load_launchd_items
[] = { "load", "-D", "all", NULL
};
2136 int load_launchd_items_cnt
= 3;
2138 if (is_safeboot()) {
2139 load_launchd_items
[2] = "system";
2142 (void)assumes(load_and_unload_cmd(load_launchd_items_cnt
, load_launchd_items
) == 0);
2147 * We need to revisit this after Leopard ships.
2149 * I want a plist defined knob for jobs to give advisory hints that
2150 * will "hopefully" serialize bootstrap. Reasons for doing so include
2151 * pragmatic performance optimizations and attempts to workaround bugs
2152 * in jobs. Something like what follows might work:
2154 * The BootCache would switch to launchd and add this to the plist:
2156 * <key>HopefullyStartsSerially<key>
2158 * <key>ReadyTimeout</key>
2159 * <integer>2</integer>
2162 * And kextd would add the following:
2164 * <key>HopefullyStartsSerially<key>
2166 * <key>ReadyTimeout</key>
2167 * <integer>5</integer>
2168 * <key>HopefullyStartsAfter</key>
2169 * <string>com.apple.BootCache.daemon</string>
2173 * Then both the BootCache and kextd could call something like:
2175 * vproc_declare_ready_state();
2177 * To tell launchd to short circuit the readiness timeout and let the
2178 * next wave of jobs start.
2180 * Yes, this mechanism smells a lot like SystemStarter, rc.d and
2181 * friends. I think as long as we document that artificial
2182 * serialization is only advisory and not guaranteed, we should be
2183 * fine. Remember: IPC is the preferred way to serialize operations.
2186 if (!do_apple_internal_magic
) {
2187 mach_timespec_t w
= { 5, 0 };
2188 IOKitWaitQuiet(kIOMasterPortDefault
, &w
);
2191 do_BootCache_magic(BOOTCACHE_TAG
);
2193 do_bootroot_magic();
2195 _vproc_set_global_on_demand(false);
2197 (void)assumes(kevent(kq
, NULL
, 0, &kev
, 1, NULL
) == 1);
2199 /* warmd now handles cutting off the BootCache. We just kick it off. */
2201 (void)assumes(close(kq
) != -1);
2205 do_BootCache_magic(BootCache_action_t what
)
2207 const char *bcc_tool
[] = { "/usr/sbin/BootCacheControl", NULL
, NULL
};
2209 if (is_safeboot() || !path_check(bcc_tool
[0])) {
2214 case BOOTCACHE_START
:
2215 bcc_tool
[1] = "start";
2218 bcc_tool
[1] = "tag";
2220 case BOOTCACHE_STOP
:
2221 bcc_tool
[1] = "stop";
2225 fwexec(bcc_tool
, NULL
);
2229 bootstrap_cmd(int argc
, char *const argv
[])
2231 char *session_type
= NULL
;
2235 while ((ch
= getopt(argc
, argv
, "sS:")) != -1) {
2241 session_type
= optarg
;
2252 if (!session_type
) {
2253 fprintf(stderr
, "usage: %s bootstrap [-s] -S <session-type>\n", getprogname());
2257 if (strcasecmp(session_type
, "System") == 0) {
2258 bootstrapping_system
= true;
2259 system_specific_bootstrap(sflag
);
2261 char *load_launchd_items
[] = { "load", "-S", session_type
, "-D", "all", NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
2264 char *load_launchd_items_user
[] = { "load", "-S", VPROCMGR_SESSION_BACKGROUND
, "-D", "user", NULL
};
2265 int the_argc_user
= 0;
2267 if (is_safeboot()) {
2268 load_launchd_items
[4] = "system";
2271 if (strcasecmp(session_type
, VPROCMGR_SESSION_BACKGROUND
) == 0 || strcasecmp(session_type
, VPROCMGR_SESSION_LOGINWINDOW
) == 0) {
2272 load_launchd_items
[4] = "system";
2273 if (!is_safeboot()) {
2274 load_launchd_items
[5] = "-D";
2275 load_launchd_items
[6] = "local";
2278 if (strcasecmp(session_type
, VPROCMGR_SESSION_LOGINWINDOW
) == 0) {
2279 load_launchd_items
[the_argc
] = "/etc/mach_init_per_login_session.d";
2282 } else if (strcasecmp(session_type
, VPROCMGR_SESSION_AQUA
) == 0) {
2283 load_launchd_items
[5] = "/etc/mach_init_per_user.d";
2285 /* For now, we'll just load user Background agents when
2286 * bootstrapping the Aqua session. This way, we can
2287 * safely assume that the home directory is present. If
2288 * we try reading the user's Background agents when we're
2289 * actually bootstrapping the Background session, we run the
2290 * risk of deadlocking against mount_url. But this fix should
2291 * satisfy <rdar://problem/5279345>.
2295 /* We want to read environment.plist, which is in the user's home directory.
2296 * Since the dance to mount a network home directory is fairly complex, all we
2297 * can do is try and read environment.plist when bootstrapping the Aqua session,
2298 * which is when we assume the home directory is present.
2300 * The drawback here is that jobs bootstrapped in the Background session won't
2301 * get the new environment until they quit and relaunch. But then again, they
2302 * won't get the updated HOME directory or anything either. This is just a messy
2305 read_environment_dot_plist();
2308 if (strcasecmp(session_type
, VPROCMGR_SESSION_BACKGROUND
) == 0) {
2309 bootstrapping_peruser
= true;
2310 read_launchd_conf();
2311 #if 0 /* XXX PR-6456403 */
2312 (void)assumes(SessionCreate(sessionKeepCurrentBootstrap
, 0) == 0);
2316 int retval
= load_and_unload_cmd(the_argc
, load_launchd_items
);
2317 if (retval
== 0 && the_argc_user
!= 0) {
2320 vproc_err_t err
= vproc_swap_integer(NULL
, VPROC_GSK_WEIRD_BOOTSTRAP
, &junk
, NULL
);
2322 retval
= load_and_unload_cmd(the_argc_user
, load_launchd_items_user
);
2333 load_and_unload_cmd(int argc
, char *const argv
[])
2335 NSSearchPathEnumerationState es
= 0;
2336 char nspath
[PATH_MAX
* 2]; /* safe side, we need to append */
2337 bool badopts
= false;
2338 struct load_unload_state lus
;
2342 memset(&lus
, 0, sizeof(lus
));
2344 if (strcmp(argv
[0], "load") == 0) {
2348 while ((ch
= getopt(argc
, argv
, "wFS:D:")) != -1) {
2351 lus
.editondisk
= true;
2354 lus
.forceload
= true;
2357 lus
.session_type
= optarg
;
2360 if (strcasecmp(optarg
, "all") == 0) {
2361 es
|= NSAllDomainsMask
;
2362 } else if (strcasecmp(optarg
, "user") == 0) {
2363 es
|= NSUserDomainMask
;
2364 } else if (strcasecmp(optarg
, "local") == 0) {
2365 es
|= NSLocalDomainMask
;
2366 } else if (strcasecmp(optarg
, "network") == 0) {
2367 es
|= NSNetworkDomainMask
;
2368 } else if (strcasecmp(optarg
, "system") == 0) {
2369 es
|= NSSystemDomainMask
;
2383 if (lus
.session_type
== NULL
) {
2384 es
&= ~NSUserDomainMask
;
2387 if (argc
== 0 && es
== 0) {
2392 fprintf(stderr
, "usage: %s load [-wF] [-D <user|local|network|system|all>] paths...\n", getprogname());
2398 vproc_err_t verr
= vproc_swap_string(NULL
, VPROC_GSK_JOB_OVERRIDES_DB
, NULL
, &db
);
2400 fprintf(stderr
, "Could not get location of job overrides database.\n");
2401 g_job_overrides_db_path
[0] = 0;
2403 strncpy(g_job_overrides_db_path
, db
, strlen(db
));
2405 /* If we can't create or lock the overrides database, we'll fall back to writing to the
2406 * plist file directly.
2408 (void)assumes((dbfd
= open(g_job_overrides_db_path
, O_RDONLY
| O_EXLOCK
| O_CREAT
, S_IRUSR
| S_IWUSR
)) != -1);
2410 g_job_overrides_db
= (CFMutableDictionaryRef
)CreateMyPropertyListFromFile(g_job_overrides_db_path
);
2411 if (!g_job_overrides_db
) {
2412 g_job_overrides_db
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2418 /* I wish I didn't need to do three passes, but I need to load mDNSResponder and use it too.
2419 * And loading legacy mach init jobs is extra fun.
2421 * In later versions of launchd, I hope to load everything in the first pass,
2422 * then do the Bonjour magic on the jobs that need it, and reload them, but for now,
2423 * I haven't thought through the various complexities of reloading jobs, and therefore
2424 * launchd doesn't have reload support right now.
2427 lus
.pass1
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
2428 lus
.pass2
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
2430 es
= NSStartSearchPathEnumeration(NSLibraryDirectory
, es
);
2432 while ((es
= NSGetNextSearchPathEnumeration(es
, nspath
))) {
2435 if (lus
.session_type
) {
2436 strcat(nspath
, "/LaunchAgents");
2438 strcat(nspath
, "/LaunchDaemons");
2441 if (glob(nspath
, GLOB_TILDE
|GLOB_NOSORT
, NULL
, &g
) == 0) {
2442 for (i
= 0; i
< g
.gl_pathc
; i
++) {
2443 readpath(g
.gl_pathv
[i
], &lus
);
2449 for (i
= 0; i
< (size_t)argc
; i
++) {
2450 readpath(argv
[i
], &lus
);
2453 if (launch_data_array_get_count(lus
.pass1
) == 0 &&
2454 launch_data_array_get_count(lus
.pass2
) == 0) {
2456 fprintf(stderr
, "nothing found to %s\n", lus
.load
? "load" : "unload");
2458 launch_data_free(lus
.pass1
);
2459 launch_data_free(lus
.pass2
);
2460 return is_managed
? 0 : 1;
2464 distill_jobs(lus
.pass1
);
2465 submit_job_pass(lus
.pass1
);
2466 distill_jobs(lus
.pass2
);
2467 submit_job_pass(lus
.pass2
);
2469 for (i
= 0; i
< launch_data_array_get_count(lus
.pass1
); i
++) {
2470 unloadjob(launch_data_array_get_index(lus
.pass1
, i
));
2472 for (i
= 0; i
< launch_data_array_get_count(lus
.pass2
); i
++) {
2473 unloadjob(launch_data_array_get_index(lus
.pass2
, i
));
2477 if (g_job_overrides_db_has_changed
) {
2478 WriteMyPropertyListToFile(g_job_overrides_db
, g_job_overrides_db_path
);
2481 flock(dbfd
, LOCK_UN
);
2487 submit_job_pass(launch_data_t jobs
)
2489 launch_data_t msg
, resp
;
2493 if (launch_data_array_get_count(jobs
) == 0)
2496 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
2498 launch_data_dict_insert(msg
, jobs
, LAUNCH_KEY_SUBMITJOB
);
2500 resp
= launch_msg(msg
);
2503 switch (launch_data_get_type(resp
)) {
2504 case LAUNCH_DATA_ERRNO
:
2505 if ((e
= launch_data_get_errno(resp
)))
2506 fprintf(stderr
, "%s\n", strerror(e
));
2508 case LAUNCH_DATA_ARRAY
:
2509 for (i
= 0; i
< launch_data_array_get_count(jobs
); i
++) {
2510 launch_data_t obatind
= launch_data_array_get_index(resp
, i
);
2511 launch_data_t jatind
= launch_data_array_get_index(jobs
, i
);
2512 const char *lab4job
= launch_data_get_string(launch_data_dict_lookup(jatind
, LAUNCH_JOBKEY_LABEL
));
2513 if (LAUNCH_DATA_ERRNO
== launch_data_get_type(obatind
)) {
2514 e
= launch_data_get_errno(obatind
);
2517 fprintf(stderr
, "%s: %s\n", lab4job
, "Already loaded");
2520 fprintf(stderr
, "%s: %s\n", lab4job
, "Not loaded");
2523 fprintf(stderr
, "%s: %s\n", lab4job
, "Could not set security session");
2525 fprintf(stderr
, "%s: %s\n", lab4job
, strerror(e
));
2533 fprintf(stderr
, "unknown respose from launchd!\n");
2536 launch_data_free(resp
);
2538 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
2541 launch_data_free(msg
);
2545 start_stop_remove_cmd(int argc
, char *const argv
[])
2547 launch_data_t resp
, msg
;
2548 const char *lmsgcmd
= LAUNCH_KEY_STOPJOB
;
2551 if (0 == strcmp(argv
[0], "start"))
2552 lmsgcmd
= LAUNCH_KEY_STARTJOB
;
2554 if (0 == strcmp(argv
[0], "remove"))
2555 lmsgcmd
= LAUNCH_KEY_REMOVEJOB
;
2558 fprintf(stderr
, "usage: %s %s <job label>\n", getprogname(), argv
[0]);
2562 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
2563 launch_data_dict_insert(msg
, launch_data_new_string(argv
[1]), lmsgcmd
);
2565 resp
= launch_msg(msg
);
2566 launch_data_free(msg
);
2569 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
2571 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
2572 if ((e
= launch_data_get_errno(resp
))) {
2573 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(e
));
2577 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
2581 launch_data_free(resp
);
2586 print_jobs(launch_data_t j
, const char *key
__attribute__((unused
)), void *context
__attribute__((unused
)))
2588 static size_t depth
= 0;
2589 launch_data_t lo
= launch_data_dict_lookup(j
, LAUNCH_JOBKEY_LABEL
);
2590 launch_data_t pido
= launch_data_dict_lookup(j
, LAUNCH_JOBKEY_PID
);
2591 launch_data_t stato
= launch_data_dict_lookup(j
, LAUNCH_JOBKEY_LASTEXITSTATUS
);
2592 const char *label
= launch_data_get_string(lo
);
2596 fprintf(stdout
, "%lld\t-\t", launch_data_get_integer(pido
));
2598 int wstatus
= (int)launch_data_get_integer(stato
);
2599 if (WIFEXITED(wstatus
)) {
2600 fprintf(stdout
, "-\t%d\t", WEXITSTATUS(wstatus
));
2601 } else if (WIFSIGNALED(wstatus
)) {
2602 fprintf(stdout
, "-\t-%d\t", WTERMSIG(wstatus
));
2604 fprintf(stdout
, "-\t???\t");
2607 fprintf(stdout
, "-\t-\t");
2609 for (i
= 0; i
< depth
; i
++)
2610 fprintf(stdout
, "\t");
2612 fprintf(stdout
, "%s\n", label
);
2616 print_obj(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
2618 static size_t indent
= 0;
2621 for (i
= 0; i
< indent
; i
++)
2622 fprintf(stdout
, "\t");
2625 fprintf(stdout
, "\"%s\" = ", key
);
2627 switch (launch_data_get_type(obj
)) {
2628 case LAUNCH_DATA_STRING
:
2629 fprintf(stdout
, "\"%s\";\n", launch_data_get_string(obj
));
2631 case LAUNCH_DATA_INTEGER
:
2632 fprintf(stdout
, "%lld;\n", launch_data_get_integer(obj
));
2634 case LAUNCH_DATA_REAL
:
2635 fprintf(stdout
, "%f;\n", launch_data_get_real(obj
));
2637 case LAUNCH_DATA_BOOL
:
2638 fprintf(stdout
, "%s;\n", launch_data_get_bool(obj
) ? "true" : "false");
2640 case LAUNCH_DATA_ARRAY
:
2641 c
= launch_data_array_get_count(obj
);
2642 fprintf(stdout
, "(\n");
2644 for (i
= 0; i
< c
; i
++)
2645 print_obj(launch_data_array_get_index(obj
, i
), NULL
, NULL
);
2647 for (i
= 0; i
< indent
; i
++)
2648 fprintf(stdout
, "\t");
2649 fprintf(stdout
, ");\n");
2651 case LAUNCH_DATA_DICTIONARY
:
2652 fprintf(stdout
, "{\n");
2654 launch_data_dict_iterate(obj
, print_obj
, NULL
);
2656 for (i
= 0; i
< indent
; i
++)
2657 fprintf(stdout
, "\t");
2658 fprintf(stdout
, "};\n");
2660 case LAUNCH_DATA_FD
:
2661 fprintf(stdout
, "file-descriptor-object;\n");
2663 case LAUNCH_DATA_MACHPORT
:
2664 fprintf(stdout
, "mach-port-object;\n");
2667 fprintf(stdout
, "???;\n");
2673 list_cmd(int argc
, char *const argv
[])
2675 launch_data_t resp
, msg
= NULL
;
2678 bool plist_output
= false;
2681 fprintf(stderr
, "usage: %s list [-x] [label]\n", getprogname());
2683 } else if (argc
>= 2) {
2684 plist_output
= ( strncmp(argv
[1], "-x", sizeof("-x")) == 0 );
2685 label
= plist_output
? argv
[2] : argv
[1];
2689 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
2690 launch_data_dict_insert(msg
, launch_data_new_string(label
), LAUNCH_KEY_GETJOB
);
2692 resp
= launch_msg(msg
);
2693 launch_data_free(msg
);
2696 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
2698 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_DICTIONARY
) {
2700 CFDictionaryRef respDict
= CFDictionaryCreateFromLaunchDictionary(resp
);
2701 CFStringRef plistStr
= NULL
;
2703 CFDataRef plistData
= CFPropertyListCreateXMLData(NULL
, (CFPropertyListRef
)respDict
);
2704 CFRelease(respDict
);
2706 plistStr
= CFStringCreateWithBytes(NULL
, CFDataGetBytePtr(plistData
), CFDataGetLength(plistData
), kCFStringEncodingUTF8
, false);
2707 CFRelease(plistData
);
2717 CFRelease(plistStr
);
2721 print_obj(resp
, NULL
, NULL
);
2724 launch_data_free(resp
);
2726 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
2728 launch_data_free(resp
);
2730 } else if (vproc_swap_complex(NULL
, VPROC_GSK_ALLJOBS
, NULL
, &resp
) == NULL
) {
2731 fprintf(stdout
, "PID\tStatus\tLabel\n");
2732 launch_data_dict_iterate(resp
, print_jobs
, NULL
);
2733 launch_data_free(resp
);
2742 stdio_cmd(int argc
__attribute__((unused
)), char *const argv
[])
2744 fprintf(stderr
, "%s %s: This sub-command no longer does anything\n", getprogname(), argv
[0]);
2749 fyi_cmd(int argc
, char *const argv
[])
2751 launch_data_t resp
, msg
;
2752 const char *lmsgk
= NULL
;
2756 fprintf(stderr
, "usage: %s %s\n", getprogname(), argv
[0]);
2760 if (!strcmp(argv
[0], "shutdown")) {
2761 lmsgk
= LAUNCH_KEY_SHUTDOWN
;
2762 } else if (!strcmp(argv
[0], "singleuser")) {
2763 lmsgk
= LAUNCH_KEY_SINGLEUSER
;
2768 msg
= launch_data_new_string(lmsgk
);
2769 resp
= launch_msg(msg
);
2770 launch_data_free(msg
);
2773 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
2775 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
2776 if ((e
= launch_data_get_errno(resp
))) {
2777 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(e
));
2781 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
2785 launch_data_free(resp
);
2791 logupdate_cmd(int argc
, char *const argv
[])
2793 int64_t inval
, outval
;
2794 bool badargs
= false, maskmode
= false, onlymode
= false, levelmode
= false;
2795 static const struct {
2799 { "debug", LOG_DEBUG
},
2800 { "info", LOG_INFO
},
2801 { "notice", LOG_NOTICE
},
2802 { "warning", LOG_WARNING
},
2803 { "error", LOG_ERR
},
2804 { "critical", LOG_CRIT
},
2805 { "alert", LOG_ALERT
},
2806 { "emergency", LOG_EMERG
},
2808 size_t i
, j
, logtblsz
= sizeof logtbl
/ sizeof logtbl
[0];
2812 if (!strcmp(argv
[1], "mask"))
2814 else if (!strcmp(argv
[1], "only"))
2816 else if (!strcmp(argv
[1], "level"))
2823 m
= LOG_UPTO(LOG_DEBUG
);
2825 if (argc
> 2 && (maskmode
|| onlymode
)) {
2826 for (i
= 2; i
< (size_t)argc
; i
++) {
2827 for (j
= 0; j
< logtblsz
; j
++) {
2828 if (!strcmp(argv
[i
], logtbl
[j
].name
)) {
2830 m
&= ~(LOG_MASK(logtbl
[j
].level
));
2832 m
|= LOG_MASK(logtbl
[j
].level
);
2836 if (j
== logtblsz
) {
2841 } else if (argc
> 2 && levelmode
) {
2842 for (j
= 0; j
< logtblsz
; j
++) {
2843 if (!strcmp(argv
[2], logtbl
[j
].name
)) {
2844 m
= LOG_UPTO(logtbl
[j
].level
);
2850 } else if (argc
!= 1) {
2855 fprintf(stderr
, "usage: %s [[mask loglevels...] | [only loglevels...] [level loglevel]]\n", getprogname());
2861 if (vproc_swap_integer(NULL
, VPROC_GSK_GLOBAL_LOG_MASK
, argc
!= 1 ? &inval
: NULL
, &outval
) == NULL
) {
2863 for (j
= 0; j
< logtblsz
; j
++) {
2864 if (outval
& LOG_MASK(logtbl
[j
].level
)) {
2865 fprintf(stdout
, "%s ", logtbl
[j
].name
);
2868 fprintf(stdout
, "\n");
2876 static const struct {
2880 { "cpu", RLIMIT_CPU
},
2881 { "filesize", RLIMIT_FSIZE
},
2882 { "data", RLIMIT_DATA
},
2883 { "stack", RLIMIT_STACK
},
2884 { "core", RLIMIT_CORE
},
2885 { "rss", RLIMIT_RSS
},
2886 { "memlock", RLIMIT_MEMLOCK
},
2887 { "maxproc", RLIMIT_NPROC
},
2888 { "maxfiles", RLIMIT_NOFILE
}
2891 static const size_t limlookupcnt
= sizeof limlookup
/ sizeof limlookup
[0];
2894 name2num(const char *n
)
2898 for (i
= 0; i
< limlookupcnt
; i
++) {
2899 if (!strcmp(limlookup
[i
].name
, n
)) {
2900 return limlookup
[i
].lim
;
2911 for (i
= 0; i
< limlookupcnt
; i
++) {
2912 if (limlookup
[i
].lim
== n
)
2913 return limlookup
[i
].name
;
2919 lim2str(rlim_t val
, char *buf
)
2921 if (val
== RLIM_INFINITY
)
2922 strcpy(buf
, "unlimited");
2924 sprintf(buf
, "%lld", val
);
2929 str2lim(const char *buf
, rlim_t
*res
)
2932 *res
= strtoll(buf
, &endptr
, 10);
2933 if (!strcmp(buf
, "unlimited")) {
2934 *res
= RLIM_INFINITY
;
2936 } else if (*endptr
== '\0') {
2943 limit_cmd(int argc
, char *const argv
[])
2947 struct rlimit
*lmts
= NULL
;
2948 launch_data_t resp
, resp1
= NULL
, msg
, tmp
;
2952 rlim_t slim
= -1, hlim
= -1;
2953 bool badargs
= false;
2958 if (argc
>= 3 && str2lim(argv
[2], &slim
))
2963 if (argc
== 4 && str2lim(argv
[3], &hlim
))
2966 if (argc
>= 2 && -1 == (which
= name2num(argv
[1])))
2970 fprintf(stderr
, "usage: %s %s [", getprogname(), argv
[0]);
2971 for (i
= 0; i
< limlookupcnt
; i
++)
2972 fprintf(stderr
, "%s %s", limlookup
[i
].name
, (i
+ 1) == limlookupcnt
? "" : "| ");
2973 fprintf(stderr
, "[both | soft hard]]\n");
2977 msg
= launch_data_new_string(LAUNCH_KEY_GETRESOURCELIMITS
);
2978 resp
= launch_msg(msg
);
2979 launch_data_free(msg
);
2982 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
2984 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_OPAQUE
) {
2985 lmts
= launch_data_get_opaque(resp
);
2986 lsz
= launch_data_get_opaque_size(resp
);
2988 for (i
= 0; i
< (lsz
/ sizeof(struct rlimit
)); i
++) {
2989 if (argc
== 2 && (size_t)which
!= i
)
2991 fprintf(stdout
, "\t%-12s%-15s%-15s\n", num2name((int)i
),
2992 lim2str(lmts
[i
].rlim_cur
, slimstr
),
2993 lim2str(lmts
[i
].rlim_max
, hlimstr
));
2996 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_STRING
) {
2997 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], launch_data_get_string(resp
));
3000 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
3004 if (argc
<= 2 || r
!= 0) {
3005 launch_data_free(resp
);
3011 lmts
[which
].rlim_cur
= slim
;
3012 lmts
[which
].rlim_max
= hlim
;
3014 bool maxfiles_exceeded
= false;
3015 if (strncmp(argv
[1], "maxfiles", sizeof("maxfiles")) == 0) {
3017 maxfiles_exceeded
= ( strncmp(argv
[2], "unlimited", sizeof("unlimited")) == 0 );
3021 maxfiles_exceeded
= ( maxfiles_exceeded
|| strncmp(argv
[3], "unlimited", sizeof("unlimited")) == 0 );
3024 if (maxfiles_exceeded
) {
3025 fprintf(stderr
, "Neither the hard nor soft limit for \"maxfiles\" can be unlimited. Please use a numeric parameter for both.\n");
3030 msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
3031 tmp
= launch_data_new_opaque(lmts
, lsz
);
3032 launch_data_dict_insert(msg
, tmp
, LAUNCH_KEY_SETRESOURCELIMITS
);
3033 resp
= launch_msg(msg
);
3034 launch_data_free(msg
);
3037 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
3039 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_STRING
) {
3040 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], launch_data_get_string(resp
));
3042 } else if (launch_data_get_type(resp
) != LAUNCH_DATA_OPAQUE
) {
3043 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
3047 launch_data_free(resp
);
3048 launch_data_free(resp1
);
3054 umask_cmd(int argc
, char *const argv
[])
3056 bool badargs
= false;
3059 int64_t inval
, outval
;
3062 m
= strtol(argv
[1], &endptr
, 8);
3063 if (*endptr
!= '\0' || m
> 0777)
3067 if (argc
> 2 || badargs
) {
3068 fprintf(stderr
, "usage: %s %s <mask>\n", getprogname(), argv
[0]);
3074 if (vproc_swap_integer(NULL
, VPROC_GSK_GLOBAL_UMASK
, argc
== 2 ? &inval
: NULL
, &outval
) == NULL
) {
3076 fprintf(stdout
, "%o\n", (unsigned int)outval
);
3085 setup_system_context(void)
3087 if (getenv(LAUNCHD_SOCKET_ENV
)) {
3091 if (getenv(LAUNCH_ENV_KEEPCONTEXT
)) {
3095 if (geteuid() != 0) {
3096 fprintf(stderr
, "You must be the root user to perform this operation.\n");
3100 /* Use the system launchd's socket. */
3101 setenv("__USE_SYSTEM_LAUNCHD", "1", 0);
3103 /* Put ourselves in the system launchd's bootstrap. */
3104 mach_port_t rootbs
= str2bsport("/");
3105 mach_port_deallocate(mach_task_self(), bootstrap_port
);
3106 task_set_bootstrap_port(mach_task_self(), rootbs
);
3107 bootstrap_port
= rootbs
;
3111 submit_cmd(int argc
, char *const argv
[])
3113 launch_data_t msg
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
3114 launch_data_t job
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
3115 launch_data_t resp
, largv
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
3118 launch_data_dict_insert(job
, launch_data_new_bool(false), LAUNCH_JOBKEY_ONDEMAND
);
3120 while ((ch
= getopt(argc
, argv
, "l:p:o:e:")) != -1) {
3123 launch_data_dict_insert(job
, launch_data_new_string(optarg
), LAUNCH_JOBKEY_LABEL
);
3126 launch_data_dict_insert(job
, launch_data_new_string(optarg
), LAUNCH_JOBKEY_PROGRAM
);
3129 launch_data_dict_insert(job
, launch_data_new_string(optarg
), LAUNCH_JOBKEY_STANDARDOUTPATH
);
3132 launch_data_dict_insert(job
, launch_data_new_string(optarg
), LAUNCH_JOBKEY_STANDARDERRORPATH
);
3135 fprintf(stderr
, "usage: %s submit ...\n", getprogname());
3142 for (i
= 0; argv
[i
]; i
++) {
3143 launch_data_array_append(largv
, launch_data_new_string(argv
[i
]));
3146 launch_data_dict_insert(job
, largv
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
3148 launch_data_dict_insert(msg
, job
, LAUNCH_KEY_SUBMITJOB
);
3150 resp
= launch_msg(msg
);
3151 launch_data_free(msg
);
3154 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
3156 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
3157 errno
= launch_data_get_errno(resp
);
3159 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(errno
));
3163 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], "unknown response");
3166 launch_data_free(resp
);
3172 getrusage_cmd(int argc
, char *const argv
[])
3174 launch_data_t resp
, msg
;
3175 bool badargs
= false;
3180 else if (strcmp(argv
[1], "self") && strcmp(argv
[1], "children"))
3184 fprintf(stderr
, "usage: %s %s self | children\n", getprogname(), argv
[0]);
3188 if (!strcmp(argv
[1], "self")) {
3189 msg
= launch_data_new_string(LAUNCH_KEY_GETRUSAGESELF
);
3191 msg
= launch_data_new_string(LAUNCH_KEY_GETRUSAGECHILDREN
);
3194 resp
= launch_msg(msg
);
3195 launch_data_free(msg
);
3198 fprintf(stderr
, "launch_msg(): %s\n", strerror(errno
));
3200 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_ERRNO
) {
3201 fprintf(stderr
, "%s %s error: %s\n", getprogname(), argv
[0], strerror(launch_data_get_errno(resp
)));
3203 } else if (launch_data_get_type(resp
) == LAUNCH_DATA_OPAQUE
) {
3204 struct rusage
*rusage
= launch_data_get_opaque(resp
);
3205 fprintf(stdout
, "\t%-10f\tuser time used\n",
3206 (double)rusage
->ru_utime
.tv_sec
+ (double)rusage
->ru_utime
.tv_usec
/ (double)1000000);
3207 fprintf(stdout
, "\t%-10f\tsystem time used\n",
3208 (double)rusage
->ru_stime
.tv_sec
+ (double)rusage
->ru_stime
.tv_usec
/ (double)1000000);
3209 fprintf(stdout
, "\t%-10ld\tmax resident set size\n", rusage
->ru_maxrss
);
3210 fprintf(stdout
, "\t%-10ld\tshared text memory size\n", rusage
->ru_ixrss
);
3211 fprintf(stdout
, "\t%-10ld\tunshared data size\n", rusage
->ru_idrss
);
3212 fprintf(stdout
, "\t%-10ld\tunshared stack size\n", rusage
->ru_isrss
);
3213 fprintf(stdout
, "\t%-10ld\tpage reclaims\n", rusage
->ru_minflt
);
3214 fprintf(stdout
, "\t%-10ld\tpage faults\n", rusage
->ru_majflt
);
3215 fprintf(stdout
, "\t%-10ld\tswaps\n", rusage
->ru_nswap
);
3216 fprintf(stdout
, "\t%-10ld\tblock input operations\n", rusage
->ru_inblock
);
3217 fprintf(stdout
, "\t%-10ld\tblock output operations\n", rusage
->ru_oublock
);
3218 fprintf(stdout
, "\t%-10ld\tmessages sent\n", rusage
->ru_msgsnd
);
3219 fprintf(stdout
, "\t%-10ld\tmessages received\n", rusage
->ru_msgrcv
);
3220 fprintf(stdout
, "\t%-10ld\tsignals received\n", rusage
->ru_nsignals
);
3221 fprintf(stdout
, "\t%-10ld\tvoluntary context switches\n", rusage
->ru_nvcsw
);
3222 fprintf(stdout
, "\t%-10ld\tinvoluntary context switches\n", rusage
->ru_nivcsw
);
3224 fprintf(stderr
, "%s %s returned unknown response\n", getprogname(), argv
[0]);
3228 launch_data_free(resp
);
3234 launch_data_array_append(launch_data_t a
, launch_data_t o
)
3236 size_t offt
= launch_data_array_get_count(a
);
3238 return launch_data_array_set_index(a
, o
, offt
);
3242 str2bsport(const char *s
)
3244 bool getrootbs
= strcmp(s
, "/") == 0;
3245 mach_port_t last_bport
, bport
= bootstrap_port
;
3246 task_t task
= mach_task_self();
3247 kern_return_t result
;
3249 if (strcmp(s
, "..") == 0 || getrootbs
) {
3252 result
= bootstrap_parent(last_bport
, &bport
);
3254 if (result
== BOOTSTRAP_NOT_PRIVILEGED
) {
3255 fprintf(stderr
, "Permission denied\n");
3257 } else if (result
!= BOOTSTRAP_SUCCESS
) {
3258 fprintf(stderr
, "bootstrap_parent() %d\n", result
);
3261 } while (getrootbs
&& last_bport
!= bport
);
3262 } else if (strcmp(s
, "0") == 0 || strcmp(s
, "NULL") == 0) {
3263 bport
= MACH_PORT_NULL
;
3267 result
= task_for_pid(mach_task_self(), pid
, &task
);
3269 if (result
!= KERN_SUCCESS
) {
3270 fprintf(stderr
, "task_for_pid() %s\n", mach_error_string(result
));
3274 result
= task_get_bootstrap_port(task
, &bport
);
3276 if (result
!= KERN_SUCCESS
) {
3277 fprintf(stderr
, "Couldn't get bootstrap port: %s\n", mach_error_string(result
));
3286 bsexec_cmd(int argc
, char *const argv
[])
3288 kern_return_t result
;
3292 fprintf(stderr
, "usage: %s bsexec <PID> prog...\n", getprogname());
3296 bport
= str2bsport(argv
[1]);
3298 result
= task_set_bootstrap_port(mach_task_self(), bport
);
3300 if (result
!= KERN_SUCCESS
) {
3301 fprintf(stderr
, "Couldn't switch to new bootstrap port: %s\n", mach_error_string(result
));
3308 setenv(LAUNCH_ENV_KEEPCONTEXT
, "1", 1);
3309 if (fwexec((const char *const *)argv
+ 2, NULL
) == -1) {
3310 fprintf(stderr
, "%s bsexec failed: %s\n", getprogname(), strerror(errno
));
3318 _bslist_cmd(mach_port_t bport
, unsigned int depth
, bool show_job
, bool local_only
)
3320 kern_return_t result
;
3321 name_array_t service_names
;
3322 name_array_t service_jobs
;
3323 mach_msg_type_number_t service_cnt
, service_jobs_cnt
, service_active_cnt
;
3324 bootstrap_status_array_t service_actives
;
3327 if (bport
== MACH_PORT_NULL
) {
3328 fprintf(stderr
, "Invalid bootstrap port\n");
3333 flags
|= local_only
? BOOTSTRAP_FORCE_LOCAL
: 0;
3334 result
= bootstrap_info(bport
, &service_names
, &service_cnt
, &service_jobs
, &service_jobs_cnt
, &service_actives
, &service_active_cnt
, flags
);
3335 if (result
!= BOOTSTRAP_SUCCESS
) {
3336 fprintf(stderr
, "bootstrap_info(): %d\n", result
);
3340 #define bport_state(x) (((x) == BOOTSTRAP_STATUS_ACTIVE) ? "A" : ((x) == BOOTSTRAP_STATUS_ON_DEMAND) ? "D" : "I")
3342 for (i
= 0; i
< service_cnt
; i
++) {
3343 fprintf(stdout
, "%*s%-3s%s", depth
, "", bport_state((service_actives
[i
])), service_names
[i
]);
3345 fprintf(stdout
, " (%s)", service_jobs
[i
]);
3347 fprintf(stdout
, "\n");
3354 bslist_cmd(int argc
, char *const argv
[])
3356 mach_port_t bport
= bootstrap_port
;
3357 bool show_jobs
= false;
3358 if (argc
> 2 && strcmp(argv
[2], "-j") == 0) {
3364 bport
= str2bsport(argv
[1]);
3365 } else if (strcmp(argv
[1], "-j") == 0) {
3370 if (bport
== MACH_PORT_NULL
) {
3371 fprintf(stderr
, "Invalid bootstrap port\n");
3375 return _bslist_cmd(bport
, 0, show_jobs
, false);
3379 _bstree_cmd(mach_port_t bsport
, unsigned int depth
, bool show_jobs
)
3381 if (bsport
== MACH_PORT_NULL
) {
3382 fprintf(stderr
, "No root port!\n");
3386 mach_port_array_t child_ports
= NULL
;
3387 name_array_t child_names
= NULL
;
3388 bootstrap_property_array_t child_props
= NULL
;
3389 unsigned int cnt
= 0;
3391 kern_return_t kr
= bootstrap_lookup_children(bsport
, &child_ports
, &child_names
, &child_props
, (mach_msg_type_number_t
*)&cnt
);
3392 if (kr
!= BOOTSTRAP_SUCCESS
&& kr
!= BOOTSTRAP_NO_CHILDREN
) {
3393 if (kr
== BOOTSTRAP_NOT_PRIVILEGED
) {
3394 fprintf(stderr
, "You must be root to perform this operation.\n");
3396 fprintf(stderr
, "bootstrap_lookup_children(): %d\n", kr
);
3403 _bslist_cmd(bsport
, depth
, show_jobs
, true);
3405 for (i
= 0; i
< cnt
; i
++) {
3407 if (child_props
[i
] & BOOTSTRAP_PROPERTY_PERUSER
) {
3409 } else if (child_props
[i
] & BOOTSTRAP_PROPERTY_EXPLICITSUBSET
) {
3410 type
= "Explicit Subset";
3411 } else if (child_props
[i
] & BOOTSTRAP_PROPERTY_IMPLICITSUBSET
) {
3412 type
= "Implicit Subset";
3413 } else if (child_props
[i
] & BOOTSTRAP_PROPERTY_MOVEDSUBSET
) {
3414 type
= "Moved Subset";
3415 } else if (child_props
[i
] & BOOTSTRAP_PROPERTY_XPC_SINGLETON
) {
3416 type
= "XPC Singleton Domain";
3417 } else if (child_props
[i
] & BOOTSTRAP_PROPERTY_XPC_DOMAIN
) {
3418 type
= "XPC Private Domain";
3423 fprintf(stdout
, "%*s%s (%s)/\n", depth
, "", child_names
[i
], type
);
3424 if (child_ports
[i
] != MACH_PORT_NULL
) {
3425 _bstree_cmd(child_ports
[i
], depth
+ 4, show_jobs
);
3433 bstree_cmd(int argc
, char * const argv
[])
3435 bool show_jobs
= false;
3436 if (geteuid() != 0) {
3437 fprintf(stderr
, "You must be root to perform this operation.\n");
3440 if (argc
== 2 && strcmp(argv
[1], "-j") == 0) {
3443 fprintf(stdout
, "System/\n");
3446 return _bstree_cmd(str2bsport("/"), 4, show_jobs
);
3450 managerpid_cmd(int argc
__attribute__((unused
)), char * const argv
[] __attribute__((unused
)))
3452 int64_t manager_pid
= 0;
3453 vproc_err_t verr
= vproc_swap_integer(NULL
, VPROC_GSK_MGR_PID
, NULL
, (int64_t *)&manager_pid
);
3455 fprintf(stdout
, "Unknown job manager!\n");
3459 fprintf(stdout
, "%d\n", (pid_t
)manager_pid
);
3464 manageruid_cmd(int argc
__attribute__((unused
)), char * const argv
[] __attribute__((unused
)))
3466 int64_t manager_uid
= 0;
3467 vproc_err_t verr
= vproc_swap_integer(NULL
, VPROC_GSK_MGR_UID
, NULL
, (int64_t *)&manager_uid
);
3469 fprintf(stdout
, "Unknown job manager!\n");
3473 fprintf(stdout
, "%lli\n", manager_uid
);
3478 managername_cmd(int argc
__attribute__((unused
)), char * const argv
[] __attribute__((unused
)))
3480 char *manager_name
= NULL
;
3481 vproc_err_t verr
= vproc_swap_string(NULL
, VPROC_GSK_MGR_NAME
, NULL
, &manager_name
);
3483 fprintf(stdout
, "Unknown job manager!\n");
3487 fprintf(stdout
, "%s\n", manager_name
);
3494 asuser_cmd(int argc
, char * const argv
[])
3496 /* This code plays fast and loose with Mach ports. Do NOT use it as any sort
3497 * of reference for port handling. Or really anything else in this file.
3499 uid_t req_uid
= (uid_t
)-2;
3501 req_uid
= atoi(argv
[1]);
3502 if (req_uid
== (uid_t
)-2) {
3503 fprintf(stderr
, "You cannot run a command nobody.\n");
3507 fprintf(stderr
, "Usage: launchctl asuser <UID> <command> [arguments...].\n");
3511 if (geteuid() != 0) {
3512 fprintf(stderr
, "You must be root to run a command as another user.\n");
3516 mach_port_t rbs
= MACH_PORT_NULL
;
3517 kern_return_t kr
= bootstrap_get_root(bootstrap_port
, &rbs
);
3518 if (kr
!= BOOTSTRAP_SUCCESS
) {
3519 fprintf(stderr
, "bootstrap_get_root(): %u\n", kr
);
3523 mach_port_t bp
= MACH_PORT_NULL
;
3524 kr
= bootstrap_look_up_per_user(rbs
, NULL
, req_uid
, &bp
);
3525 if (kr
!= BOOTSTRAP_SUCCESS
) {
3526 fprintf(stderr
, "bootstrap_look_up_per_user(): %u\n", kr
);
3530 bootstrap_port
= bp
;
3531 kr
= task_set_bootstrap_port(mach_task_self(), bp
);
3532 if (kr
!= KERN_SUCCESS
) {
3533 fprintf(stderr
, "task_set_bootstrap_port(): 0x%x: %s\n", kr
, mach_error_string(kr
));
3539 kr
= _vprocmgr_getsocket(sockpath
);
3540 if (kr
!= BOOTSTRAP_SUCCESS
) {
3541 fprintf(stderr
, "_vprocmgr_getsocket(): %u\n", kr
);
3545 setenv(LAUNCHD_SOCKET_ENV
, sockpath
, 1);
3546 setenv(LAUNCH_ENV_KEEPCONTEXT
, "1", 1);
3547 if (fwexec((const char *const *)argv
+ 2, NULL
) == -1) {
3548 fprintf(stderr
, "Couldn't spawn command: %s\n", argv
[2]);
3556 _log_launchctl_bug(const char *rcs_rev
, const char *path
, unsigned int line
, const char *test
)
3558 int saved_errno
= errno
;
3560 const char *file
= strrchr(path
, '/');
3561 char *rcs_rev_tmp
= strchr(rcs_rev
, ' ');
3570 strlcpy(buf
, rcs_rev
, sizeof(buf
));
3572 strlcpy(buf
, rcs_rev_tmp
+ 1, sizeof(buf
));
3573 rcs_rev_tmp
= strchr(buf
, ' ');
3575 *rcs_rev_tmp
= '\0';
3578 fprintf(stderr
, "Bug: %s:%u (%s):%u: %s\n", file
, line
, buf
, saved_errno
, test
);
3582 loopback_setup_ipv4(void)
3584 struct ifaliasreq ifra
;
3588 memset(&ifr
, 0, sizeof(ifr
));
3589 strcpy(ifr
.ifr_name
, "lo0");
3591 if ((s
= socket(AF_INET
, SOCK_DGRAM
, 0)) == -1)
3594 if (assumes(ioctl(s
, SIOCGIFFLAGS
, &ifr
) != -1)) {
3595 ifr
.ifr_flags
|= IFF_UP
;
3596 (void)assumes(ioctl(s
, SIOCSIFFLAGS
, &ifr
) != -1);
3599 memset(&ifra
, 0, sizeof(ifra
));
3600 strcpy(ifra
.ifra_name
, "lo0");
3601 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_family
= AF_INET
;
3602 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
3603 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_len
= sizeof(struct sockaddr_in
);
3604 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_family
= AF_INET
;
3605 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_addr
.s_addr
= htonl(IN_CLASSA_NET
);
3606 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_len
= sizeof(struct sockaddr_in
);
3608 (void)assumes(ioctl(s
, SIOCAIFADDR
, &ifra
) != -1);
3610 (void)assumes(close(s
) == 0);
3614 loopback_setup_ipv6(void)
3616 struct in6_aliasreq ifra6
;
3620 memset(&ifr
, 0, sizeof(ifr
));
3621 strcpy(ifr
.ifr_name
, "lo0");
3623 if ((s6
= socket(AF_INET6
, SOCK_DGRAM
, 0)) == -1)
3626 memset(&ifr
, 0, sizeof(ifr
));
3627 strcpy(ifr
.ifr_name
, "lo0");
3629 if (assumes(ioctl(s6
, SIOCGIFFLAGS
, &ifr
) != -1)) {
3630 ifr
.ifr_flags
|= IFF_UP
;
3631 (void)assumes(ioctl(s6
, SIOCSIFFLAGS
, &ifr
) != -1);
3634 memset(&ifra6
, 0, sizeof(ifra6
));
3635 strcpy(ifra6
.ifra_name
, "lo0");
3637 ifra6
.ifra_addr
.sin6_family
= AF_INET6
;
3638 ifra6
.ifra_addr
.sin6_addr
= in6addr_loopback
;
3639 ifra6
.ifra_addr
.sin6_len
= sizeof(struct sockaddr_in6
);
3640 ifra6
.ifra_prefixmask
.sin6_family
= AF_INET6
;
3641 memset(&ifra6
.ifra_prefixmask
.sin6_addr
, 0xff, sizeof(struct in6_addr
));
3642 ifra6
.ifra_prefixmask
.sin6_len
= sizeof(struct sockaddr_in6
);
3643 ifra6
.ifra_lifetime
.ia6t_vltime
= ND6_INFINITE_LIFETIME
;
3644 ifra6
.ifra_lifetime
.ia6t_pltime
= ND6_INFINITE_LIFETIME
;
3646 if (ioctl(s6
, SIOCAIFADDR_IN6
, &ifra6
) == -1) {
3647 (void)assumes(errno
== EEXIST
);
3650 (void)assumes(close(s6
) == 0);
3654 fwexec(const char *const *argv
, int *wstatus
)
3659 /* We'd use posix_spawnp(), but we want to workaround: 6288899 */
3660 if ((p
= vfork()) == -1) {
3662 } else if (p
== 0) {
3663 execvp(argv
[0], (char *const *)argv
);
3664 _exit(EXIT_FAILURE
);
3667 if (waitpid(p
, wstatus
? wstatus
: &wstatus2
, 0) == -1) {
3673 } else if (WIFEXITED(wstatus2
) && WEXITSTATUS(wstatus2
) == EXIT_SUCCESS
) {
3681 do_potential_fsck(void)
3683 const char *safe_fsck_tool
[] = { "fsck", "-fy", NULL
};
3684 const char *fsck_tool
[] = { "fsck", "-q", NULL
};
3685 const char *remount_tool
[] = { "mount", "-uw", "/", NULL
};
3686 #if TARGET_OS_EMBEDDED
3687 const char *nvram_tool
[] = { "/usr/sbin/nvram", "auto-boot=false", NULL
};
3691 if (!assumes(statfs("/", &sfs
) != -1)) {
3695 if (!(sfs
.f_flags
& MNT_RDONLY
)) {
3699 if (!is_safeboot()) {
3701 /* We have disabled this block for now. We need to revisit this optimization after Leopard. */
3702 if (sfs
.f_flags
& MNT_JOURNALED
) {
3706 fprintf(stdout
, "Running fsck on the boot volume...\n");
3707 if (fwexec(fsck_tool
, NULL
) != -1) {
3712 fprintf(stdout
, "Running safe fsck on the boot volume...\n");
3713 if (fwexec(safe_fsck_tool
, NULL
) != -1) {
3717 fprintf(stdout
, "fsck failed!\n");
3719 /* someday, we should keep booting read-only, but as of today, other sub-systems cannot handle that scenario */
3720 #if TARGET_OS_EMBEDDED
3721 (void)assumes(fwexec(nvram_tool
, NULL
) != -1);
3722 (void)assumes(reboot(RB_AUTOBOOT
) != -1);
3724 (void)assumes(reboot(RB_HALT
) != -1);
3730 * Once this is fixed:
3732 * <rdar://problem/3948774> Mount flag updates should be possible with NULL as the forth argument to mount()
3734 * We can then do this one system call instead of calling out a full blown process.
3736 * assumes(mount(sfs.f_fstypename, "/", MNT_UPDATE, NULL) != -1);
3738 #if TARGET_OS_EMBEDDED
3739 if (path_check("/etc/fstab")) {
3740 const char *mount_tool
[] = { "mount", "-vat", "nonfs", NULL
};
3741 if (!assumes(fwexec(mount_tool
, NULL
) != -1)) {
3742 (void)assumes(fwexec(nvram_tool
, NULL
) != -1);
3743 (void)assumes(reboot(RB_AUTOBOOT
) != -1);
3748 (void)assumes(fwexec(remount_tool
, NULL
) != -1);
3751 fix_bogus_file_metadata();
3755 fix_bogus_file_metadata(void)
3757 static const struct {
3761 const mode_t needed_bits
;
3762 const mode_t bad_bits
;
3765 { "/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 },
3766 { _PATH_TMP
, 0, 0, S_ISTXT
|S_IRWXU
|S_IRWXG
|S_IRWXO
, S_ISUID
|S_ISGID
, true },
3767 { _PATH_VARTMP
, 0, 0, S_ISTXT
|S_IRWXU
|S_IRWXG
|S_IRWXO
, S_ISUID
|S_ISGID
, true },
3768 { "/var/folders", 0, 0, S_IRWXU
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
, S_ISUID
| S_ISGID
, true },
3769 { LAUNCHD_DB_PREFIX
, 0, 0, S_IRWXU
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
, S_IWGRP
| S_IWOTH
, true },
3770 { LAUNCHD_DB_PREFIX
"/com.apple.launchd", 0, 0, S_IRWXU
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
, S_IWGRP
| S_IWOTH
, true },
3771 // Fixing <rdar://problem/7571633>.
3772 { _PATH_VARDB
, 0, 0, S_IRWXU
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
, S_IWGRP
| S_IWOTH
| S_ISUID
| S_ISGID
, true },
3773 { _PATH_VARDB
"mds/", 0, 0, S_IRWXU
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
, S_IWGRP
| S_IWOTH
| S_ISUID
| S_ISGID
, true },
3774 #if !TARGET_OS_EMBEDDED
3775 // Similar fix for <rdar://problem/6550172>.
3776 { "/Library/StartupItems", 0, 0, S_IRWXU
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
, S_IWGRP
| S_IWOTH
| S_ISUID
| S_ISGID
, true },
3782 for (i
= 0; i
< (sizeof(f
) / sizeof(f
[0])); i
++) {
3783 mode_t i_needed_bits
;
3785 bool fix_mode
= false;
3786 bool fix_id
= false;
3788 if (!assumes(stat(f
[i
].path
, &sb
) != -1)) {
3789 fprintf(stdout
, "Crucial filesystem check: Path not present: %s. %s\n", f
[i
].path
, f
[i
].create
? "Will create." : "");
3791 if (!assumes(mkdir(f
[i
].path
, f
[i
].needed_bits
) != -1)) {
3793 } else if (!assumes(stat(f
[i
].path
, &sb
) != -1)) {
3801 i_needed_bits
= ~sb
.st_mode
& f
[i
].needed_bits
;
3802 i_bad_bits
= sb
.st_mode
& f
[i
].bad_bits
;
3805 fprintf(stderr
, "Crucial filesystem check: Removing bogus mode bits 0%o on path: %s\n", i_bad_bits
, f
[i
].path
);
3808 if (i_needed_bits
) {
3809 fprintf(stderr
, "Crucial filesystem check: Adding missing mode bits 0%o on path: %s\n", i_needed_bits
, f
[i
].path
);
3812 if (sb
.st_uid
!= f
[i
].owner
) {
3813 fprintf(stderr
, "Crucial filesystem check: Fixing bogus UID %u on path: %s\n", sb
.st_uid
, f
[i
].path
);
3816 if (sb
.st_gid
!= f
[i
].group
) {
3817 fprintf(stderr
, "Crucial filesystem check: Fixing bogus GID %u on path: %s\n", sb
.st_gid
, f
[i
].path
);
3822 (void)assumes(chmod(f
[i
].path
, (sb
.st_mode
& ~i_bad_bits
) | i_needed_bits
) != -1);
3825 (void)assumes(chown(f
[i
].path
, f
[i
].owner
, f
[i
].group
) != -1);
3832 path_check(const char *path
)
3836 if (stat(path
, &sb
) == 0)
3844 int sbmib
[] = { CTL_KERN
, KERN_SAFEBOOT
};
3846 size_t sbsz
= sizeof(sb
);
3848 if (!assumes(sysctl(sbmib
, 2, &sb
, &sbsz
, NULL
, 0) == 0))
3857 int nbmib
[] = { CTL_KERN
, KERN_NETBOOT
};
3859 size_t nbsz
= sizeof(nb
);
3861 if (!assumes(sysctl(nbmib
, 2, &nb
, &nbsz
, NULL
, 0) == 0))
3868 empty_dir(const char *thedir
, struct stat
*psb
)
3877 if (!assumes(lstat(thedir
, psb
) != -1)) {
3882 if (!assumes((currend_dir_fd
= open(".", 0)) != -1)) {
3886 if (!assumes(chdir(thedir
) != -1)) {
3890 if (!assumes(od
= opendir("."))) {
3894 while ((de
= readdir(od
))) {
3897 if (strcmp(de
->d_name
, ".") == 0) {
3901 if (strcmp(de
->d_name
, "..") == 0) {
3905 if (!assumes(lstat(de
->d_name
, &sb
) != -1)) {
3909 if (psb
->st_dev
!= sb
.st_dev
) {
3910 (void)assumes(unmount(de
->d_name
, MNT_FORCE
) != -1);
3912 /* Let's lstat() again to see if the unmount() worked and what was under it */
3913 if (!assumes(lstat(de
->d_name
, &sb
) != -1)) {
3917 if (!assumes(psb
->st_dev
== sb
.st_dev
)) {
3922 if (S_ISDIR(sb
.st_mode
)) {
3923 empty_dir(de
->d_name
, &sb
);
3926 (void)assumes(lchflags(de
->d_name
, 0) != -1);
3927 (void)assumes(remove(de
->d_name
) != -1);
3930 (void)assumes(closedir(od
) != -1);
3933 (void)assumes(fchdir(currend_dir_fd
) != -1);
3934 (void)assumes(close(currend_dir_fd
) != -1);
3938 touch_file(const char *path
, mode_t m
)
3940 int fd
= open(path
, O_CREAT
, m
);
3949 apply_sysctls_from_file(const char *thefile
)
3951 const char *sysctl_tool
[] = { "sysctl", "-w", NULL
, NULL
};
3956 if (!(sf
= fopen(thefile
, "r")))
3959 while ((val
= fgetln(sf
, &ln_len
))) {
3963 if (!assumes((tmpstr
= malloc(ln_len
+ 1)) != NULL
)) {
3966 memcpy(tmpstr
, val
, ln_len
);
3970 if (val
[ln_len
- 1] == '\n' || val
[ln_len
- 1] == '\r') {
3971 val
[ln_len
- 1] = '\0';
3974 while (*val
&& isspace(*val
))
3976 if (*val
== '\0' || *val
== '#') {
3977 goto skip_sysctl_tool
;
3979 sysctl_tool
[2] = val
;
3980 (void)assumes(fwexec(sysctl_tool
, NULL
) != -1);
3985 (void)assumes(fclose(sf
) == 0);
3989 copySystemBuildVersion(void)
3991 CFStringRef build
= NULL
;
3992 const char path
[] = "/System/Library/CoreServices/SystemVersion.plist";
3993 CFURLRef plistURL
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault
, (const uint8_t *)path
, sizeof(path
) - 1, false);
3995 CFPropertyListRef plist
= NULL
;
3996 if (plistURL
&& (plist
= CFPropertyListCreateFromFile(plistURL
))) {
3997 if (CFTypeCheck(plist
, CFDictionary
)) {
3998 build
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)plist
, _kCFSystemVersionBuildVersionKey
);
3999 if (build
&& CFTypeCheck(build
, CFString
)) {
4002 build
= CFSTR("99Z999");
4008 build
= CFSTR("99Z999");
4012 CFRelease(plistURL
);
4019 do_sysversion_sysctl(void)
4021 int mib
[] = { CTL_KERN
, KERN_OSVERSION
};
4022 CFStringRef buildvers
;
4024 size_t bufsz
= sizeof(buf
);
4026 /* <rdar://problem/4477682> ER: launchd should set kern.osversion very early in boot */
4028 if (sysctl(mib
, 2, buf
, &bufsz
, NULL
, 0) == -1) {
4029 fprintf(stderr
, "sysctl(): %s\n", strerror(errno
));
4033 if (buf
[0] != '\0') {
4037 buildvers
= copySystemBuildVersion();
4038 if (assumes(buildvers
)) {
4039 CFStringGetCString(buildvers
, buf
, sizeof(buf
), kCFStringEncodingUTF8
);
4040 (void)assumes(sysctl(mib
, 2, NULL
, 0, buf
, strlen(buf
) + 1) != -1);
4043 CFRelease(buildvers
);
4047 do_application_firewall_magic(int sfd
, launch_data_t thejob
)
4049 const char *prog
= NULL
, *partialprog
= NULL
;
4050 char *path
, *pathtmp
, **pathstmp
;
4056 * <rdar://problem/4684434> setsockopt() with the executable path as the argument
4059 if ((tmp
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_PROGRAM
))) {
4060 prog
= launch_data_get_string(tmp
);
4064 if ((tmp
= launch_data_dict_lookup(thejob
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
))) {
4065 if ((tmp
= launch_data_array_get_index(tmp
, 0))) {
4066 if (assumes((partialprog
= launch_data_get_string(tmp
)) != NULL
)) {
4067 if (partialprog
[0] == '/') {
4076 pathtmp
= path
= strdup(getenv("PATH"));
4080 while ((*pathstmp
= strsep(&pathtmp
, ":"))) {
4081 if (**pathstmp
!= '\0') {
4087 pathtmp
= alloca(MAXPATHLEN
);
4091 for (; *pathstmp
; pathstmp
++) {
4092 snprintf(pathtmp
, MAXPATHLEN
, "%s/%s", *pathstmp
, partialprog
);
4093 if (path_check(pathtmp
)) {
4100 if (assumes(prog
!= NULL
)) {
4101 /* The networking team has asked us to ignore the failure of this API if errno == ENOPROTOOPT */
4102 (void)assumes(setsockopt(sfd
, SOL_SOCKET
, SO_EXECPATH
, prog
, (socklen_t
)(strlen(prog
) + 1)) != -1 || errno
== ENOPROTOOPT
);
4108 preheat_page_cache_hack(void)
4113 /* Disable this hack for now */
4116 if ((thedir
= opendir("/etc/preheat_at_boot")) == NULL
) {
4120 while ((de
= readdir(thedir
))) {
4125 if (de
->d_name
[0] == '.') {
4129 if ((fd
= open(de
->d_name
, O_RDONLY
)) == -1) {
4133 if (fstat(fd
, &sb
) != -1) {
4134 if ((sb
.st_size
< 10*1024*1024) && (junkbuf
= malloc((size_t)sb
.st_size
)) != NULL
) {
4135 (void)assumes(read(fd
, junkbuf
, (size_t)sb
.st_size
) == (ssize_t
)sb
.st_size
);
4148 do_bootroot_magic(void)
4150 const char *kextcache_tool
[] = { "kextcache", "-U", "/", NULL
};
4151 CFTypeRef bootrootProp
;
4152 io_service_t chosen
;
4156 chosen
= IORegistryEntryFromPath(kIOMasterPortDefault
, "IODeviceTree:/chosen");
4158 if (!assumes(chosen
)) {
4162 bootrootProp
= IORegistryEntryCreateCFProperty(chosen
, CFSTR(kBootRootActiveKey
), kCFAllocatorDefault
, 0);
4164 IOObjectRelease(chosen
);
4166 if (!bootrootProp
) {
4170 CFRelease(bootrootProp
);
4172 if (!assumes((p
= fwexec(kextcache_tool
, &wstatus
)) != -1)) {
4176 if (WIFEXITED(wstatus
) && WEXITSTATUS(wstatus
) == EX_OSFILE
) {
4177 (void)assumes(reboot(RB_AUTOBOOT
) != -1);
4186 if (stat("/AppleInternal", &sb
) == 0 && stat("/var/db/disableAppleInternal", &sb
) == -1) {
4187 do_apple_internal_magic
= true;
4191 size_t len
= sizeof(bootargs
);
4192 int r
= sysctlbyname("kern.bootargs", bootargs
, &len
, NULL
, 0);
4193 if (r
== 0 && (strnstr(bootargs
, "-v", len
) != NULL
|| strnstr(bootargs
, "-s", len
))) {
4194 g_verbose_boot
= true;
4197 if (stat("/var/db/.launchd_shutdown_debugging", &sb
) == 0 && g_verbose_boot
) {
4198 g_startup_debugging
= true;