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@
24 #include <sys/types.h>
25 #include <sys/queue.h>
26 #include <sys/event.h>
28 #include <sys/ucred.h>
29 #include <sys/fcntl.h>
32 #include <sys/sysctl.h>
33 #include <sys/sockio.h>
35 #include <sys/resource.h>
36 #include <sys/ioctl.h>
37 #include <sys/mount.h>
38 #include <sys/kern_event.h>
39 #include <sys/reboot.h>
40 #include <sys/socket.h>
41 #include <sys/syscall.h>
43 #include <netinet/in.h>
44 #include <netinet/in_var.h>
45 #include <netinet6/nd6.h>
67 #include <os/assumes.h>
70 #include <bsm/auditd_lib.h>
71 #include <bsm/audit_session.h>
74 #include "bootstrap.h"
76 #include "vproc_priv.h"
77 #include "vproc_internal.h"
79 #include "launch_internal.h"
85 #define LAUNCHD_CONF ".launchd.conf"
87 extern char **environ
;
89 static void pfsystem_callback(void *, struct kevent
*);
91 static kq_callback kqpfsystem_callback
= pfsystem_callback
;
93 static void pid1_magic_init(void);
95 static void testfd_or_openfd(int fd
, const char *path
, int flags
);
96 static bool get_network_state(void);
97 static void monitor_networking_state(void);
98 static void fatal_signal_handler(int sig
, siginfo_t
*si
, void *uap
);
99 static void handle_pid1_crashes_separately(void);
100 static void do_pid1_crash_diagnosis_mode(const char *msg
);
101 static int basic_fork(void);
102 static bool do_pid1_crash_diagnosis_mode2(const char *msg
);
104 static void *update_thread(void *nothing
);
106 static void *crash_addr
;
107 static pid_t crash_pid
;
109 char *_launchd_database_dir
;
110 char *_launchd_log_dir
;
112 bool launchd_shutting_down
;
115 FILE *launchd_console
= NULL
;
116 int32_t launchd_sync_frequency
= 30;
119 main(int argc
, char *const *argv
)
124 /* This needs to be cleaned up. Currently, we risk tripping assumes() macros
125 * before we've properly set things like launchd's log database paths, the
126 * global launchd label for syslog messages and the like. Luckily, these are
127 * operations that will probably never fail, like test_of_openfd(), the
128 * stuff in launchd_runtime_init() and the stuff in
129 * handle_pid1_crashes_separately().
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 (launchd_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");
144 } else if (launchd_malloc_log_stacks
) {
145 if (!getenv("MallocStackLogging")) {
146 setenv("MallocStackLogging", "1", 1);
147 execv(argv
[0], argv
);
149 unsetenv("MallocStackLogging");
153 while ((ch
= getopt(argc
, argv
, "s")) != -1) {
155 case 's': sflag
= true; break; /* single user */
156 case '?': /* we should do something with the global optopt variable here */
158 fprintf(stderr
, "%s: ignoring unknown arguments\n", getprogname());
163 if (getpid() != 1 && getppid() != 1) {
164 fprintf(stderr
, "%s: This program is not meant to be run directly.\n", getprogname());
168 launchd_runtime_init();
170 if (NULL
== getenv("PATH")) {
171 setenv("PATH", _PATH_STDPATH
, 1);
178 if ((cfd
= open(_PATH_CONSOLE
, O_WRONLY
| O_NOCTTY
)) != -1) {
180 if (!(launchd_console
= fdopen(cfd
, "w"))) {
186 if (launchd_osinstaller
) {
187 extra
= " in the OS Installer";
189 extra
= " in single-user mode";
192 launchd_syslog(LOG_NOTICE
| LOG_CONSOLE
, "*** launchd[1] has started up%s. ***", extra
);
193 if (launchd_use_gmalloc
) {
194 launchd_syslog(LOG_NOTICE
| LOG_CONSOLE
, "*** Using libgmalloc. ***");
197 if (launchd_verbose_boot
) {
198 launchd_syslog(LOG_NOTICE
| LOG_CONSOLE
, "*** Verbose boot, will log to /dev/console. ***");
201 if (launchd_shutdown_debugging
) {
202 launchd_syslog(LOG_NOTICE
| LOG_CONSOLE
, "*** Shutdown debugging is enabled. ***");
205 if (launchd_log_shutdown
) {
206 launchd_syslog(LOG_NOTICE
| LOG_CONSOLE
, "*** Shutdown logging is enabled. ***");
209 if (launchd_log_perf
) {
210 launchd_syslog(LOG_NOTICE
| LOG_CONSOLE
, "*** Performance logging is enabled. ***");
213 if (launchd_log_debug
) {
214 launchd_syslog(LOG_NOTICE
| LOG_CONSOLE
, "*** Debug logging is enabled. ***");
217 handle_pid1_crashes_separately();
219 /* Start the update thread.
221 * <rdar://problem/5039559&6153301>
224 (void)os_assumes_zero(pthread_create(&t
, NULL
, update_thread
, NULL
));
225 (void)os_assumes_zero(pthread_detach(t
));
227 /* PID 1 doesn't have a flat namespace. */
228 launchd_flat_mach_namespace
= false;
229 fflush(launchd_console
);
231 launchd_uid
= getuid();
232 launchd_var_available
= true;
233 if (asprintf(&launchd_label
, "com.apple.launchd.peruser.%u", launchd_uid
) == 0) {
234 launchd_label
= "com.apple.launchd.peruser.unknown";
237 struct passwd
*pwent
= getpwuid(launchd_uid
);
239 launchd_username
= strdup(pwent
->pw_name
);
241 launchd_username
= "(unknown)";
244 if (asprintf(&_launchd_database_dir
, LAUNCHD_DB_PREFIX
"/com.apple.launchd.peruser.%u", launchd_uid
) == 0) {
245 _launchd_database_dir
= "";
248 if (asprintf(&_launchd_log_dir
, LAUNCHD_LOG_PREFIX
"/com.apple.launchd.peruser.%u", launchd_uid
) == 0) {
249 _launchd_log_dir
= "";
252 if (launchd_allow_global_dyld_envvars
) {
253 launchd_syslog(LOG_WARNING
, "Per-user launchd will allow DYLD_* environment variables in the global environment.");
259 auditinfo_addr_t auinfo
;
260 if (posix_assumes_zero(getaudit_addr(&auinfo
, sizeof(auinfo
))) != -1) {
261 launchd_audit_session
= auinfo
.ai_asid
;
262 launchd_syslog(LOG_DEBUG
, "Our audit session ID is %i", launchd_audit_session
);
265 launchd_audit_port
= _audit_session_self();
267 vproc_transaction_begin(NULL
);
268 vproc_transaction_end(NULL
, NULL
);
270 launchd_syslog(LOG_DEBUG
, "Per-user launchd started (UID/username): %u/%s.", launchd_uid
, launchd_username
);
273 monitor_networking_state();
276 launchd_runtime_init2();
281 handle_pid1_crashes_separately(void)
283 struct sigaction fsa
;
285 fsa
.sa_sigaction
= fatal_signal_handler
;
286 fsa
.sa_flags
= SA_SIGINFO
;
287 sigemptyset(&fsa
.sa_mask
);
289 (void)posix_assumes_zero(sigaction(SIGILL
, &fsa
, NULL
));
290 (void)posix_assumes_zero(sigaction(SIGFPE
, &fsa
, NULL
));
291 (void)posix_assumes_zero(sigaction(SIGBUS
, &fsa
, NULL
));
292 (void)posix_assumes_zero(sigaction(SIGTRAP
, &fsa
, NULL
));
293 (void)posix_assumes_zero(sigaction(SIGABRT
, &fsa
, NULL
));
294 (void)posix_assumes_zero(sigaction(SIGSEGV
, &fsa
, NULL
));
298 update_thread(void *nothing
__attribute__((unused
)))
300 (void)posix_assumes_zero(setiopolicy_np(IOPOL_TYPE_DISK
, IOPOL_SCOPE_THREAD
, IOPOL_THROTTLE
));
302 while (launchd_sync_frequency
) {
304 sleep(launchd_sync_frequency
);
307 launchd_syslog(LOG_DEBUG
, "Update thread exiting.");
311 #define PID1_CRASH_LOGFILE "/var/log/launchd-pid1.crash"
313 /* This hack forces the dynamic linker to resolve these symbols ASAP */
314 static __attribute__((unused
)) typeof(sync
) *__junk_dyld_trick1
= sync
;
315 static __attribute__((unused
)) typeof(sleep
) *__junk_dyld_trick2
= sleep
;
316 static __attribute__((unused
)) typeof(reboot
) *__junk_dyld_trick3
= reboot
;
319 do_pid1_crash_diagnosis_mode(const char *msg
)
322 kill(launchd_wsp
, SIGKILL
);
327 while (launchd_shutdown_debugging
&& !do_pid1_crash_diagnosis_mode2(msg
)) {
338 switch ((p
= fork())) {
340 launchd_syslog(LOG_ERR
| LOG_CONSOLE
, "Can't fork PID 1 copy for crash debugging: %m");
346 (void)waitpid(p
, &wstatus
, 0);
347 } while(!WIFEXITED(wstatus
));
349 fprintf(stdout
, "PID 1 copy: exit status: %d\n", WEXITSTATUS(wstatus
));
358 do_pid1_crash_diagnosis_mode2(const char *msg
)
360 if (basic_fork() == 0) {
361 /* Neuter our bootstrap port so that the shell doesn't try talking to us
362 * while we're blocked waiting on it.
364 if (launchd_console
) {
365 fflush(launchd_console
);
368 task_set_bootstrap_port(mach_task_self(), MACH_PORT_NULL
);
369 if (basic_fork() != 0) {
370 if (launchd_console
) {
371 fflush(launchd_console
);
381 revoke(_PATH_CONSOLE
);
382 if ((fd
= open(_PATH_CONSOLE
, O_RDWR
)) == -1) {
385 if (login_tty(fd
) == -1) {
389 setenv("TERM", "vt100", 1);
390 fprintf(stdout
, "\n");
391 fprintf(stdout
, "Entering launchd PID 1 debugging mode...\n");
392 fprintf(stdout
, "The PID 1 launchd has crashed %s.\n", msg
);
393 fprintf(stdout
, "It has fork(2)ed itself for debugging.\n");
394 fprintf(stdout
, "To debug the crashing thread of PID 1:\n");
395 fprintf(stdout
, " gdb attach %d\n", getppid());
396 fprintf(stdout
, "To exit this shell and shut down:\n");
397 fprintf(stdout
, " kill -9 1\n");
398 fprintf(stdout
, "A sample of PID 1 has been written to %s\n", PID1_CRASH_LOGFILE
);
399 fprintf(stdout
, "\n");
402 execl(_PATH_BSHELL
, "-sh", NULL
);
403 syslog(LOG_ERR
, "can't exec %s for PID 1 crash debugging: %m", _PATH_BSHELL
);
408 fatal_signal_handler(int sig
, siginfo_t
*si
, void *uap
__attribute__((unused
)))
410 const char *doom_why
= "at instruction";
413 char *sample_args
[] = { "/usr/bin/sample", "1", "1", "-file", PID1_CRASH_LOGFILE
, NULL
};
418 crash_addr
= si
->si_addr
;
419 crash_pid
= si
->si_pid
;
421 setenv("XPC_SERVICES_UNAVAILABLE", "1", 0);
422 unlink(PID1_CRASH_LOGFILE
);
424 switch ((sample_p
= vfork())) {
426 execve(sample_args
[0], sample_args
, environ
);
430 waitpid(sample_p
, &wstatus
, 0);
442 doom_why
= "trying to read/write";
446 snprintf(msg
, sizeof(msg
), "%s: %p (%s sent by PID %u)", doom_why
, crash_addr
, strsignal(sig
), crash_pid
);
448 do_pid1_crash_diagnosis_mode(msg
);
456 pid1_magic_init(void)
458 launchd_label
= "com.apple.launchd";
459 launchd_username
= "system";
461 _launchd_database_dir
= LAUNCHD_DB_PREFIX
"/com.apple.launchd";
462 _launchd_log_dir
= LAUNCHD_LOG_PREFIX
"/com.apple.launchd";
464 (void)posix_assumes_zero(setsid());
465 (void)posix_assumes_zero(chdir("/"));
466 (void)posix_assumes_zero(setlogin("root"));
468 #if !TARGET_OS_EMBEDDED
469 auditinfo_addr_t auinfo
= {
473 .ai_asid
= AU_ASSIGN_ASID
,
474 .ai_auid
= AU_DEFAUDITID
,
475 .ai_flags
= AU_SESSION_FLAG_IS_INITIAL
,
478 if (setaudit_addr(&auinfo
, sizeof(auinfo
)) == -1) {
479 launchd_syslog(LOG_WARNING
| LOG_CONSOLE
, "Could not set audit session: %d: %s.", errno
, strerror(errno
));
483 launchd_audit_session
= auinfo
.ai_asid
;
484 launchd_syslog(LOG_DEBUG
, "Audit Session ID: %i", launchd_audit_session
);
486 launchd_audit_port
= _audit_session_self();
487 #endif // !TARGET_OS_EMBEDDED
491 launchd_copy_persistent_store(int type
, const char *file
)
499 case LAUNCHD_PERSISTENT_STORE_DB
:
500 (void)asprintf(&result
, "%s/%s", _launchd_database_dir
, file
);
502 case LAUNCHD_PERSISTENT_STORE_LOGS
:
503 (void)asprintf(&result
, "%s/%s", _launchd_log_dir
, file
);
516 (void)posix_assumes_zero(fcntl(fd
, F_SETFD
, 1));
522 launchd_shutdown(void)
526 if (launchd_shutting_down
) {
530 runtime_ktrace0(RTKT_LAUNCHD_EXITING
);
532 launchd_shutting_down
= true;
535 now
= runtime_get_wall_time();
537 char *term_who
= pid1_magic
? "System shutdown" : "Per-user launchd termination for ";
538 launchd_syslog(LOG_INFO
, "%s%s began", term_who
, pid1_magic
? "" : launchd_username
);
540 os_assert(jobmgr_shutdown(root_jobmgr
) != NULL
);
544 (void)os_assumes_zero(audit_quick_stop());
550 launchd_SessionCreate(void)
552 #if !TARGET_OS_EMBEDDED
553 auditinfo_addr_t auinfo
= {
554 .ai_termid
= { .at_type
= AU_IPv4
},
555 .ai_asid
= AU_ASSIGN_ASID
,
559 if (setaudit_addr(&auinfo
, sizeof(auinfo
)) == 0) {
561 snprintf(session
, sizeof(session
), "%x", auinfo
.ai_asid
);
562 setenv("SECURITYSESSIONID", session
, 1);
564 launchd_syslog(LOG_WARNING
, "Could not set audit session: %d: %s.", errno
, strerror(errno
));
566 #endif // !TARGET_OS_EMBEDDED
570 testfd_or_openfd(int fd
, const char *path
, int flags
)
574 if (-1 != (tmpfd
= dup(fd
))) {
575 (void)posix_assumes_zero(runtime_close(tmpfd
));
577 if (-1 == (tmpfd
= open(path
, flags
| O_NOCTTY
, DEFFILEMODE
))) {
578 launchd_syslog(LOG_ERR
, "open(\"%s\", ...): %m", path
);
579 } else if (tmpfd
!= fd
) {
580 (void)posix_assumes_zero(dup2(tmpfd
, fd
));
581 (void)posix_assumes_zero(runtime_close(tmpfd
));
587 get_network_state(void)
589 struct ifaddrs
*ifa
, *ifai
;
593 /* Workaround 4978696: getifaddrs() reports false ENOMEM */
594 while ((r
= getifaddrs(&ifa
)) == -1 && errno
== ENOMEM
) {
595 launchd_syslog(LOG_DEBUG
, "Worked around bug: 4978696");
596 (void)posix_assumes_zero(sched_yield());
599 if (posix_assumes_zero(r
) == -1) {
603 for (ifai
= ifa
; ifai
; ifai
= ifai
->ifa_next
) {
604 if (!(ifai
->ifa_flags
& IFF_UP
)) {
607 if (ifai
->ifa_flags
& IFF_LOOPBACK
) {
610 if (ifai
->ifa_addr
->sa_family
!= AF_INET
&& ifai
->ifa_addr
->sa_family
!= AF_INET6
) {
623 monitor_networking_state(void)
625 int pfs
= _fd(socket(PF_SYSTEM
, SOCK_RAW
, SYSPROTO_EVENT
));
626 struct kev_request kev_req
;
628 network_up
= get_network_state();
631 (void)os_assumes_zero(errno
);
635 memset(&kev_req
, 0, sizeof(kev_req
));
636 kev_req
.vendor_code
= KEV_VENDOR_APPLE
;
637 kev_req
.kev_class
= KEV_NETWORK_CLASS
;
639 if (posix_assumes_zero(ioctl(pfs
, SIOCSKEVFILT
, &kev_req
)) == -1) {
644 (void)posix_assumes_zero(kevent_mod(pfs
, EVFILT_READ
, EV_ADD
, 0, 0, &kqpfsystem_callback
));
648 pfsystem_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
)
650 bool new_networking_state
;
653 (void)posix_assumes_zero(read((int)kev
->ident
, &buf
, sizeof(buf
)));
655 new_networking_state
= get_network_state();
657 if (new_networking_state
!= network_up
) {
658 network_up
= new_networking_state
;
659 jobmgr_dispatch_all_semaphores(root_jobmgr
);