]> git.saurik.com Git - apple/launchd.git/blob - launchd/src/launchd.c
launchd-329.3.1.tar.gz
[apple/launchd.git] / launchd / src / launchd.c
1 /*
2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_APACHE_LICENSE_HEADER_START@
5 *
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
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
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.
17 *
18 * @APPLE_APACHE_LICENSE_HEADER_END@
19 */
20
21 static const char *const __rcs_file_version__ = "$Revision: 23925 $";
22
23 #include "config.h"
24 #include "launchd.h"
25
26 #if HAVE_SECURITY
27 #include <Security/Authorization.h>
28 #include <Security/AuthorizationTags.h>
29 #include <Security/AuthSession.h>
30 #endif
31 #include <sys/types.h>
32 #include <sys/queue.h>
33 #include <sys/event.h>
34 #include <sys/stat.h>
35 #include <sys/ucred.h>
36 #include <sys/fcntl.h>
37 #include <sys/un.h>
38 #include <sys/wait.h>
39 #include <sys/sysctl.h>
40 #include <sys/sockio.h>
41 #include <sys/time.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>
49 #include <net/if.h>
50 #include <netinet/in.h>
51 #include <netinet/in_var.h>
52 #include <netinet6/nd6.h>
53 #include <ifaddrs.h>
54 #include <unistd.h>
55 #include <signal.h>
56 #include <errno.h>
57 #include <libgen.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <stdarg.h>
61 #include <stdbool.h>
62 #include <paths.h>
63 #include <pwd.h>
64 #include <grp.h>
65 #include <ttyent.h>
66 #include <dlfcn.h>
67 #include <dirent.h>
68 #include <string.h>
69 #include <setjmp.h>
70 #include <spawn.h>
71 #include <sched.h>
72 #include <pthread.h>
73 #include <util.h>
74
75 #if HAVE_LIBAUDITD
76 #include <bsm/auditd_lib.h>
77 #endif
78
79 #include "bootstrap.h"
80 #include "vproc.h"
81 #include "vproc_priv.h"
82 #include "vproc_internal.h"
83 #include "launch.h"
84 #include "launch_internal.h"
85
86 #include "launchd_runtime.h"
87 #include "launchd_core_logic.h"
88 #include "launchd_unix_ipc.h"
89
90 #define LAUNCHD_CONF ".launchd.conf"
91 #define SECURITY_LIB "/System/Library/Frameworks/Security.framework/Versions/A/Security"
92
93 extern char **environ;
94
95 static void pfsystem_callback(void *, struct kevent *);
96
97 static kq_callback kqpfsystem_callback = pfsystem_callback;
98
99 static void pid1_magic_init(void);
100
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);
109
110 static void *update_thread(void *nothing);
111
112 static bool re_exec_in_single_user_mode;
113 static void *crash_addr;
114 static pid_t crash_pid;
115
116 bool shutdown_in_progress;
117 bool fake_shutdown_in_progress;
118 bool network_up;
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;
124
125 int
126 main(int argc, char *const *argv)
127 {
128 bool sflag = false;
129 int ch;
130
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);
134
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);
140 } else {
141 unsetenv("DYLD_INSERT_LIBRARIES");
142 unsetenv("MALLOC_STRICT_SIZE");
143 }
144 }
145
146 while ((ch = getopt(argc, argv, "s")) != -1) {
147 switch (ch) {
148 case 's': sflag = true; break; /* single user */
149 case '?': /* we should do something with the global optopt variable here */
150 default:
151 fprintf(stderr, "%s: ignoring unknown arguments\n", getprogname());
152 break;
153 }
154 }
155
156 if (getpid() != 1 && getppid() != 1) {
157 fprintf(stderr, "%s: This program is not meant to be run directly.\n", getprogname());
158 exit(EXIT_FAILURE);
159 }
160
161 launchd_runtime_init();
162
163 if( pid1_magic ) {
164 int cfd = -1;
165 if( launchd_assumes((cfd = open(_PATH_CONSOLE, O_WRONLY | O_NOCTTY)) != -1) ) {
166 _fd(cfd);
167 if( !launchd_assumes((g_console = fdopen(cfd, "w")) != NULL) ) {
168 close(cfd);
169 }
170 }
171 }
172
173 if (NULL == getenv("PATH")) {
174 setenv("PATH", _PATH_STDPATH, 1);
175 }
176
177 if (pid1_magic) {
178 pid1_magic_init();
179 } else {
180 ipc_server_init();
181
182 runtime_log_push();
183
184 struct passwd *pwent = getpwuid(getuid());
185 if( pwent ) {
186 strlcpy(g_username, pwent->pw_name, sizeof(g_username) - 1);
187 }
188
189 snprintf(g_my_label, sizeof(g_my_label), "com.apple.launchd.peruser.%u", getuid());
190
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);
195 }
196
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);
200 }
201
202 if( pid1_magic ) {
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 ***");
206 }
207
208 if( g_verbose_boot ) {
209 runtime_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Verbose boot, will log to /dev/console. ***");
210 }
211
212 if( g_shutdown_debugging ) {
213 runtime_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Shutdown debugging is enabled. ***");
214 }
215
216 /* PID 1 doesn't have a flat namespace. */
217 g_flat_mach_namespace = false;
218 }
219
220 monitor_networking_state();
221
222 if (pid1_magic) {
223 handle_pid1_crashes_separately();
224 } else {
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();
229 #endif
230 }
231
232 if( pid1_magic ) {
233 /* Start the update thread -- rdar://problem/5039559&6153301 */
234 pthread_t t = NULL;
235 int err = pthread_create(&t, NULL, update_thread, NULL);
236 launchd_assumes(err == 0);
237 launchd_assumes(pthread_detach(t) == 0);
238 }
239
240 jobmgr_init(sflag);
241
242 launchd_runtime_init2();
243
244 launchd_runtime();
245 }
246
247 void
248 handle_pid1_crashes_separately(void)
249 {
250 struct sigaction fsa;
251
252 fsa.sa_sigaction = fatal_signal_handler;
253 fsa.sa_flags = SA_SIGINFO;
254 sigemptyset(&fsa.sa_mask);
255
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);
260 }
261
262 void *update_thread(void *nothing __attribute__((unused)))
263 {
264 while( g_sync_frequency ) {
265 sync();
266 sleep(g_sync_frequency);
267 }
268
269 runtime_syslog(LOG_DEBUG, "Update thread exiting.");
270 return NULL;
271 }
272
273 #define PID1_CRASH_LOGFILE "/var/log/launchd-pid1.crash"
274
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;
279
280 void
281 do_pid1_crash_diagnosis_mode(void)
282 {
283 if( g_wsp ) {
284 kill(g_wsp, SIGKILL);
285 sleep(3);
286 g_wsp = 0;
287 }
288
289 while( g_shutdown_debugging && !do_pid1_crash_diagnosis_mode2() ) {
290 sleep(1);
291 }
292 }
293
294 int
295 basic_fork(void)
296 {
297 int wstatus = 0;
298 pid_t p;
299
300 switch ((p = fork())) {
301 case -1:
302 runtime_syslog(LOG_ERR | LOG_CONSOLE, "Can't fork PID 1 copy for crash debugging: %m");
303 return p;
304 case 0:
305 return p;
306 default:
307 #if 0
308 /* If we attach with the debugger, the kernel reparenting could
309 * cause this to return prematurely.
310 */
311 waitpid(p, &wstatus, 0);
312 #else
313 sleep(UINT_MAX);
314 #endif
315 if (WIFEXITED(wstatus)) {
316 if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
317 return 1;
318 } else {
319 fprintf(stdout, "PID 1 copy: exit status: %d\n", WEXITSTATUS(wstatus));
320 }
321 } else {
322 fprintf(stdout, "PID 1 copy: %s\n", strsignal(WTERMSIG(wstatus)));
323 }
324 return 1;
325 }
326
327 return -1;
328 }
329
330 bool
331 do_pid1_crash_diagnosis_mode2(void)
332 {
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.
336 */
337 if( g_console ) {
338 fflush(g_console);
339 }
340 task_set_bootstrap_port(mach_task_self(), MACH_PORT_NULL);
341 if( basic_fork() != 0 ) {
342 if( g_console ) {
343 fflush(g_console);
344 }
345 return true;
346 }
347 } else {
348 return true;
349 }
350
351 int fd;
352 revoke(_PATH_CONSOLE);
353 if ((fd = open(_PATH_CONSOLE, O_RDWR)) == -1) {
354 _exit(2);
355 }
356 if (login_tty(fd) == -1) {
357 _exit(3);
358 }
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");
369 fflush(stdout);
370
371 execl(_PATH_BSHELL, "-sh", NULL);
372 syslog(LOG_ERR, "can't exec %s for PID 1 crash debugging: %m", _PATH_BSHELL);
373 _exit(EXIT_FAILURE);
374 }
375
376 void
377 fatal_signal_handler(int sig, siginfo_t *si, void *uap __attribute__((unused)))
378 {
379 const char *doom_why = "at instruction";
380 char *sample_args[] = { "/usr/bin/sample", "1", "1", "-file", PID1_CRASH_LOGFILE, NULL };
381 pid_t sample_p;
382 int wstatus;
383
384 crash_addr = si->si_addr;
385 crash_pid = si->si_pid;
386
387 unlink(PID1_CRASH_LOGFILE);
388
389 switch ((sample_p = vfork())) {
390 case 0:
391 execve(sample_args[0], sample_args, environ);
392 _exit(EXIT_FAILURE);
393 break;
394 default:
395 waitpid(sample_p, &wstatus, 0);
396 break;
397 case -1:
398 break;
399 }
400
401 do_pid1_crash_diagnosis_mode();
402
403 switch (sig) {
404 default:
405 case 0:
406 break;
407 case SIGBUS:
408 case SIGSEGV:
409 doom_why = "trying to read/write";
410 case SIGILL:
411 case SIGFPE:
412 runtime_syslog(LOG_EMERG, "We crashed %s: %p (sent by PID %u)", doom_why, crash_addr, crash_pid);
413 sync();
414 sleep(3);
415 reboot(0);
416 break;
417 }
418 }
419
420 void
421 pid1_magic_init(void)
422 {
423 launchd_assumes(setsid() != -1);
424 launchd_assumes(chdir("/") != -1);
425 launchd_assumes(setlogin("root") != -1);
426
427 strcpy(g_my_label, "com.apple.launchd");
428
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,
435 };
436
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));
439 _exit(EXIT_FAILURE);
440 }
441
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);
445 }
446
447 g_audit_session_port = _audit_session_self();
448 #endif
449
450 strcpy(g_launchd_database_dir, LAUNCHD_DB_PREFIX "/com.apple.launchd");
451 }
452
453 char *
454 launchd_data_base_path(int db_type)
455 {
456 static char result[PATH_MAX];
457 static int last_db_type = -1;
458
459 if( db_type == last_db_type ) {
460 return result;
461 }
462
463 switch( 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;
467 break;
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;
471 break;
472 default :
473 break;
474 }
475
476 return result;
477 }
478
479 int
480 _fd(int fd)
481 {
482 if (fd >= 0) {
483 launchd_assumes(fcntl(fd, F_SETFD, 1) != -1);
484 }
485 return fd;
486 }
487
488 void
489 launchd_shutdown(void)
490 {
491 int64_t now;
492
493 if (shutdown_in_progress) {
494 return;
495 }
496
497 runtime_ktrace0(RTKT_LAUNCHD_EXITING);
498
499 shutdown_in_progress = true;
500
501 if( pid1_magic || g_log_per_user_shutdown ) {
502 /*
503 * When this changes to a more sustainable API, update this:
504 * http://howto.apple.com/db.cgi?Debugging_Apps_Non-Responsive_At_Shutdown
505 */
506 runtime_setlogmask(LOG_UPTO(LOG_DEBUG));
507 }
508
509 runtime_log_push();
510
511 now = runtime_get_wall_time();
512
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);
515
516 launchd_assert(jobmgr_shutdown(root_jobmgr) != NULL);
517
518 #if HAVE_LIBAUDITD
519 if( pid1_magic ) {
520 launchd_assumes(audit_quick_stop() == 0);
521 }
522 #endif
523 }
524
525 void
526 launchd_single_user(void)
527 {
528 runtime_syslog(LOG_NOTICE, "Going to single-user mode");
529
530 re_exec_in_single_user_mode = true;
531
532 launchd_shutdown();
533
534 sleep(3);
535
536 runtime_kill(-1, SIGKILL);
537 }
538
539 void
540 launchd_SessionCreate(void)
541 {
542 #if HAVE_SECURITY
543 OSStatus (*sescr)(SessionCreationFlags flags, SessionAttributeBits attributes);
544 void *seclib;
545
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);
549 }
550 launchd_assumes(dlclose(seclib) != -1);
551 }
552 #endif
553 }
554
555 void
556 testfd_or_openfd(int fd, const char *path, int flags)
557 {
558 int tmpfd;
559
560 if (-1 != (tmpfd = dup(fd))) {
561 launchd_assumes(runtime_close(tmpfd) == 0);
562 } else {
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);
568 }
569 }
570 }
571
572 bool
573 get_network_state(void)
574 {
575 struct ifaddrs *ifa, *ifai;
576 bool up = false;
577 int r;
578
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);
583 }
584
585 if (!launchd_assumes(r != -1)) {
586 return network_up;
587 }
588
589 for (ifai = ifa; ifai; ifai = ifai->ifa_next) {
590 if (!(ifai->ifa_flags & IFF_UP)) {
591 continue;
592 }
593 if (ifai->ifa_flags & IFF_LOOPBACK) {
594 continue;
595 }
596 if (ifai->ifa_addr->sa_family != AF_INET && ifai->ifa_addr->sa_family != AF_INET6) {
597 continue;
598 }
599 up = true;
600 break;
601 }
602
603 freeifaddrs(ifa);
604
605 return up;
606 }
607
608 void
609 monitor_networking_state(void)
610 {
611 int pfs = _fd(socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT));
612 struct kev_request kev_req;
613
614 network_up = get_network_state();
615
616 if (!launchd_assumes(pfs != -1)) {
617 return;
618 }
619
620 memset(&kev_req, 0, sizeof(kev_req));
621 kev_req.vendor_code = KEV_VENDOR_APPLE;
622 kev_req.kev_class = KEV_NETWORK_CLASS;
623
624 if (!launchd_assumes(ioctl(pfs, SIOCSKEVFILT, &kev_req) != -1)) {
625 runtime_close(pfs);
626 return;
627 }
628
629 launchd_assumes(kevent_mod(pfs, EVFILT_READ, EV_ADD, 0, 0, &kqpfsystem_callback) != -1);
630 }
631
632 void
633 pfsystem_callback(void *obj __attribute__((unused)), struct kevent *kev)
634 {
635 bool new_networking_state;
636 char buf[1024];
637
638 launchd_assumes(read((int)kev->ident, &buf, sizeof(buf)) != -1);
639
640 new_networking_state = get_network_state();
641
642 if (new_networking_state != network_up) {
643 network_up = new_networking_state;
644 jobmgr_dispatch_all_semaphores(root_jobmgr);
645 }
646 }
647
648 void
649 _log_launchd_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test)
650 {
651 int saved_errno = errno;
652 char buf[100];
653 const char *file = strrchr(path, '/');
654 char *rcs_rev_tmp = strchr(rcs_rev, ' ');
655
656 runtime_ktrace1(RTKT_LAUNCHD_BUG);
657
658 if (!file) {
659 file = path;
660 } else {
661 file += 1;
662 }
663
664 if (!rcs_rev_tmp) {
665 strlcpy(buf, rcs_rev, sizeof(buf));
666 } else {
667 strlcpy(buf, rcs_rev_tmp + 1, sizeof(buf));
668 rcs_rev_tmp = strchr(buf, ' ');
669 if (rcs_rev_tmp) {
670 *rcs_rev_tmp = '\0';
671 }
672 }
673
674 runtime_syslog(LOG_NOTICE, "Bug: %s:%u (%s):%u: %s", file, line, buf, saved_errno, test);
675 }