xref: /NextBSD/sbin/launchd/runtime.c (revision 38677e281787d115c07c9b5bff7c36ce0496b2d9)
1 /*
2  * Copyright (c) 1999-2008 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 "runtime.h"
23 
24 #include <mach/mach.h>
25 #include <mach/mach_error.h>
26 #include <mach/boolean.h>
27 #include <mach/message.h>
28 #include <mach/notify.h>
29 #include <mach/mig_errors.h>
30 #include <mach/mach_traps.h>
31 #include <mach/mach_interface.h>
32 #include <mach/host_info.h>
33 #include <mach/mach_host.h>
34 #include <mach/mach_time.h>
35 #include <mach/exception.h>
36 #include <sys/types.h>
37 #include <sys/event.h>
38 #include <sys/stat.h>
39 #include <sys/sysctl.h>
40 #include <sys/time.h>
41 #include <sys/proc.h>
42 #include <sys/proc_info.h>
43 #include <libproc.h>
44 #include <sys/event.h>
45 #include <sys/queue.h>
46 #include <sys/socket.h>
47 #include <sys/mount.h>
48 #include <sys/reboot.h>
49 #include <sys/fcntl.h>
50 #include <sys/kdebug.h>
51 #include <sys/wait.h>
52 #include <bsm/libbsm.h>
53 #include <unistd.h>
54 #include <pthread.h>
55 #include <errno.h>
56 #include <string.h>
57 #include <ctype.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <stdbool.h>
61 #include <syslog.h>
62 #include <signal.h>
63 #include <dlfcn.h>
64 #include <os/assumes.h>
65 
66 #include <bsm/libbsm.h>
67 
68 
69 #include "internalServer.h"
70 #include "internal.h"
71 #include "notifyServer.h"
72 #include "mach_excServer.h"
73 
74 /* We shouldn't be including these */
75 #include "launch.h"
76 #include "launchd.h"
77 #include "core.h"
78 #include "vproc.h"
79 #include "vproc_priv.h"
80 #include "vproc_internal.h"
81 #include "jobServer.h"
82 #include "job_reply.h"
83 #include <xpc/launchd.h>
84 #include "shim.h"
85 static mach_port_t ipc_port_set;
86 static mach_port_t demand_port_set;
87 static mach_port_t launchd_internal_port;
88 static int mainkq;
89 
90 #define BULK_KEV_MAX 100
91 static struct kevent *bulk_kev;
92 static int bulk_kev_i;
93 static int bulk_kev_cnt;
94 
95 static pthread_t kqueue_demand_thread;
96 static pthread_t waitpid_thread;
97 
98 static void mportset_callback(void);
99 static kq_callback kqmportset_callback = (kq_callback)mportset_callback;
100 static void *kqueue_demand_loop(void *arg);
101 static void *waitpid_loop(void *arg);
102 
103 boolean_t launchd_internal_demux(mach_msg_header_t *Request, mach_msg_header_t *Reply);
104 static void launchd_runtime2(mach_msg_size_t msg_size);
105 static mach_msg_size_t max_msg_size;
106 static mig_callback *mig_cb_table;
107 static size_t mig_cb_table_sz;
108 static timeout_callback runtime_idle_callback;
109 static mach_msg_timeout_t runtime_idle_timeout;
110 static struct ldcred ldc;
111 static audit_token_t ldc_token;
112 static size_t runtime_standby_cnt;
113 
114 static void do_file_init(void) __attribute__((constructor));
115 static mach_timebase_info_data_t tbi;
116 static uint64_t tbi_safe_math_max;
117 static uint64_t time_of_mach_msg_return;
118 static double tbi_float_val;
119 
120 
121 #define VERBOSE_DEBUGGING 0
122 
123 #if VERBOSE_DEBUGGING
124 #define _launchd_syslog syslog
125 #else
126 #define _launchd_syslog launchd_syslog
127 #endif
128 
129 
130 static const int init_compat_signals[] = {
131 	SIGUSR1,	// halt
132 	SIGUSR2,	// halt & power off
133 	SIGTERM,	// Go to single user mode
134 	SIGINT,	// Reboot
135 	SIGTSTP,	// Stop further logins
136 };
137 
138 static const int sigigns[] = { SIGHUP, SIGPIPE, SIGALRM,
139 	SIGURG, SIGTSTP, SIGCONT, SIGTTIN, SIGTTOU, SIGIO, SIGXCPU,
140 	SIGXFSZ, SIGVTALRM, SIGPROF, SIGWINCH, SIGINFO,
141 };
142 static sigset_t sigign_set;
143 bool pid1_magic;
144 bool launchd_apple_internal;
145 bool launchd_flat_mach_namespace = true;
146 bool launchd_malloc_log_stacks = false;
147 bool launchd_use_gmalloc = false;
148 #if !TARGET_OS_EMBEDDED
149 bool launchd_log_shutdown = true;
150 #else
151 bool launchd_log_shutdown = false;
152 #endif
153 bool launchd_log_perf = false;
154 bool launchd_log_debug = false;
155 bool launchd_trap_sigkill_bugs = false;
156 bool launchd_no_jetsam_perm_check = false;
157 bool launchd_osinstaller = false;
158 bool launchd_allow_global_dyld_envvars = false;
159 #if TARGET_OS_EMBEDDED
160 bool launchd_appletv = false;
161 #endif
162 pid_t launchd_wsp = 0;
163 size_t runtime_busy_cnt;
164 
165 #if TARGET_OS_EMBEDDED
166 #define LAUNCHD_CONFIG_PREFIX "/"
167 #else
168 #define LAUNCHD_CONFIG_PREFIX "/private/var/db/"
169 #endif
170 
171 #define config_check(s, sb) (stat(LAUNCHD_CONFIG_PREFIX s, &sb) == 0)
172 
173 #ifdef notyet
174 /* defined in libbsm */
175 int audit_set_terminal_host(uint32_t *m);
176 
177 int
audit_set_terminal_host(uint32_t * m)178 audit_set_terminal_host(uint32_t *m)
179 {
180 
181 #ifdef KERN_HOSTID
182         int name[2] = { CTL_KERN, KERN_HOSTID };
183         size_t len;
184 
185         if (m == NULL)
186                 return (kAUBadParamErr);
187         *m = 0;
188         len = sizeof(*m);
189         if (sysctl(name, 2, m, &len, NULL, 0) != 0) {
190                 syslog(LOG_ERR, "sysctl() failed (%s)", strerror(errno));
191                 return (kAUSysctlErr);
192         }
193         return (kAUNoErr);
194 #else
195         *m = -1;
196         return (kAUNoErr);
197 #endif
198 }
199 #endif
200 
201 mach_port_t
runtime_get_kernel_port(void)202 runtime_get_kernel_port(void)
203 {
204 	return launchd_internal_port;
205 }
206 
207 union vproc_mig_max_sz {
208 	union __RequestUnion__job_mig_job_subsystem req;
209 	union __ReplyUnion__job_mig_job_subsystem rep;
210 };
211 
212 union internal_max_sz {
213 	union __RequestUnion__x_internal_subsystem req;
214 	union __ReplyUnion__internal_subsystem rep;
215 };
216 
217 union xpc_domain_max_sz {
218 #ifdef notyet
219 	union __RequestUnion__xpc_domain_xpc_domain_subsystem req;
220 	union __ReplyUnion__xpc_domain_xpc_domain_subsystem rep;
221 #endif
222 };
223 
224 union mach_exc_max_sz {
225 	union __RequestUnion__catch_mach_exc_subsystem req;
226 	union __ReplyUnion__catch_mach_exc_subsystem rep;
227 };
228 
229 union do_notify_max_sz {
230 	union __RequestUnion__do_notify_subsystem req;
231 	union __ReplyUnion__do_notify_subsystem rep;
232 };
233 
234 void
launchd_runtime_init(void)235 launchd_runtime_init(void)
236 {
237 #ifdef notyet
238 	pid_t p = getpid();
239 #endif
240 	(void)posix_assert_zero((mainkq = kqueue()));
241 
242 	os_assert_zero(mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &demand_port_set));
243 	os_assert_zero(mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &ipc_port_set));
244 	posix_assert_zero(kevent_mod(demand_port_set, EVFILT_MACHPORT, EV_ADD, 0, 0, &kqmportset_callback));
245 
246 	os_assert_zero(launchd_mport_create_recv(&launchd_internal_port));
247 	os_assert_zero(launchd_mport_make_send(launchd_internal_port));
248 
249 	max_msg_size = sizeof(union vproc_mig_max_sz);
250 #ifdef notyet
251 	if (sizeof(union xpc_domain_max_sz) > max_msg_size) {
252 		max_msg_size = sizeof(union xpc_domain_max_sz);
253 	}
254 #endif
255 
256 	os_assert_zero(runtime_add_mport(launchd_internal_port, launchd_internal_demux));
257 	os_assert_zero(pthread_create(&kqueue_demand_thread, NULL, kqueue_demand_loop, NULL));
258 	os_assert_zero(pthread_detach(kqueue_demand_thread));
259 
260 	os_assert_zero(pthread_create(&waitpid_thread, NULL, waitpid_loop, NULL));
261 	os_assert_zero(pthread_detach(waitpid_thread));
262 
263 #ifdef notyet
264 	(void)posix_assumes_zero(sysctlbyname("vfs.generic.noremotehang", NULL, NULL, &p, sizeof(p)));
265 #endif
266 }
267 
268 static void
sighandler_init_compat(int signo)269 sighandler_init_compat(int signo)
270 {
271 	int kr;
272 	int rflags = 0;
273 	_launchd_syslog(LOG_CRIT, "%s(%d)", __FUNCTION__, signo);
274 	switch (signo) {
275 	case SIGUSR2:
276 		rflags = RB_POWEROFF;
277 	case SIGUSR1:
278 		rflags |= RB_HALT;
279 		// halt and power off
280 		kr = job_mig_reboot2(root_jobmgr, rflags);
281 		if (kr != KERN_SUCCESS) {
282 			_launchd_syslog(LOG_CRIT, "%s(%d):  kr = %d", __FUNCTION__, __LINE__, kr);
283 		}
284 		break;
285 	case SIGTERM:
286 		_launchd_syslog(LOG_CRIT, "%s(%d):  Got SIGTERM", __FUNCTION__, __LINE__);
287 		// Single user mode
288 		break;
289 	case SIGINT:
290 		// Reboot
291 		kr = job_mig_reboot2(root_jobmgr, RB_AUTOBOOT);
292 		if (kr != KERN_SUCCESS) {
293 			_launchd_syslog(LOG_CRIT, "%s(%d):  kr = %d", __FUNCTION__, __LINE__, kr);
294 		}
295 		break;
296 	case SIGTSTP:
297 		// Block further logins
298 		break;
299 	case SIGHUP:
300 		// Rescan the ttys file
301 		break;
302 	default:
303 		_launchd_syslog(LOG_DEBUG, "%s:  unknown signal number %d", __FUNCTION__, signo);
304 		break;
305 	}
306 }
307 
308 void
launchd_runtime_init2(void)309 launchd_runtime_init2(void)
310 {
311 	size_t i;
312 
313 	__OS_COMPILETIME_ASSERT__(SIG_ERR == (typeof(SIG_ERR))-1);
314 	for (i = 0; i < (sizeof(init_compat_signals) / sizeof(int)); i++) {
315 		signal(init_compat_signals[i], sighandler_init_compat);
316 	}
317 	for (i = 0; i < (sizeof(sigigns) / sizeof(int)); i++) {
318 		sigaddset(&sigign_set, sigigns[i]);
319 		(void)posix_assumes_zero(signal(sigigns[i], SIG_IGN));
320 	}
321 }
322 
323 #define FLAGIF(f) if (flags & f) { flags_off += sprintf(flags_off, #f); flags &= ~f; }
324 const char *
reboot_flags_to_C_names(unsigned int flags)325 reboot_flags_to_C_names(unsigned int flags)
326 {
327 #define MAX_RB_STR "RB_ASKNAME|RB_SINGLE|RB_NOSYNC|RB_HALT|RB_INITNAME|RB_DFLTROOT|RB_ALTBOOT|RB_UNIPROC|RB_SAFEBOOT|RB_UPSDELAY|0xdeadbeeffeedface"
328 	static char flags_buf[sizeof(MAX_RB_STR)];
329 	char *flags_off = NULL;
330 
331 	if (flags == 0) {
332 		return "RB_AUTOBOOT";
333 	}
334 
335 	while (flags) {
336 		if (flags_off) {
337 			*flags_off = '|';
338 			flags_off++;
339 			*flags_off = '\0';
340 		} else {
341 			flags_off = flags_buf;
342 		}
343 
344 		FLAGIF(RB_ASKNAME)
345 		else FLAGIF(RB_SINGLE)
346 		else FLAGIF(RB_NOSYNC)
347 		else FLAGIF(RB_HALT)
348 		else FLAGIF(RB_INITNAME)
349 		else FLAGIF(RB_DFLTROOT)
350 		else FLAGIF(RB_ALTBOOT)
351 		else FLAGIF(RB_UNIPROC)
352 		else FLAGIF(RB_SAFEBOOT)
353 		else FLAGIF(RB_UPSDELAY)
354 		else {
355 			flags_off += sprintf(flags_off, "0x%x", flags);
356 			flags = 0;
357 		}
358 	}
359 
360 	return flags_buf;
361 }
362 
363 const char *
signal_to_C_name(unsigned int sig)364 signal_to_C_name(unsigned int sig)
365 {
366 	static char unknown[25];
367 
368 #define SIG2CASE(sg)	case sg: return #sg
369 
370 	switch (sig) {
371 	SIG2CASE(SIGHUP);
372 	SIG2CASE(SIGINT);
373 	SIG2CASE(SIGQUIT);
374 	SIG2CASE(SIGILL);
375 	SIG2CASE(SIGTRAP);
376 	SIG2CASE(SIGABRT);
377 	SIG2CASE(SIGFPE);
378 	SIG2CASE(SIGKILL);
379 	SIG2CASE(SIGBUS);
380 	SIG2CASE(SIGSEGV);
381 	SIG2CASE(SIGSYS);
382 	SIG2CASE(SIGPIPE);
383 	SIG2CASE(SIGALRM);
384 	SIG2CASE(SIGTERM);
385 	SIG2CASE(SIGURG);
386 	SIG2CASE(SIGSTOP);
387 	SIG2CASE(SIGTSTP);
388 	SIG2CASE(SIGCONT);
389 	SIG2CASE(SIGCHLD);
390 	SIG2CASE(SIGTTIN);
391 	SIG2CASE(SIGTTOU);
392 	SIG2CASE(SIGIO);
393 	SIG2CASE(SIGXCPU);
394 	SIG2CASE(SIGXFSZ);
395 	SIG2CASE(SIGVTALRM);
396 	SIG2CASE(SIGPROF);
397 	SIG2CASE(SIGWINCH);
398 	SIG2CASE(SIGINFO);
399 	SIG2CASE(SIGUSR1);
400 	SIG2CASE(SIGUSR2);
401 	default:
402 		snprintf(unknown, sizeof(unknown), "%u", sig);
403 		return unknown;
404 	}
405 }
406 
407 void
log_kevent_struct(int level,struct kevent * kev_base,int indx)408 log_kevent_struct(int level, struct kevent *kev_base, int indx)
409 {
410 	struct kevent *kev = &kev_base[indx];
411 	const char *filter_str;
412 	char ident_buf[100];
413 	char filter_buf[100];
414 	char fflags_buf[1000];
415 	char flags_buf[1000] = "0x0";
416 	char *flags_off = NULL;
417 	char *fflags_off = NULL;
418 	unsigned short flags = kev->flags;
419 	unsigned int fflags = kev->fflags;
420 
421 	if (likely(!(LOG_MASK(level & ~LOG_CONSOLE) & LOG_DEBUG))) {
422 		return;
423 	}
424 
425 	if (flags) while (flags) {
426 		if (flags_off) {
427 			*flags_off = '|';
428 			flags_off++;
429 			*flags_off = '\0';
430 		} else {
431 			flags_off = flags_buf;
432 		}
433 
434 		FLAGIF(EV_ADD)
435 		else FLAGIF(EV_RECEIPT)
436 		else FLAGIF(EV_DELETE)
437 		else FLAGIF(EV_ENABLE)
438 		else FLAGIF(EV_DISABLE)
439 		else FLAGIF(EV_CLEAR)
440 		else FLAGIF(EV_EOF)
441 		else FLAGIF(EV_ONESHOT)
442 		else FLAGIF(EV_ERROR)
443 		else {
444 			flags_off += sprintf(flags_off, "0x%hx", flags);
445 			flags = 0;
446 		}
447 	}
448 
449 	snprintf(ident_buf, sizeof(ident_buf), "%ld", kev->ident);
450 	snprintf(fflags_buf, sizeof(fflags_buf), "0x%x", fflags);
451 
452 	switch (kev->filter) {
453 	case EVFILT_READ:
454 		filter_str = "EVFILT_READ";
455 		break;
456 	case EVFILT_WRITE:
457 		filter_str = "EVFILT_WRITE";
458 		break;
459 	case EVFILT_AIO:
460 		filter_str = "EVFILT_AIO";
461 		break;
462 	case EVFILT_VNODE:
463 		filter_str = "EVFILT_VNODE";
464 		if (fflags) while (fflags) {
465 			if (fflags_off) {
466 				*fflags_off = '|';
467 				fflags_off++;
468 				*fflags_off = '\0';
469 			} else {
470 				fflags_off = fflags_buf;
471 			}
472 
473 #define FFLAGIF(ff) if (fflags & ff) { fflags_off += sprintf(fflags_off, #ff); fflags &= ~ff; }
474 
475 			FFLAGIF(NOTE_DELETE)
476 			else FFLAGIF(NOTE_WRITE)
477 			else FFLAGIF(NOTE_EXTEND)
478 			else FFLAGIF(NOTE_ATTRIB)
479 			else FFLAGIF(NOTE_LINK)
480 			else FFLAGIF(NOTE_RENAME)
481 			else FFLAGIF(NOTE_REVOKE)
482 			else {
483 				fflags_off += sprintf(fflags_off, "0x%x", fflags);
484 				fflags = 0;
485 			}
486 		}
487 		break;
488 	case EVFILT_PROC:
489 		filter_str = "EVFILT_PROC";
490 		if (fflags) while (fflags) {
491 			if (fflags_off) {
492 				*fflags_off = '|';
493 				fflags_off++;
494 				*fflags_off = '\0';
495 			} else {
496 				fflags_off = fflags_buf;
497 			}
498 
499 			FFLAGIF(NOTE_EXIT)
500 			else FFLAGIF(NOTE_FORK)
501 			else FFLAGIF(NOTE_EXEC)
502 			else FFLAGIF(NOTE_SIGNAL)
503 			else FFLAGIF(NOTE_TRACK)
504 			else FFLAGIF(NOTE_TRACKERR)
505 			else FFLAGIF(NOTE_CHILD)
506 			else {
507 				fflags_off += sprintf(fflags_off, "0x%x", fflags);
508 				fflags = 0;
509 			}
510 		}
511 		break;
512 	case EVFILT_SIGNAL:
513 		filter_str = "EVFILT_SIGNAL";
514 		strcpy(ident_buf, signal_to_C_name(kev->ident));
515 		break;
516 	case EVFILT_TIMER:
517 		filter_str = "EVFILT_TIMER";
518 		snprintf(ident_buf, sizeof(ident_buf), "0x%lx", kev->ident);
519 		if (fflags) while (fflags) {
520 			if (fflags_off) {
521 				*fflags_off = '|';
522 				fflags_off++;
523 				*fflags_off = '\0';
524 			} else {
525 				fflags_off = fflags_buf;
526 			}
527 
528 			FFLAGIF(NOTE_SECONDS)
529 			else FFLAGIF(NOTE_USECONDS)
530 			else FFLAGIF(NOTE_NSECONDS)
531 			else FFLAGIF(NOTE_ABSOLUTE)
532 			else {
533 				fflags_off += sprintf(fflags_off, "0x%x", fflags);
534 				fflags = 0;
535 			}
536 		}
537 		break;
538 	case EVFILT_MACHPORT:
539 		filter_str = "EVFILT_MACHPORT";
540 		snprintf(ident_buf, sizeof(ident_buf), "0x%lx", kev->ident);
541 		break;
542 	case EVFILT_FS:
543 		filter_str = "EVFILT_FS";
544 		snprintf(ident_buf, sizeof(ident_buf), "0x%lx", kev->ident);
545 		if (fflags) while (fflags) {
546 			if (fflags_off) {
547 				*fflags_off = '|';
548 				fflags_off++;
549 				*fflags_off = '\0';
550 			} else {
551 				fflags_off = fflags_buf;
552 			}
553 
554 			FFLAGIF(VQ_NOTRESP)
555 			else FFLAGIF(VQ_NEEDAUTH)
556 			else FFLAGIF(VQ_LOWDISK)
557 			else FFLAGIF(VQ_MOUNT)
558 			else FFLAGIF(VQ_UNMOUNT)
559 			else FFLAGIF(VQ_DEAD)
560 			else FFLAGIF(VQ_ASSIST)
561 			else FFLAGIF(VQ_NOTRESPLOCK)
562 			else FFLAGIF(VQ_UPDATE)
563 			else {
564 				fflags_off += sprintf(fflags_off, "0x%x", fflags);
565 				fflags = 0;
566 			}
567 		}
568 		break;
569 	default:
570 		snprintf(filter_buf, sizeof(filter_buf), "%hd", kev->filter);
571 		filter_str = filter_buf;
572 		break;
573 	}
574 
575 	_launchd_syslog(level, "KEVENT[%d]: udata = %p data = 0x%lx ident = %s filter = %s flags = %s fflags = %s",
576 			indx, kev->udata, kev->data, ident_buf, filter_str, flags_buf, fflags_buf);
577 }
578 
579 void
mportset_callback(void)580 mportset_callback(void)
581 {
582 	mach_port_name_array_t members;
583 	mach_msg_type_number_t membersCnt;
584 	mach_port_status_t status;
585 	mach_msg_type_number_t statusCnt;
586 	struct kevent kev;
587 	unsigned int i;
588 
589 	if (os_assumes_zero(mach_port_get_set_status(mach_task_self(), demand_port_set, &members, &membersCnt)) != 0) {
590 		return;
591 	}
592 
593 	for (i = 0; i < membersCnt; i++) {
594 		statusCnt = MACH_PORT_RECEIVE_STATUS_COUNT;
595 		if (mach_port_get_attributes(mach_task_self(), members[i], MACH_PORT_RECEIVE_STATUS, (mach_port_info_t)&status,
596 					&statusCnt) != KERN_SUCCESS) {
597 			continue;
598 		}
599 		if (status.mps_msgcount) {
600 			EV_SET(&kev, members[i], EVFILT_MACHPORT, 0, 0, 0, job_find_by_service_port(members[i]));
601 #if 0
602 			if (kev.udata != NULL) {
603 #endif
604 				log_kevent_struct(LOG_DEBUG, &kev, 0);
605 				(*((kq_callback *)kev.udata))(kev.udata, &kev);
606 #if 0
607 			} else {
608 				log_kevent_struct(LOG_ERR, &kev, 0);
609 			}
610 #endif
611 			/* the callback may have tainted our ability to continue this for loop */
612 			break;
613 		}
614 	}
615 
616 	(void)os_assumes_zero(vm_deallocate(mach_task_self(), (vm_address_t)members, (vm_size_t) membersCnt * sizeof(mach_port_name_t)));
617 }
618 
619 void *
kqueue_demand_loop(void * arg)620 kqueue_demand_loop(void *arg __attribute__((unused)))
621 {
622 	fd_set rfds;
623 
624 	/*
625 	 * Yes, at first glance, calling select() on a kqueue seems silly.
626 	 *
627 	 * This avoids a race condition between the main thread and this helper
628 	 * thread by ensuring that we drain kqueue events on the same thread
629 	 * that manipulates the kqueue.
630 	 */
631 
632 	for (;;) {
633 		FD_ZERO(&rfds);
634 		FD_SET(mainkq, &rfds);
635 		int r = select(mainkq + 1, &rfds, NULL, NULL, NULL);
636 		if (r == 1) {
637 			(void)os_assumes_zero(handle_kqueue(launchd_internal_port, mainkq));
638 		} else if (posix_assumes_zero(r) != -1) {
639 			(void)os_assumes_zero(r);
640 		}
641 	}
642 
643 	return NULL;
644 }
645 
646 void *
waitpid_loop(void * arg)647 waitpid_loop(void *arg __attribute__((unused)))
648 {
649 	pid_t pid;
650 
651 	for (;;) {
652 		if ((pid = waitpid(-1, (int *) 0, WNOWAIT)) != -1)
653 			jobmgr_reap_pid(root_jobmgr, pid);
654 	}
655 
656 	return (NULL);
657 }
658 
659 kern_return_t
x_handle_kqueue(mach_port_t junk,integer_t fd)660 x_handle_kqueue(mach_port_t junk __attribute__((unused)), integer_t fd)
661 {
662 	struct timespec ts = { 0, 0 };
663 	struct kevent *kevi, kev[BULK_KEV_MAX];
664 	int i;
665 
666 	bulk_kev = kev;
667 
668 	if ((bulk_kev_cnt = kevent(fd, NULL, 0, kev, BULK_KEV_MAX, &ts)) != -1) {
669 #if 0
670 		for (i = 0; i < bulk_kev_cnt; i++) {
671 			log_kevent_struct(LOG_DEBUG, &kev[0], i);
672 		}
673 #endif
674 		for (i = 0; i < bulk_kev_cnt; i++) {
675 			bulk_kev_i = i;
676 			kevi = &kev[i];
677 
678 			if (kevi->filter) {
679 				log_kevent_struct(LOG_DEBUG, kev, i);
680 
681 				struct job_check_s {
682 					kq_callback kqc;
683 				};
684 
685 				struct job_check_s *check = kevi->udata;
686 				if (check && check->kqc) {
687 					runtime_ktrace(RTKT_LAUNCHD_BSD_KEVENT|DBG_FUNC_START, kevi->ident, kevi->filter, kevi->fflags);
688 					(*((kq_callback *)kevi->udata))(kevi->udata, kevi);
689 					runtime_ktrace0(RTKT_LAUNCHD_BSD_KEVENT|DBG_FUNC_END);
690 				} else {
691 					_launchd_syslog(LOG_ERR, "The following kevent had invalid context data. Please file a bug with the following information:");
692 					log_kevent_struct(LOG_EMERG, &kev[0], i);
693 				}
694 			}
695 		}
696 	} else {
697 		(void)os_assumes_zero(errno);
698 	}
699 
700 	bulk_kev = NULL;
701 
702 	return 0;
703 }
704 
705 void
launchd_runtime(void)706 launchd_runtime(void)
707 {
708 	launchd_runtime2(max_msg_size);
709 	dispatch_main();
710 }
711 
712 kern_return_t
launchd_set_bport(mach_port_t name)713 launchd_set_bport(mach_port_t name)
714 {
715 	return errno = task_set_bootstrap_port(mach_task_self(), name);
716 }
717 
718 kern_return_t
launchd_get_bport(mach_port_t * name)719 launchd_get_bport(mach_port_t *name)
720 {
721 	return errno = task_get_bootstrap_port(mach_task_self(), name);
722 }
723 
724 kern_return_t
launchd_mport_notify_req(mach_port_t name,mach_msg_id_t which)725 launchd_mport_notify_req(mach_port_t name, mach_msg_id_t which)
726 {
727 	mach_port_mscount_t msgc = (which == MACH_NOTIFY_PORT_DESTROYED) ? 0 : 1;
728 	mach_port_t previous, where = (which == MACH_NOTIFY_NO_SENDERS) ? name : launchd_internal_port;
729 	int err;
730 
731 	if (which == MACH_NOTIFY_NO_SENDERS) {
732 		/* Always make sure the send count is zero, in case a receive right is
733 		 * reused
734 		 */
735 		err = mach_port_set_mscount(mach_task_self(), name, 0);
736 		if (unlikely(err != KERN_SUCCESS)) {
737 			return err;
738 		}
739 	}
740 
741 	err = mach_port_request_notification(mach_task_self(), name, which, msgc, where, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous);
742 
743 	if (likely(err == 0) && previous != MACH_PORT_NULL) {
744 		(void)os_assumes_zero(launchd_mport_deallocate(previous));
745 	}
746 
747 	return (err);
748 }
749 
750 pid_t
runtime_fork(mach_port_t bsport)751 runtime_fork(mach_port_t bsport)
752 {
753 	sigset_t emptyset, oset;
754 	pid_t r = -1;
755 	int saved_errno;
756 	size_t i;
757 
758 	sigemptyset(&emptyset);
759 
760 	(void)os_assumes_zero(launchd_mport_make_send(bsport));
761 	(void)os_assumes_zero(launchd_set_bport(bsport));
762 	(void)os_assumes_zero(launchd_mport_deallocate(bsport));
763 
764 	__OS_COMPILETIME_ASSERT__(SIG_ERR == (typeof(SIG_ERR))-1);
765 	if (uflag == false) {
766 		(void)posix_assumes_zero(sigprocmask(SIG_BLOCK, &sigign_set, &oset));
767 		for (i = 0; i < (sizeof(sigigns) / sizeof(int)); i++) {
768 			(void)posix_assumes_zero(signal(sigigns[i], SIG_DFL));
769 		}
770 	}
771 
772 	r = fork();
773 	saved_errno = errno;
774 
775 	if (r != 0) {
776 		for (i = 0; i < (sizeof(sigigns) / sizeof(int)); i++) {
777 			(void)posix_assumes_zero(signal(sigigns[i], SIG_IGN));
778 		}
779 		if (uflag == false)
780 			(void)posix_assumes_zero(sigprocmask(SIG_SETMASK, &oset, NULL));
781 		(void)os_assumes_zero(launchd_set_bport(MACH_PORT_NULL));
782 	} else {
783 		pid_t p = -getpid();
784 		(void)posix_assumes_zero(sysctlbyname("vfs.generic.noremotehang", NULL, NULL, &p, sizeof(p)));
785 		(void)posix_assumes_zero(sigprocmask(SIG_SETMASK, &emptyset, NULL));
786 	}
787 
788 	errno = saved_errno;
789 
790 	return r;
791 }
792 
793 
794 void
runtime_set_timeout(timeout_callback to_cb,unsigned int sec)795 runtime_set_timeout(timeout_callback to_cb, unsigned int sec)
796 {
797 	if (sec == 0 || to_cb == NULL) {
798 		runtime_idle_callback = NULL;
799 		runtime_idle_timeout = 0;
800 	}
801 
802 	runtime_idle_callback = to_cb;
803 	runtime_idle_timeout = sec * 1000;
804 }
805 
806 kern_return_t
runtime_add_mport(mach_port_t name,mig_callback demux)807 runtime_add_mport(mach_port_t name, mig_callback demux)
808 {
809 	size_t needed_table_sz = (MACH_PORT_INDEX(name) + 1) * sizeof(mig_callback);
810 	mach_port_t target_set = demux ? ipc_port_set : demand_port_set;
811 
812 	if (unlikely(needed_table_sz > mig_cb_table_sz)) {
813 		needed_table_sz *= 2; /* Let's try and avoid realloc'ing for a while */
814 		mig_callback *new_table = malloc(needed_table_sz);
815 
816 		if (!new_table) {
817 			return KERN_RESOURCE_SHORTAGE;
818 		}
819 
820 		if (likely(mig_cb_table)) {
821 			memcpy(new_table, mig_cb_table, mig_cb_table_sz);
822 			free(mig_cb_table);
823 		}
824 
825 		mig_cb_table_sz = needed_table_sz;
826 		mig_cb_table = new_table;
827 	}
828 
829 	mig_cb_table[MACH_PORT_INDEX(name)] = demux;
830 
831 	return errno = mach_port_move_member(mach_task_self(), name, target_set);
832 }
833 
834 kern_return_t
runtime_remove_mport(mach_port_t name)835 runtime_remove_mport(mach_port_t name)
836 {
837 	mig_cb_table[MACH_PORT_INDEX(name)] = NULL;
838 
839 	return errno = mach_port_move_member(mach_task_self(), name, MACH_PORT_NULL);
840 }
841 
842 kern_return_t
launchd_mport_make_send(mach_port_t name)843 launchd_mport_make_send(mach_port_t name)
844 {
845 	return errno = mach_port_insert_right(mach_task_self(), name, name, MACH_MSG_TYPE_MAKE_SEND);
846 }
847 
848 kern_return_t
launchd_mport_copy_send(mach_port_t name)849 launchd_mport_copy_send(mach_port_t name)
850 {
851 	return errno = mach_port_insert_right(mach_task_self(), name, name, MACH_MSG_TYPE_COPY_SEND);
852 }
853 
854 kern_return_t
launchd_mport_make_send_once(mach_port_t name,mach_port_t * so)855 launchd_mport_make_send_once(mach_port_t name, mach_port_t *so)
856 {
857 	mach_msg_type_name_t right = 0;
858 	return errno = mach_port_extract_right(mach_task_self(), name, MACH_MSG_TYPE_MAKE_SEND_ONCE, so, &right);
859 }
860 
861 kern_return_t
launchd_mport_close_recv(mach_port_t name)862 launchd_mport_close_recv(mach_port_t name)
863 {
864 	return errno = mach_port_mod_refs(mach_task_self(), name, MACH_PORT_RIGHT_RECEIVE, -1);
865 }
866 
867 kern_return_t
launchd_mport_create_recv(mach_port_t * name)868 launchd_mport_create_recv(mach_port_t *name)
869 {
870 	return errno = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, name);
871 }
872 
873 kern_return_t
launchd_mport_deallocate(mach_port_t name)874 launchd_mport_deallocate(mach_port_t name)
875 {
876 	return errno = mach_port_deallocate(mach_task_self(), name);
877 }
878 
879 int
kevent_bulk_mod(struct kevent * kev,size_t kev_cnt)880 kevent_bulk_mod(struct kevent *kev, size_t kev_cnt)
881 {
882 	size_t i;
883 
884 	for (i = 0; i < kev_cnt; i++) {
885 		kev[i].flags |= EV_CLEAR|EV_RECEIPT;
886 	}
887 
888 	return kevent(mainkq, kev, kev_cnt, kev, kev_cnt, NULL);
889 }
890 
891 int
kevent_mod(uintptr_t ident,short filter,u_short flags,u_int fflags,intptr_t data,void * udata)892 kevent_mod(uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t data, void *udata)
893 {
894 	struct kevent kev;
895 	int r;
896 
897 	switch (filter) {
898 	case EVFILT_READ:
899 	case EVFILT_WRITE:
900 		break;
901 	case EVFILT_TIMER:
902 		/* Workaround 5225889 */
903 		if (flags & EV_ADD) {
904 			(void)kevent_mod(ident, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
905 		}
906 		/* fall through */
907 	default:
908 		flags |= EV_CLEAR;
909 		break;
910 	}
911 
912 	flags |= EV_RECEIPT;
913 
914 	if (flags & EV_ADD && !udata) {
915 		errno = EINVAL;
916 		return -1;
917 	} else if ((flags & EV_DELETE) && bulk_kev) {
918 		int i = 0;
919 		for (i = bulk_kev_i + 1; i < bulk_kev_cnt; i++) {
920 			if (bulk_kev[i].filter == filter && bulk_kev[i].ident == ident) {
921 				_launchd_syslog(LOG_DEBUG, "Pruning the following kevent:");
922 				log_kevent_struct(LOG_DEBUG, &bulk_kev[0], i);
923 				bulk_kev[i].filter = (short)0;
924 			}
925 		}
926 	}
927 
928 	EV_SET(&kev, ident, filter, flags, fflags, data, udata);
929 
930 	r = kevent(mainkq, &kev, 1, &kev, 1, NULL);
931 
932 	if (r != 1) {
933 		return -1;
934 	}
935 
936 	if (kev.flags & EV_ERROR) {
937 		if ((flags & EV_ADD) && kev.data) {
938 			_launchd_syslog(LOG_DEBUG, "%s(): See the next line...", __func__);
939 			log_kevent_struct(LOG_DEBUG, &kev, 0);
940 			errno = kev.data;
941 			return -1;
942 		}
943 	} else {
944 		(void)os_assert_zero(kev.flags);
945 	}
946 
947 	return r;
948 }
949 
950 boolean_t
launchd_internal_demux(mach_msg_header_t * Request,mach_msg_header_t * Reply)951 launchd_internal_demux(mach_msg_header_t *Request, mach_msg_header_t *Reply)
952 {
953 	if (internal_server_routine(Request)) {
954 		return internal_server(Request, Reply);
955 	} else if (notify_server_routine(Request)) {
956 		return notify_server(Request, Reply);
957 	} else {
958 		return mach_exc_server(Request, Reply);
959 	}
960 }
961 
962 kern_return_t
do_mach_notify_port_destroyed(mach_port_t notify,mach_port_t rights)963 do_mach_notify_port_destroyed(mach_port_t notify __attribute__((unused)), mach_port_t rights)
964 {
965 	/* This message is sent to us when a receive right is returned to us. */
966 	if (!job_ack_port_destruction(rights)) {
967 		(void)os_assumes_zero(launchd_mport_close_recv(rights));
968 	}
969 
970 	return KERN_SUCCESS;
971 }
972 
973 kern_return_t
do_mach_notify_port_deleted(mach_port_t notify,mach_port_name_t name)974 do_mach_notify_port_deleted(mach_port_t notify __attribute__((unused)), mach_port_name_t name __attribute__((unused)))
975 {
976 	/* If we deallocate/destroy/mod_ref away a port with a pending
977 	 * notification, the original notification message is replaced with
978 	 * this message. To quote a Mach kernel expert, "the kernel has a
979 	 * send-once right that has to be used somehow."
980 	 */
981 	return KERN_SUCCESS;
982 }
983 
984 kern_return_t
do_mach_notify_no_senders(mach_port_t notify,mach_port_mscount_t mscount)985 do_mach_notify_no_senders(mach_port_t notify, mach_port_mscount_t mscount __attribute__((unused)))
986 {
987 	job_t j = job_mig_intran(notify);
988 
989 	/* This message is sent to us when the last customer of one of our objects
990 	 * goes away.
991 	 */
992 
993 	if (!j) {
994 		return KERN_FAILURE;
995 	}
996 
997 	job_ack_no_senders(j);
998 
999 	return KERN_SUCCESS;
1000 }
1001 
1002 kern_return_t
do_mach_notify_send_once(mach_port_t notify)1003 do_mach_notify_send_once(mach_port_t notify __attribute__((unused)))
1004 {
1005 	/*
1006 	 * This message is sent for each send-once right that is deallocated without
1007 	 * being used.
1008 	 */
1009 
1010 	return KERN_SUCCESS;
1011 }
1012 
1013 kern_return_t
do_mach_notify_dead_name(mach_port_t notify,mach_port_name_t name)1014 do_mach_notify_dead_name(mach_port_t notify __attribute__((unused)), mach_port_name_t name)
1015 {
1016 	/* This message is sent to us when one of our send rights no longer has a
1017 	 * receiver somewhere else on the system.
1018 	 */
1019 	if (name == launchd_drain_reply_port) {
1020 		(void)os_assumes_zero(launchd_mport_deallocate(name));
1021 		launchd_drain_reply_port = MACH_PORT_NULL;
1022 	}
1023 
1024 	if (root_jobmgr) {
1025 		root_jobmgr = jobmgr_delete_anything_with_port(root_jobmgr, name);
1026 	}
1027 
1028 	/* A dead-name notification about a port appears to increment the rights on
1029 	 * said port. Let's deallocate it so that we don't leak dead-name ports.
1030 	 */
1031 	(void)os_assumes_zero(launchd_mport_deallocate(name));
1032 
1033 	return KERN_SUCCESS;
1034 }
1035 
1036 mach_msg_return_t
launchd_exc_runtime_once(mach_port_t port,mach_msg_size_t rcv_msg_size,mach_msg_size_t send_msg_size,mig_reply_error_t * bufRequest,mig_reply_error_t * bufReply,mach_msg_timeout_t to)1037 launchd_exc_runtime_once(mach_port_t port, mach_msg_size_t rcv_msg_size, mach_msg_size_t send_msg_size, mig_reply_error_t *bufRequest, mig_reply_error_t *bufReply, mach_msg_timeout_t to)
1038 {
1039 	mach_msg_return_t mr = ~MACH_MSG_SUCCESS;
1040 	mach_msg_option_t rcv_options =	MACH_RCV_MSG
1041 		| MACH_RCV_TIMEOUT
1042 		| MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT)
1043 		| MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0);
1044 
1045 	do {
1046 		mr = mach_msg(&bufRequest->Head, rcv_options, 0, rcv_msg_size, port, to, MACH_PORT_NULL);
1047 		switch (mr) {
1048 		case MACH_RCV_TIMED_OUT:
1049 			_launchd_syslog(LOG_DEBUG, "Message queue is empty.");
1050 			break;
1051 		case MACH_RCV_TOO_LARGE:
1052 			_launchd_syslog(LOG_INFO, "Message is larger than %u bytes.", rcv_msg_size);
1053 			break;
1054 		default:
1055 			(void)os_assumes_zero(mr);
1056 		}
1057 
1058 		if (mr == MACH_MSG_SUCCESS) {
1059 			if (!mach_exc_server(&bufRequest->Head, &bufReply->Head)) {
1060 				_launchd_syslog(LOG_WARNING, "Exception server routine failed.");
1061 				break;
1062 			}
1063 
1064 			mach_msg_return_t smr = ~MACH_MSG_SUCCESS;
1065 			mach_msg_option_t send_options = MACH_SEND_MSG | MACH_SEND_TIMEOUT;
1066 
1067 			(void)os_assumes(bufReply->Head.msgh_size <= send_msg_size);
1068 			smr = mach_msg(&bufReply->Head, send_options, bufReply->Head.msgh_size, 0, MACH_PORT_NULL, to + 100, MACH_PORT_NULL);
1069 			switch (smr) {
1070 			case MACH_SEND_TIMED_OUT:
1071 				_launchd_syslog(LOG_WARNING, "Timed out while trying to send reply to exception message.");
1072 				break;
1073 			case MACH_SEND_INVALID_DEST:
1074 				_launchd_syslog(LOG_WARNING, "Tried sending a message to a port that we don't possess a send right to.");
1075 				break;
1076 			default:
1077 				if (smr) {
1078 					_launchd_syslog(LOG_WARNING, "Couldn't deliver exception reply: 0x%x", smr);
1079 				}
1080 				break;
1081 			}
1082 		}
1083 	} while (0);
1084 
1085 	return mr;
1086 }
1087 
1088 void
runtime_record_caller_creds(audit_token_t * token)1089 runtime_record_caller_creds(audit_token_t *token)
1090 {
1091 	(void)memcpy(&ldc_token, token, sizeof(*token));
1092 	audit_token_to_au32(*token, NULL, &ldc.euid,&ldc.egid, &ldc.uid, &ldc.gid,
1093 						&ldc.pid, &ldc.asid, NULL);
1094 }
1095 
1096 struct ldcred *
runtime_get_caller_creds(void)1097 runtime_get_caller_creds(void)
1098 {
1099 	return &ldc;
1100 }
1101 
1102 audit_token_t *
runtime_get_caller_token(void)1103 runtime_get_caller_token(void)
1104 {
1105 	return &ldc_token;
1106 }
1107 
1108 static boolean_t
launchd_mig_demux(mach_msg_header_t * request,mach_msg_header_t * reply)1109 launchd_mig_demux(mach_msg_header_t *request, mach_msg_header_t *reply)
1110 {
1111 	boolean_t result = false;
1112 
1113 	time_of_mach_msg_return = runtime_get_opaque_time();
1114 #ifdef _EXTRA_LAUNCHD_SPEW
1115 	_launchd_syslog(LOG_DEBUG, "MIG callout: %u", request->msgh_id);
1116 #endif
1117 	mig_callback the_demux = mig_cb_table[MACH_PORT_INDEX(request->msgh_local_port)];
1118 	mach_msg_audit_trailer_t *tp = (mach_msg_audit_trailer_t *)((vm_offset_t)request + round_msg(request->msgh_size));
1119 	runtime_record_caller_creds(&tp->msgh_audit);
1120 
1121 	result = the_demux(request, reply);
1122 	if (!result) {
1123 		_launchd_syslog(LOG_DEBUG, "Demux failed. Trying other subsystems...");
1124 		if (request->msgh_id == MACH_NOTIFY_NO_SENDERS) {
1125 			_launchd_syslog(LOG_DEBUG, "MACH_NOTIFY_NO_SENDERS");
1126 			result = notify_server(request, reply);
1127 #ifdef notyet
1128 		} else if (the_demux == job_server) {
1129 			_launchd_syslog(LOG_DEBUG, "Trying domain subsystem...");
1130 			result = xpc_domain_server(request, reply);
1131 #endif
1132 		} else {
1133 			_launchd_syslog(LOG_ERR, "Cannot handle MIG request with ID: 0x%x", request->msgh_id);
1134 		}
1135 	} else {
1136 #ifdef _EXTRA_LAUNCHD_SPEW
1137 		_launchd_syslog(LOG_DEBUG, "MIG demux succeeded.");
1138 #endif
1139 	}
1140 
1141 	return result;
1142 }
1143 
1144 void
launchd_runtime2(mach_msg_size_t msg_size)1145 launchd_runtime2(mach_msg_size_t msg_size)
1146 {
1147 	for (;;) {
1148 		launchd_log_push();
1149 
1150 		mach_port_t recvp = MACH_PORT_NULL;
1151 		xpc_object_t request = NULL;
1152 
1153 		int result;
1154 		result = xpc_pipe_try_receive(ipc_port_set, &request, &recvp, launchd_mig_demux, msg_size, 0);
1155 		if (result != 1)
1156 			syslog(LOG_ERR, "result=%d", result);
1157 		if (result == 0 && request) {
1158 			boolean_t handled = false;
1159 			time_of_mach_msg_return = runtime_get_opaque_time();
1160 			_launchd_syslog(LOG_DEBUG, "XPC request.");
1161 
1162 			xpc_object_t reply = NULL;
1163 			if (xpc_event_demux(recvp, request, &reply)) {
1164 				handled = true;
1165 			} else if (xpc_process_demux(recvp, request, &reply)) {
1166 				handled = true;
1167 			}
1168 
1169 			if (!handled) {
1170 				_launchd_syslog(LOG_DEBUG, "XPC routine could not be handled.");
1171 				xpc_release(request);
1172 				continue;
1173 			}
1174 
1175 			_launchd_syslog(LOG_DEBUG, "XPC routine was handled.");
1176 			if (reply) {
1177 				_launchd_syslog(LOG_DEBUG, "Sending reply.");
1178 				result = xpc_pipe_routine_reply(reply);
1179 				if (result == 0) {
1180 					_launchd_syslog(LOG_DEBUG, "Reply sent successfully.");
1181 				} else if (result != EPIPE) {
1182 					_launchd_syslog(LOG_ERR, "Failed to send reply message: 0x%x", result);
1183 				}
1184 
1185 				xpc_release(reply);
1186 			}
1187 
1188 			xpc_release(request);
1189 		} else if (result == 0) {
1190 			_launchd_syslog(LOG_DEBUG, "MIG request.");
1191 		} else if (result == EINVAL) {
1192 			_launchd_syslog(LOG_ERR, "Rejected invalid request message.");
1193 		}
1194 	}
1195 }
1196 
1197 int
runtime_close(int fd)1198 runtime_close(int fd)
1199 {
1200 	int i;
1201 
1202 	if (bulk_kev) for (i = bulk_kev_i + 1; i < bulk_kev_cnt; i++) {
1203 		switch (bulk_kev[i].filter) {
1204 		case EVFILT_VNODE:
1205 		case EVFILT_WRITE:
1206 		case EVFILT_READ:
1207 			if (unlikely((int)bulk_kev[i].ident == fd)) {
1208 				_launchd_syslog(LOG_DEBUG, "Skipping kevent index: %d", i);
1209 				bulk_kev[i].filter = 0;
1210 			}
1211 		default:
1212 			break;
1213 		}
1214 	}
1215 
1216 	return close(fd);
1217 }
1218 
1219 int
runtime_fsync(int fd)1220 runtime_fsync(int fd)
1221 {
1222 #if 0
1223 	if (launchd_apple_internal) {
1224 		return fcntl(fd, F_FULLFSYNC, NULL);
1225 	} else {
1226 		return fsync(fd);
1227 	}
1228 #else
1229 	return fsync(fd);
1230 #endif
1231 }
1232 
1233 #undef TARGET_OS_EMBEDDED
1234 #define TARGET_OS_EMBEDDED 1
1235 static int launchd_appletv;
1236 
1237 /*
1238  * We should break this into two reference counts.
1239  *
1240  * One for hard references that would prevent exiting.
1241  * One for soft references that would only prevent idle exiting.
1242  *
1243  * In the long run, reference counting should completely automate when a
1244  * process can and should exit.
1245  */
1246 void
runtime_add_ref(void)1247 runtime_add_ref(void)
1248 {
1249 	if (!pid1_magic) {
1250 #if !TARGET_OS_EMBEDDED
1251 		vproc_transaction_begin(NULL);
1252 #endif
1253 	}
1254 
1255 	runtime_busy_cnt++;
1256 	_launchd_syslog(LOG_PERF, "Incremented busy count. Now: %lu", runtime_busy_cnt);
1257 	runtime_remove_timer();
1258 }
1259 
1260 void
runtime_del_ref(void)1261 runtime_del_ref(void)
1262 {
1263 	if (!pid1_magic) {
1264 #if !TARGET_OS_EMBEDDED
1265 		if (_vproc_transaction_count() == 0) {
1266 			_launchd_syslog(LOG_PERF, "Exiting cleanly.");
1267 		}
1268 
1269 		vproc_transaction_end(NULL, NULL);
1270 #endif
1271 	}
1272 
1273 	runtime_busy_cnt--;
1274 	_launchd_syslog(LOG_PERF, "Decremented busy count. Now: %lu", runtime_busy_cnt);
1275 	runtime_install_timer();
1276 }
1277 
1278 void
runtime_add_weak_ref(void)1279 runtime_add_weak_ref(void)
1280 {
1281 	if (!pid1_magic) {
1282 #if !TARGET_OS_EMBEDDED
1283 		_vproc_standby_begin();
1284 #endif
1285 	}
1286 	runtime_standby_cnt++;
1287 }
1288 
1289 void
runtime_del_weak_ref(void)1290 runtime_del_weak_ref(void)
1291 {
1292 	if (!pid1_magic) {
1293 #if !TARGET_OS_EMBEDDED
1294 		_vproc_standby_end();
1295 #endif
1296 	}
1297 	runtime_standby_cnt--;
1298 }
1299 
1300 void
runtime_install_timer(void)1301 runtime_install_timer(void)
1302 {
1303 	if (!pid1_magic && runtime_busy_cnt == 0) {
1304 		_launchd_syslog(LOG_PERF, "Gone idle. Installing idle-exit timer.");
1305 		(void)posix_assumes_zero(kevent_mod((uintptr_t)&launchd_runtime_busy_time, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 10, root_jobmgr));
1306 	}
1307 }
1308 
1309 void
runtime_remove_timer(void)1310 runtime_remove_timer(void)
1311 {
1312 	if (!pid1_magic && runtime_busy_cnt > 0) {
1313 		if (runtime_busy_cnt == 1) {
1314 			_launchd_syslog(LOG_PERF, "No longer idle. Removing idle-exit timer.");
1315 		}
1316 		(void)posix_assumes_zero(kevent_mod((uintptr_t)&launchd_runtime_busy_time, EVFILT_TIMER, EV_DELETE, 0, 0, NULL));
1317 	}
1318 }
1319 
1320 kern_return_t
catch_mach_exception_raise(mach_port_t exception_port,mach_port_t thread,mach_port_t task,exception_type_t exception,mach_exception_data_t code,mach_msg_type_number_t codeCnt)1321 catch_mach_exception_raise(mach_port_t exception_port __attribute__((unused)), mach_port_t thread, mach_port_t task,
1322 		exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt)
1323 {
1324 	pid_t p4t = -1;
1325 
1326 	(void)os_assumes_zero(pid_for_task(task, &p4t));
1327 
1328 	_launchd_syslog(LOG_NOTICE, "%s(): PID: %u thread: 0x%x type: 0x%x code: %p codeCnt: 0x%x",
1329 			__func__, p4t, thread, exception, code, codeCnt);
1330 
1331 	(void)os_assumes_zero(launchd_mport_deallocate(thread));
1332 	(void)os_assumes_zero(launchd_mport_deallocate(task));
1333 
1334 	return KERN_SUCCESS;
1335 }
1336 
1337 kern_return_t
catch_mach_exception_raise_state(mach_port_t exception_port,exception_type_t exception,const mach_exception_data_t code,mach_msg_type_number_t codeCnt,int * flavor,const thread_state_t old_state,mach_msg_type_number_t old_stateCnt,thread_state_t new_state,mach_msg_type_number_t * new_stateCnt)1338 catch_mach_exception_raise_state(mach_port_t exception_port __attribute__((unused)),
1339 		exception_type_t exception, const mach_exception_data_t code, mach_msg_type_number_t codeCnt,
1340 		int *flavor, const thread_state_t old_state, mach_msg_type_number_t old_stateCnt,
1341 		thread_state_t new_state, mach_msg_type_number_t *new_stateCnt)
1342 {
1343 	_launchd_syslog(LOG_NOTICE, "%s(): type: 0x%x code: %p codeCnt: 0x%x flavor: %p old_state: %p old_stateCnt: 0x%x new_state: %p new_stateCnt: %p",
1344 			__func__, exception, code, codeCnt, flavor, old_state, old_stateCnt, new_state, new_stateCnt);
1345 
1346 	memcpy(new_state, old_state, old_stateCnt * sizeof(old_state[0]));
1347 	*new_stateCnt = old_stateCnt;
1348 
1349 	return KERN_SUCCESS;
1350 }
1351 
1352 kern_return_t
catch_mach_exception_raise_state_identity(mach_port_t exception_port,mach_port_t thread,mach_port_t task,exception_type_t exception,mach_exception_data_t code,mach_msg_type_number_t codeCnt,int * flavor,thread_state_t old_state,mach_msg_type_number_t old_stateCnt,thread_state_t new_state,mach_msg_type_number_t * new_stateCnt)1353 catch_mach_exception_raise_state_identity(mach_port_t exception_port __attribute__((unused)), mach_port_t thread, mach_port_t task,
1354 		exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt,
1355 		int *flavor, thread_state_t old_state, mach_msg_type_number_t old_stateCnt,
1356 		thread_state_t new_state, mach_msg_type_number_t *new_stateCnt)
1357 {
1358 	pid_t p4t = -1;
1359 
1360 	(void)os_assumes_zero(pid_for_task(task, &p4t));
1361 
1362 	_launchd_syslog(LOG_NOTICE, "%s(): PID: %u thread: 0x%x type: 0x%x code: %p codeCnt: 0x%x flavor: %p old_state: %p old_stateCnt: 0x%x new_state: %p new_stateCnt: %p",
1363 			__func__, p4t, thread, exception, code, codeCnt, flavor, old_state, old_stateCnt, new_state, new_stateCnt);
1364 
1365 	memcpy(new_state, old_state, old_stateCnt * sizeof(old_state[0]));
1366 	*new_stateCnt = old_stateCnt;
1367 
1368 	(void)os_assumes_zero(launchd_mport_deallocate(thread));
1369 	(void)os_assumes_zero(launchd_mport_deallocate(task));
1370 
1371 	return KERN_SUCCESS;
1372 }
1373 
1374 // FIXME: should this be thread safe? With dispatch_once?
1375 uint64_t
runtime_get_uniqueid(void)1376 runtime_get_uniqueid(void)
1377 {
1378 	static bool once;
1379 	static uint64_t uniqueid;
1380 	if (unlikely(!once)) {
1381 		once = true;
1382 
1383 		struct proc_uniqidentifierinfo info;
1384 		int size;
1385 		size = proc_pidinfo(getpid(), PROC_PIDUNIQIDENTIFIERINFO, 0, &info, sizeof(info));
1386 		if (size == PROC_PIDUNIQIDENTIFIERINFO_SIZE) {
1387 			uniqueid = info.p_uniqueid;
1388 		}
1389 	}
1390 	return uniqueid;
1391 }
1392 
1393 void
launchd_log_vm_stats(void)1394 launchd_log_vm_stats(void)
1395 {
1396 	static struct vm_statistics orig_stats;
1397 	static bool did_first_pass;
1398 	unsigned int count = HOST_VM_INFO_COUNT;
1399 	struct vm_statistics stats, *statsp;
1400 	mach_port_t mhs = mach_host_self();
1401 
1402 	statsp = did_first_pass ? &stats : &orig_stats;
1403 
1404 	if (os_assumes_zero(host_statistics(mhs, HOST_VM_INFO, (host_info_t)statsp, &count)) != KERN_SUCCESS) {
1405 		return;
1406 	}
1407 
1408 	if (count != HOST_VM_INFO_COUNT) {
1409 		(void)os_assumes_zero(count);
1410 	}
1411 
1412 	if (did_first_pass) {
1413 		_launchd_syslog(LOG_DEBUG, "VM statistics (now - orig): Free: %d Active: %d Inactive: %d Reactivations: %d PageIns: %d PageOuts: %d Faults: %d COW-Faults: %d Purgeable: %d Purges: %d",
1414 				stats.free_count - orig_stats.free_count,
1415 				stats.active_count - orig_stats.active_count,
1416 				stats.inactive_count - orig_stats.inactive_count,
1417 				stats.reactivations - orig_stats.reactivations,
1418 				stats.pageins - orig_stats.pageins,
1419 				stats.pageouts - orig_stats.pageouts,
1420 				stats.faults - orig_stats.faults,
1421 				stats.cow_faults - orig_stats.cow_faults,
1422 				stats.purgeable_count - orig_stats.purgeable_count,
1423 				stats.purges - orig_stats.purges);
1424 	} else {
1425 		_launchd_syslog(LOG_DEBUG, "VM statistics (now): Free: %d Active: %d Inactive: %d Reactivations: %d PageIns: %d PageOuts: %d Faults: %d COW-Faults: %d Purgeable: %d Purges: %d",
1426 				orig_stats.free_count,
1427 				orig_stats.active_count,
1428 				orig_stats.inactive_count,
1429 				orig_stats.reactivations,
1430 				orig_stats.pageins,
1431 				orig_stats.pageouts,
1432 				orig_stats.faults,
1433 				orig_stats.cow_faults,
1434 				orig_stats.purgeable_count,
1435 				orig_stats.purges);
1436 
1437 		did_first_pass = true;
1438 	}
1439 
1440 	launchd_mport_deallocate(mhs);
1441 }
1442 
1443 int64_t
runtime_get_wall_time(void)1444 runtime_get_wall_time(void)
1445 {
1446 	struct timeval tv;
1447 	int64_t r;
1448 
1449 	(void)posix_assumes_zero(gettimeofday(&tv, NULL));
1450 
1451 	r = tv.tv_sec;
1452 	r *= USEC_PER_SEC;
1453 	r += tv.tv_usec;
1454 
1455 	return r;
1456 }
1457 
1458 uint64_t
runtime_get_opaque_time(void)1459 runtime_get_opaque_time(void)
1460 {
1461 	return mach_absolute_time();
1462 }
1463 
1464 uint64_t
runtime_get_opaque_time_of_event(void)1465 runtime_get_opaque_time_of_event(void)
1466 {
1467 	return time_of_mach_msg_return;
1468 }
1469 
1470 uint64_t
runtime_get_nanoseconds_since(uint64_t o)1471 runtime_get_nanoseconds_since(uint64_t o)
1472 {
1473 	return runtime_opaque_time_to_nano(runtime_get_opaque_time_of_event() - o);
1474 }
1475 
1476 uint64_t
runtime_opaque_time_to_nano(uint64_t o)1477 runtime_opaque_time_to_nano(uint64_t o)
1478 {
1479 #if defined(__i386__) || defined(__x86_64__)
1480 	if (unlikely(tbi.numer != tbi.denom)) {
1481 #elif defined(__ppc__) || defined(__ppc64__)
1482 	if (likely(tbi.numer != tbi.denom)) {
1483 #else
1484 	if (tbi.numer != tbi.denom) {
1485 #endif
1486 #ifdef __LP64__
1487 		__uint128_t tmp = o;
1488 		tmp *= tbi.numer;
1489 		tmp /= tbi.denom;
1490 		o = tmp;
1491 #else
1492 		if (o <= tbi_safe_math_max) {
1493 			o *= tbi.numer;
1494 			o /= tbi.denom;
1495 		} else {
1496 			double d = o;
1497 			d *= tbi_float_val;
1498 			o = d;
1499 		}
1500 #endif
1501 	}
1502 
1503 	return o;
1504 }
1505 
1506 void
1507 do_file_init(void)
1508 {
1509 	struct stat sb;
1510 
1511 	os_assert_zero(mach_timebase_info(&tbi));
1512 	tbi_float_val = tbi.numer;
1513 	tbi_float_val /= tbi.denom;
1514 	tbi_safe_math_max = UINT64_MAX / tbi.numer;
1515 
1516 	launchd_system_start = runtime_get_wall_time();
1517 
1518 	if (getpid() == 1) {
1519 		pid1_magic = true;
1520 	}
1521 
1522 	if (stat("/AppleInternal", &sb) == 0 && stat("/var/db/disableAppleInternal", &sb) == -1) {
1523 		launchd_apple_internal = true;
1524 	}
1525 
1526 	if (config_check(".launchd_use_gmalloc", sb)) {
1527 		launchd_use_gmalloc = true;
1528 	}
1529 
1530 	if (config_check(".launchd_log_shutdown", sb)) {
1531 		launchd_log_shutdown = true;
1532 	}
1533 
1534 	if (config_check(".launchd_log_debug", sb)) {
1535 		launchd_log_debug = true;
1536 	}
1537 
1538 	if (config_check(".launchd_log_perf", sb)) {
1539 		launchd_log_perf = true;
1540 	}
1541 
1542 	if (config_check("/etc/rc.cdrom", sb)) {
1543 		launchd_osinstaller = true;
1544 	}
1545 
1546 	if (!pid1_magic && config_check(".launchd_allow_global_dyld_envvars", sb)) {
1547 		launchd_allow_global_dyld_envvars = true;
1548 	}
1549 
1550 	char buff[1024];
1551 	size_t len = sizeof(buff) - 1;
1552 	int r = pid1_magic ? sysctlbyname("kern.bootargs", buff, &len, NULL, 0) : -1;
1553 	if (r == 0) {
1554 		if (strnstr(buff, "-v", len)) {
1555 			launchd_verbose_boot = true;
1556 		}
1557 		if (strnstr(buff, "launchd_trap_sigkill_bugs", len)) {
1558 			launchd_trap_sigkill_bugs = true;
1559 		}
1560 		if (strnstr(buff, "launchd_no_jetsam_perm_check", len)) {
1561 			launchd_no_jetsam_perm_check = true;
1562 		}
1563 	}
1564 
1565 	len = sizeof(buff) - 1;
1566 #if TARGET_OS_EMBEDDED
1567 	r = sysctlbyname("hw.machine", buff, &len, NULL, 0);
1568 	if (r == 0) {
1569 		if (strnstr(buff, "AppleTV", len)) {
1570 			launchd_appletv = true;
1571 		}
1572 	}
1573 #endif
1574 
1575 #if !TARGET_OS_EMBEDDED
1576 	if (pid1_magic && launchd_verbose_boot && config_check(".launchd_shutdown_debugging", sb)) {
1577 		launchd_shutdown_debugging = true;
1578 	}
1579 #else
1580 	if (pid1_magic && config_check(".launchd_shutdown_debugging", sb)) {
1581 		launchd_shutdown_debugging = true;
1582 	}
1583 #endif
1584 }
1585