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: 23925 $";
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>
47 #include <sys/socket.h>
48 #include <sys/syscall.h>
50 #include <netinet/in.h>
51 #include <netinet/in_var.h>
52 #include <netinet6/nd6.h>
76 #include <bsm/auditd_lib.h>
79 #include "bootstrap.h"
81 #include "vproc_priv.h"
82 #include "vproc_internal.h"
84 #include "launch_internal.h"
86 #include "launchd_runtime.h"
87 #include "launchd_core_logic.h"
88 #include "launchd_unix_ipc.h"
90 #define LAUNCHD_CONF ".launchd.conf"
91 #define SECURITY_LIB "/System/Library/Frameworks/Security.framework/Versions/A/Security"
93 extern char **environ
;
95 static void pfsystem_callback(void *, struct kevent
*);
97 static kq_callback kqpfsystem_callback
= pfsystem_callback
;
99 static void pid1_magic_init(void);
101 static void testfd_or_openfd(int fd
, const char *path
, int flags
);
102 static bool get_network_state(void);
103 static void monitor_networking_state(void);
104 static void fatal_signal_handler(int sig
, siginfo_t
*si
, void *uap
);
105 static void handle_pid1_crashes_separately(void);
106 static void do_pid1_crash_diagnosis_mode(void);
107 static int basic_fork(void);
108 static bool do_pid1_crash_diagnosis_mode2(void);
110 static void *update_thread(void *nothing
);
112 static bool re_exec_in_single_user_mode
;
113 static void *crash_addr
;
114 static pid_t crash_pid
;
116 bool shutdown_in_progress
;
117 bool fake_shutdown_in_progress
;
119 char g_username
[128] = "__Uninitialized__";
120 char g_my_label
[128] = "__Uninitialized__";
121 char g_launchd_database_dir
[PATH_MAX
];
122 FILE *g_console
= NULL
;
123 int32_t g_sync_frequency
= 30;
126 main(int argc
, char *const *argv
)
131 testfd_or_openfd(STDIN_FILENO
, _PATH_DEVNULL
, O_RDONLY
);
132 testfd_or_openfd(STDOUT_FILENO
, _PATH_DEVNULL
, O_WRONLY
);
133 testfd_or_openfd(STDERR_FILENO
, _PATH_DEVNULL
, O_WRONLY
);
135 if (pid1_magic
&& g_use_gmalloc
) {
136 if (!getenv("DYLD_INSERT_LIBRARIES")) {
137 setenv("DYLD_INSERT_LIBRARIES", "/usr/lib/libgmalloc.dylib", 1);
138 setenv("MALLOC_STRICT_SIZE", "1", 1);
139 execv(argv
[0], argv
);
141 unsetenv("DYLD_INSERT_LIBRARIES");
142 unsetenv("MALLOC_STRICT_SIZE");
146 while ((ch
= getopt(argc
, argv
, "s")) != -1) {
148 case 's': sflag
= true; break; /* single user */
149 case '?': /* we should do something with the global optopt variable here */
151 fprintf(stderr
, "%s: ignoring unknown arguments\n", getprogname());
156 if (getpid() != 1 && getppid() != 1) {
157 fprintf(stderr
, "%s: This program is not meant to be run directly.\n", getprogname());
161 launchd_runtime_init();
165 if( launchd_assumes((cfd
= open(_PATH_CONSOLE
, O_WRONLY
| O_NOCTTY
)) != -1) ) {
167 if( !launchd_assumes((g_console
= fdopen(cfd
, "w")) != NULL
) ) {
173 if (NULL
== getenv("PATH")) {
174 setenv("PATH", _PATH_STDPATH
, 1);
184 struct passwd
*pwent
= getpwuid(getuid());
186 strlcpy(g_username
, pwent
->pw_name
, sizeof(g_username
) - 1);
189 snprintf(g_my_label
, sizeof(g_my_label
), "com.apple.launchd.peruser.%u", getuid());
191 auditinfo_addr_t auinfo
;
192 if( launchd_assumes(getaudit_addr(&auinfo
, sizeof(auinfo
)) != -1) ) {
193 g_audit_session
= auinfo
.ai_asid
;
194 runtime_syslog(LOG_DEBUG
, "Our audit session ID is %i", g_audit_session
);
197 g_audit_session_port
= _audit_session_self();
198 snprintf(g_launchd_database_dir
, sizeof(g_launchd_database_dir
), LAUNCHD_DB_PREFIX
"/com.apple.launchd.peruser.%u", getuid());
199 runtime_syslog(LOG_DEBUG
, "Per-user launchd for UID %u (%s) has begun.", getuid(), g_username
);
203 runtime_syslog(LOG_NOTICE
| LOG_CONSOLE
, "*** launchd[1] has started up. ***");
204 if( g_use_gmalloc
) {
205 runtime_syslog(LOG_NOTICE
| LOG_CONSOLE
, "*** Using libgmalloc ***");
208 if( g_verbose_boot
) {
209 runtime_syslog(LOG_NOTICE
| LOG_CONSOLE
, "*** Verbose boot, will log to /dev/console. ***");
212 if( g_shutdown_debugging
) {
213 runtime_syslog(LOG_NOTICE
| LOG_CONSOLE
, "*** Shutdown debugging is enabled. ***");
216 /* PID 1 doesn't have a flat namespace. */
217 g_flat_mach_namespace
= false;
220 monitor_networking_state();
223 handle_pid1_crashes_separately();
225 #if !TARGET_OS_EMBEDDED
226 /* prime shared memory before the 'bootstrap_port' global is set to zero */
227 _vproc_transaction_begin();
228 _vproc_transaction_end();
233 /* Start the update thread -- rdar://problem/5039559&6153301 */
235 int err
= pthread_create(&t
, NULL
, update_thread
, NULL
);
236 launchd_assumes(err
== 0);
237 launchd_assumes(pthread_detach(t
) == 0);
242 launchd_runtime_init2();
248 handle_pid1_crashes_separately(void)
250 struct sigaction fsa
;
252 fsa
.sa_sigaction
= fatal_signal_handler
;
253 fsa
.sa_flags
= SA_SIGINFO
;
254 sigemptyset(&fsa
.sa_mask
);
256 launchd_assumes(sigaction(SIGILL
, &fsa
, NULL
) != -1);
257 launchd_assumes(sigaction(SIGFPE
, &fsa
, NULL
) != -1);
258 launchd_assumes(sigaction(SIGBUS
, &fsa
, NULL
) != -1);
259 launchd_assumes(sigaction(SIGSEGV
, &fsa
, NULL
) != -1);
262 void *update_thread(void *nothing
__attribute__((unused
)))
264 while( g_sync_frequency
) {
266 sleep(g_sync_frequency
);
269 runtime_syslog(LOG_DEBUG
, "Update thread exiting.");
273 #define PID1_CRASH_LOGFILE "/var/log/launchd-pid1.crash"
275 /* This hack forces the dynamic linker to resolve these symbols ASAP */
276 static __attribute__((unused
)) typeof(sync
) *__junk_dyld_trick1
= sync
;
277 static __attribute__((unused
)) typeof(sleep
) *__junk_dyld_trick2
= sleep
;
278 static __attribute__((unused
)) typeof(reboot
) *__junk_dyld_trick3
= reboot
;
281 do_pid1_crash_diagnosis_mode(void)
284 kill(g_wsp
, SIGKILL
);
289 while( g_shutdown_debugging
&& !do_pid1_crash_diagnosis_mode2() ) {
300 switch ((p
= fork())) {
302 runtime_syslog(LOG_ERR
| LOG_CONSOLE
, "Can't fork PID 1 copy for crash debugging: %m");
308 /* If we attach with the debugger, the kernel reparenting could
309 * cause this to return prematurely.
311 waitpid(p
, &wstatus
, 0);
315 if (WIFEXITED(wstatus
)) {
316 if (WEXITSTATUS(wstatus
) == EXIT_SUCCESS
) {
319 fprintf(stdout
, "PID 1 copy: exit status: %d\n", WEXITSTATUS(wstatus
));
322 fprintf(stdout
, "PID 1 copy: %s\n", strsignal(WTERMSIG(wstatus
)));
331 do_pid1_crash_diagnosis_mode2(void)
333 if( basic_fork() == 0 ) {
334 /* Neuter our bootstrap port so that the shell doesn't try talking to us while
335 * we're blocked waiting on it.
340 task_set_bootstrap_port(mach_task_self(), MACH_PORT_NULL
);
341 if( basic_fork() != 0 ) {
352 revoke(_PATH_CONSOLE
);
353 if ((fd
= open(_PATH_CONSOLE
, O_RDWR
)) == -1) {
356 if (login_tty(fd
) == -1) {
359 setenv("TERM", "vt100", 1);
360 fprintf(stdout
, "\n");
361 fprintf(stdout
, "Entering launchd PID 1 debugging mode...\n");
362 fprintf(stdout
, "The PID 1 launchd has crashed. It has fork(2)ed itself for debugging.\n");
363 fprintf(stdout
, "To debug the main thread of PID 1:\n");
364 fprintf(stdout
, " gdb attach %d\n", getppid());
365 fprintf(stdout
, "To exit this shell and shut down:\n");
366 fprintf(stdout
, " kill -9 1\n");
367 fprintf(stdout
, "A sample of PID 1 has been written to %s\n", PID1_CRASH_LOGFILE
);
368 fprintf(stdout
, "\n");
371 execl(_PATH_BSHELL
, "-sh", NULL
);
372 syslog(LOG_ERR
, "can't exec %s for PID 1 crash debugging: %m", _PATH_BSHELL
);
377 fatal_signal_handler(int sig
, siginfo_t
*si
, void *uap
__attribute__((unused
)))
379 const char *doom_why
= "at instruction";
380 char *sample_args
[] = { "/usr/bin/sample", "1", "1", "-file", PID1_CRASH_LOGFILE
, NULL
};
384 crash_addr
= si
->si_addr
;
385 crash_pid
= si
->si_pid
;
387 unlink(PID1_CRASH_LOGFILE
);
389 switch ((sample_p
= vfork())) {
391 execve(sample_args
[0], sample_args
, environ
);
395 waitpid(sample_p
, &wstatus
, 0);
401 do_pid1_crash_diagnosis_mode();
409 doom_why
= "trying to read/write";
412 runtime_syslog(LOG_EMERG
, "We crashed %s: %p (sent by PID %u)", doom_why
, crash_addr
, crash_pid
);
421 pid1_magic_init(void)
423 launchd_assumes(setsid() != -1);
424 launchd_assumes(chdir("/") != -1);
425 launchd_assumes(setlogin("root") != -1);
427 strcpy(g_my_label
, "com.apple.launchd");
429 #if !TARGET_OS_EMBEDDED
430 auditinfo_addr_t auinfo
= {
431 .ai_termid
= { .at_type
= AU_IPv4
},
432 .ai_asid
= AU_ASSIGN_ASID
,
433 .ai_auid
= AU_DEFAUDITID
,
434 .ai_flags
= sessionIsRoot
,
437 if( !launchd_assumes(setaudit_addr(&auinfo
, sizeof(auinfo
)) != -1) ) {
438 runtime_syslog(LOG_WARNING
| LOG_CONSOLE
, "Could not set audit session: %s.", strerror(errno
));
442 if( launchd_assumes(getaudit_addr(&auinfo
, sizeof(auinfo
)) != -1) ) {
443 g_audit_session
= auinfo
.ai_asid
;
444 runtime_syslog(LOG_DEBUG
, "Our audit session ID is %i", g_audit_session
);
447 g_audit_session_port
= _audit_session_self();
450 strcpy(g_launchd_database_dir
, LAUNCHD_DB_PREFIX
"/com.apple.launchd");
454 launchd_data_base_path(int db_type
)
456 static char result
[PATH_MAX
];
457 static int last_db_type
= -1;
459 if( db_type
== last_db_type
) {
464 case LAUNCHD_DB_TYPE_OVERRIDES
:
465 snprintf(result
, sizeof(result
), "%s/%s", g_launchd_database_dir
, "overrides.plist");
466 last_db_type
= db_type
;
468 case LAUNCHD_DB_TYPE_JOBCACHE
:
469 snprintf(result
, sizeof(result
), "%s/%s", g_launchd_database_dir
, "jobcache.launchdata");
470 last_db_type
= db_type
;
483 launchd_assumes(fcntl(fd
, F_SETFD
, 1) != -1);
489 launchd_shutdown(void)
493 if (shutdown_in_progress
) {
497 runtime_ktrace0(RTKT_LAUNCHD_EXITING
);
499 shutdown_in_progress
= true;
501 if( pid1_magic
|| g_log_per_user_shutdown
) {
503 * When this changes to a more sustainable API, update this:
504 * http://howto.apple.com/db.cgi?Debugging_Apps_Non-Responsive_At_Shutdown
506 runtime_setlogmask(LOG_UPTO(LOG_DEBUG
));
511 now
= runtime_get_wall_time();
513 char *term_who
= pid1_magic
? "System shutdown" : "Per-user launchd termination for ";
514 runtime_syslog(LOG_INFO
, "%s%s began", term_who
, pid1_magic
? "" : g_username
);
516 launchd_assert(jobmgr_shutdown(root_jobmgr
) != NULL
);
520 launchd_assumes(audit_quick_stop() == 0);
526 launchd_single_user(void)
528 runtime_syslog(LOG_NOTICE
, "Going to single-user mode");
530 re_exec_in_single_user_mode
= true;
536 runtime_kill(-1, SIGKILL
);
540 launchd_SessionCreate(void)
543 OSStatus (*sescr
)(SessionCreationFlags flags
, SessionAttributeBits attributes
);
546 if (launchd_assumes((seclib
= dlopen(SECURITY_LIB
, RTLD_LAZY
)) != NULL
)) {
547 if (launchd_assumes((sescr
= dlsym(seclib
, "SessionCreate")) != NULL
)) {
548 launchd_assumes(sescr(0, 0) == noErr
);
550 launchd_assumes(dlclose(seclib
) != -1);
556 testfd_or_openfd(int fd
, const char *path
, int flags
)
560 if (-1 != (tmpfd
= dup(fd
))) {
561 launchd_assumes(runtime_close(tmpfd
) == 0);
563 if (-1 == (tmpfd
= open(path
, flags
| O_NOCTTY
, DEFFILEMODE
))) {
564 runtime_syslog(LOG_ERR
, "open(\"%s\", ...): %m", path
);
565 } else if (tmpfd
!= fd
) {
566 launchd_assumes(dup2(tmpfd
, fd
) != -1);
567 launchd_assumes(runtime_close(tmpfd
) == 0);
573 get_network_state(void)
575 struct ifaddrs
*ifa
, *ifai
;
579 /* Workaround 4978696: getifaddrs() reports false ENOMEM */
580 while ((r
= getifaddrs(&ifa
)) == -1 && errno
== ENOMEM
) {
581 runtime_syslog(LOG_DEBUG
, "Worked around bug: 4978696");
582 launchd_assumes(sched_yield() != -1);
585 if (!launchd_assumes(r
!= -1)) {
589 for (ifai
= ifa
; ifai
; ifai
= ifai
->ifa_next
) {
590 if (!(ifai
->ifa_flags
& IFF_UP
)) {
593 if (ifai
->ifa_flags
& IFF_LOOPBACK
) {
596 if (ifai
->ifa_addr
->sa_family
!= AF_INET
&& ifai
->ifa_addr
->sa_family
!= AF_INET6
) {
609 monitor_networking_state(void)
611 int pfs
= _fd(socket(PF_SYSTEM
, SOCK_RAW
, SYSPROTO_EVENT
));
612 struct kev_request kev_req
;
614 network_up
= get_network_state();
616 if (!launchd_assumes(pfs
!= -1)) {
620 memset(&kev_req
, 0, sizeof(kev_req
));
621 kev_req
.vendor_code
= KEV_VENDOR_APPLE
;
622 kev_req
.kev_class
= KEV_NETWORK_CLASS
;
624 if (!launchd_assumes(ioctl(pfs
, SIOCSKEVFILT
, &kev_req
) != -1)) {
629 launchd_assumes(kevent_mod(pfs
, EVFILT_READ
, EV_ADD
, 0, 0, &kqpfsystem_callback
) != -1);
633 pfsystem_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
)
635 bool new_networking_state
;
638 launchd_assumes(read((int)kev
->ident
, &buf
, sizeof(buf
)) != -1);
640 new_networking_state
= get_network_state();
642 if (new_networking_state
!= network_up
) {
643 network_up
= new_networking_state
;
644 jobmgr_dispatch_all_semaphores(root_jobmgr
);
649 _log_launchd_bug(const char *rcs_rev
, const char *path
, unsigned int line
, const char *test
)
651 int saved_errno
= errno
;
653 const char *file
= strrchr(path
, '/');
654 char *rcs_rev_tmp
= strchr(rcs_rev
, ' ');
656 runtime_ktrace1(RTKT_LAUNCHD_BUG
);
665 strlcpy(buf
, rcs_rev
, sizeof(buf
));
667 strlcpy(buf
, rcs_rev_tmp
+ 1, sizeof(buf
));
668 rcs_rev_tmp
= strchr(buf
, ' ');
674 runtime_syslog(LOG_NOTICE
, "Bug: %s:%u (%s):%u: %s", file
, line
, buf
, saved_errno
, test
);