xref: /trueos/sbin/launchd/runtime.c (revision 21a1900895cebd43dde4defb2cae3c71587036fc)
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 1
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 				_launchd_syslog(LOG_DEBUG, "Dispatching kevent (ident/filter): %lu/%hd", kevi->ident, kevi->filter);
680 				log_kevent_struct(LOG_DEBUG, kev, i);
681 
682 				struct job_check_s {
683 					kq_callback kqc;
684 				};
685 
686 				struct job_check_s *check = kevi->udata;
687 				if (check && check->kqc) {
688 					runtime_ktrace(RTKT_LAUNCHD_BSD_KEVENT|DBG_FUNC_START, kevi->ident, kevi->filter, kevi->fflags);
689 					(*((kq_callback *)kevi->udata))(kevi->udata, kevi);
690 					runtime_ktrace0(RTKT_LAUNCHD_BSD_KEVENT|DBG_FUNC_END);
691 				} else {
692 					_launchd_syslog(LOG_ERR, "The following kevent had invalid context data. Please file a bug with the following information:");
693 					log_kevent_struct(LOG_EMERG, &kev[0], i);
694 				}
695 				_launchd_syslog(LOG_DEBUG, "Handled kevent.");
696 			}
697 		}
698 	} else {
699 		(void)os_assumes_zero(errno);
700 	}
701 
702 	bulk_kev = NULL;
703 
704 	return 0;
705 }
706 
707 void
launchd_runtime(void)708 launchd_runtime(void)
709 {
710 	launchd_runtime2(max_msg_size);
711 	dispatch_main();
712 }
713 
714 kern_return_t
launchd_set_bport(mach_port_t name)715 launchd_set_bport(mach_port_t name)
716 {
717 	return errno = task_set_bootstrap_port(mach_task_self(), name);
718 }
719 
720 kern_return_t
launchd_get_bport(mach_port_t * name)721 launchd_get_bport(mach_port_t *name)
722 {
723 	return errno = task_get_bootstrap_port(mach_task_self(), name);
724 }
725 
726 kern_return_t
launchd_mport_notify_req(mach_port_t name,mach_msg_id_t which)727 launchd_mport_notify_req(mach_port_t name, mach_msg_id_t which)
728 {
729 	mach_port_mscount_t msgc = (which == MACH_NOTIFY_PORT_DESTROYED) ? 0 : 1;
730 	mach_port_t previous, where = (which == MACH_NOTIFY_NO_SENDERS) ? name : launchd_internal_port;
731 	int err;
732 
733 	if (which == MACH_NOTIFY_NO_SENDERS) {
734 		/* Always make sure the send count is zero, in case a receive right is
735 		 * reused
736 		 */
737 		err = mach_port_set_mscount(mach_task_self(), name, 0);
738 		if (unlikely(err != KERN_SUCCESS)) {
739 			return err;
740 		}
741 	}
742 
743 	err = mach_port_request_notification(mach_task_self(), name, which, msgc, where, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous);
744 
745 	if (likely(err == 0) && previous != MACH_PORT_NULL) {
746 		(void)os_assumes_zero(launchd_mport_deallocate(previous));
747 	}
748 
749 	return (err);
750 }
751 
752 pid_t
runtime_fork(mach_port_t bsport)753 runtime_fork(mach_port_t bsport)
754 {
755 	sigset_t emptyset, oset;
756 	pid_t r = -1;
757 	int saved_errno;
758 	size_t i;
759 
760 	sigemptyset(&emptyset);
761 
762 	syslog(LOG_ERR, "runtime_fork(bsport=%d)", bsport);
763 	(void)os_assumes_zero(launchd_mport_make_send(bsport));
764 	(void)os_assumes_zero(launchd_set_bport(bsport));
765 	(void)os_assumes_zero(launchd_mport_deallocate(bsport));
766 
767 	__OS_COMPILETIME_ASSERT__(SIG_ERR == (typeof(SIG_ERR))-1);
768 	if (uflag == false) {
769 		(void)posix_assumes_zero(sigprocmask(SIG_BLOCK, &sigign_set, &oset));
770 		for (i = 0; i < (sizeof(sigigns) / sizeof(int)); i++) {
771 			(void)posix_assumes_zero(signal(sigigns[i], SIG_DFL));
772 		}
773 	}
774 
775 	r = fork();
776 	saved_errno = errno;
777 
778 	if (r != 0) {
779 		for (i = 0; i < (sizeof(sigigns) / sizeof(int)); i++) {
780 			(void)posix_assumes_zero(signal(sigigns[i], SIG_IGN));
781 		}
782 		if (uflag == false)
783 			(void)posix_assumes_zero(sigprocmask(SIG_SETMASK, &oset, NULL));
784 		(void)os_assumes_zero(launchd_set_bport(MACH_PORT_NULL));
785 	} else {
786 		pid_t p = -getpid();
787 		(void)posix_assumes_zero(sysctlbyname("vfs.generic.noremotehang", NULL, NULL, &p, sizeof(p)));
788 		(void)posix_assumes_zero(sigprocmask(SIG_SETMASK, &emptyset, NULL));
789 	}
790 
791 	errno = saved_errno;
792 
793 	return r;
794 }
795 
796 
797 void
runtime_set_timeout(timeout_callback to_cb,unsigned int sec)798 runtime_set_timeout(timeout_callback to_cb, unsigned int sec)
799 {
800 	if (sec == 0 || to_cb == NULL) {
801 		runtime_idle_callback = NULL;
802 		runtime_idle_timeout = 0;
803 	}
804 
805 	runtime_idle_callback = to_cb;
806 	runtime_idle_timeout = sec * 1000;
807 }
808 
809 kern_return_t
runtime_add_mport(mach_port_t name,mig_callback demux)810 runtime_add_mport(mach_port_t name, mig_callback demux)
811 {
812 	size_t needed_table_sz = (MACH_PORT_INDEX(name) + 1) * sizeof(mig_callback);
813 	mach_port_t target_set = demux ? ipc_port_set : demand_port_set;
814 
815 	if (unlikely(needed_table_sz > mig_cb_table_sz)) {
816 		needed_table_sz *= 2; /* Let's try and avoid realloc'ing for a while */
817 		mig_callback *new_table = malloc(needed_table_sz);
818 
819 		if (!new_table) {
820 			return KERN_RESOURCE_SHORTAGE;
821 		}
822 
823 		if (likely(mig_cb_table)) {
824 			memcpy(new_table, mig_cb_table, mig_cb_table_sz);
825 			free(mig_cb_table);
826 		}
827 
828 		mig_cb_table_sz = needed_table_sz;
829 		mig_cb_table = new_table;
830 	}
831 
832 	mig_cb_table[MACH_PORT_INDEX(name)] = demux;
833 
834 	return errno = mach_port_move_member(mach_task_self(), name, target_set);
835 }
836 
837 kern_return_t
runtime_remove_mport(mach_port_t name)838 runtime_remove_mport(mach_port_t name)
839 {
840 	mig_cb_table[MACH_PORT_INDEX(name)] = NULL;
841 
842 	return errno = mach_port_move_member(mach_task_self(), name, MACH_PORT_NULL);
843 }
844 
845 kern_return_t
launchd_mport_make_send(mach_port_t name)846 launchd_mport_make_send(mach_port_t name)
847 {
848 	return errno = mach_port_insert_right(mach_task_self(), name, name, MACH_MSG_TYPE_MAKE_SEND);
849 }
850 
851 kern_return_t
launchd_mport_copy_send(mach_port_t name)852 launchd_mport_copy_send(mach_port_t name)
853 {
854 	return errno = mach_port_insert_right(mach_task_self(), name, name, MACH_MSG_TYPE_COPY_SEND);
855 }
856 
857 kern_return_t
launchd_mport_make_send_once(mach_port_t name,mach_port_t * so)858 launchd_mport_make_send_once(mach_port_t name, mach_port_t *so)
859 {
860 	mach_msg_type_name_t right = 0;
861 	return errno = mach_port_extract_right(mach_task_self(), name, MACH_MSG_TYPE_MAKE_SEND_ONCE, so, &right);
862 }
863 
864 kern_return_t
launchd_mport_close_recv(mach_port_t name)865 launchd_mport_close_recv(mach_port_t name)
866 {
867 	return errno = mach_port_mod_refs(mach_task_self(), name, MACH_PORT_RIGHT_RECEIVE, -1);
868 }
869 
870 kern_return_t
launchd_mport_create_recv(mach_port_t * name)871 launchd_mport_create_recv(mach_port_t *name)
872 {
873 	return errno = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, name);
874 }
875 
876 kern_return_t
launchd_mport_deallocate(mach_port_t name)877 launchd_mport_deallocate(mach_port_t name)
878 {
879 	return errno = mach_port_deallocate(mach_task_self(), name);
880 }
881 
882 int
kevent_bulk_mod(struct kevent * kev,size_t kev_cnt)883 kevent_bulk_mod(struct kevent *kev, size_t kev_cnt)
884 {
885 	size_t i;
886 
887 	for (i = 0; i < kev_cnt; i++) {
888 		kev[i].flags |= EV_CLEAR|EV_RECEIPT;
889 	}
890 
891 	return kevent(mainkq, kev, kev_cnt, kev, kev_cnt, NULL);
892 }
893 
894 int
kevent_mod(uintptr_t ident,short filter,u_short flags,u_int fflags,intptr_t data,void * udata)895 kevent_mod(uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t data, void *udata)
896 {
897 	struct kevent kev;
898 	int r;
899 
900 	switch (filter) {
901 	case EVFILT_READ:
902 	case EVFILT_WRITE:
903 		break;
904 	case EVFILT_TIMER:
905 		/* Workaround 5225889 */
906 		if (flags & EV_ADD) {
907 			(void)kevent_mod(ident, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
908 		}
909 		/* fall through */
910 	default:
911 		flags |= EV_CLEAR;
912 		break;
913 	}
914 
915 	flags |= EV_RECEIPT;
916 
917 	if (flags & EV_ADD && !udata) {
918 		errno = EINVAL;
919 		return -1;
920 	} else if ((flags & EV_DELETE) && bulk_kev) {
921 		int i = 0;
922 		for (i = bulk_kev_i + 1; i < bulk_kev_cnt; i++) {
923 			if (bulk_kev[i].filter == filter && bulk_kev[i].ident == ident) {
924 				_launchd_syslog(LOG_DEBUG, "Pruning the following kevent:");
925 				log_kevent_struct(LOG_DEBUG, &bulk_kev[0], i);
926 				bulk_kev[i].filter = (short)0;
927 			}
928 		}
929 	}
930 
931 	EV_SET(&kev, ident, filter, flags, fflags, data, udata);
932 
933 	r = kevent(mainkq, &kev, 1, &kev, 1, NULL);
934 
935 	if (r != 1) {
936 		return -1;
937 	}
938 
939 	if (kev.flags & EV_ERROR) {
940 		if ((flags & EV_ADD) && kev.data) {
941 			_launchd_syslog(LOG_DEBUG, "%s(): See the next line...", __func__);
942 			log_kevent_struct(LOG_DEBUG, &kev, 0);
943 			errno = kev.data;
944 			return -1;
945 		}
946 	} else {
947 		(void)os_assert_zero(kev.flags);
948 	}
949 
950 	return r;
951 }
952 
953 boolean_t
launchd_internal_demux(mach_msg_header_t * Request,mach_msg_header_t * Reply)954 launchd_internal_demux(mach_msg_header_t *Request, mach_msg_header_t *Reply)
955 {
956 	if (internal_server_routine(Request)) {
957 		return internal_server(Request, Reply);
958 	} else if (notify_server_routine(Request)) {
959 		return notify_server(Request, Reply);
960 	} else {
961 		return mach_exc_server(Request, Reply);
962 	}
963 }
964 
965 kern_return_t
do_mach_notify_port_destroyed(mach_port_t notify,mach_port_t rights)966 do_mach_notify_port_destroyed(mach_port_t notify __attribute__((unused)), mach_port_t rights)
967 {
968 	/* This message is sent to us when a receive right is returned to us. */
969 	if (!job_ack_port_destruction(rights)) {
970 		(void)os_assumes_zero(launchd_mport_close_recv(rights));
971 	}
972 
973 	return KERN_SUCCESS;
974 }
975 
976 kern_return_t
do_mach_notify_port_deleted(mach_port_t notify,mach_port_name_t name)977 do_mach_notify_port_deleted(mach_port_t notify __attribute__((unused)), mach_port_name_t name __attribute__((unused)))
978 {
979 	/* If we deallocate/destroy/mod_ref away a port with a pending
980 	 * notification, the original notification message is replaced with
981 	 * this message. To quote a Mach kernel expert, "the kernel has a
982 	 * send-once right that has to be used somehow."
983 	 */
984 	return KERN_SUCCESS;
985 }
986 
987 kern_return_t
do_mach_notify_no_senders(mach_port_t notify,mach_port_mscount_t mscount)988 do_mach_notify_no_senders(mach_port_t notify, mach_port_mscount_t mscount __attribute__((unused)))
989 {
990 	job_t j = job_mig_intran(notify);
991 
992 	/* This message is sent to us when the last customer of one of our objects
993 	 * goes away.
994 	 */
995 
996 	if (!j) {
997 		return KERN_FAILURE;
998 	}
999 
1000 	job_ack_no_senders(j);
1001 
1002 	return KERN_SUCCESS;
1003 }
1004 
1005 kern_return_t
do_mach_notify_send_once(mach_port_t notify)1006 do_mach_notify_send_once(mach_port_t notify __attribute__((unused)))
1007 {
1008 	/*
1009 	 * This message is sent for each send-once right that is deallocated without
1010 	 * being used.
1011 	 */
1012 
1013 	return KERN_SUCCESS;
1014 }
1015 
1016 kern_return_t
do_mach_notify_dead_name(mach_port_t notify,mach_port_name_t name)1017 do_mach_notify_dead_name(mach_port_t notify __attribute__((unused)), mach_port_name_t name)
1018 {
1019 	/* This message is sent to us when one of our send rights no longer has a
1020 	 * receiver somewhere else on the system.
1021 	 */
1022 	if (name == launchd_drain_reply_port) {
1023 		(void)os_assumes_zero(launchd_mport_deallocate(name));
1024 		launchd_drain_reply_port = MACH_PORT_NULL;
1025 	}
1026 
1027 	if (root_jobmgr) {
1028 		root_jobmgr = jobmgr_delete_anything_with_port(root_jobmgr, name);
1029 	}
1030 
1031 	/* A dead-name notification about a port appears to increment the rights on
1032 	 * said port. Let's deallocate it so that we don't leak dead-name ports.
1033 	 */
1034 	(void)os_assumes_zero(launchd_mport_deallocate(name));
1035 
1036 	return KERN_SUCCESS;
1037 }
1038 
1039 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)1040 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)
1041 {
1042 	mach_msg_return_t mr = ~MACH_MSG_SUCCESS;
1043 	mach_msg_option_t rcv_options =	MACH_RCV_MSG
1044 		| MACH_RCV_TIMEOUT
1045 		| MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT)
1046 		| MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0);
1047 
1048 	do {
1049 		mr = mach_msg(&bufRequest->Head, rcv_options, 0, rcv_msg_size, port, to, MACH_PORT_NULL);
1050 		switch (mr) {
1051 		case MACH_RCV_TIMED_OUT:
1052 			_launchd_syslog(LOG_DEBUG, "Message queue is empty.");
1053 			break;
1054 		case MACH_RCV_TOO_LARGE:
1055 			_launchd_syslog(LOG_INFO, "Message is larger than %u bytes.", rcv_msg_size);
1056 			break;
1057 		default:
1058 			(void)os_assumes_zero(mr);
1059 		}
1060 
1061 		if (mr == MACH_MSG_SUCCESS) {
1062 			if (!mach_exc_server(&bufRequest->Head, &bufReply->Head)) {
1063 				_launchd_syslog(LOG_WARNING, "Exception server routine failed.");
1064 				break;
1065 			}
1066 
1067 			mach_msg_return_t smr = ~MACH_MSG_SUCCESS;
1068 			mach_msg_option_t send_options = MACH_SEND_MSG | MACH_SEND_TIMEOUT;
1069 
1070 			(void)os_assumes(bufReply->Head.msgh_size <= send_msg_size);
1071 			smr = mach_msg(&bufReply->Head, send_options, bufReply->Head.msgh_size, 0, MACH_PORT_NULL, to + 100, MACH_PORT_NULL);
1072 			switch (smr) {
1073 			case MACH_SEND_TIMED_OUT:
1074 				_launchd_syslog(LOG_WARNING, "Timed out while trying to send reply to exception message.");
1075 				break;
1076 			case MACH_SEND_INVALID_DEST:
1077 				_launchd_syslog(LOG_WARNING, "Tried sending a message to a port that we don't possess a send right to.");
1078 				break;
1079 			default:
1080 				if (smr) {
1081 					_launchd_syslog(LOG_WARNING, "Couldn't deliver exception reply: 0x%x", smr);
1082 				}
1083 				break;
1084 			}
1085 		}
1086 	} while (0);
1087 
1088 	return mr;
1089 }
1090 
1091 void
runtime_record_caller_creds(audit_token_t * token)1092 runtime_record_caller_creds(audit_token_t *token)
1093 {
1094 	(void)memcpy(&ldc_token, token, sizeof(*token));
1095 	audit_token_to_au32(*token, NULL, &ldc.euid,&ldc.egid, &ldc.uid, &ldc.gid,
1096 						&ldc.pid, &ldc.asid, NULL);
1097 }
1098 
1099 struct ldcred *
runtime_get_caller_creds(void)1100 runtime_get_caller_creds(void)
1101 {
1102 	return &ldc;
1103 }
1104 
1105 audit_token_t *
runtime_get_caller_token(void)1106 runtime_get_caller_token(void)
1107 {
1108 	return &ldc_token;
1109 }
1110 
1111 static boolean_t
launchd_mig_demux(mach_msg_header_t * request,mach_msg_header_t * reply)1112 launchd_mig_demux(mach_msg_header_t *request, mach_msg_header_t *reply)
1113 {
1114 	boolean_t result = false;
1115 
1116 	time_of_mach_msg_return = runtime_get_opaque_time();
1117 #ifdef _EXTRA_LAUNCHD_SPEW
1118 	_launchd_syslog(LOG_DEBUG, "MIG callout: %u", request->msgh_id);
1119 #endif
1120 	mig_callback the_demux = mig_cb_table[MACH_PORT_INDEX(request->msgh_local_port)];
1121 	mach_msg_audit_trailer_t *tp = (mach_msg_audit_trailer_t *)((vm_offset_t)request + round_msg(request->msgh_size));
1122 	runtime_record_caller_creds(&tp->msgh_audit);
1123 
1124 	result = the_demux(request, reply);
1125 	if (!result) {
1126 		_launchd_syslog(LOG_DEBUG, "Demux failed. Trying other subsystems...");
1127 		if (request->msgh_id == MACH_NOTIFY_NO_SENDERS) {
1128 			_launchd_syslog(LOG_DEBUG, "MACH_NOTIFY_NO_SENDERS");
1129 			result = notify_server(request, reply);
1130 #ifdef notyet
1131 		} else if (the_demux == job_server) {
1132 			_launchd_syslog(LOG_DEBUG, "Trying domain subsystem...");
1133 			result = xpc_domain_server(request, reply);
1134 #endif
1135 		} else {
1136 			_launchd_syslog(LOG_ERR, "Cannot handle MIG request with ID: 0x%x", request->msgh_id);
1137 		}
1138 	} else {
1139 #ifdef _EXTRA_LAUNCHD_SPEW
1140 		_launchd_syslog(LOG_DEBUG, "MIG demux succeeded.");
1141 #endif
1142 	}
1143 
1144 	return result;
1145 }
1146 
1147 void
launchd_runtime2(mach_msg_size_t msg_size)1148 launchd_runtime2(mach_msg_size_t msg_size)
1149 {
1150 	for (;;) {
1151 		launchd_log_push();
1152 
1153 		mach_port_t recvp = MACH_PORT_NULL;
1154 		xpc_object_t request = NULL;
1155 
1156 		int result;
1157 		result = xpc_pipe_try_receive(ipc_port_set, &request, &recvp, launchd_mig_demux, msg_size, 0);
1158 		if (result != 1)
1159 			syslog(LOG_ERR, "result=%d", result);
1160 		if (result == 0 && request) {
1161 			boolean_t handled = false;
1162 			time_of_mach_msg_return = runtime_get_opaque_time();
1163 			_launchd_syslog(LOG_DEBUG, "XPC request.");
1164 
1165 			xpc_object_t reply = NULL;
1166 			if (xpc_event_demux(recvp, request, &reply)) {
1167 				handled = true;
1168 			} else if (xpc_process_demux(recvp, request, &reply)) {
1169 				handled = true;
1170 			}
1171 
1172 			if (!handled) {
1173 				_launchd_syslog(LOG_DEBUG, "XPC routine could not be handled.");
1174 				xpc_release(request);
1175 				continue;
1176 			}
1177 
1178 			_launchd_syslog(LOG_DEBUG, "XPC routine was handled.");
1179 			if (reply) {
1180 				_launchd_syslog(LOG_DEBUG, "Sending reply.");
1181 				result = xpc_pipe_routine_reply(reply);
1182 				if (result == 0) {
1183 					_launchd_syslog(LOG_DEBUG, "Reply sent successfully.");
1184 				} else if (result != EPIPE) {
1185 					_launchd_syslog(LOG_ERR, "Failed to send reply message: 0x%x", result);
1186 				}
1187 
1188 				xpc_release(reply);
1189 			}
1190 
1191 			xpc_release(request);
1192 		} else if (result == 0) {
1193 			_launchd_syslog(LOG_DEBUG, "MIG request.");
1194 		} else if (result == EINVAL) {
1195 			_launchd_syslog(LOG_ERR, "Rejected invalid request message.");
1196 		}
1197 	}
1198 }
1199 
1200 int
runtime_close(int fd)1201 runtime_close(int fd)
1202 {
1203 	int i;
1204 
1205 	if (bulk_kev) for (i = bulk_kev_i + 1; i < bulk_kev_cnt; i++) {
1206 		switch (bulk_kev[i].filter) {
1207 		case EVFILT_VNODE:
1208 		case EVFILT_WRITE:
1209 		case EVFILT_READ:
1210 			if (unlikely((int)bulk_kev[i].ident == fd)) {
1211 				_launchd_syslog(LOG_DEBUG, "Skipping kevent index: %d", i);
1212 				bulk_kev[i].filter = 0;
1213 			}
1214 		default:
1215 			break;
1216 		}
1217 	}
1218 
1219 	return close(fd);
1220 }
1221 
1222 int
runtime_fsync(int fd)1223 runtime_fsync(int fd)
1224 {
1225 #if 0
1226 	if (launchd_apple_internal) {
1227 		return fcntl(fd, F_FULLFSYNC, NULL);
1228 	} else {
1229 		return fsync(fd);
1230 	}
1231 #else
1232 	return fsync(fd);
1233 #endif
1234 }
1235 
1236 #undef TARGET_OS_EMBEDDED
1237 #define TARGET_OS_EMBEDDED 1
1238 static int launchd_appletv;
1239 
1240 /*
1241  * We should break this into two reference counts.
1242  *
1243  * One for hard references that would prevent exiting.
1244  * One for soft references that would only prevent idle exiting.
1245  *
1246  * In the long run, reference counting should completely automate when a
1247  * process can and should exit.
1248  */
1249 void
runtime_add_ref(void)1250 runtime_add_ref(void)
1251 {
1252 	if (!pid1_magic) {
1253 #if !TARGET_OS_EMBEDDED
1254 		vproc_transaction_begin(NULL);
1255 #endif
1256 	}
1257 
1258 	runtime_busy_cnt++;
1259 	_launchd_syslog(LOG_PERF, "Incremented busy count. Now: %lu", runtime_busy_cnt);
1260 	runtime_remove_timer();
1261 }
1262 
1263 void
runtime_del_ref(void)1264 runtime_del_ref(void)
1265 {
1266 	if (!pid1_magic) {
1267 #if !TARGET_OS_EMBEDDED
1268 		if (_vproc_transaction_count() == 0) {
1269 			_launchd_syslog(LOG_PERF, "Exiting cleanly.");
1270 		}
1271 
1272 		vproc_transaction_end(NULL, NULL);
1273 #endif
1274 	}
1275 
1276 	runtime_busy_cnt--;
1277 	_launchd_syslog(LOG_PERF, "Decremented busy count. Now: %lu", runtime_busy_cnt);
1278 	runtime_install_timer();
1279 }
1280 
1281 void
runtime_add_weak_ref(void)1282 runtime_add_weak_ref(void)
1283 {
1284 	if (!pid1_magic) {
1285 #if !TARGET_OS_EMBEDDED
1286 		_vproc_standby_begin();
1287 #endif
1288 	}
1289 	runtime_standby_cnt++;
1290 }
1291 
1292 void
runtime_del_weak_ref(void)1293 runtime_del_weak_ref(void)
1294 {
1295 	if (!pid1_magic) {
1296 #if !TARGET_OS_EMBEDDED
1297 		_vproc_standby_end();
1298 #endif
1299 	}
1300 	runtime_standby_cnt--;
1301 }
1302 
1303 void
runtime_install_timer(void)1304 runtime_install_timer(void)
1305 {
1306 	if (!pid1_magic && runtime_busy_cnt == 0) {
1307 		_launchd_syslog(LOG_PERF, "Gone idle. Installing idle-exit timer.");
1308 		(void)posix_assumes_zero(kevent_mod((uintptr_t)&launchd_runtime_busy_time, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 10, root_jobmgr));
1309 	}
1310 }
1311 
1312 void
runtime_remove_timer(void)1313 runtime_remove_timer(void)
1314 {
1315 	if (!pid1_magic && runtime_busy_cnt > 0) {
1316 		if (runtime_busy_cnt == 1) {
1317 			_launchd_syslog(LOG_PERF, "No longer idle. Removing idle-exit timer.");
1318 		}
1319 		(void)posix_assumes_zero(kevent_mod((uintptr_t)&launchd_runtime_busy_time, EVFILT_TIMER, EV_DELETE, 0, 0, NULL));
1320 	}
1321 }
1322 
1323 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)1324 catch_mach_exception_raise(mach_port_t exception_port __attribute__((unused)), mach_port_t thread, mach_port_t task,
1325 		exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt)
1326 {
1327 	pid_t p4t = -1;
1328 
1329 	(void)os_assumes_zero(pid_for_task(task, &p4t));
1330 
1331 	_launchd_syslog(LOG_NOTICE, "%s(): PID: %u thread: 0x%x type: 0x%x code: %p codeCnt: 0x%x",
1332 			__func__, p4t, thread, exception, code, codeCnt);
1333 
1334 	(void)os_assumes_zero(launchd_mport_deallocate(thread));
1335 	(void)os_assumes_zero(launchd_mport_deallocate(task));
1336 
1337 	return KERN_SUCCESS;
1338 }
1339 
1340 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)1341 catch_mach_exception_raise_state(mach_port_t exception_port __attribute__((unused)),
1342 		exception_type_t exception, const mach_exception_data_t code, mach_msg_type_number_t codeCnt,
1343 		int *flavor, const thread_state_t old_state, mach_msg_type_number_t old_stateCnt,
1344 		thread_state_t new_state, mach_msg_type_number_t *new_stateCnt)
1345 {
1346 	_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",
1347 			__func__, exception, code, codeCnt, flavor, old_state, old_stateCnt, new_state, new_stateCnt);
1348 
1349 	memcpy(new_state, old_state, old_stateCnt * sizeof(old_state[0]));
1350 	*new_stateCnt = old_stateCnt;
1351 
1352 	return KERN_SUCCESS;
1353 }
1354 
1355 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)1356 catch_mach_exception_raise_state_identity(mach_port_t exception_port __attribute__((unused)), mach_port_t thread, mach_port_t task,
1357 		exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt,
1358 		int *flavor, thread_state_t old_state, mach_msg_type_number_t old_stateCnt,
1359 		thread_state_t new_state, mach_msg_type_number_t *new_stateCnt)
1360 {
1361 	pid_t p4t = -1;
1362 
1363 	(void)os_assumes_zero(pid_for_task(task, &p4t));
1364 
1365 	_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",
1366 			__func__, p4t, thread, exception, code, codeCnt, flavor, old_state, old_stateCnt, new_state, new_stateCnt);
1367 
1368 	memcpy(new_state, old_state, old_stateCnt * sizeof(old_state[0]));
1369 	*new_stateCnt = old_stateCnt;
1370 
1371 	(void)os_assumes_zero(launchd_mport_deallocate(thread));
1372 	(void)os_assumes_zero(launchd_mport_deallocate(task));
1373 
1374 	return KERN_SUCCESS;
1375 }
1376 
1377 // FIXME: should this be thread safe? With dispatch_once?
1378 uint64_t
runtime_get_uniqueid(void)1379 runtime_get_uniqueid(void)
1380 {
1381 	static bool once;
1382 	static uint64_t uniqueid;
1383 	if (unlikely(!once)) {
1384 		once = true;
1385 
1386 		struct proc_uniqidentifierinfo info;
1387 		int size;
1388 		size = proc_pidinfo(getpid(), PROC_PIDUNIQIDENTIFIERINFO, 0, &info, sizeof(info));
1389 		if (size == PROC_PIDUNIQIDENTIFIERINFO_SIZE) {
1390 			uniqueid = info.p_uniqueid;
1391 		}
1392 	}
1393 	return uniqueid;
1394 }
1395 
1396 void
launchd_log_vm_stats(void)1397 launchd_log_vm_stats(void)
1398 {
1399 	static struct vm_statistics orig_stats;
1400 	static bool did_first_pass;
1401 	unsigned int count = HOST_VM_INFO_COUNT;
1402 	struct vm_statistics stats, *statsp;
1403 	mach_port_t mhs = mach_host_self();
1404 
1405 	statsp = did_first_pass ? &stats : &orig_stats;
1406 
1407 	if (os_assumes_zero(host_statistics(mhs, HOST_VM_INFO, (host_info_t)statsp, &count)) != KERN_SUCCESS) {
1408 		return;
1409 	}
1410 
1411 	if (count != HOST_VM_INFO_COUNT) {
1412 		(void)os_assumes_zero(count);
1413 	}
1414 
1415 	if (did_first_pass) {
1416 		_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",
1417 				stats.free_count - orig_stats.free_count,
1418 				stats.active_count - orig_stats.active_count,
1419 				stats.inactive_count - orig_stats.inactive_count,
1420 				stats.reactivations - orig_stats.reactivations,
1421 				stats.pageins - orig_stats.pageins,
1422 				stats.pageouts - orig_stats.pageouts,
1423 				stats.faults - orig_stats.faults,
1424 				stats.cow_faults - orig_stats.cow_faults,
1425 				stats.purgeable_count - orig_stats.purgeable_count,
1426 				stats.purges - orig_stats.purges);
1427 	} else {
1428 		_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",
1429 				orig_stats.free_count,
1430 				orig_stats.active_count,
1431 				orig_stats.inactive_count,
1432 				orig_stats.reactivations,
1433 				orig_stats.pageins,
1434 				orig_stats.pageouts,
1435 				orig_stats.faults,
1436 				orig_stats.cow_faults,
1437 				orig_stats.purgeable_count,
1438 				orig_stats.purges);
1439 
1440 		did_first_pass = true;
1441 	}
1442 
1443 	launchd_mport_deallocate(mhs);
1444 }
1445 
1446 int64_t
runtime_get_wall_time(void)1447 runtime_get_wall_time(void)
1448 {
1449 	struct timeval tv;
1450 	int64_t r;
1451 
1452 	(void)posix_assumes_zero(gettimeofday(&tv, NULL));
1453 
1454 	r = tv.tv_sec;
1455 	r *= USEC_PER_SEC;
1456 	r += tv.tv_usec;
1457 
1458 	return r;
1459 }
1460 
1461 uint64_t
runtime_get_opaque_time(void)1462 runtime_get_opaque_time(void)
1463 {
1464 	return mach_absolute_time();
1465 }
1466 
1467 uint64_t
runtime_get_opaque_time_of_event(void)1468 runtime_get_opaque_time_of_event(void)
1469 {
1470 	return time_of_mach_msg_return;
1471 }
1472 
1473 uint64_t
runtime_get_nanoseconds_since(uint64_t o)1474 runtime_get_nanoseconds_since(uint64_t o)
1475 {
1476 	return runtime_opaque_time_to_nano(runtime_get_opaque_time_of_event() - o);
1477 }
1478 
1479 uint64_t
runtime_opaque_time_to_nano(uint64_t o)1480 runtime_opaque_time_to_nano(uint64_t o)
1481 {
1482 #if defined(__i386__) || defined(__x86_64__)
1483 	if (unlikely(tbi.numer != tbi.denom)) {
1484 #elif defined(__ppc__) || defined(__ppc64__)
1485 	if (likely(tbi.numer != tbi.denom)) {
1486 #else
1487 	if (tbi.numer != tbi.denom) {
1488 #endif
1489 #ifdef __LP64__
1490 		__uint128_t tmp = o;
1491 		tmp *= tbi.numer;
1492 		tmp /= tbi.denom;
1493 		o = tmp;
1494 #else
1495 		if (o <= tbi_safe_math_max) {
1496 			o *= tbi.numer;
1497 			o /= tbi.denom;
1498 		} else {
1499 			double d = o;
1500 			d *= tbi_float_val;
1501 			o = d;
1502 		}
1503 #endif
1504 	}
1505 
1506 	return o;
1507 }
1508 
1509 void
1510 do_file_init(void)
1511 {
1512 	struct stat sb;
1513 
1514 	os_assert_zero(mach_timebase_info(&tbi));
1515 	tbi_float_val = tbi.numer;
1516 	tbi_float_val /= tbi.denom;
1517 	tbi_safe_math_max = UINT64_MAX / tbi.numer;
1518 
1519 	launchd_system_start = runtime_get_wall_time();
1520 
1521 	if (getpid() == 1) {
1522 		pid1_magic = true;
1523 	}
1524 
1525 	if (stat("/AppleInternal", &sb) == 0 && stat("/var/db/disableAppleInternal", &sb) == -1) {
1526 		launchd_apple_internal = true;
1527 	}
1528 
1529 	if (config_check(".launchd_use_gmalloc", sb)) {
1530 		launchd_use_gmalloc = true;
1531 	}
1532 
1533 	if (config_check(".launchd_log_shutdown", sb)) {
1534 		launchd_log_shutdown = true;
1535 	}
1536 
1537 	if (config_check(".launchd_log_debug", sb)) {
1538 		launchd_log_debug = true;
1539 	}
1540 
1541 	if (config_check(".launchd_log_perf", sb)) {
1542 		launchd_log_perf = true;
1543 	}
1544 
1545 	if (config_check("/etc/rc.cdrom", sb)) {
1546 		launchd_osinstaller = true;
1547 	}
1548 
1549 	if (!pid1_magic && config_check(".launchd_allow_global_dyld_envvars", sb)) {
1550 		launchd_allow_global_dyld_envvars = true;
1551 	}
1552 
1553 	char buff[1024];
1554 	size_t len = sizeof(buff) - 1;
1555 	int r = pid1_magic ? sysctlbyname("kern.bootargs", buff, &len, NULL, 0) : -1;
1556 	if (r == 0) {
1557 		if (strnstr(buff, "-v", len)) {
1558 			launchd_verbose_boot = true;
1559 		}
1560 		if (strnstr(buff, "launchd_trap_sigkill_bugs", len)) {
1561 			launchd_trap_sigkill_bugs = true;
1562 		}
1563 		if (strnstr(buff, "launchd_no_jetsam_perm_check", len)) {
1564 			launchd_no_jetsam_perm_check = true;
1565 		}
1566 	}
1567 
1568 	len = sizeof(buff) - 1;
1569 #if TARGET_OS_EMBEDDED
1570 	r = sysctlbyname("hw.machine", buff, &len, NULL, 0);
1571 	if (r == 0) {
1572 		if (strnstr(buff, "AppleTV", len)) {
1573 			launchd_appletv = true;
1574 		}
1575 	}
1576 #endif
1577 
1578 #if !TARGET_OS_EMBEDDED
1579 	if (pid1_magic && launchd_verbose_boot && config_check(".launchd_shutdown_debugging", sb)) {
1580 		launchd_shutdown_debugging = true;
1581 	}
1582 #else
1583 	if (pid1_magic && config_check(".launchd_shutdown_debugging", sb)) {
1584 		launchd_shutdown_debugging = true;
1585 	}
1586 #endif
1587 }
1588