2 * Copyright (c) 2005 Apple Computer, 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: 23506 $";
27 #include <Security/Authorization.h>
28 #include <Security/AuthorizationTags.h>
29 #include <Security/AuthSession.h>
31 #include <sys/types.h>
32 #include <sys/queue.h>
33 #include <sys/event.h>
35 #include <sys/ucred.h>
36 #include <sys/fcntl.h>
39 #include <sys/sysctl.h>
40 #include <sys/sockio.h>
42 #include <sys/resource.h>
43 #include <sys/ioctl.h>
44 #include <sys/mount.h>
45 #include <sys/kern_event.h>
46 #include <sys/reboot.h>
48 #include <netinet/in.h>
49 #include <netinet/in_var.h>
50 #include <netinet6/nd6.h>
71 #include "libbootstrap_public.h"
72 #include "libvproc_public.h"
73 #include "libvproc_internal.h"
74 #include "liblaunch_public.h"
76 #include "launchd_runtime.h"
77 #include "launchd_core_logic.h"
78 #include "launchd_unix_ipc.h"
80 #define LAUNCHD_CONF ".launchd.conf"
81 #define SECURITY_LIB "/System/Library/Frameworks/Security.framework/Versions/A/Security"
82 #define SHUTDOWN_LOG_DIR "/var/log/shutdown"
85 extern char **environ
;
87 static void pfsystem_callback(void *, struct kevent
*);
89 static kq_callback kqpfsystem_callback
= pfsystem_callback
;
91 static void pid1_magic_init(void);
93 static void testfd_or_openfd(int fd
, const char *path
, int flags
);
94 static bool get_network_state(void);
95 static void monitor_networking_state(void);
96 static void fatal_signal_handler(int sig
, siginfo_t
*si
, void *uap
);
97 static void handle_pid1_crashes_separately(void);
98 static void prep_shutdown_log_dir(void);
100 static bool re_exec_in_single_user_mode
= false;
101 static void *crash_addr
;
102 static pid_t crash_pid
;
104 static bool shutdown_in_progress
= false;
105 bool debug_shutdown_hangs
= false;
106 bool network_up
= false;
109 main(int argc
, char *const *argv
)
114 testfd_or_openfd(STDIN_FILENO
, _PATH_DEVNULL
, O_RDONLY
);
115 testfd_or_openfd(STDOUT_FILENO
, _PATH_DEVNULL
, O_WRONLY
);
116 testfd_or_openfd(STDERR_FILENO
, _PATH_DEVNULL
, O_WRONLY
);
118 while ((ch
= getopt(argc
, argv
, "s")) != -1) {
120 case 's': sflag
= true; break; /* single user */
121 case '?': /* we should do something with the global optopt variable here */
123 fprintf(stderr
, "%s: ignoring unknown arguments\n", getprogname());
128 if (getpid() != 1 && getppid() != 1) {
129 fprintf(stderr
, "%s: This program is not meant to be run directly.\n", getprogname());
133 launchd_runtime_init();
135 if (NULL
== getenv("PATH")) {
136 setenv("PATH", _PATH_STDPATH
, 1);
145 monitor_networking_state();
148 handle_pid1_crashes_separately();
153 launchd_runtime_init2();
159 handle_pid1_crashes_separately(void)
161 struct sigaction fsa
;
163 fsa
.sa_sigaction
= fatal_signal_handler
;
164 fsa
.sa_flags
= SA_SIGINFO
;
165 sigemptyset(&fsa
.sa_mask
);
167 launchd_assumes(sigaction(SIGILL
, &fsa
, NULL
) != -1);
168 launchd_assumes(sigaction(SIGFPE
, &fsa
, NULL
) != -1);
169 launchd_assumes(sigaction(SIGBUS
, &fsa
, NULL
) != -1);
170 launchd_assumes(sigaction(SIGSEGV
, &fsa
, NULL
) != -1);
173 #define PID1_CRASH_LOGFILE "/var/log/launchd-pid1.crash"
175 /* This hack forces the dynamic linker to resolve these symbols ASAP */
176 static __attribute__((unused
)) typeof(sync
) *__junk_dyld_trick1
= sync
;
177 static __attribute__((unused
)) typeof(sleep
) *__junk_dyld_trick2
= sleep
;
178 static __attribute__((unused
)) typeof(reboot
) *__junk_dyld_trick3
= reboot
;
181 fatal_signal_handler(int sig
, siginfo_t
*si
, void *uap
)
183 const char *doom_why
= "at instruction";
184 char *sample_args
[] = { "/usr/bin/sample", "1", "1", "-file", PID1_CRASH_LOGFILE
, NULL
};
188 crash_addr
= si
->si_addr
;
189 crash_pid
= si
->si_pid
;
191 unlink(PID1_CRASH_LOGFILE
);
193 switch ((sample_p
= vfork())) {
195 execve(sample_args
[0], sample_args
, environ
);
199 waitpid(sample_p
, &wstatus
, 0);
211 doom_why
= "trying to read/write";
214 runtime_syslog(LOG_EMERG
, "We crashed %s: %p (sent by PID %u)", doom_why
, crash_addr
, crash_pid
);
223 pid1_magic_init(void)
225 launchd_assumes(setsid() != -1);
226 launchd_assumes(chdir("/") != -1);
227 launchd_assumes(setlogin("root") != -1);
228 launchd_assumes(mount("fdesc", "/dev", MNT_UNION
, NULL
) != -1);
236 launchd_assumes(fcntl(fd
, F_SETFD
, 1) != -1);
242 prep_shutdown_log_dir(void)
244 launchd_assumes(mkdir(SHUTDOWN_LOG_DIR
, S_IRWXU
) != -1 || errno
== EEXIST
);
248 launchd_shutdown(void)
252 if (shutdown_in_progress
) {
256 shutdown_in_progress
= true;
258 if (getpid() == 1 && stat("/var/db/debugShutdownHangs", &sb
) != -1) {
260 * When this changes to a more sustainable API, update this:
261 * http://howto.apple.com/db.cgi?Debugging_Apps_Non-Responsive_At_Shutdown
263 runtime_setlogmask(LOG_UPTO(LOG_DEBUG
));
264 prep_shutdown_log_dir();
265 debug_shutdown_hangs
= true;
268 launchd_assert(jobmgr_shutdown(root_jobmgr
) != NULL
);
272 launchd_single_user(void)
274 runtime_syslog(LOG_NOTICE
, "Going to single-user mode");
276 re_exec_in_single_user_mode
= true;
282 runtime_kill(-1, SIGKILL
);
286 launchd_SessionCreate(void)
289 OSStatus (*sescr
)(SessionCreationFlags flags
, SessionAttributeBits attributes
);
292 if (launchd_assumes((seclib
= dlopen(SECURITY_LIB
, RTLD_LAZY
)) != NULL
)) {
293 if (launchd_assumes((sescr
= dlsym(seclib
, "SessionCreate")) != NULL
)) {
294 launchd_assumes(sescr(0, 0) == noErr
);
296 launchd_assumes(dlclose(seclib
) != -1);
302 testfd_or_openfd(int fd
, const char *path
, int flags
)
306 if (-1 != (tmpfd
= dup(fd
))) {
307 launchd_assumes(runtime_close(tmpfd
) == 0);
309 if (-1 == (tmpfd
= open(path
, flags
| O_NOCTTY
, DEFFILEMODE
))) {
310 runtime_syslog(LOG_ERR
, "open(\"%s\", ...): %m", path
);
311 } else if (tmpfd
!= fd
) {
312 launchd_assumes(dup2(tmpfd
, fd
) != -1);
313 launchd_assumes(runtime_close(tmpfd
) == 0);
319 get_network_state(void)
321 struct ifaddrs
*ifa
, *ifai
;
325 /* Workaround 4978696: getifaddrs() reports false ENOMEM */
326 while ((r
= getifaddrs(&ifa
)) == -1 && errno
== ENOMEM
) {
327 runtime_syslog(LOG_DEBUG
, "Worked around bug: 4978696");
328 launchd_assumes(sched_yield() != -1);
331 if (!launchd_assumes(r
!= -1)) {
335 for (ifai
= ifa
; ifai
; ifai
= ifai
->ifa_next
) {
336 if (!(ifai
->ifa_flags
& IFF_UP
)) {
339 if (ifai
->ifa_flags
& IFF_LOOPBACK
) {
342 if (ifai
->ifa_addr
->sa_family
!= AF_INET
&& ifai
->ifa_addr
->sa_family
!= AF_INET6
) {
355 monitor_networking_state(void)
357 int pfs
= _fd(socket(PF_SYSTEM
, SOCK_RAW
, SYSPROTO_EVENT
));
358 struct kev_request kev_req
;
360 network_up
= get_network_state();
362 if (!launchd_assumes(pfs
!= -1)) {
366 memset(&kev_req
, 0, sizeof(kev_req
));
367 kev_req
.vendor_code
= KEV_VENDOR_APPLE
;
368 kev_req
.kev_class
= KEV_NETWORK_CLASS
;
370 if (!launchd_assumes(ioctl(pfs
, SIOCSKEVFILT
, &kev_req
) != -1)) {
375 launchd_assumes(kevent_mod(pfs
, EVFILT_READ
, EV_ADD
, 0, 0, &kqpfsystem_callback
) != -1);
379 pfsystem_callback(void *obj
, struct kevent
*kev
)
381 bool new_networking_state
;
384 launchd_assumes(read(kev
->ident
, &buf
, sizeof(buf
)) != -1);
386 new_networking_state
= get_network_state();
388 if (new_networking_state
!= network_up
) {
389 network_up
= new_networking_state
;
390 jobmgr_dispatch_all_semaphores(root_jobmgr
);
395 _log_launchd_bug(const char *rcs_rev
, const char *path
, unsigned int line
, const char *test
)
397 int saved_errno
= errno
;
399 const char *file
= strrchr(path
, '/');
400 char *rcs_rev_tmp
= strchr(rcs_rev
, ' ');
409 strlcpy(buf
, rcs_rev
, sizeof(buf
));
411 strlcpy(buf
, rcs_rev_tmp
+ 1, sizeof(buf
));
412 rcs_rev_tmp
= strchr(buf
, ' ');
418 runtime_syslog(LOG_NOTICE
, "Bug: %s:%u (%s):%u: %s", file
, line
, buf
, saved_errno
, test
);