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#include "config.h"
22#include "launchd.h"
23
24#include <sys/types.h>
25#include <sys/queue.h>
26#include <sys/event.h>
27#include <sys/stat.h>
28#include <sys/ucred.h>
29#include <sys/fcntl.h>
30#include <sys/un.h>
31#include <sys/wait.h>
32#include <sys/sysctl.h>
33#include <sys/sockio.h>
34#include <sys/time.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>
42#include <net/if.h>
43#include <netinet/in.h>
44#include <netinet/in_var.h>
45#include <netinet6/nd6.h>
46#include <ifaddrs.h>
47#include <unistd.h>
48#include <signal.h>
49#include <errno.h>
50#include <libgen.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <stdarg.h>
54#include <stdbool.h>
55#include <paths.h>
56#include <pwd.h>
57#include <grp.h>
58#include <ttyent.h>
59#include <dlfcn.h>
60#include <dirent.h>
61#include <string.h>
62#include <setjmp.h>
63#include <spawn.h>
64#include <sched.h>
65#include <pthread.h>
66#include <util.h>
67#include <os/assumes.h>
68
69#if HAVE_LIBAUDITD
70#include <bsm/auditd_lib.h>
71#include <bsm/audit_session.h>
72#endif
73
74#include "bootstrap.h"
75#include "vproc.h"
76#include "vproc_priv.h"
77#include "vproc_internal.h"
78#include "launch.h"
79#include "launch_internal.h"
80
81#include "runtime.h"
82#include "core.h"
83#include "ipc.h"
84
85#define LAUNCHD_CONF ".launchd.conf"
86
87extern char **environ;
88
89static void pfsystem_callback(void *, struct kevent *);
90
91static kq_callback kqpfsystem_callback = pfsystem_callback;
92
93static void pid1_magic_init(void);
94
95static void testfd_or_openfd(int fd, const char *path, int flags);
96static bool get_network_state(void);
97static void monitor_networking_state(void);
98static void fatal_signal_handler(int sig, siginfo_t *si, void *uap);
99static void handle_pid1_crashes_separately(void);
100static void do_pid1_crash_diagnosis_mode(const char *msg);
101static int basic_fork(void);
102static bool do_pid1_crash_diagnosis_mode2(const char *msg);
103
104static void *update_thread(void *nothing);
105
106static void *crash_addr;
107static pid_t crash_pid;
108
109char *_launchd_database_dir;
110char *_launchd_log_dir;
111
112bool launchd_shutting_down;
113bool network_up;
114uid_t launchd_uid;
115FILE *launchd_console = NULL;
116int32_t launchd_sync_frequency = 30;
117
118int
119main(int argc, char *const *argv)
120{
121	bool sflag = false;
122	int ch;
123
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().
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 (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);
140		} else {
141			unsetenv("DYLD_INSERT_LIBRARIES");
142			unsetenv("MALLOC_STRICT_SIZE");
143		}
144	} else if (launchd_malloc_log_stacks) {
145		if (!getenv("MallocStackLogging")) {
146			setenv("MallocStackLogging", "1", 1);
147			execv(argv[0], argv);
148		} else {
149			unsetenv("MallocStackLogging");
150		}
151	}
152
153	while ((ch = getopt(argc, argv, "s")) != -1) {
154		switch (ch) {
155		case 's': sflag = true; break;	/* single user */
156		case '?': /* we should do something with the global optopt variable here */
157		default:
158			fprintf(stderr, "%s: ignoring unknown arguments\n", getprogname());
159			break;
160		}
161	}
162
163	if (getpid() != 1 && getppid() != 1) {
164		fprintf(stderr, "%s: This program is not meant to be run directly.\n", getprogname());
165		exit(EXIT_FAILURE);
166	}
167
168	launchd_runtime_init();
169
170	if (NULL == getenv("PATH")) {
171		setenv("PATH", _PATH_STDPATH, 1);
172	}
173
174	if (pid1_magic) {
175		pid1_magic_init();
176
177		int cfd = -1;
178		if ((cfd = open(_PATH_CONSOLE, O_WRONLY | O_NOCTTY)) != -1) {
179			_fd(cfd);
180			if (!(launchd_console = fdopen(cfd, "w"))) {
181				(void)close(cfd);
182			}
183		}
184
185		char *extra = "";
186		if (launchd_osinstaller) {
187			extra = " in the OS Installer";
188		} else if (sflag) {
189			extra = " in single-user mode";
190		}
191
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. ***");
195		}
196
197		if (launchd_verbose_boot) {
198			launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Verbose boot, will log to /dev/console. ***");
199		}
200
201		if (launchd_shutdown_debugging) {
202			launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Shutdown debugging is enabled. ***");
203		}
204
205		if (launchd_log_shutdown) {
206			launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Shutdown logging is enabled. ***");
207		}
208
209		if (launchd_log_perf) {
210			launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Performance logging is enabled. ***");
211		}
212
213		if (launchd_log_debug) {
214			launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** Debug logging is enabled. ***");
215		}
216
217		handle_pid1_crashes_separately();
218
219		/* Start the update thread.
220		 *
221		 * <rdar://problem/5039559&6153301>
222		 */
223		pthread_t t = NULL;
224		(void)os_assumes_zero(pthread_create(&t, NULL, update_thread, NULL));
225		(void)os_assumes_zero(pthread_detach(t));
226
227		/* PID 1 doesn't have a flat namespace. */
228		launchd_flat_mach_namespace = false;
229		fflush(launchd_console);
230	} else {
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";
235		}
236
237		struct passwd *pwent = getpwuid(launchd_uid);
238		if (pwent) {
239			launchd_username = strdup(pwent->pw_name);
240		} else {
241			launchd_username = "(unknown)";
242		}
243
244		if (asprintf(&_launchd_database_dir, LAUNCHD_DB_PREFIX "/com.apple.launchd.peruser.%u", launchd_uid) == 0) {
245			_launchd_database_dir = "";
246		}
247
248		if (asprintf(&_launchd_log_dir, LAUNCHD_LOG_PREFIX "/com.apple.launchd.peruser.%u", launchd_uid) == 0) {
249			_launchd_log_dir = "";
250		}
251
252		if (launchd_allow_global_dyld_envvars) {
253			launchd_syslog(LOG_WARNING, "Per-user launchd will allow DYLD_* environment variables in the global environment.");
254		}
255
256		ipc_server_init();
257		launchd_log_push();
258
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);
263		}
264
265		launchd_audit_port = _audit_session_self();
266
267		vproc_transaction_begin(NULL);
268		vproc_transaction_end(NULL, NULL);
269
270		launchd_syslog(LOG_DEBUG, "Per-user launchd started (UID/username): %u/%s.", launchd_uid, launchd_username);
271	}
272
273	monitor_networking_state();
274	jobmgr_init(sflag);
275
276	launchd_runtime_init2();
277	launchd_runtime();
278}
279
280void
281handle_pid1_crashes_separately(void)
282{
283	struct sigaction fsa;
284
285	fsa.sa_sigaction = fatal_signal_handler;
286	fsa.sa_flags = SA_SIGINFO;
287	sigemptyset(&fsa.sa_mask);
288
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));
295}
296
297void *
298update_thread(void *nothing __attribute__((unused)))
299{
300	(void)posix_assumes_zero(setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD, IOPOL_THROTTLE));
301
302	while (launchd_sync_frequency) {
303		sync();
304		sleep(launchd_sync_frequency);
305	}
306
307	launchd_syslog(LOG_DEBUG, "Update thread exiting.");
308	return NULL;
309}
310
311#define PID1_CRASH_LOGFILE "/var/log/launchd-pid1.crash"
312
313/* This hack forces the dynamic linker to resolve these symbols ASAP */
314static __attribute__((unused)) typeof(sync) *__junk_dyld_trick1 = sync;
315static __attribute__((unused)) typeof(sleep) *__junk_dyld_trick2 = sleep;
316static __attribute__((unused)) typeof(reboot) *__junk_dyld_trick3 = reboot;
317
318void
319do_pid1_crash_diagnosis_mode(const char *msg)
320{
321	if (launchd_wsp) {
322		kill(launchd_wsp, SIGKILL);
323		sleep(3);
324		launchd_wsp = 0;
325	}
326
327	while (launchd_shutdown_debugging && !do_pid1_crash_diagnosis_mode2(msg)) {
328		sleep(1);
329	}
330}
331
332int
333basic_fork(void)
334{
335	int wstatus = 0;
336	pid_t p;
337
338	switch ((p = fork())) {
339	case -1:
340		launchd_syslog(LOG_ERR | LOG_CONSOLE, "Can't fork PID 1 copy for crash debugging: %m");
341		return p;
342	case 0:
343		return p;
344	default:
345		do {
346			(void)waitpid(p, &wstatus, 0);
347		} while(!WIFEXITED(wstatus));
348
349		fprintf(stdout, "PID 1 copy: exit status: %d\n", WEXITSTATUS(wstatus));
350
351		return 1;
352	}
353
354	return -1;
355}
356
357bool
358do_pid1_crash_diagnosis_mode2(const char *msg)
359{
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.
363		 */
364		if (launchd_console) {
365			fflush(launchd_console);
366		}
367
368		task_set_bootstrap_port(mach_task_self(), MACH_PORT_NULL);
369		if (basic_fork() != 0) {
370			if (launchd_console) {
371				fflush(launchd_console);
372			}
373
374			return true;
375		}
376	} else {
377		return true;
378	}
379
380	int fd;
381	revoke(_PATH_CONSOLE);
382	if ((fd = open(_PATH_CONSOLE, O_RDWR)) == -1) {
383		_exit(2);
384	}
385	if (login_tty(fd) == -1) {
386		_exit(3);
387	}
388
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");
400	fflush(stdout);
401
402	execl(_PATH_BSHELL, "-sh", NULL);
403	syslog(LOG_ERR, "can't exec %s for PID 1 crash debugging: %m", _PATH_BSHELL);
404	_exit(EXIT_FAILURE);
405}
406
407void
408fatal_signal_handler(int sig, siginfo_t *si, void *uap __attribute__((unused)))
409{
410	const char *doom_why = "at instruction";
411	char msg[128];
412#if 0
413	char *sample_args[] = { "/usr/bin/sample", "1", "1", "-file", PID1_CRASH_LOGFILE, NULL };
414	pid_t sample_p;
415	int wstatus;
416#endif
417
418	crash_addr = si->si_addr;
419	crash_pid = si->si_pid;
420#if 0
421	setenv("XPC_SERVICES_UNAVAILABLE", "1", 0);
422	unlink(PID1_CRASH_LOGFILE);
423
424	switch ((sample_p = vfork())) {
425	case 0:
426		execve(sample_args[0], sample_args, environ);
427		_exit(EXIT_FAILURE);
428		break;
429	default:
430		waitpid(sample_p, &wstatus, 0);
431		break;
432	case -1:
433		break;
434	}
435#endif
436	switch (sig) {
437	default:
438	case 0:
439		break;
440	case SIGBUS:
441	case SIGSEGV:
442		doom_why = "trying to read/write";
443	case SIGILL:
444	case SIGFPE:
445	case SIGTRAP:
446		snprintf(msg, sizeof(msg), "%s: %p (%s sent by PID %u)", doom_why, crash_addr, strsignal(sig), crash_pid);
447		sync();
448		do_pid1_crash_diagnosis_mode(msg);
449		sleep(3);
450		reboot(0);
451		break;
452	}
453}
454
455void
456pid1_magic_init(void)
457{
458	launchd_label = "com.apple.launchd";
459	launchd_username = "system";
460
461	_launchd_database_dir = LAUNCHD_DB_PREFIX "/com.apple.launchd";
462	_launchd_log_dir = LAUNCHD_LOG_PREFIX "/com.apple.launchd";
463
464	(void)posix_assumes_zero(setsid());
465	(void)posix_assumes_zero(chdir("/"));
466	(void)posix_assumes_zero(setlogin("root"));
467
468#if !TARGET_OS_EMBEDDED
469	auditinfo_addr_t auinfo = {
470		.ai_termid = {
471			.at_type = AU_IPv4
472		},
473		.ai_asid = AU_ASSIGN_ASID,
474		.ai_auid = AU_DEFAUDITID,
475		.ai_flags = AU_SESSION_FLAG_IS_INITIAL,
476	};
477
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));
480		_exit(EXIT_FAILURE);
481	}
482
483	launchd_audit_session = auinfo.ai_asid;
484	launchd_syslog(LOG_DEBUG, "Audit Session ID: %i", launchd_audit_session);
485
486	launchd_audit_port = _audit_session_self();
487#endif // !TARGET_OS_EMBEDDED
488}
489
490char *
491launchd_copy_persistent_store(int type, const char *file)
492{
493	char *result = NULL;
494	if (!file) {
495		file = "";
496	}
497
498	switch (type) {
499	case LAUNCHD_PERSISTENT_STORE_DB:
500		(void)asprintf(&result, "%s/%s", _launchd_database_dir, file);
501		break;
502	case LAUNCHD_PERSISTENT_STORE_LOGS:
503		(void)asprintf(&result, "%s/%s", _launchd_log_dir, file);
504		break;
505	default:
506		break;
507	}
508
509	return result;
510}
511
512int
513_fd(int fd)
514{
515	if (fd >= 0) {
516		(void)posix_assumes_zero(fcntl(fd, F_SETFD, 1));
517	}
518	return fd;
519}
520
521void
522launchd_shutdown(void)
523{
524	int64_t now;
525
526	if (launchd_shutting_down) {
527		return;
528	}
529
530	runtime_ktrace0(RTKT_LAUNCHD_EXITING);
531
532	launchd_shutting_down = true;
533	launchd_log_push();
534
535	now = runtime_get_wall_time();
536
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);
539
540	os_assert(jobmgr_shutdown(root_jobmgr) != NULL);
541
542#if HAVE_LIBAUDITD
543	if (pid1_magic) {
544		(void)os_assumes_zero(audit_quick_stop());
545	}
546#endif
547}
548
549void
550launchd_SessionCreate(void)
551{
552#if !TARGET_OS_EMBEDDED
553	auditinfo_addr_t auinfo = {
554		.ai_termid = { .at_type = AU_IPv4 },
555		.ai_asid = AU_ASSIGN_ASID,
556		.ai_auid = getuid(),
557		.ai_flags = 0,
558	};
559	if (setaudit_addr(&auinfo, sizeof(auinfo)) == 0) {
560		char session[16];
561		snprintf(session, sizeof(session), "%x", auinfo.ai_asid);
562		setenv("SECURITYSESSIONID", session, 1);
563	} else {
564		launchd_syslog(LOG_WARNING, "Could not set audit session: %d: %s.", errno, strerror(errno));
565	}
566#endif // !TARGET_OS_EMBEDDED
567}
568
569void
570testfd_or_openfd(int fd, const char *path, int flags)
571{
572	int tmpfd;
573
574	if (-1 != (tmpfd = dup(fd))) {
575		(void)posix_assumes_zero(runtime_close(tmpfd));
576	} else {
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));
582		}
583	}
584}
585
586bool
587get_network_state(void)
588{
589	struct ifaddrs *ifa, *ifai;
590	bool up = false;
591	int r;
592
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());
597	}
598
599	if (posix_assumes_zero(r) == -1) {
600		return network_up;
601	}
602
603	for (ifai = ifa; ifai; ifai = ifai->ifa_next) {
604		if (!(ifai->ifa_flags & IFF_UP)) {
605			continue;
606		}
607		if (ifai->ifa_flags & IFF_LOOPBACK) {
608			continue;
609		}
610		if (ifai->ifa_addr->sa_family != AF_INET && ifai->ifa_addr->sa_family != AF_INET6) {
611			continue;
612		}
613		up = true;
614		break;
615	}
616
617	freeifaddrs(ifa);
618
619	return up;
620}
621
622void
623monitor_networking_state(void)
624{
625	int pfs = _fd(socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT));
626	struct kev_request kev_req;
627
628	network_up = get_network_state();
629
630	if (pfs == -1) {
631		(void)os_assumes_zero(errno);
632		return;
633	}
634
635	memset(&kev_req, 0, sizeof(kev_req));
636	kev_req.vendor_code = KEV_VENDOR_APPLE;
637	kev_req.kev_class = KEV_NETWORK_CLASS;
638
639	if (posix_assumes_zero(ioctl(pfs, SIOCSKEVFILT, &kev_req)) == -1) {
640		runtime_close(pfs);
641		return;
642	}
643
644	(void)posix_assumes_zero(kevent_mod(pfs, EVFILT_READ, EV_ADD, 0, 0, &kqpfsystem_callback));
645}
646
647void
648pfsystem_callback(void *obj __attribute__((unused)), struct kevent *kev)
649{
650	bool new_networking_state;
651	char buf[1024];
652
653	(void)posix_assumes_zero(read((int)kev->ident, &buf, sizeof(buf)));
654
655	new_networking_state = get_network_state();
656
657	if (new_networking_state != network_up) {
658		network_up = new_networking_state;
659		jobmgr_dispatch_all_semaphores(root_jobmgr);
660	}
661}
662