]> git.saurik.com Git - apple/launchd.git/blame - launchd/src/launchd_runtime.c
launchd-392.35.tar.gz
[apple/launchd.git] / launchd / src / launchd_runtime.c
CommitLineData
5b0a4722 1/*
ddbbfbc1 2 * Copyright (c) 1999-2008 Apple Computer, Inc. All rights reserved.
5b0a4722
A
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
5c88273d 21static const char *const __rcs_file_version__ = "$Revision: 25122 $";
5b0a4722
A
22
23#include "config.h"
24#include "launchd_runtime.h"
25
26#include <mach/mach.h>
27#include <mach/mach_error.h>
28#include <mach/boolean.h>
29#include <mach/message.h>
30#include <mach/notify.h>
31#include <mach/mig_errors.h>
32#include <mach/mach_traps.h>
33#include <mach/mach_interface.h>
34#include <mach/host_info.h>
35#include <mach/mach_host.h>
ddbbfbc1 36#include <mach/mach_time.h>
5b0a4722
A
37#include <mach/exception.h>
38#include <sys/types.h>
39#include <sys/stat.h>
ddbbfbc1 40#include <sys/sysctl.h>
5b0a4722
A
41#include <sys/time.h>
42#include <sys/proc.h>
43#include <sys/event.h>
44#include <sys/queue.h>
45#include <sys/socket.h>
46#include <sys/mount.h>
47#include <sys/reboot.h>
48#include <sys/fcntl.h>
ddbbfbc1 49#include <sys/kdebug.h>
5b0a4722
A
50#include <bsm/libbsm.h>
51#include <malloc/malloc.h>
52#include <unistd.h>
53#include <pthread.h>
54#include <errno.h>
55#include <string.h>
56#include <ctype.h>
57#include <stdio.h>
58#include <stdlib.h>
59#include <stdbool.h>
60#include <syslog.h>
61#include <signal.h>
62#include <dlfcn.h>
63
64#include "launchd_internalServer.h"
65#include "launchd_internal.h"
66#include "notifyServer.h"
ddbbfbc1 67#include "mach_excServer.h"
5b0a4722
A
68
69/* We shouldn't be including these */
70#include "launch.h"
71#include "launchd.h"
72#include "launchd_core_logic.h"
ddbbfbc1
A
73#include "vproc.h"
74#include "vproc_priv.h"
ef398931 75#include "vproc_internal.h"
dcace88f 76#include "protocol_vprocServer.h"
ef398931 77#include "protocol_job_reply.h"
5b0a4722 78
dcace88f
A
79#if !TARGET_OS_EMBEDDED
80#include "domainServer.h"
5c88273d 81#endif /* !TARGET_OS_EMBEDDED */
dcace88f
A
82#include "eventsServer.h"
83
5b0a4722
A
84static mach_port_t ipc_port_set;
85static mach_port_t demand_port_set;
86static mach_port_t launchd_internal_port;
87static int mainkq;
88
89#define BULK_KEV_MAX 100
90static struct kevent *bulk_kev;
91static int bulk_kev_i;
92static int bulk_kev_cnt;
93
94static pthread_t kqueue_demand_thread;
5b0a4722 95
ddbbfbc1
A
96static void mportset_callback(void);
97static kq_callback kqmportset_callback = (kq_callback)mportset_callback;
5b0a4722 98static void *kqueue_demand_loop(void *arg);
5b0a4722 99
ddbbfbc1 100boolean_t launchd_internal_demux(mach_msg_header_t *Request, mach_msg_header_t *Reply);
5b0a4722
A
101static void record_caller_creds(mach_msg_header_t *mh);
102static void launchd_runtime2(mach_msg_size_t msg_size, mig_reply_error_t *bufRequest, mig_reply_error_t *bufReply);
103static mach_msg_size_t max_msg_size;
104static mig_callback *mig_cb_table;
105static size_t mig_cb_table_sz;
106static timeout_callback runtime_idle_callback;
107static mach_msg_timeout_t runtime_idle_timeout;
ddbbfbc1 108static struct ldcred ldc;
ddbbfbc1 109static size_t runtime_standby_cnt;
5b0a4722 110
5b0a4722
A
111static STAILQ_HEAD(, logmsg_s) logmsg_queue = STAILQ_HEAD_INITIALIZER(logmsg_queue);
112static size_t logmsg_queue_sz;
113static size_t logmsg_queue_cnt;
114static mach_port_t drain_reply_port;
115static void runtime_log_uncork_pending_drain(void);
116static kern_return_t runtime_log_pack(vm_offset_t *outval, mach_msg_type_number_t *outvalCnt);
5b0a4722
A
117
118static bool logmsg_add(struct runtime_syslog_attr *attr, int err_num, const char *msg);
119static void logmsg_remove(struct logmsg_s *lm);
120
ddbbfbc1
A
121static void do_file_init(void) __attribute__((constructor));
122static mach_timebase_info_data_t tbi;
123static uint64_t tbi_safe_math_max;
124static uint64_t time_of_mach_msg_return;
125static double tbi_float_val;
5b0a4722
A
126
127static const int sigigns[] = { SIGHUP, SIGINT, SIGPIPE, SIGALRM, SIGTERM,
128 SIGURG, SIGTSTP, SIGTSTP, SIGCONT, SIGTTIN, SIGTTOU, SIGIO, SIGXCPU,
129 SIGXFSZ, SIGVTALRM, SIGPROF, SIGWINCH, SIGINFO, SIGUSR1, SIGUSR2
130};
131static sigset_t sigign_set;
ddbbfbc1
A
132static FILE *ourlogfile;
133bool pid1_magic;
134bool do_apple_internal_logging;
135bool low_level_debug;
ddbbfbc1
A
136bool g_flat_mach_namespace = true;
137bool g_simulate_pid1_crash = false;
dcace88f 138bool g_malloc_log_stacks = false;
ddbbfbc1
A
139bool g_use_gmalloc = false;
140bool g_log_per_user_shutdown = false;
141#if !TARGET_OS_EMBEDDED
142bool g_log_pid1_shutdown = true;
143#else
144bool g_log_pid1_shutdown = false;
145#endif
146bool g_log_strict_usage = false;
dcace88f 147bool g_trap_sigkill_bugs = false;
ddbbfbc1 148pid_t g_wsp = 0;
587e987e 149size_t runtime_busy_cnt;
5b0a4722 150
fe044cc9
A
151mach_port_t
152runtime_get_kernel_port(void)
153{
154 return launchd_internal_port;
155}
156
ddbbfbc1
A
157// static const char *__crashreporter_info__ = "";
158
159static int internal_mask_pri = LOG_UPTO(LOG_NOTICE);
160
161
5b0a4722
A
162void
163launchd_runtime_init(void)
164{
165 mach_msg_size_t mxmsgsz;
ddbbfbc1 166 pid_t p = getpid();
5b0a4722
A
167
168 launchd_assert((mainkq = kqueue()) != -1);
169
170 launchd_assert((errno = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &demand_port_set)) == KERN_SUCCESS);
171 launchd_assert((errno = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &ipc_port_set)) == KERN_SUCCESS);
172
ddbbfbc1
A
173 launchd_assert(kevent_mod(demand_port_set, EVFILT_MACHPORT, EV_ADD, 0, 0, &kqmportset_callback) != -1);
174
5b0a4722
A
175 launchd_assert(launchd_mport_create_recv(&launchd_internal_port) == KERN_SUCCESS);
176 launchd_assert(launchd_mport_make_send(launchd_internal_port) == KERN_SUCCESS);
177
178 /* Sigh... at the moment, MIG has maxsize == sizeof(reply union) */
179 mxmsgsz = sizeof(union __RequestUnion__x_launchd_internal_subsystem);
180 if (x_launchd_internal_subsystem.maxsize > mxmsgsz) {
181 mxmsgsz = x_launchd_internal_subsystem.maxsize;
182 }
183
184 launchd_assert(runtime_add_mport(launchd_internal_port, launchd_internal_demux, mxmsgsz) == KERN_SUCCESS);
ddbbfbc1
A
185 launchd_assert(pthread_create(&kqueue_demand_thread, NULL, kqueue_demand_loop, NULL) == 0);
186 launchd_assert(pthread_detach(kqueue_demand_thread) == 0);
5b0a4722 187
dcace88f 188 (void)launchd_assumes(sysctlbyname("vfs.generic.noremotehang", NULL, NULL, &p, sizeof(p)) != -1);
5b0a4722
A
189}
190
191void
192launchd_runtime_init2(void)
193{
194 size_t i;
195
196 for (i = 0; i < (sizeof(sigigns) / sizeof(int)); i++) {
197 sigaddset(&sigign_set, sigigns[i]);
dcace88f 198 (void)launchd_assumes(signal(sigigns[i], SIG_IGN) != SIG_ERR);
5b0a4722
A
199 }
200}
201
5b0a4722 202#define FLAGIF(f) if (flags & f) { flags_off += sprintf(flags_off, #f); flags &= ~f; }
5b0a4722
A
203const char *
204reboot_flags_to_C_names(unsigned int flags)
205{
ddbbfbc1 206#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"
5b0a4722
A
207 static char flags_buf[sizeof(MAX_RB_STR)];
208 char *flags_off = NULL;
209
fe044cc9
A
210 if (flags == 0) {
211 return "RB_AUTOBOOT";
212 }
213
214 while (flags) {
5b0a4722
A
215 if (flags_off) {
216 *flags_off = '|';
217 flags_off++;
218 *flags_off = '\0';
219 } else {
220 flags_off = flags_buf;
221 }
222
223 FLAGIF(RB_ASKNAME)
224 else FLAGIF(RB_SINGLE)
225 else FLAGIF(RB_NOSYNC)
5b0a4722
A
226 else FLAGIF(RB_HALT)
227 else FLAGIF(RB_INITNAME)
228 else FLAGIF(RB_DFLTROOT)
229 else FLAGIF(RB_ALTBOOT)
230 else FLAGIF(RB_UNIPROC)
231 else FLAGIF(RB_SAFEBOOT)
232 else FLAGIF(RB_UPSDELAY)
233 else {
234 flags_off += sprintf(flags_off, "0x%x", flags);
235 flags = 0;
236 }
5b0a4722 237 }
fe044cc9
A
238
239 return flags_buf;
5b0a4722
A
240}
241
242const char *
243signal_to_C_name(unsigned int sig)
244{
245 static char unknown[25];
246
247#define SIG2CASE(sg) case sg: return #sg
248
249 switch (sig) {
250 SIG2CASE(SIGHUP);
251 SIG2CASE(SIGINT);
252 SIG2CASE(SIGQUIT);
253 SIG2CASE(SIGILL);
254 SIG2CASE(SIGTRAP);
255 SIG2CASE(SIGABRT);
256 SIG2CASE(SIGFPE);
257 SIG2CASE(SIGKILL);
258 SIG2CASE(SIGBUS);
259 SIG2CASE(SIGSEGV);
260 SIG2CASE(SIGSYS);
261 SIG2CASE(SIGPIPE);
262 SIG2CASE(SIGALRM);
263 SIG2CASE(SIGTERM);
264 SIG2CASE(SIGURG);
265 SIG2CASE(SIGSTOP);
266 SIG2CASE(SIGTSTP);
267 SIG2CASE(SIGCONT);
268 SIG2CASE(SIGCHLD);
269 SIG2CASE(SIGTTIN);
270 SIG2CASE(SIGTTOU);
271 SIG2CASE(SIGIO);
272 SIG2CASE(SIGXCPU);
273 SIG2CASE(SIGXFSZ);
274 SIG2CASE(SIGVTALRM);
275 SIG2CASE(SIGPROF);
276 SIG2CASE(SIGWINCH);
277 SIG2CASE(SIGINFO);
278 SIG2CASE(SIGUSR1);
279 SIG2CASE(SIGUSR2);
280 default:
281 snprintf(unknown, sizeof(unknown), "%u", sig);
282 return unknown;
283 }
284}
285
286void
ddbbfbc1 287log_kevent_struct(int level, struct kevent *kev_base, int indx)
5b0a4722 288{
ddbbfbc1 289 struct kevent *kev = &kev_base[indx];
5b0a4722
A
290 const char *filter_str;
291 char ident_buf[100];
292 char filter_buf[100];
293 char fflags_buf[1000];
294 char flags_buf[1000] = "0x0";
295 char *flags_off = NULL;
296 char *fflags_off = NULL;
297 unsigned short flags = kev->flags;
298 unsigned int fflags = kev->fflags;
299
ddbbfbc1
A
300 if (likely(!(LOG_MASK(level) & internal_mask_pri))) {
301 return;
302 }
303
5b0a4722
A
304 if (flags) while (flags) {
305 if (flags_off) {
306 *flags_off = '|';
307 flags_off++;
308 *flags_off = '\0';
309 } else {
310 flags_off = flags_buf;
311 }
312
313 FLAGIF(EV_ADD)
314 else FLAGIF(EV_RECEIPT)
315 else FLAGIF(EV_DELETE)
316 else FLAGIF(EV_ENABLE)
317 else FLAGIF(EV_DISABLE)
318 else FLAGIF(EV_CLEAR)
319 else FLAGIF(EV_EOF)
320 else FLAGIF(EV_ONESHOT)
321 else FLAGIF(EV_ERROR)
322 else {
ddbbfbc1 323 flags_off += sprintf(flags_off, "0x%hx", flags);
5b0a4722
A
324 flags = 0;
325 }
326 }
327
328 snprintf(ident_buf, sizeof(ident_buf), "%ld", kev->ident);
329 snprintf(fflags_buf, sizeof(fflags_buf), "0x%x", fflags);
330
331 switch (kev->filter) {
332 case EVFILT_READ:
333 filter_str = "EVFILT_READ";
334 break;
335 case EVFILT_WRITE:
336 filter_str = "EVFILT_WRITE";
337 break;
338 case EVFILT_AIO:
339 filter_str = "EVFILT_AIO";
340 break;
341 case EVFILT_VNODE:
342 filter_str = "EVFILT_VNODE";
343 if (fflags) while (fflags) {
344 if (fflags_off) {
345 *fflags_off = '|';
346 fflags_off++;
347 *fflags_off = '\0';
348 } else {
349 fflags_off = fflags_buf;
350 }
351
352#define FFLAGIF(ff) if (fflags & ff) { fflags_off += sprintf(fflags_off, #ff); fflags &= ~ff; }
353
354 FFLAGIF(NOTE_DELETE)
355 else FFLAGIF(NOTE_WRITE)
356 else FFLAGIF(NOTE_EXTEND)
357 else FFLAGIF(NOTE_ATTRIB)
358 else FFLAGIF(NOTE_LINK)
359 else FFLAGIF(NOTE_RENAME)
360 else FFLAGIF(NOTE_REVOKE)
361 else {
362 fflags_off += sprintf(fflags_off, "0x%x", fflags);
363 fflags = 0;
364 }
365 }
366 break;
367 case EVFILT_PROC:
368 filter_str = "EVFILT_PROC";
369 if (fflags) while (fflags) {
370 if (fflags_off) {
371 *fflags_off = '|';
372 fflags_off++;
373 *fflags_off = '\0';
374 } else {
375 fflags_off = fflags_buf;
376 }
377
378 FFLAGIF(NOTE_EXIT)
379 else FFLAGIF(NOTE_REAP)
380 else FFLAGIF(NOTE_FORK)
381 else FFLAGIF(NOTE_EXEC)
382 else FFLAGIF(NOTE_SIGNAL)
383 else FFLAGIF(NOTE_TRACK)
384 else FFLAGIF(NOTE_TRACKERR)
385 else FFLAGIF(NOTE_CHILD)
386 else {
387 fflags_off += sprintf(fflags_off, "0x%x", fflags);
388 fflags = 0;
389 }
390 }
391 break;
392 case EVFILT_SIGNAL:
393 filter_str = "EVFILT_SIGNAL";
394 strcpy(ident_buf, signal_to_C_name(kev->ident));
395 break;
396 case EVFILT_TIMER:
397 filter_str = "EVFILT_TIMER";
398 snprintf(ident_buf, sizeof(ident_buf), "0x%lx", kev->ident);
399 if (fflags) while (fflags) {
400 if (fflags_off) {
401 *fflags_off = '|';
402 fflags_off++;
403 *fflags_off = '\0';
404 } else {
405 fflags_off = fflags_buf;
406 }
407
408 FFLAGIF(NOTE_SECONDS)
409 else FFLAGIF(NOTE_USECONDS)
410 else FFLAGIF(NOTE_NSECONDS)
411 else FFLAGIF(NOTE_ABSOLUTE)
412 else {
413 fflags_off += sprintf(fflags_off, "0x%x", fflags);
414 fflags = 0;
415 }
416 }
417 break;
418 case EVFILT_MACHPORT:
419 filter_str = "EVFILT_MACHPORT";
420 snprintf(ident_buf, sizeof(ident_buf), "0x%lx", kev->ident);
421 break;
422 case EVFILT_FS:
423 filter_str = "EVFILT_FS";
424 snprintf(ident_buf, sizeof(ident_buf), "0x%lx", kev->ident);
425 if (fflags) while (fflags) {
426 if (fflags_off) {
427 *fflags_off = '|';
428 fflags_off++;
429 *fflags_off = '\0';
430 } else {
431 fflags_off = fflags_buf;
432 }
433
434 FFLAGIF(VQ_NOTRESP)
435 else FFLAGIF(VQ_NEEDAUTH)
436 else FFLAGIF(VQ_LOWDISK)
437 else FFLAGIF(VQ_MOUNT)
438 else FFLAGIF(VQ_UNMOUNT)
439 else FFLAGIF(VQ_DEAD)
440 else FFLAGIF(VQ_ASSIST)
441 else FFLAGIF(VQ_NOTRESPLOCK)
442 else FFLAGIF(VQ_UPDATE)
443 else {
444 fflags_off += sprintf(fflags_off, "0x%x", fflags);
445 fflags = 0;
446 }
447 }
448 break;
449 default:
ddbbfbc1 450 snprintf(filter_buf, sizeof(filter_buf), "%hd", kev->filter);
5b0a4722
A
451 filter_str = filter_buf;
452 break;
453 }
454
455 runtime_syslog(level, "KEVENT[%d]: udata = %p data = 0x%lx ident = %s filter = %s flags = %s fflags = %s",
456 indx, kev->udata, kev->data, ident_buf, filter_str, flags_buf, fflags_buf);
457}
458
ddbbfbc1
A
459void
460mportset_callback(void)
5b0a4722
A
461{
462 mach_port_name_array_t members;
463 mach_msg_type_number_t membersCnt;
464 mach_port_status_t status;
465 mach_msg_type_number_t statusCnt;
466 struct kevent kev;
467 unsigned int i;
468
469 if (!launchd_assumes((errno = mach_port_get_set_status(mach_task_self(), demand_port_set, &members, &membersCnt)) == KERN_SUCCESS)) {
ddbbfbc1 470 return;
5b0a4722
A
471 }
472
473 for (i = 0; i < membersCnt; i++) {
474 statusCnt = MACH_PORT_RECEIVE_STATUS_COUNT;
475 if (mach_port_get_attributes(mach_task_self(), members[i], MACH_PORT_RECEIVE_STATUS, (mach_port_info_t)&status,
476 &statusCnt) != KERN_SUCCESS) {
477 continue;
478 }
479 if (status.mps_msgcount) {
480 EV_SET(&kev, members[i], EVFILT_MACHPORT, 0, 0, 0, job_find_by_service_port(members[i]));
481#if 0
482 if (launchd_assumes(kev.udata != NULL)) {
483#endif
587e987e 484 log_kevent_struct(LOG_DEBUG, &kev, 0);
5b0a4722
A
485 (*((kq_callback *)kev.udata))(kev.udata, &kev);
486#if 0
487 } else {
587e987e 488 log_kevent_struct(LOG_ERR, &kev, 0);
5b0a4722
A
489 }
490#endif
491 /* the callback may have tainted our ability to continue this for loop */
492 break;
493 }
494 }
495
dcace88f 496 (void)launchd_assumes(vm_deallocate(mach_task_self(), (vm_address_t)members,
5b0a4722 497 (vm_size_t) membersCnt * sizeof(mach_port_name_t)) == KERN_SUCCESS);
5b0a4722
A
498}
499
500void *
501kqueue_demand_loop(void *arg __attribute__((unused)))
502{
503 fd_set rfds;
504
505 /*
506 * Yes, at first glance, calling select() on a kqueue seems silly.
507 *
508 * This avoids a race condition between the main thread and this helper
509 * thread by ensuring that we drain kqueue events on the same thread
510 * that manipulates the kqueue.
511 */
512
513 for (;;) {
514 FD_ZERO(&rfds);
515 FD_SET(mainkq, &rfds);
516 if (launchd_assumes(select(mainkq + 1, &rfds, NULL, NULL, NULL) == 1)) {
dcace88f 517 (void)launchd_assumes(handle_kqueue(launchd_internal_port, mainkq) == 0);
5b0a4722
A
518 }
519 }
520
521 return NULL;
522}
523
524kern_return_t
525x_handle_kqueue(mach_port_t junk __attribute__((unused)), integer_t fd)
526{
527 struct timespec ts = { 0, 0 };
ddbbfbc1 528 struct kevent *kevi, kev[BULK_KEV_MAX];
5b0a4722
A
529 int i;
530
531 bulk_kev = kev;
532
ddbbfbc1 533 if (launchd_assumes((bulk_kev_cnt = kevent(fd, NULL, 0, kev, BULK_KEV_MAX, &ts)) != -1)) {
dcace88f 534#if 0
5b0a4722 535 for (i = 0; i < bulk_kev_cnt; i++) {
587e987e 536 log_kevent_struct(LOG_DEBUG, &kev[0], i);
5b0a4722 537 }
dcace88f 538#endif
5b0a4722
A
539 for (i = 0; i < bulk_kev_cnt; i++) {
540 bulk_kev_i = i;
ddbbfbc1
A
541 kevi = &kev[i];
542
543 if (kevi->filter) {
544 runtime_syslog(LOG_DEBUG, "Dispatching kevent...");
545 log_kevent_struct(LOG_DEBUG, kev, i);
dcace88f 546#if 0
ddbbfbc1
A
547 /* Check if kevi->udata was either malloc(3)ed or is a valid function pointer.
548 * If neither, it's probably an invalid pointer and we should log it.
549 */
550 Dl_info dli;
551 if (launchd_assumes(malloc_size(kevi->udata) || dladdr(kevi->udata, &dli))) {
552 runtime_ktrace(RTKT_LAUNCHD_BSD_KEVENT|DBG_FUNC_START, kevi->ident, kevi->filter, kevi->fflags);
553 (*((kq_callback *)kevi->udata))(kevi->udata, kevi);
554 runtime_ktrace0(RTKT_LAUNCHD_BSD_KEVENT|DBG_FUNC_END);
555 } else {
556 runtime_syslog(LOG_ERR, "The following kevent had invalid context data.");
587e987e 557 log_kevent_struct(LOG_EMERG, &kev[0], i);
ddbbfbc1 558 }
dcace88f 559#else
5c88273d
A
560 struct job_check_s {
561 kq_callback kqc;
562 };
563
564 struct job_check_s *check = kevi->udata;
565 if (check && check->kqc) {
566 runtime_ktrace(RTKT_LAUNCHD_BSD_KEVENT|DBG_FUNC_START, kevi->ident, kevi->filter, kevi->fflags);
567 (*((kq_callback *)kevi->udata))(kevi->udata, kevi);
568 runtime_ktrace0(RTKT_LAUNCHD_BSD_KEVENT|DBG_FUNC_END);
569 } else {
570 runtime_syslog(LOG_ERR, "The following kevent had invalid context data. Please file a bug with the following information:");
571 log_kevent_struct(LOG_EMERG, &kev[0], i);
572 }
dcace88f 573#endif
5b0a4722
A
574 }
575 }
5b0a4722
A
576 }
577
578 bulk_kev = NULL;
579
580 return 0;
581}
582
5b0a4722
A
583void
584launchd_runtime(void)
585{
586 mig_reply_error_t *req = NULL, *resp = NULL;
587 mach_msg_size_t mz = max_msg_size;
588 int flags = VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE;
589
590 for (;;) {
ddbbfbc1 591 if (likely(req)) {
dcace88f 592 (void)launchd_assumes(vm_deallocate(mach_task_self(), (vm_address_t)req, mz) == KERN_SUCCESS);
5b0a4722
A
593 req = NULL;
594 }
ddbbfbc1 595 if (likely(resp)) {
dcace88f 596 (void)launchd_assumes(vm_deallocate(mach_task_self(), (vm_address_t)resp, mz) == KERN_SUCCESS);
5b0a4722
A
597 resp = NULL;
598 }
599
600 mz = max_msg_size;
601
602 if (!launchd_assumes(vm_allocate(mach_task_self(), (vm_address_t *)&req, mz, flags) == KERN_SUCCESS)) {
603 continue;
604 }
605 if (!launchd_assumes(vm_allocate(mach_task_self(), (vm_address_t *)&resp, mz, flags) == KERN_SUCCESS)) {
606 continue;
607 }
608
609 launchd_runtime2(mz, req, resp);
610
611 /* If we get here, max_msg_size probably changed... */
612 }
613}
614
615kern_return_t
616launchd_set_bport(mach_port_t name)
617{
618 return errno = task_set_bootstrap_port(mach_task_self(), name);
619}
620
621kern_return_t
622launchd_get_bport(mach_port_t *name)
623{
624 return errno = task_get_bootstrap_port(mach_task_self(), name);
625}
626
627kern_return_t
628launchd_mport_notify_req(mach_port_t name, mach_msg_id_t which)
629{
fe044cc9 630 mach_port_mscount_t msgc = (which == MACH_NOTIFY_PORT_DESTROYED) ? 0 : 1;
5b0a4722
A
631 mach_port_t previous, where = (which == MACH_NOTIFY_NO_SENDERS) ? name : launchd_internal_port;
632
633 if (which == MACH_NOTIFY_NO_SENDERS) {
634 /* Always make sure the send count is zero, in case a receive right is reused */
635 errno = mach_port_set_mscount(mach_task_self(), name, 0);
ddbbfbc1 636 if (unlikely(errno != KERN_SUCCESS)) {
5b0a4722
A
637 return errno;
638 }
639 }
640
641 errno = mach_port_request_notification(mach_task_self(), name, which, msgc, where,
642 MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous);
643
ddbbfbc1 644 if (likely(errno == 0) && previous != MACH_PORT_NULL) {
dcace88f 645 (void)launchd_assumes(launchd_mport_deallocate(previous) == KERN_SUCCESS);
5b0a4722
A
646 }
647
648 return errno;
649}
650
651pid_t
652runtime_fork(mach_port_t bsport)
653{
654 sigset_t emptyset, oset;
655 pid_t r = -1;
656 int saved_errno;
657 size_t i;
658
659 sigemptyset(&emptyset);
660
dcace88f
A
661 (void)launchd_assumes(launchd_mport_make_send(bsport) == KERN_SUCCESS);
662 (void)launchd_assumes(launchd_set_bport(bsport) == KERN_SUCCESS);
663 (void)launchd_assumes(launchd_mport_deallocate(bsport) == KERN_SUCCESS);
5b0a4722 664
dcace88f 665 (void)launchd_assumes(sigprocmask(SIG_BLOCK, &sigign_set, &oset) != -1);
5b0a4722 666 for (i = 0; i < (sizeof(sigigns) / sizeof(int)); i++) {
dcace88f 667 (void)launchd_assumes(signal(sigigns[i], SIG_DFL) != SIG_ERR);
5b0a4722
A
668 }
669
670 r = fork();
671 saved_errno = errno;
672
673 if (r != 0) {
674 for (i = 0; i < (sizeof(sigigns) / sizeof(int)); i++) {
dcace88f 675 (void)launchd_assumes(signal(sigigns[i], SIG_IGN) != SIG_ERR);
5b0a4722 676 }
dcace88f
A
677 (void)launchd_assumes(sigprocmask(SIG_SETMASK, &oset, NULL) != -1);
678 (void)launchd_assumes(launchd_set_bport(MACH_PORT_NULL) == KERN_SUCCESS);
5b0a4722 679 } else {
ddbbfbc1 680 pid_t p = -getpid();
dcace88f 681 (void)launchd_assumes(sysctlbyname("vfs.generic.noremotehang", NULL, NULL, &p, sizeof(p)) != -1);
ddbbfbc1 682
dcace88f 683 (void)launchd_assumes(sigprocmask(SIG_SETMASK, &emptyset, NULL) != -1);
5b0a4722
A
684 }
685
686 errno = saved_errno;
687
688 return r;
689}
690
691
692void
693runtime_set_timeout(timeout_callback to_cb, unsigned int sec)
694{
695 if (sec == 0 || to_cb == NULL) {
696 runtime_idle_callback = NULL;
697 runtime_idle_timeout = 0;
698 }
699
700 runtime_idle_callback = to_cb;
701 runtime_idle_timeout = sec * 1000;
702}
703
704kern_return_t
705runtime_add_mport(mach_port_t name, mig_callback demux, mach_msg_size_t msg_size)
706{
707 size_t needed_table_sz = (MACH_PORT_INDEX(name) + 1) * sizeof(mig_callback);
708 mach_port_t target_set = demux ? ipc_port_set : demand_port_set;
709
710 msg_size = round_page(msg_size + MAX_TRAILER_SIZE);
711
ddbbfbc1 712 if (unlikely(needed_table_sz > mig_cb_table_sz)) {
5b0a4722
A
713 needed_table_sz *= 2; /* Let's try and avoid realloc'ing for a while */
714 mig_callback *new_table = malloc(needed_table_sz);
715
716 if (!launchd_assumes(new_table != NULL)) {
717 return KERN_RESOURCE_SHORTAGE;
718 }
719
ddbbfbc1 720 if (likely(mig_cb_table)) {
5b0a4722
A
721 memcpy(new_table, mig_cb_table, mig_cb_table_sz);
722 free(mig_cb_table);
723 }
724
725 mig_cb_table_sz = needed_table_sz;
726 mig_cb_table = new_table;
727 }
728
729 mig_cb_table[MACH_PORT_INDEX(name)] = demux;
730
731 if (msg_size > max_msg_size) {
732 max_msg_size = msg_size;
733 }
734
735 return errno = mach_port_move_member(mach_task_self(), name, target_set);
736}
737
738kern_return_t
739runtime_remove_mport(mach_port_t name)
740{
741 mig_cb_table[MACH_PORT_INDEX(name)] = NULL;
742
743 return errno = mach_port_move_member(mach_task_self(), name, MACH_PORT_NULL);
744}
745
746kern_return_t
747launchd_mport_make_send(mach_port_t name)
748{
749 return errno = mach_port_insert_right(mach_task_self(), name, name, MACH_MSG_TYPE_MAKE_SEND);
750}
751
ddbbfbc1
A
752kern_return_t
753launchd_mport_copy_send(mach_port_t name)
754{
755 return errno = mach_port_insert_right(mach_task_self(), name, name, MACH_MSG_TYPE_COPY_SEND);
756}
757
dcace88f
A
758kern_return_t
759launchd_mport_make_send_once(mach_port_t name, mach_port_t *so)
760{
761 mach_msg_type_name_t right = 0;
762 return errno = mach_port_extract_right(mach_task_self(), name, MACH_MSG_TYPE_MAKE_SEND_ONCE, so, &right);
763}
764
5b0a4722
A
765kern_return_t
766launchd_mport_close_recv(mach_port_t name)
767{
768 return errno = mach_port_mod_refs(mach_task_self(), name, MACH_PORT_RIGHT_RECEIVE, -1);
769}
770
771kern_return_t
772launchd_mport_create_recv(mach_port_t *name)
773{
774 return errno = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, name);
775}
776
777kern_return_t
778launchd_mport_deallocate(mach_port_t name)
779{
780 return errno = mach_port_deallocate(mach_task_self(), name);
781}
782
783int
784kevent_bulk_mod(struct kevent *kev, size_t kev_cnt)
785{
786 size_t i;
787
788 for (i = 0; i < kev_cnt; i++) {
789 kev[i].flags |= EV_CLEAR|EV_RECEIPT;
790 }
791
792 return kevent(mainkq, kev, kev_cnt, kev, kev_cnt, NULL);
793}
794
795int
796kevent_mod(uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t data, void *udata)
797{
798 struct kevent kev;
799 int r;
800
801 switch (filter) {
802 case EVFILT_READ:
803 case EVFILT_WRITE:
804 break;
ddbbfbc1
A
805 case EVFILT_TIMER:
806 /* Workaround 5225889 */
807 if (flags & EV_ADD) {
808 kevent_mod(ident, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
809 }
810 /* fall through */
5b0a4722
A
811 default:
812 flags |= EV_CLEAR;
813 break;
814 }
815
816 flags |= EV_RECEIPT;
817
818 if (flags & EV_ADD && !launchd_assumes(udata != NULL)) {
819 errno = EINVAL;
820 return -1;
dcace88f 821 } else if ((flags & EV_DELETE) && bulk_kev) {
ddbbfbc1 822 int i = 0;
dcace88f
A
823 for (i = bulk_kev_i + 1; i < bulk_kev_cnt; i++) {
824 if (bulk_kev[i].filter == filter && bulk_kev[i].ident == ident) {
ddbbfbc1 825 runtime_syslog(LOG_DEBUG, "Pruning the following kevent:");
587e987e 826 log_kevent_struct(LOG_DEBUG, &bulk_kev[0], i);
ddbbfbc1
A
827 bulk_kev[i].filter = (short)0;
828 }
829 }
5b0a4722
A
830 }
831
832 EV_SET(&kev, ident, filter, flags, fflags, data, udata);
833
834 r = kevent(mainkq, &kev, 1, &kev, 1, NULL);
835
836 if (!launchd_assumes(r == 1)) {
837 return -1;
838 }
839
840 if (launchd_assumes(kev.flags & EV_ERROR)) {
841 if ((flags & EV_ADD) && kev.data) {
842 runtime_syslog(LOG_DEBUG, "%s(): See the next line...", __func__);
843 log_kevent_struct(LOG_DEBUG, &kev, 0);
844 errno = kev.data;
845 return -1;
846 }
847 }
848
849 return r;
850}
851
852boolean_t
853launchd_internal_demux(mach_msg_header_t *Request, mach_msg_header_t *Reply)
854{
855 if (launchd_internal_server_routine(Request)) {
856 return launchd_internal_server(Request, Reply);
fe044cc9
A
857 } else if (notify_server_routine(Request)) {
858 return notify_server(Request, Reply);
859 } else {
ddbbfbc1 860 return mach_exc_server(Request, Reply);
5b0a4722 861 }
5b0a4722
A
862}
863
864kern_return_t
ddbbfbc1 865do_mach_notify_port_destroyed(mach_port_t notify __attribute__((unused)), mach_port_t rights)
5b0a4722
A
866{
867 /* This message is sent to us when a receive right is returned to us. */
868
869 if (!launchd_assumes(job_ack_port_destruction(rights))) {
dcace88f 870 (void)launchd_assumes(launchd_mport_close_recv(rights) == KERN_SUCCESS);
5b0a4722
A
871 }
872
873 return KERN_SUCCESS;
874}
875
876kern_return_t
ddbbfbc1 877do_mach_notify_port_deleted(mach_port_t notify __attribute__((unused)), mach_port_name_t name __attribute__((unused)))
5b0a4722
A
878{
879 /* If we deallocate/destroy/mod_ref away a port with a pending
880 * notification, the original notification message is replaced with
881 * this message. To quote a Mach kernel expert, "the kernel has a
882 * send-once right that has to be used somehow."
883 */
884 return KERN_SUCCESS;
885}
886
887kern_return_t
ddbbfbc1 888do_mach_notify_no_senders(mach_port_t notify, mach_port_mscount_t mscount __attribute__((unused)))
5b0a4722
A
889{
890 job_t j = job_mig_intran(notify);
891
892 /* This message is sent to us when the last customer of one of our
893 * objects goes away.
894 */
895
896 if (!launchd_assumes(j != NULL)) {
897 return KERN_FAILURE;
898 }
899
900 job_ack_no_senders(j);
901
902 return KERN_SUCCESS;
903}
904
905kern_return_t
ef398931 906do_mach_notify_send_once(mach_port_t notify __attribute__((unused)))
5b0a4722 907{
ddbbfbc1
A
908 /*
909 * This message is sent for each send-once right that is deallocated
910 * without being used.
5b0a4722
A
911 */
912
913 return KERN_SUCCESS;
914}
915
916kern_return_t
ddbbfbc1 917do_mach_notify_dead_name(mach_port_t notify __attribute__((unused)), mach_port_name_t name)
5b0a4722
A
918{
919 /* This message is sent to us when one of our send rights no longer has
920 * a receiver somewhere else on the system.
921 */
922
923 if (name == drain_reply_port) {
dcace88f 924 (void)launchd_assumes(launchd_mport_deallocate(name) == KERN_SUCCESS);
5b0a4722
A
925 drain_reply_port = MACH_PORT_NULL;
926 }
927
928 if (launchd_assumes(root_jobmgr != NULL)) {
929 root_jobmgr = jobmgr_delete_anything_with_port(root_jobmgr, name);
930 }
931
932 /* A dead-name notification about a port appears to increment the
933 * rights on said port. Let's deallocate it so that we don't leak
934 * dead-name ports.
935 */
dcace88f 936 (void)launchd_assumes(launchd_mport_deallocate(name) == KERN_SUCCESS);
5b0a4722
A
937
938 return KERN_SUCCESS;
939}
940
941void
942record_caller_creds(mach_msg_header_t *mh)
943{
944 mach_msg_max_trailer_t *tp;
945 size_t trailer_size;
946
947 tp = (mach_msg_max_trailer_t *)((vm_offset_t)mh + round_msg(mh->msgh_size));
948
949 trailer_size = tp->msgh_trailer_size - (mach_msg_size_t)(sizeof(mach_msg_trailer_type_t) - sizeof(mach_msg_trailer_size_t));
950
ddbbfbc1
A
951 if (launchd_assumes(trailer_size >= (mach_msg_size_t)sizeof(audit_token_t))) {
952 audit_token_to_au32(tp->msgh_audit, /* audit UID */ NULL, &ldc.euid,
953 &ldc.egid, &ldc.uid, &ldc.gid, &ldc.pid,
dcace88f 954 &ldc.asid, /* au_tid_t */ NULL);
5b0a4722
A
955 }
956
5b0a4722
A
957}
958
ddbbfbc1
A
959struct ldcred *
960runtime_get_caller_creds(void)
5b0a4722 961{
ddbbfbc1
A
962 return &ldc;
963}
5b0a4722 964
ddbbfbc1
A
965mach_msg_return_t
966launchd_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)
967{
968 mach_msg_return_t mr = ~MACH_MSG_SUCCESS;
969 mach_msg_option_t rcv_options = MACH_RCV_MSG |
970 MACH_RCV_TIMEOUT |
971 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) |
972 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) ;
973
974 do {
975 mr = mach_msg(&bufRequest->Head, rcv_options, 0, rcv_msg_size, port, to, MACH_PORT_NULL);
dcace88f 976 switch (mr) {
ddbbfbc1
A
977 case MACH_RCV_TIMED_OUT :
978 runtime_syslog(LOG_DEBUG, "Message queue is empty.");
979 break;
980 case MACH_RCV_TOO_LARGE :
981 runtime_syslog(LOG_INFO, "Message is larger than %u bytes.", rcv_msg_size);
982 break;
983 default :
dcace88f 984 (void)launchd_assumes(mr == MACH_MSG_SUCCESS);
ddbbfbc1
A
985 }
986
dcace88f
A
987 if (mr == MACH_MSG_SUCCESS) {
988 if (!launchd_assumes(mach_exc_server(&bufRequest->Head, &bufReply->Head) == TRUE)) {
ddbbfbc1
A
989 runtime_syslog(LOG_WARNING, "Exception server routine failed.");
990 break;
991 }
992
993 mach_msg_return_t smr = ~MACH_MSG_SUCCESS;
994 mach_msg_option_t send_options = MACH_SEND_MSG |
995 MACH_SEND_TIMEOUT ;
996
dcace88f 997 (void)launchd_assumes(bufReply->Head.msgh_size <= send_msg_size);
ddbbfbc1 998 smr = mach_msg(&bufReply->Head, send_options, bufReply->Head.msgh_size, 0, MACH_PORT_NULL, to + 100, MACH_PORT_NULL);
dcace88f 999 switch (smr) {
ddbbfbc1
A
1000 case MACH_SEND_TIMED_OUT :
1001 runtime_syslog(LOG_WARNING, "Timed out while trying to send reply to exception message.");
1002 break;
1003 case MACH_SEND_INVALID_DEST :
1004 runtime_syslog(LOG_WARNING, "Tried sending a message to a port that we don't possess a send right to.");
1005 break;
1006 default :
dcace88f 1007 if (!launchd_assumes(smr == MACH_MSG_SUCCESS)) {
ddbbfbc1
A
1008 runtime_syslog(LOG_WARNING, "Couldn't deliver exception reply: 0x%x", smr);
1009 }
1010 break;
1011 }
1012 }
dcace88f 1013 } while (0);
ddbbfbc1
A
1014
1015 return mr;
5b0a4722
A
1016}
1017
1018void
1019launchd_runtime2(mach_msg_size_t msg_size, mig_reply_error_t *bufRequest, mig_reply_error_t *bufReply)
1020{
1021 mach_msg_options_t options, tmp_options;
1022 mig_reply_error_t *bufTemp;
1023 mig_callback the_demux;
1024 mach_msg_timeout_t to;
1025 mach_msg_return_t mr;
ddbbfbc1 1026 size_t busy_cnt;
5b0a4722
A
1027
1028 options = MACH_RCV_MSG|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) |
1029 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0);
1030
1031 tmp_options = options;
1032
1033 for (;;) {
ddbbfbc1 1034 busy_cnt = runtime_busy_cnt + runtime_standby_cnt;
5b0a4722
A
1035 to = MACH_MSG_TIMEOUT_NONE;
1036
ddbbfbc1
A
1037 if (unlikely(msg_size != max_msg_size)) {
1038 /* The buffer isn't big enough to receive messages anymore... */
5b0a4722
A
1039 tmp_options &= ~MACH_RCV_MSG;
1040 options &= ~MACH_RCV_MSG;
1041 if (!(tmp_options & MACH_SEND_MSG)) {
1042 return;
1043 }
1044 }
1045
ddbbfbc1 1046 if ((tmp_options & MACH_RCV_MSG) && (runtime_idle_callback || (busy_cnt == 0))) {
5b0a4722
A
1047 tmp_options |= MACH_RCV_TIMEOUT;
1048
1049 if (!(tmp_options & MACH_SEND_TIMEOUT)) {
dcace88f 1050#if !TARGET_OS_EMBEDDED
ddbbfbc1 1051 to = busy_cnt ? runtime_idle_timeout : (_vproc_standby_timeout() * 1000);
dcace88f 1052#else
ddbbfbc1 1053 to = runtime_idle_timeout;
dcace88f 1054#endif
5b0a4722
A
1055 }
1056 }
1057
1058 runtime_log_push();
1059
1060 mr = mach_msg(&bufReply->Head, tmp_options, bufReply->Head.msgh_size,
1061 msg_size, ipc_port_set, to, MACH_PORT_NULL);
1062
ddbbfbc1
A
1063 time_of_mach_msg_return = runtime_get_opaque_time();
1064
5b0a4722
A
1065 tmp_options = options;
1066
ddbbfbc1
A
1067 /* It looks like the compiler doesn't optimize switch(unlikely(...)) See: 5691066 */
1068 if (unlikely(mr)) switch (mr) {
1069 case MACH_SEND_INVALID_DEST:
1070 case MACH_SEND_TIMED_OUT:
5b0a4722
A
1071 /* We need to clean up and start over. */
1072 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) {
1073 mach_msg_destroy(&bufReply->Head);
1074 }
1075 continue;
ddbbfbc1 1076 case MACH_RCV_TIMED_OUT:
5b0a4722 1077 if (to != MACH_MSG_TIMEOUT_NONE) {
ddbbfbc1
A
1078 if (busy_cnt == 0) {
1079 runtime_syslog(LOG_INFO, "Idle exiting.");
5b0a4722
A
1080 launchd_shutdown();
1081 } else if (runtime_idle_callback) {
1082 runtime_idle_callback();
1083 }
1084 }
1085 continue;
ddbbfbc1 1086 default:
dcace88f 1087 if (!launchd_assumes(mr == MACH_MSG_SUCCESS)) {
ddbbfbc1
A
1088 runtime_syslog(LOG_ERR, "mach_msg(): %u: %s", mr, mach_error_string(mr));
1089 }
5b0a4722
A
1090 continue;
1091 }
1092
1093 bufTemp = bufRequest;
1094 bufRequest = bufReply;
1095 bufReply = bufTemp;
1096
ddbbfbc1 1097 if (unlikely(!(tmp_options & MACH_RCV_MSG))) {
5b0a4722
A
1098 continue;
1099 }
1100
1101 /* we have another request message */
ddbbfbc1 1102#if 0
5b0a4722
A
1103 if (!launchd_assumes(mig_cb_table != NULL)) {
1104 break;
1105 }
ddbbfbc1 1106#endif
5b0a4722
A
1107
1108 the_demux = mig_cb_table[MACH_PORT_INDEX(bufRequest->Head.msgh_local_port)];
1109
ddbbfbc1 1110#if 0
5b0a4722
A
1111 if (!launchd_assumes(the_demux != NULL)) {
1112 break;
1113 }
ddbbfbc1 1114#endif
5b0a4722
A
1115
1116 record_caller_creds(&bufRequest->Head);
ddbbfbc1 1117 runtime_ktrace(RTKT_LAUNCHD_MACH_IPC|DBG_FUNC_START, bufRequest->Head.msgh_local_port, bufRequest->Head.msgh_id, (long)the_demux);
5b0a4722
A
1118
1119 if (the_demux(&bufRequest->Head, &bufReply->Head) == FALSE) {
1120 /* XXX - also gross */
ddbbfbc1 1121 if (likely(bufRequest->Head.msgh_id == MACH_NOTIFY_NO_SENDERS)) {
5b0a4722 1122 notify_server(&bufRequest->Head, &bufReply->Head);
dcace88f
A
1123 } else if (the_demux == protocol_vproc_server) {
1124
1125#if !TARGET_OS_EMBEDDED
1126 /* Similarly gross. */
1127 if (xpc_domain_server(&bufRequest->Head, &bufReply->Head) == FALSE) {
1128 (void)xpc_events_server(&bufRequest->Head, &bufReply->Head);
1129 }
1130#else
1131 (void)xpc_events_server(&bufRequest->Head, &bufReply->Head);
5c88273d 1132#endif /* !TARGET_OS_EMBEDDED */
5b0a4722
A
1133 }
1134 }
1135
ddbbfbc1
A
1136 runtime_ktrace(RTKT_LAUNCHD_MACH_IPC|DBG_FUNC_END, bufReply->Head.msgh_remote_port, bufReply->Head.msgh_bits, bufReply->RetCode);
1137
1138 /* bufReply is a union. If MACH_MSGH_BITS_COMPLEX is set, then bufReply->RetCode is assumed to be zero. */
5b0a4722 1139 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
ddbbfbc1
A
1140 if (unlikely(bufReply->RetCode != KERN_SUCCESS)) {
1141 if (likely(bufReply->RetCode == MIG_NO_REPLY)) {
1142 bufReply->Head.msgh_remote_port = MACH_PORT_NULL;
1143 } else if (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) {
1144 /* destroy the request - but not the reply port */
1145 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
1146 mach_msg_destroy(&bufRequest->Head);
1147 }
5b0a4722
A
1148 }
1149 }
1150
ddbbfbc1 1151 if (likely(bufReply->Head.msgh_remote_port != MACH_PORT_NULL)) {
5b0a4722
A
1152 tmp_options |= MACH_SEND_MSG;
1153
ddbbfbc1 1154 if (unlikely(MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) != MACH_MSG_TYPE_MOVE_SEND_ONCE)) {
5b0a4722
A
1155 tmp_options |= MACH_SEND_TIMEOUT;
1156 }
1157 }
1158 }
1159}
1160
1161int
1162runtime_close(int fd)
1163{
1164 int i;
1165
1166 if (bulk_kev) for (i = bulk_kev_i + 1; i < bulk_kev_cnt; i++) {
1167 switch (bulk_kev[i].filter) {
1168 case EVFILT_VNODE:
1169 case EVFILT_WRITE:
1170 case EVFILT_READ:
ddbbfbc1 1171 if (unlikely((int)bulk_kev[i].ident == fd)) {
5b0a4722
A
1172 runtime_syslog(LOG_DEBUG, "Skipping kevent index: %d", i);
1173 bulk_kev[i].filter = 0;
1174 }
1175 default:
1176 break;
1177 }
1178 }
1179
1180 return close(fd);
1181}
1182
5b0a4722
A
1183void
1184runtime_closelog(void)
1185{
ddbbfbc1
A
1186 runtime_log_push();
1187
5b0a4722 1188 if (ourlogfile) {
dcace88f
A
1189 (void)launchd_assumes(fflush(ourlogfile) == 0);
1190 (void)launchd_assumes(runtime_fsync(fileno(ourlogfile)) != -1);
5b0a4722
A
1191 }
1192}
1193
1194int
1195runtime_fsync(int fd)
1196{
ddbbfbc1
A
1197#if 0
1198 if (do_apple_internal_logging) {
5b0a4722
A
1199 return fcntl(fd, F_FULLFSYNC, NULL);
1200 } else {
1201 return fsync(fd);
1202 }
ddbbfbc1
A
1203#else
1204 return fsync(fd);
1205#endif
5b0a4722
A
1206}
1207
5b0a4722
A
1208int
1209runtime_setlogmask(int maskpri)
1210{
1211 internal_mask_pri = maskpri;
1212
1213 return internal_mask_pri;
1214}
1215
1216void
1217runtime_syslog(int pri, const char *message, ...)
1218{
1219 struct runtime_syslog_attr attr = {
ddbbfbc1
A
1220 g_my_label,
1221 g_my_label,
1222 pid1_magic ? "System" : "Background",
1223 pri,
1224 getuid(),
1225 getpid(),
1226 getpid()
5b0a4722
A
1227 };
1228 va_list ap;
1229
1230 va_start(ap, message);
5b0a4722
A
1231 runtime_vsyslog(&attr, message, ap);
1232
1233 va_end(ap);
1234}
1235
1236void
1237runtime_vsyslog(struct runtime_syslog_attr *attr, const char *message, va_list args)
1238{
5b0a4722
A
1239 int saved_errno = errno;
1240 char newmsg[10000];
ddbbfbc1 1241 bool echo_to_console= false;
5b0a4722 1242
ddbbfbc1
A
1243 if (attr->priority == LOG_APPLEONLY) {
1244 if (do_apple_internal_logging) {
5b0a4722 1245 attr->priority = LOG_NOTICE;
ddbbfbc1
A
1246 } else {
1247 return;
5b0a4722 1248 }
dcace88f 1249 } else if (attr->priority == LOG_SCOLDING) {
ddbbfbc1 1250 attr->priority = g_log_strict_usage ? LOG_NOTICE : LOG_DEBUG;
5b0a4722
A
1251 }
1252
dcace88f 1253 if (attr->priority & LOG_CONSOLE) {
ddbbfbc1
A
1254 echo_to_console = true;
1255 attr->priority &= ~LOG_CONSOLE;
5b0a4722
A
1256 }
1257
ddbbfbc1
A
1258 if (!(LOG_MASK(attr->priority) & internal_mask_pri)) {
1259 return;
5b0a4722
A
1260 }
1261
ddbbfbc1 1262 vsnprintf(newmsg, sizeof(newmsg), message, args);
5b0a4722 1263
dcace88f 1264 if (g_console && (unlikely(low_level_debug) || echo_to_console)) {
ddbbfbc1 1265 fprintf(g_console, "%s %u\t%s %u\t%s\n", attr->from_name, attr->from_pid, attr->about_name, attr->about_pid, newmsg);
5b0a4722
A
1266 }
1267
ddbbfbc1 1268 logmsg_add(attr, saved_errno, newmsg);
5b0a4722
A
1269}
1270
1271bool
1272logmsg_add(struct runtime_syslog_attr *attr, int err_num, const char *msg)
1273{
1274 size_t lm_sz = sizeof(struct logmsg_s) + strlen(msg) + strlen(attr->from_name) + strlen(attr->about_name) + strlen(attr->session_name) + 4;
1275 char *data_off;
1276 struct logmsg_s *lm;
1277
1278#define ROUND_TO_64BIT_WORD_SIZE(x) ((x + 7) & ~7)
1279
1280 /* we do this to make the unpacking for the log_drain cause unalignment faults */
1281 lm_sz = ROUND_TO_64BIT_WORD_SIZE(lm_sz);
1282
ddbbfbc1 1283 if (unlikely((lm = calloc(1, lm_sz)) == NULL)) {
5b0a4722
A
1284 return false;
1285 }
1286
1287 data_off = lm->data;
1288
ddbbfbc1 1289 lm->when = runtime_get_wall_time();
5b0a4722
A
1290 lm->from_pid = attr->from_pid;
1291 lm->about_pid = attr->about_pid;
1292 lm->err_num = err_num;
1293 lm->pri = attr->priority;
1294 lm->obj_sz = lm_sz;
1295 lm->msg = data_off;
1296 data_off += sprintf(data_off, "%s", msg) + 1;
1297 lm->from_name = data_off;
1298 data_off += sprintf(data_off, "%s", attr->from_name) + 1;
1299 lm->about_name = data_off;
1300 data_off += sprintf(data_off, "%s", attr->about_name) + 1;
1301 lm->session_name = data_off;
1302 data_off += sprintf(data_off, "%s", attr->session_name) + 1;
1303
1304 STAILQ_INSERT_TAIL(&logmsg_queue, lm, sqe);
1305 logmsg_queue_sz += lm_sz;
1306 logmsg_queue_cnt++;
1307
1308 return true;
1309}
1310
1311void
1312logmsg_remove(struct logmsg_s *lm)
1313{
1314 STAILQ_REMOVE(&logmsg_queue, lm, logmsg_s, sqe);
1315 logmsg_queue_sz -= lm->obj_sz;
1316 logmsg_queue_cnt--;
1317
1318 free(lm);
1319}
1320
1321kern_return_t
1322runtime_log_pack(vm_offset_t *outval, mach_msg_type_number_t *outvalCnt)
1323{
1324 struct logmsg_s *lm;
1325 void *offset;
1326
1327 *outvalCnt = logmsg_queue_sz;
1328
1329 mig_allocate(outval, *outvalCnt);
1330
ddbbfbc1 1331 if (unlikely(*outval == 0)) {
5b0a4722
A
1332 return 1;
1333 }
1334
1335 offset = (void *)*outval;
1336
dcace88f 1337 if (g_log_per_user_shutdown && !ourlogfile && !pid1_magic && shutdown_in_progress) {
ddbbfbc1
A
1338 char logfile[NAME_MAX];
1339 snprintf(logfile, sizeof(logfile), "/var/tmp/launchd-%s.shutdown.log", g_username);
1340
1341 char logfile1[NAME_MAX];
1342 snprintf(logfile1, sizeof(logfile1), "/var/tmp/launchd-%s.shutdown.log.1", g_username);
1343
1344 rename(logfile, logfile1);
1345 ourlogfile = fopen(logfile, "a");
1346 }
1347
1348 static int64_t shutdown_start = 0;
dcace88f 1349 if (shutdown_start == 0) {
ddbbfbc1
A
1350 shutdown_start = runtime_get_wall_time();
1351 }
1352
5b0a4722 1353 while ((lm = STAILQ_FIRST(&logmsg_queue))) {
ddbbfbc1 1354 int64_t log_delta = lm->when - shutdown_start;
dcace88f 1355 if (!pid1_magic && ourlogfile) {
ddbbfbc1
A
1356 fprintf(ourlogfile, "%8lld%6u %-40s%6u %-40s %s\n", log_delta,
1357 lm->from_pid, lm->from_name, lm->about_pid, lm->about_name, lm->msg);
dcace88f 1358 fflush(ourlogfile);
ddbbfbc1
A
1359 }
1360
1361 lm->from_name_offset = lm->from_name - (char *)lm;
1362 lm->about_name_offset = lm->about_name - (char *)lm;
1363 lm->msg_offset = lm->msg - (char *)lm;
1364 lm->session_name_offset = lm->session_name - (char *)lm;
5b0a4722
A
1365
1366 memcpy(offset, lm, lm->obj_sz);
1367
1368 offset += lm->obj_sz;
1369
1370 logmsg_remove(lm);
1371 }
ddbbfbc1 1372
dcace88f 1373 if (ourlogfile) {
ddbbfbc1
A
1374 fflush(ourlogfile);
1375 }
5b0a4722
A
1376
1377 return 0;
1378}
1379
1380void
1381runtime_log_uncork_pending_drain(void)
1382{
1383 mach_msg_type_number_t outvalCnt;
1384 mach_port_t tmp_port;
1385 vm_offset_t outval;
1386
1387 if (!drain_reply_port) {
1388 return;
1389 }
1390
1391 if (logmsg_queue_cnt == 0) {
1392 return;
1393 }
1394
1395 if (runtime_log_pack(&outval, &outvalCnt) != 0) {
1396 return;
1397 }
1398
1399 tmp_port = drain_reply_port;
1400 drain_reply_port = MACH_PORT_NULL;
1401
ddbbfbc1 1402 if (unlikely(errno = job_mig_log_drain_reply(tmp_port, 0, outval, outvalCnt))) {
dcace88f
A
1403 (void)launchd_assumes(errno == MACH_SEND_INVALID_DEST);
1404 (void)launchd_assumes(launchd_mport_deallocate(tmp_port) == KERN_SUCCESS);
5b0a4722
A
1405 }
1406
1407 mig_deallocate(outval, outvalCnt);
1408}
1409
1410void
1411runtime_log_push(void)
1412{
ddbbfbc1
A
1413 static pthread_mutex_t ourlock = PTHREAD_MUTEX_INITIALIZER;
1414 static int64_t shutdown_start, log_delta;
5b0a4722 1415 mach_msg_type_number_t outvalCnt;
ddbbfbc1 1416 struct logmsg_s *lm;
5b0a4722
A
1417 vm_offset_t outval;
1418
1419 if (logmsg_queue_cnt == 0) {
dcace88f 1420 (void)launchd_assumes(STAILQ_EMPTY(&logmsg_queue));
5b0a4722 1421 return;
ddbbfbc1
A
1422 } else if (!pid1_magic) {
1423 if (runtime_log_pack(&outval, &outvalCnt) == 0) {
dcace88f 1424 (void)launchd_assumes(_vprocmgr_log_forward(inherited_bootstrap_port, (void *)outval, outvalCnt) == NULL);
ddbbfbc1
A
1425 mig_deallocate(outval, outvalCnt);
1426 }
5b0a4722
A
1427 return;
1428 }
1429
ddbbfbc1
A
1430 if (likely(!shutdown_in_progress && !fake_shutdown_in_progress)) {
1431 runtime_log_uncork_pending_drain();
5b0a4722
A
1432 return;
1433 }
1434
ddbbfbc1
A
1435 if (unlikely(shutdown_start == 0)) {
1436 shutdown_start = runtime_get_wall_time();
1437 launchd_log_vm_stats();
1438 }
5b0a4722 1439
ddbbfbc1
A
1440 pthread_mutex_lock(&ourlock);
1441
dcace88f 1442 if (unlikely(ourlogfile == NULL) && g_log_pid1_shutdown) {
ddbbfbc1
A
1443 rename("/var/log/launchd-shutdown.log", "/var/log/launchd-shutdown.log.1");
1444 ourlogfile = fopen("/var/log/launchd-shutdown.log", "a");
1445 }
1446
1447 pthread_mutex_unlock(&ourlock);
1448
1449 if (unlikely(!ourlogfile)) {
1450 return;
1451 }
1452
1453 while ((lm = STAILQ_FIRST(&logmsg_queue))) {
1454 log_delta = lm->when - shutdown_start;
1455
1456 fprintf(ourlogfile, "%8lld%6u %-40s%6u %-40s %s\n", log_delta,
1457 lm->from_pid, lm->from_name, lm->about_pid, lm->about_name, lm->msg);
1458
1459 logmsg_remove(lm);
1460 }
1461
1462 fflush(ourlogfile);
5b0a4722
A
1463}
1464
1465kern_return_t
1466runtime_log_forward(uid_t forward_uid, gid_t forward_gid, vm_offset_t inval, mach_msg_type_number_t invalCnt)
1467{
1468 struct logmsg_s *lm, *lm_walk;
1469 mach_msg_type_number_t data_left = invalCnt;
1470
1471 if (inval == 0) {
1472 return 0;
1473 }
1474
1475 for (lm_walk = (struct logmsg_s *)inval; (data_left > 0) && (lm_walk->obj_sz <= data_left); lm_walk = ((void *)lm_walk + lm_walk->obj_sz)) {
ddbbfbc1
A
1476 /* malloc() does not return NULL if you ask it for an allocation of size 0.
1477 * It will return a valid pointer that can be passed to free(). If we don't
1478 * do this check, we'll wind up corrupting our heap in the subsequent
1479 * assignments.
1480 *
1481 * We break out if this check fails because, obj_sz is supposed to include
1482 * the size of the logmsg_s struct. If it claims to be of zero size, we
1483 * can't safely increment our counter because something obviously got screwed
1484 * up along the way, since this should always be at least sizeof(struct logmsg_s).
1485 */
dcace88f 1486 if (!launchd_assumes(lm_walk->obj_sz > 0)) {
ddbbfbc1
A
1487 runtime_syslog(LOG_WARNING, "Encountered a log message of size 0 with %u bytes left in forwarded data. Ignoring remaining messages.", data_left);
1488 break;
1489 }
1490
1491 /* If malloc() keeps failing, we shouldn't put additional pressure on the system
1492 * by attempting to add more messages to the log until it returns success
1493 * log a failure, hope pressure lets off, and move on.
1494 */
5b0a4722 1495 if (!launchd_assumes(lm = malloc(lm_walk->obj_sz))) {
ddbbfbc1
A
1496 runtime_syslog(LOG_WARNING, "Failed to allocate %llu bytes for log message with %u bytes left in forwarded data. Ignoring remaining messages.", lm_walk->obj_sz, data_left);
1497 break;
5b0a4722
A
1498 }
1499
1500 memcpy(lm, lm_walk, lm_walk->obj_sz);
1501 lm->sender_uid = forward_uid;
1502 lm->sender_gid = forward_gid;
1503
1504 lm->from_name += (size_t)lm;
1505 lm->about_name += (size_t)lm;
1506 lm->msg += (size_t)lm;
1507 lm->session_name += (size_t)lm;
1508
1509 STAILQ_INSERT_TAIL(&logmsg_queue, lm, sqe);
1510 logmsg_queue_sz += lm->obj_sz;
1511 logmsg_queue_cnt++;
1512
1513 data_left -= lm->obj_sz;
1514 }
1515
1516 mig_deallocate(inval, invalCnt);
1517
1518 return 0;
1519}
1520
1521kern_return_t
1522runtime_log_drain(mach_port_t srp, vm_offset_t *outval, mach_msg_type_number_t *outvalCnt)
1523{
dcace88f 1524 (void)launchd_assumes(drain_reply_port == 0);
5b0a4722 1525
ddbbfbc1 1526 if ((logmsg_queue_cnt == 0) || shutdown_in_progress || fake_shutdown_in_progress) {
5b0a4722 1527 drain_reply_port = srp;
dcace88f 1528 (void)launchd_assumes(launchd_mport_notify_req(drain_reply_port, MACH_NOTIFY_DEAD_NAME) == KERN_SUCCESS);
5b0a4722
A
1529
1530 return MIG_NO_REPLY;
1531 }
1532
1533 return runtime_log_pack(outval, outvalCnt);
1534}
1535
1536/*
1537 * We should break this into two reference counts.
1538 *
1539 * One for hard references that would prevent exiting.
1540 * One for soft references that would only prevent idle exiting.
1541 *
1542 * In the long run, reference counting should completely automate when a
1543 * process can and should exit.
1544 */
1545void
1546runtime_add_ref(void)
1547{
ddbbfbc1
A
1548 if (!pid1_magic) {
1549 #if !TARGET_OS_EMBEDDED
1550 _vproc_transaction_begin();
1551 #endif
1552 }
587e987e 1553
5b0a4722 1554 runtime_busy_cnt++;
587e987e 1555 runtime_remove_timer();
5b0a4722
A
1556}
1557
1558void
1559runtime_del_ref(void)
1560{
ddbbfbc1
A
1561 if (!pid1_magic) {
1562 #if !TARGET_OS_EMBEDDED
dcace88f 1563 if (_vproc_transaction_count() == 0) {
ddbbfbc1
A
1564 runtime_syslog(LOG_INFO, "Exiting cleanly.");
1565 }
1566
1567 runtime_closelog();
1568 _vproc_transaction_end();
1569 #endif
1570 }
587e987e 1571
5b0a4722 1572 runtime_busy_cnt--;
587e987e 1573 runtime_install_timer();
5b0a4722 1574}
fe044cc9 1575
ddbbfbc1
A
1576void
1577runtime_add_weak_ref(void)
1578{
1579 if (!pid1_magic) {
1580 #if !TARGET_OS_EMBEDDED
1581 _vproc_standby_begin();
1582 #endif
1583 }
1584 runtime_standby_cnt++;
1585}
1586
1587void
1588runtime_del_weak_ref(void)
1589{
1590 if (!pid1_magic) {
1591 #if !TARGET_OS_EMBEDDED
1592 _vproc_standby_end();
1593 #endif
1594 }
1595 runtime_standby_cnt--;
1596}
1597
587e987e
A
1598void
1599runtime_install_timer(void)
1600{
dcace88f
A
1601 if (!pid1_magic && runtime_busy_cnt == 0) {
1602 (void)launchd_assumes(kevent_mod((uintptr_t)&g_runtime_busy_time, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 30, root_jobmgr) != -1);
587e987e
A
1603 }
1604}
1605
1606void
1607runtime_remove_timer(void)
1608{
dcace88f
A
1609 if (!pid1_magic && runtime_busy_cnt > 0) {
1610 (void)launchd_assumes(kevent_mod((uintptr_t)&g_runtime_busy_time, EVFILT_TIMER, EV_DELETE, 0, 0, NULL) != -1);
587e987e
A
1611 }
1612}
1613
fe044cc9 1614kern_return_t
ddbbfbc1
A
1615catch_mach_exception_raise(mach_port_t exception_port __attribute__((unused)), mach_port_t thread, mach_port_t task,
1616 exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt)
fe044cc9 1617{
ddbbfbc1 1618 pid_t p4t = -1;
fe044cc9 1619
dcace88f 1620 (void)launchd_assumes(pid_for_task(task, &p4t) == 0);
ddbbfbc1
A
1621
1622 runtime_syslog(LOG_NOTICE, "%s(): PID: %u thread: 0x%x type: 0x%x code: %p codeCnt: 0x%x",
1623 __func__, p4t, thread, exception, code, codeCnt);
1624
dcace88f
A
1625 (void)launchd_assumes(launchd_mport_deallocate(thread) == KERN_SUCCESS);
1626 (void)launchd_assumes(launchd_mport_deallocate(task) == KERN_SUCCESS);
fe044cc9 1627
ddbbfbc1 1628 return KERN_SUCCESS;
fe044cc9
A
1629}
1630
1631kern_return_t
ddbbfbc1
A
1632catch_mach_exception_raise_state(mach_port_t exception_port __attribute__((unused)),
1633 exception_type_t exception, const mach_exception_data_t code, mach_msg_type_number_t codeCnt,
1634 int *flavor, const thread_state_t old_state, mach_msg_type_number_t old_stateCnt,
1635 thread_state_t new_state, mach_msg_type_number_t *new_stateCnt)
fe044cc9
A
1636{
1637 runtime_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",
1638 __func__, exception, code, codeCnt, flavor, old_state, old_stateCnt, new_state, new_stateCnt);
ddbbfbc1 1639
fe044cc9
A
1640 memcpy(new_state, old_state, old_stateCnt * sizeof(old_state[0]));
1641 *new_stateCnt = old_stateCnt;
1642
ddbbfbc1 1643 return KERN_SUCCESS;
fe044cc9
A
1644}
1645
1646kern_return_t
ddbbfbc1
A
1647catch_mach_exception_raise_state_identity(mach_port_t exception_port __attribute__((unused)), mach_port_t thread, mach_port_t task,
1648 exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt,
1649 int *flavor, thread_state_t old_state, mach_msg_type_number_t old_stateCnt,
1650 thread_state_t new_state, mach_msg_type_number_t *new_stateCnt)
fe044cc9 1651{
ddbbfbc1
A
1652 pid_t p4t = -1;
1653
dcace88f 1654 (void)launchd_assumes(pid_for_task(task, &p4t) == 0);
fe044cc9 1655
ddbbfbc1
A
1656 runtime_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",
1657 __func__, p4t, thread, exception, code, codeCnt, flavor, old_state, old_stateCnt, new_state, new_stateCnt);
1658
fe044cc9
A
1659 memcpy(new_state, old_state, old_stateCnt * sizeof(old_state[0]));
1660 *new_stateCnt = old_stateCnt;
1661
dcace88f
A
1662 (void)launchd_assumes(launchd_mport_deallocate(thread) == KERN_SUCCESS);
1663 (void)launchd_assumes(launchd_mport_deallocate(task) == KERN_SUCCESS);
fe044cc9 1664
ddbbfbc1
A
1665 return KERN_SUCCESS;
1666}
1667
1668void
1669launchd_log_vm_stats(void)
1670{
1671 static struct vm_statistics orig_stats;
1672 static bool did_first_pass;
1673 unsigned int count = HOST_VM_INFO_COUNT;
1674 struct vm_statistics stats, *statsp;
1675 mach_port_t mhs = mach_host_self();
1676
1677 statsp = did_first_pass ? &stats : &orig_stats;
1678
1679 if (!launchd_assumes(host_statistics(mhs, HOST_VM_INFO, (host_info_t)statsp, &count) == KERN_SUCCESS)) {
1680 return;
1681 }
1682
dcace88f 1683 (void)launchd_assumes(count == HOST_VM_INFO_COUNT);
ddbbfbc1
A
1684
1685 if (did_first_pass) {
1686 runtime_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",
1687 stats.free_count - orig_stats.free_count,
1688 stats.active_count - orig_stats.active_count,
1689 stats.inactive_count - orig_stats.inactive_count,
1690 stats.reactivations - orig_stats.reactivations,
1691 stats.pageins - orig_stats.pageins,
1692 stats.pageouts - orig_stats.pageouts,
1693 stats.faults - orig_stats.faults,
1694 stats.cow_faults - orig_stats.cow_faults,
1695 stats.purgeable_count - orig_stats.purgeable_count,
1696 stats.purges - orig_stats.purges);
1697 } else {
1698 runtime_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",
1699 orig_stats.free_count,
1700 orig_stats.active_count,
1701 orig_stats.inactive_count,
1702 orig_stats.reactivations,
1703 orig_stats.pageins,
1704 orig_stats.pageouts,
1705 orig_stats.faults,
1706 orig_stats.cow_faults,
1707 orig_stats.purgeable_count,
1708 orig_stats.purges);
1709
1710 did_first_pass = true;
1711 }
1712
1713 launchd_mport_deallocate(mhs);
1714}
1715
1716int64_t
1717runtime_get_wall_time(void)
1718{
1719 struct timeval tv;
1720 int64_t r;
1721
dcace88f 1722 (void)launchd_assumes(gettimeofday(&tv, NULL) != -1);
ddbbfbc1
A
1723
1724 r = tv.tv_sec;
1725 r *= USEC_PER_SEC;
1726 r += tv.tv_usec;
1727
1728 return r;
1729}
1730
1731uint64_t
1732runtime_get_opaque_time(void)
1733{
1734 return mach_absolute_time();
1735}
1736
1737uint64_t
1738runtime_get_opaque_time_of_event(void)
1739{
1740 return time_of_mach_msg_return;
1741}
1742
1743uint64_t
1744runtime_get_nanoseconds_since(uint64_t o)
1745{
1746 return runtime_opaque_time_to_nano(runtime_get_opaque_time_of_event() - o);
1747}
1748
1749uint64_t
1750runtime_opaque_time_to_nano(uint64_t o)
1751{
1752#if defined(__i386__) || defined(__x86_64__)
1753 if (unlikely(tbi.numer != tbi.denom)) {
1754#elif defined(__ppc__) || defined(__ppc64__)
1755 if (likely(tbi.numer != tbi.denom)) {
1756#else
1757 if (tbi.numer != tbi.denom) {
1758#endif
1759#ifdef __LP64__
1760 __uint128_t tmp = o;
1761 tmp *= tbi.numer;
1762 tmp /= tbi.denom;
1763 o = tmp;
1764#else
1765 if (o <= tbi_safe_math_max) {
1766 o *= tbi.numer;
1767 o /= tbi.denom;
1768 } else {
1769 double d = o;
1770 d *= tbi_float_val;
1771 o = d;
1772 }
1773#endif
1774 }
1775
1776 return o;
1777}
1778
1779void
1780do_file_init(void)
1781{
1782 struct stat sb;
1783
1784 launchd_assert(mach_timebase_info(&tbi) == 0);
1785 tbi_float_val = tbi.numer;
1786 tbi_float_val /= tbi.denom;
1787 tbi_safe_math_max = UINT64_MAX / tbi.numer;
1788
1789 if (getpid() == 1) {
1790 pid1_magic = true;
1791 }
1792
1793 if (stat("/AppleInternal", &sb) == 0 && stat("/var/db/disableAppleInternal", &sb) == -1) {
1794 do_apple_internal_logging = true;
1795 }
1796
1797 if (stat("/var/db/.debug_launchd", &sb) == 0) {
1798 internal_mask_pri = LOG_UPTO(LOG_DEBUG);
1799 low_level_debug = true;
1800 }
1801
dcace88f 1802 if (stat("/var/db/.launchd_log_per_user_shutdown", &sb) == 0) {
ddbbfbc1
A
1803 g_log_per_user_shutdown = true;
1804 }
1805
dcace88f 1806 if (stat("/var/db/.launchd_use_gmalloc", &sb) == 0) {
ddbbfbc1
A
1807 g_use_gmalloc = true;
1808 }
dcace88f
A
1809
1810 if (stat("/var/db/.launchd_malloc_log_stacks", &sb) == 0) {
1811 g_malloc_log_stacks = true;
1812 g_use_gmalloc = false;
1813 }
ddbbfbc1 1814
dcace88f 1815 if (pid1_magic && stat("/var/db/.launchd_log_pid1_shutdown", &sb) == 0) {
ddbbfbc1
A
1816 g_log_pid1_shutdown = true;
1817 }
1818
1819 char bootargs[128];
1820 size_t len = sizeof(bootargs) - 1;
1821 int r = pid1_magic ? sysctlbyname("kern.bootargs", bootargs, &len, NULL, 0) : -1;
dcace88f
A
1822 if (r == 0) {
1823 if (strnstr(bootargs, "-v", len)) {
1824 g_verbose_boot = true;
1825 }
1826 if (strnstr(bootargs, "launchd_trap_sigkill_bugs", len)) {
1827 g_trap_sigkill_bugs = true;
1828 }
ddbbfbc1
A
1829 }
1830
dcace88f 1831 if (pid1_magic && g_verbose_boot && stat("/var/db/.launchd_shutdown_debugging", &sb) == 0) {
ddbbfbc1
A
1832 g_shutdown_debugging = true;
1833 }
1834
dcace88f 1835 if (stat("/var/db/.launchd_log_strict_usage", &sb) == 0) {
ddbbfbc1
A
1836 g_log_strict_usage = true;
1837 }
fe044cc9 1838}