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: 24863 $";
26 #include <sys/types.h>
27 #include <sys/queue.h>
28 #include <sys/event.h>
30 #include <sys/ucred.h>
31 #include <sys/fcntl.h>
34 #include <sys/sysctl.h>
35 #include <sys/sockio.h>
37 #include <sys/resource.h>
38 #include <sys/ioctl.h>
39 #include <sys/mount.h>
40 #include <sys/kern_event.h>
41 #include <sys/reboot.h>
42 #include <sys/socket.h>
43 #include <sys/syscall.h>
45 #include <netinet/in.h>
46 #include <netinet/in_var.h>
47 #include <netinet6/nd6.h>
71 #include <bsm/auditd_lib.h>
72 #include <bsm/audit_session.h>
75 #include "bootstrap.h"
77 #include "vproc_priv.h"
78 #include "vproc_internal.h"
80 #include "launch_internal.h"
82 #include "launchd_runtime.h"
83 #include "launchd_core_logic.h"
84 #include "launchd_unix_ipc.h"
86 #define LAUNCHD_CONF ".launchd.conf"
88 extern char **environ
;
90 static void pfsystem_callback(void *, struct kevent
*);
92 static kq_callback kqpfsystem_callback
= pfsystem_callback
;
94 static void pid1_magic_init(void);
96 static void testfd_or_openfd(int fd
, const char *path
, int flags
);
97 static bool get_network_state(void);
98 static void monitor_networking_state(void);
99 static void fatal_signal_handler(int sig
, siginfo_t
*si
, void *uap
);
100 static void handle_pid1_crashes_separately(void);
101 static void do_pid1_crash_diagnosis_mode(const char *msg
);
102 static int basic_fork(void);
103 static bool do_pid1_crash_diagnosis_mode2(const char *msg
);
105 static void *update_thread(void *nothing
);
107 static bool re_exec_in_single_user_mode
;
108 static void *crash_addr
;
109 static pid_t crash_pid
;
111 bool shutdown_in_progress
;
112 bool fake_shutdown_in_progress
;
114 char g_username
[128] = "__Uninitialized__";
115 char g_my_label
[128] = "__Uninitialized__";
116 char g_launchd_database_dir
[PATH_MAX
];
117 FILE *g_console
= NULL
;
118 int32_t g_sync_frequency
= 30;
121 main(int argc
, char *const *argv
)
126 testfd_or_openfd(STDIN_FILENO
, _PATH_DEVNULL
, O_RDONLY
);
127 testfd_or_openfd(STDOUT_FILENO
, _PATH_DEVNULL
, O_WRONLY
);
128 testfd_or_openfd(STDERR_FILENO
, _PATH_DEVNULL
, O_WRONLY
);
131 if (!getenv("DYLD_INSERT_LIBRARIES")) {
132 setenv("DYLD_INSERT_LIBRARIES", "/usr/lib/libgmalloc.dylib", 1);
133 setenv("MALLOC_STRICT_SIZE", "1", 1);
134 execv(argv
[0], argv
);
136 unsetenv("DYLD_INSERT_LIBRARIES");
137 unsetenv("MALLOC_STRICT_SIZE");
139 } else if (g_malloc_log_stacks
) {
140 if (!getenv("MallocStackLogging")) {
141 setenv("MallocStackLogging", "1", 1);
142 execv(argv
[0], argv
);
144 unsetenv("MallocStackLogging");
148 while ((ch
= getopt(argc
, argv
, "s")) != -1) {
150 case 's': sflag
= true; break; /* single user */
151 case '?': /* we should do something with the global optopt variable here */
153 fprintf(stderr
, "%s: ignoring unknown arguments\n", getprogname());
158 if (getpid() != 1 && getppid() != 1) {
159 fprintf(stderr
, "%s: This program is not meant to be run directly.\n", getprogname());
163 launchd_runtime_init();
167 if (launchd_assumes((cfd
= open(_PATH_CONSOLE
, O_WRONLY
| O_NOCTTY
)) != -1)) {
169 if (!launchd_assumes((g_console
= fdopen(cfd
, "w")) != NULL
)) {
175 if (NULL
== getenv("PATH")) {
176 setenv("PATH", _PATH_STDPATH
, 1);
186 struct passwd
*pwent
= getpwuid(getuid());
188 strlcpy(g_username
, pwent
->pw_name
, sizeof(g_username
) - 1);
191 snprintf(g_my_label
, sizeof(g_my_label
), "com.apple.launchd.peruser.%u", getuid());
193 auditinfo_addr_t auinfo
;
194 if (launchd_assumes(getaudit_addr(&auinfo
, sizeof(auinfo
)) != -1)) {
195 g_audit_session
= auinfo
.ai_asid
;
196 runtime_syslog(LOG_DEBUG
, "Our audit session ID is %i", g_audit_session
);
199 g_audit_session_port
= _audit_session_self();
200 snprintf(g_launchd_database_dir
, sizeof(g_launchd_database_dir
), LAUNCHD_DB_PREFIX
"/com.apple.launchd.peruser.%u", getuid());
201 runtime_syslog(LOG_DEBUG
, "Per-user launchd for UID %u (%s) has begun.", getuid(), g_username
);
205 runtime_syslog(LOG_NOTICE
| LOG_CONSOLE
, "*** launchd[1] has started up. ***");
207 runtime_syslog(LOG_NOTICE
| LOG_CONSOLE
, "*** Using libgmalloc. ***");
209 if (g_malloc_log_stacks
) {
210 runtime_syslog(LOG_NOTICE
| LOG_CONSOLE
, "*** Logging stacks of malloc(3) allocations. ***");
213 if (g_verbose_boot
) {
214 runtime_syslog(LOG_NOTICE
| LOG_CONSOLE
, "*** Verbose boot, will log to /dev/console. ***");
217 if (g_shutdown_debugging
) {
218 runtime_syslog(LOG_NOTICE
| LOG_CONSOLE
, "*** Shutdown debugging is enabled. ***");
221 /* PID 1 doesn't have a flat namespace. */
222 g_flat_mach_namespace
= false;
225 runtime_syslog(LOG_NOTICE
, "*** Per-user launchd using libgmalloc. ***");
229 monitor_networking_state();
232 handle_pid1_crashes_separately();
234 #if !TARGET_OS_EMBEDDED
235 /* prime shared memory before the 'bootstrap_port' global is set to zero */
236 _vproc_transaction_begin();
237 _vproc_transaction_end();
242 /* Start the update thread -- rdar://problem/5039559&6153301 */
244 int err
= pthread_create(&t
, NULL
, update_thread
, NULL
);
245 (void)launchd_assumes(err
== 0);
246 (void)launchd_assumes(pthread_detach(t
) == 0);
251 launchd_runtime_init2();
257 handle_pid1_crashes_separately(void)
259 struct sigaction fsa
;
261 fsa
.sa_sigaction
= fatal_signal_handler
;
262 fsa
.sa_flags
= SA_SIGINFO
;
263 sigemptyset(&fsa
.sa_mask
);
265 (void)launchd_assumes(sigaction(SIGILL
, &fsa
, NULL
) != -1);
266 (void)launchd_assumes(sigaction(SIGFPE
, &fsa
, NULL
) != -1);
267 (void)launchd_assumes(sigaction(SIGBUS
, &fsa
, NULL
) != -1);
268 (void)launchd_assumes(sigaction(SIGSEGV
, &fsa
, NULL
) != -1);
269 (void)launchd_assumes(sigaction(SIGABRT
, &fsa
, NULL
) != -1);
270 (void)launchd_assumes(sigaction(SIGTRAP
, &fsa
, NULL
) != -1);
273 void *update_thread(void *nothing
__attribute__((unused
)))
275 /* <rdar://problem/7385963> use IOPOL_PASSIVE for sync thread */
276 (void)launchd_assumes(setiopolicy_np(IOPOL_TYPE_DISK
, IOPOL_SCOPE_THREAD
, IOPOL_PASSIVE
) != -1);
278 while( g_sync_frequency
) {
280 sleep(g_sync_frequency
);
283 runtime_syslog(LOG_DEBUG
, "Update thread exiting.");
287 #define PID1_CRASH_LOGFILE "/var/log/launchd-pid1.crash"
289 /* This hack forces the dynamic linker to resolve these symbols ASAP */
290 static __attribute__((unused
)) typeof(sync
) *__junk_dyld_trick1
= sync
;
291 static __attribute__((unused
)) typeof(sleep
) *__junk_dyld_trick2
= sleep
;
292 static __attribute__((unused
)) typeof(reboot
) *__junk_dyld_trick3
= reboot
;
295 do_pid1_crash_diagnosis_mode(const char *msg
)
298 kill(g_wsp
, SIGKILL
);
303 while (g_shutdown_debugging
&& !do_pid1_crash_diagnosis_mode2(msg
)) {
314 switch ((p
= fork())) {
316 runtime_syslog(LOG_ERR
| LOG_CONSOLE
, "Can't fork PID 1 copy for crash debugging: %m");
322 (void)waitpid(p
, &wstatus
, 0);
323 } while(!WIFEXITED(wstatus
));
325 fprintf(stdout
, "PID 1 copy: exit status: %d\n", WEXITSTATUS(wstatus
));
334 do_pid1_crash_diagnosis_mode2(const char *msg
)
336 if (basic_fork() == 0) {
337 /* Neuter our bootstrap port so that the shell doesn't try talking to us while
338 * we're blocked waiting on it.
343 task_set_bootstrap_port(mach_task_self(), MACH_PORT_NULL
);
344 if (basic_fork() != 0) {
355 revoke(_PATH_CONSOLE
);
356 if ((fd
= open(_PATH_CONSOLE
, O_RDWR
)) == -1) {
359 if (login_tty(fd
) == -1) {
362 setenv("TERM", "vt100", 1);
363 fprintf(stdout
, "\n");
364 fprintf(stdout
, "Entering launchd PID 1 debugging mode...\n");
365 fprintf(stdout
, "The PID 1 launchd has crashed %s.\n", msg
);
366 fprintf(stdout
, "It has fork(2)ed itself for debugging.\n");
367 fprintf(stdout
, "To debug the crashing thread of PID 1:\n");
368 fprintf(stdout
, " gdb attach %d\n", getppid());
369 fprintf(stdout
, "To exit this shell and shut down:\n");
370 fprintf(stdout
, " kill -9 1\n");
371 fprintf(stdout
, "A sample of PID 1 has been written to %s\n", PID1_CRASH_LOGFILE
);
372 fprintf(stdout
, "\n");
375 execl(_PATH_BSHELL
, "-sh", NULL
);
376 syslog(LOG_ERR
, "can't exec %s for PID 1 crash debugging: %m", _PATH_BSHELL
);
381 fatal_signal_handler(int sig
, siginfo_t
*si
, void *uap
__attribute__((unused
)))
383 const char *doom_why
= "at instruction";
385 char *sample_args
[] = { "/usr/bin/sample", "1", "1", "-file", PID1_CRASH_LOGFILE
, NULL
};
389 crash_addr
= si
->si_addr
;
390 crash_pid
= si
->si_pid
;
392 unlink(PID1_CRASH_LOGFILE
);
394 switch ((sample_p
= vfork())) {
396 execve(sample_args
[0], sample_args
, environ
);
400 waitpid(sample_p
, &wstatus
, 0);
412 doom_why
= "trying to read/write";
415 snprintf(msg
, sizeof(msg
), "%s: %p (%s sent by PID %u)", doom_why
, crash_addr
, strsignal(sig
), crash_pid
);
417 do_pid1_crash_diagnosis_mode(msg
);
425 pid1_magic_init(void)
427 (void)launchd_assumes(setsid() != -1);
428 (void)launchd_assumes(chdir("/") != -1);
429 (void)launchd_assumes(setlogin("root") != -1);
431 strcpy(g_my_label
, "com.apple.launchd");
433 #if !TARGET_OS_EMBEDDED
434 auditinfo_addr_t auinfo
= {
435 .ai_termid
= { .at_type
= AU_IPv4
},
436 .ai_asid
= AU_ASSIGN_ASID
,
437 .ai_auid
= AU_DEFAUDITID
,
438 .ai_flags
= AU_SESSION_FLAG_IS_INITIAL
,
441 if (!launchd_assumes(setaudit_addr(&auinfo
, sizeof(auinfo
)) != -1)) {
442 runtime_syslog(LOG_WARNING
| LOG_CONSOLE
, "Could not set audit session: %s.", strerror(errno
));
446 g_audit_session
= auinfo
.ai_asid
;
447 runtime_syslog(LOG_DEBUG
, "Audit Session ID: %i", g_audit_session
);
449 g_audit_session_port
= _audit_session_self();
450 #endif // !TARGET_OS_EMBEDDED
452 strcpy(g_launchd_database_dir
, LAUNCHD_DB_PREFIX
"/com.apple.launchd");
456 launchd_data_base_path(int db_type
)
458 static char result
[PATH_MAX
];
459 static int last_db_type
= -1;
461 if (db_type
== last_db_type
) {
466 case LAUNCHD_DB_TYPE_OVERRIDES
:
467 snprintf(result
, sizeof(result
), "%s/%s", g_launchd_database_dir
, "overrides.plist");
468 last_db_type
= db_type
;
470 case LAUNCHD_DB_TYPE_JOBCACHE
:
471 snprintf(result
, sizeof(result
), "%s/%s", g_launchd_database_dir
, "jobcache.launchdata");
472 last_db_type
= db_type
;
485 (void)launchd_assumes(fcntl(fd
, F_SETFD
, 1) != -1);
491 launchd_shutdown(void)
495 if (shutdown_in_progress
) {
499 runtime_ktrace0(RTKT_LAUNCHD_EXITING
);
501 shutdown_in_progress
= true;
503 if (pid1_magic
|| g_log_per_user_shutdown
) {
505 * When this changes to a more sustainable API, update this:
506 * http://howto.apple.com/db.cgi?Debugging_Apps_Non-Responsive_At_Shutdown
508 runtime_setlogmask(LOG_UPTO(LOG_DEBUG
));
513 now
= runtime_get_wall_time();
515 char *term_who
= pid1_magic
? "System shutdown" : "Per-user launchd termination for ";
516 runtime_syslog(LOG_INFO
, "%s%s began", term_who
, pid1_magic
? "" : g_username
);
518 launchd_assert(jobmgr_shutdown(root_jobmgr
) != NULL
);
522 (void)launchd_assumes(audit_quick_stop() == 0);
528 launchd_single_user(void)
530 runtime_syslog(LOG_NOTICE
, "Going to single-user mode");
532 re_exec_in_single_user_mode
= true;
538 runtime_kill(-1, SIGKILL
);
542 launchd_SessionCreate(void)
544 #if !TARGET_OS_EMBEDDED
545 auditinfo_addr_t auinfo
= {
546 .ai_termid
= { .at_type
= AU_IPv4
},
547 .ai_asid
= AU_ASSIGN_ASID
,
551 if (launchd_assumes(setaudit_addr(&auinfo
, sizeof(auinfo
)) == 0)) {
553 snprintf(session
, sizeof(session
), "%x", auinfo
.ai_asid
);
554 setenv("SECURITYSESSIONID", session
, 1);
556 runtime_syslog(LOG_WARNING
, "Could not set audit session: %s.", strerror(errno
));
558 #endif // !TARGET_OS_EMBEDDED
562 testfd_or_openfd(int fd
, const char *path
, int flags
)
566 if (-1 != (tmpfd
= dup(fd
))) {
567 (void)launchd_assumes(runtime_close(tmpfd
) == 0);
569 if (-1 == (tmpfd
= open(path
, flags
| O_NOCTTY
, DEFFILEMODE
))) {
570 runtime_syslog(LOG_ERR
, "open(\"%s\", ...): %m", path
);
571 } else if (tmpfd
!= fd
) {
572 (void)launchd_assumes(dup2(tmpfd
, fd
) != -1);
573 (void)launchd_assumes(runtime_close(tmpfd
) == 0);
579 get_network_state(void)
581 struct ifaddrs
*ifa
, *ifai
;
585 /* Workaround 4978696: getifaddrs() reports false ENOMEM */
586 while ((r
= getifaddrs(&ifa
)) == -1 && errno
== ENOMEM
) {
587 runtime_syslog(LOG_DEBUG
, "Worked around bug: 4978696");
588 (void)launchd_assumes(sched_yield() != -1);
591 if (!launchd_assumes(r
!= -1)) {
595 for (ifai
= ifa
; ifai
; ifai
= ifai
->ifa_next
) {
596 if (!(ifai
->ifa_flags
& IFF_UP
)) {
599 if (ifai
->ifa_flags
& IFF_LOOPBACK
) {
602 if (ifai
->ifa_addr
->sa_family
!= AF_INET
&& ifai
->ifa_addr
->sa_family
!= AF_INET6
) {
615 monitor_networking_state(void)
617 int pfs
= _fd(socket(PF_SYSTEM
, SOCK_RAW
, SYSPROTO_EVENT
));
618 struct kev_request kev_req
;
620 network_up
= get_network_state();
622 if (!launchd_assumes(pfs
!= -1)) {
626 memset(&kev_req
, 0, sizeof(kev_req
));
627 kev_req
.vendor_code
= KEV_VENDOR_APPLE
;
628 kev_req
.kev_class
= KEV_NETWORK_CLASS
;
630 if (!launchd_assumes(ioctl(pfs
, SIOCSKEVFILT
, &kev_req
) != -1)) {
635 (void)launchd_assumes(kevent_mod(pfs
, EVFILT_READ
, EV_ADD
, 0, 0, &kqpfsystem_callback
) != -1);
639 pfsystem_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
)
641 bool new_networking_state
;
644 (void)launchd_assumes(read((int)kev
->ident
, &buf
, sizeof(buf
)) != -1);
646 new_networking_state
= get_network_state();
648 if (new_networking_state
!= network_up
) {
649 network_up
= new_networking_state
;
650 jobmgr_dispatch_all_semaphores(root_jobmgr
);
655 _log_launchd_bug(const char *rcs_rev
, const char *path
, unsigned int line
, const char *test
)
657 int saved_errno
= errno
;
659 const char *file
= strrchr(path
, '/');
660 char *rcs_rev_tmp
= strchr(rcs_rev
, ' ');
662 runtime_ktrace1(RTKT_LAUNCHD_BUG
);
671 strlcpy(buf
, rcs_rev
, sizeof(buf
));
673 strlcpy(buf
, rcs_rev_tmp
+ 1, sizeof(buf
));
674 rcs_rev_tmp
= strchr(buf
, ' ');
680 runtime_syslog(LOG_NOTICE
, "Bug: %s:%u (%s):%u: %s", file
, line
, buf
, saved_errno
, test
);