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