]> git.saurik.com Git - apple/launchd.git/blame_incremental - launchd/src/libvproc.c
launchd-329.3.3.tar.gz
[apple/launchd.git] / launchd / src / libvproc.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 1999-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_APACHE_LICENSE_HEADER_START@
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * @APPLE_APACHE_LICENSE_HEADER_END@
19 */
20
21#include "config.h"
22#include "vproc.h"
23#include "vproc_priv.h"
24#include "vproc_internal.h"
25
26#include <mach/mach.h>
27#include <mach/vm_map.h>
28#include <sys/param.h>
29#include <stdlib.h>
30#include <stdio.h>
31#include <errno.h>
32#include <unistd.h>
33#include <syslog.h>
34#include <pthread.h>
35#include <signal.h>
36#include <assert.h>
37#include <libkern/OSAtomic.h>
38#include <sys/syscall.h>
39
40#if HAVE_QUARANTINE
41#include <quarantine.h>
42#endif
43
44#include "launch.h"
45#include "launch_priv.h"
46#include "launch_internal.h"
47#include "launchd_ktrace.h"
48
49#include "protocol_vproc.h"
50
51#include "reboot2.h"
52
53#define likely(x) __builtin_expect((bool)(x), true)
54#define unlikely(x) __builtin_expect((bool)(x), false)
55
56static mach_port_t get_root_bootstrap_port(void);
57
58const char *__crashreporter_info__; /* this should get merged with other versions of the symbol */
59
60static int64_t cached_pid = -1;
61static struct vproc_shmem_s *vproc_shmem;
62static pthread_once_t shmem_inited = PTHREAD_ONCE_INIT;
63static uint64_t s_cached_transactions_enabled = 0;
64
65struct vproc_s {
66 int32_t refcount;
67 mach_port_t j_port;
68};
69
70vproc_t vprocmgr_lookup_vproc(const char *label)
71{
72 struct vproc_s *vp = NULL;
73
74 mach_port_t mp = MACH_PORT_NULL;
75 kern_return_t kr = vproc_mig_port_for_label(bootstrap_port, (char *)label, &mp);
76 if( kr == BOOTSTRAP_SUCCESS ) {
77 vp = (struct vproc_s *)calloc(1, sizeof(struct vproc_s));
78 if( vp ) {
79 vp->refcount = 1;
80 mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_SEND, 1);
81 vp->j_port = mp;
82 }
83 mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_SEND, -1);
84 }
85
86 return vp;
87}
88
89vproc_t vproc_retain(vproc_t vp)
90{
91 int32_t orig = OSAtomicAdd32(1, &vp->refcount) - 1;
92 if( orig <= 0 ) {
93 /* We've gone from 0 to 1, which means that this object was due to be freed. */
94 __crashreporter_info__ = "Under-retain / over-release of vproc_t.";
95 abort();
96 }
97
98 return vp;
99}
100
101void vproc_release(vproc_t vp)
102{
103 int32_t newval = OSAtomicAdd32(-1, &vp->refcount);
104 if( newval < 0 ) {
105 /* We're in negative numbers, which is bad. */
106 __crashreporter_info__ = "Over-release of vproc_t.";
107 abort();
108 } else if( newval == 0 ) {
109 mach_port_deallocate(mach_task_self(), vp->j_port);
110 free(vp);
111 }
112}
113
114static void
115vproc_shmem_init(void)
116{
117 vm_address_t vm_addr = 0;
118 mach_port_t shmem_port;
119 kern_return_t kr;
120
121 kr = vproc_mig_setup_shmem(bootstrap_port, &shmem_port);
122
123 //assert(kr == 0);
124 if (kr) {
125 /* rdar://problem/6416724
126 * If we fail to set up a shared memory page, just allocate a local chunk
127 * of memory. This way, processes can still introspect their own transaction
128 * counts if they're being run under a debugger. Moral of the story: Debug
129 * from the environment you intend to run in.
130 */
131 void *_vm_addr = calloc(1, sizeof(struct vproc_shmem_s));
132 if( !_vm_addr ) {
133 return;
134 }
135
136 vm_addr = (vm_address_t)_vm_addr;
137 } else {
138 kr = vm_map(mach_task_self(), &vm_addr, getpagesize(), 0, true, shmem_port, 0, false,
139 VM_PROT_READ|VM_PROT_WRITE, VM_PROT_READ|VM_PROT_WRITE, VM_INHERIT_NONE);
140
141 //assert(kr == 0);
142 if (kr) return;
143
144 kr = mach_port_deallocate(mach_task_self(), shmem_port);
145
146 //assert(kr == 0);
147 }
148
149 vproc_shmem = (struct vproc_shmem_s *)vm_addr;
150}
151
152static void
153vproc_client_init(void)
154{
155 char *val = getenv(LAUNCHD_DO_APPLE_INTERNAL_LOGGING);
156 if( val ) {
157 if( strncmp(val, "true", sizeof("true") - 1) == 0 ) {
158 do_apple_internal_logging = true;
159 }
160 }
161
162 vproc_shmem_init();
163}
164
165vproc_transaction_t
166vproc_transaction_begin(vproc_t vp __attribute__((unused)))
167{
168 vproc_transaction_t vpt = (vproc_transaction_t)vproc_shmem_init; /* we need a "random" variable that is testable */
169#if !TARGET_OS_EMBEDDED
170 _vproc_transaction_begin();
171#endif
172
173 return vpt;
174}
175
176void
177_vproc_transaction_begin(void)
178{
179#if !TARGET_OS_EMBEDDED
180 if (unlikely(vproc_shmem == NULL)) {
181 int po_r = pthread_once(&shmem_inited, vproc_client_init);
182 if (po_r != 0 || vproc_shmem == NULL) {
183 return;
184 }
185 }
186
187 typeof(vproc_shmem->vp_shmem_transaction_cnt) old = 0;
188 do {
189 old = vproc_shmem->vp_shmem_transaction_cnt;
190
191 if (unlikely(old < 0)) {
192 if (vproc_shmem->vp_shmem_flags & VPROC_SHMEM_EXITING) {
193 _exit(0);
194 } else {
195 __crashreporter_info__ = "Unbalanced: vproc_transaction_begin()";
196 }
197 abort();
198 }
199 } while( !__sync_bool_compare_and_swap(&vproc_shmem->vp_shmem_transaction_cnt, old, old + 1) );
200
201 runtime_ktrace(RTKT_VPROC_TRANSACTION_INCREMENT, old + 1, 0, 0);
202#endif
203}
204
205size_t
206_vproc_transaction_count(void)
207{
208 return likely(vproc_shmem) ? vproc_shmem->vp_shmem_transaction_cnt : INT32_MAX;
209}
210
211size_t
212_vproc_standby_count(void)
213{
214#ifdef VPROC_STANDBY_IMPLEMENTED
215 return likely(vproc_shmem) ? vproc_shmem->vp_shmem_standby_cnt : INT32_MAX;
216#else
217 return 0;
218#endif
219}
220
221size_t
222_vproc_standby_timeout(void)
223{
224 return likely(vproc_shmem) ? vproc_shmem->vp_shmem_standby_timeout : 0;
225}
226
227bool
228_vproc_pid_is_managed(pid_t p)
229{
230 boolean_t result = false;
231 vproc_mig_pid_is_managed(bootstrap_port, p, &result);
232
233 return result;
234}
235
236kern_return_t
237_vproc_transaction_count_for_pid(pid_t p, int32_t *count, bool *condemned)
238{
239 boolean_t _condemned = false;
240 kern_return_t kr = vproc_mig_transaction_count_for_pid(bootstrap_port, p, count, &_condemned);
241 if( kr == KERN_SUCCESS && condemned ) {
242 *condemned = _condemned ? true : false;
243 }
244
245 return kr;
246}
247
248void
249#if !TARGET_OS_EMBEDDED
250_vproc_transaction_try_exit(int status)
251{
252 if (unlikely(vproc_shmem == NULL)) {
253 return;
254 }
255
256 if (__sync_bool_compare_and_swap(&vproc_shmem->vp_shmem_transaction_cnt, 0, -1)) {
257 vproc_shmem->vp_shmem_flags |= VPROC_SHMEM_EXITING;
258 _exit(status);
259 }
260}
261#else
262_vproc_transaction_try_exit(int status __attribute__((unused)))
263{
264
265}
266#endif
267
268void
269vproc_transaction_end(vproc_t vp __attribute__((unused)), vproc_transaction_t vpt)
270{
271 if (unlikely(vpt != (vproc_transaction_t)vproc_shmem_init)) {
272 __crashreporter_info__ = "Bogus transaction handle passed to vproc_transaction_end() ";
273 abort();
274 }
275
276#if !TARGET_OS_EMBEDDED
277 _vproc_transaction_end();
278#endif
279}
280
281void
282_vproc_transaction_end(void)
283{
284#if !TARGET_OS_EMBEDDED
285 typeof(vproc_shmem->vp_shmem_transaction_cnt) newval;
286
287 if (unlikely(vproc_shmem == NULL)) {
288 return;
289 }
290
291 newval = __sync_sub_and_fetch(&vproc_shmem->vp_shmem_transaction_cnt, 1);
292
293 runtime_ktrace(RTKT_VPROC_TRANSACTION_DECREMENT, newval, 0, 0);
294 if (unlikely(newval < 0)) {
295 if (vproc_shmem->vp_shmem_flags & VPROC_SHMEM_EXITING) {
296 _exit(0);
297 } else {
298 __crashreporter_info__ = "Unbalanced: vproc_transaction_end()";
299 }
300 abort();
301 }
302#endif
303}
304
305vproc_standby_t
306vproc_standby_begin(vproc_t vp __attribute__((unused)))
307{
308#ifdef VPROC_STANDBY_IMPLEMENTED
309 vproc_standby_t vpsb = (vproc_standby_t)vproc_shmem_init; /* we need a "random" variable that is testable */
310
311 _vproc_standby_begin();
312
313 return vpsb;
314#else
315 return NULL;
316#endif
317}
318
319void
320_vproc_standby_begin(void)
321{
322#ifdef VPROC_STANDBY_IMPLEMENTED
323 typeof(vproc_shmem->vp_shmem_standby_cnt) newval;
324
325 if (unlikely(vproc_shmem == NULL)) {
326 int po_r = pthread_once(&shmem_inited, vproc_client_init);
327 if (po_r != 0 || vproc_shmem == NULL) {
328 return;
329 }
330 }
331
332 newval = __sync_add_and_fetch(&vproc_shmem->vp_shmem_standby_cnt, 1);
333
334 if (unlikely(newval < 1)) {
335 __crashreporter_info__ = "Unbalanced: vproc_standby_begin()";
336 abort();
337 }
338#else
339 return;
340#endif
341}
342
343void
344vproc_standby_end(vproc_t vp __attribute__((unused)), vproc_standby_t vpt __attribute__((unused)))
345{
346#ifdef VPROC_STANDBY_IMPLEMENTED
347 if (unlikely(vpt != (vproc_standby_t)vproc_shmem_init)) {
348 __crashreporter_info__ = "Bogus standby handle passed to vproc_standby_end() ";
349 abort();
350 }
351
352 _vproc_standby_end();
353#else
354 return;
355#endif
356}
357
358void
359_vproc_standby_end(void)
360{
361#ifdef VPROC_STANDBY_IMPLEMENTED
362 typeof(vproc_shmem->vp_shmem_standby_cnt) newval;
363
364 if( unlikely(vproc_shmem == NULL) ) {
365 __crashreporter_info__ = "Process called vproc_standby_end() when not enrolled in transaction model.";
366 abort();
367 }
368
369 newval = __sync_sub_and_fetch(&vproc_shmem->vp_shmem_standby_cnt, 1);
370
371 if (unlikely(newval < 0)) {
372 __crashreporter_info__ = "Unbalanced: vproc_standby_end()";
373 abort();
374 }
375#else
376 return;
377#endif
378}
379
380kern_return_t
381_vproc_grab_subset(mach_port_t bp, mach_port_t *reqport, mach_port_t *rcvright, launch_data_t *outval,
382 mach_port_array_t *ports, mach_msg_type_number_t *portCnt)
383{
384 mach_msg_type_number_t outdata_cnt;
385 vm_offset_t outdata = 0;
386 size_t data_offset = 0;
387 launch_data_t out_obj;
388 kern_return_t kr;
389
390 if ((kr = vproc_mig_take_subset(bp, reqport, rcvright, &outdata, &outdata_cnt, ports, portCnt))) {
391 goto out;
392 }
393
394 if ((out_obj = launch_data_unpack((void *)outdata, outdata_cnt, NULL, 0, &data_offset, NULL))) {
395 *outval = launch_data_copy(out_obj);
396 } else {
397 kr = 1;
398 }
399
400out:
401 if (outdata) {
402 mig_deallocate(outdata, outdata_cnt);
403 }
404
405 return kr;
406}
407
408vproc_err_t
409_vproc_post_fork_ping(void)
410{
411#if !TARGET_OS_EMBEDDED
412 au_asid_t s = AU_DEFAUDITSID;
413 do {
414 mach_port_t session = MACH_PORT_NULL;
415 kern_return_t kr = vproc_mig_post_fork_ping(bootstrap_port, mach_task_self(), &session);
416 if( kr != KERN_SUCCESS ) {
417 /* If this happens, our bootstrap port probably got hosed. */
418 _vproc_log(LOG_ERR, "Post-fork ping failed!");
419 break;
420 }
421
422 /* If we get back MACH_PORT_NULL, that means we just stick with the session
423 * we inherited across fork(2).
424 */
425 if( session == MACH_PORT_NULL ) {
426 s = ~AU_DEFAUDITSID;
427 break;
428 }
429
430 s = _audit_session_join(session);
431 if( s == 0 ) {
432 _vproc_log_error(LOG_ERR, "Could not join security session!");
433 s = AU_DEFAUDITSID;
434 } else {
435 _vproc_log(LOG_DEBUG, "Joined session %d.", s);
436 }
437 } while( 0 );
438
439 return s != AU_DEFAUDITSID ? NULL : _vproc_post_fork_ping;
440#else
441 mach_port_t session = MACH_PORT_NULL;
442 return vproc_mig_post_fork_ping(bootstrap_port, mach_task_self(), &session) ? _vproc_post_fork_ping : NULL;
443#endif
444}
445
446vproc_err_t
447_vprocmgr_init(const char *session_type)
448{
449 if (vproc_mig_init_session(bootstrap_port, (char *)session_type, _audit_session_self()) == 0) {
450 return NULL;
451 }
452
453 return (vproc_err_t)_vprocmgr_init;
454}
455
456vproc_err_t
457_vprocmgr_move_subset_to_user(uid_t target_user, const char *session_type, uint64_t flags)
458{
459 kern_return_t kr = 0;
460 bool is_bkgd = (strcmp(session_type, VPROCMGR_SESSION_BACKGROUND) == 0);
461 int64_t ldpid, lduid;
462
463 if (vproc_swap_integer(NULL, VPROC_GSK_MGR_PID, 0, &ldpid) != 0) {
464 return (vproc_err_t)_vprocmgr_move_subset_to_user;
465 }
466
467 if (vproc_swap_integer(NULL, VPROC_GSK_MGR_UID, 0, &lduid) != 0) {
468 return (vproc_err_t)_vprocmgr_move_subset_to_user;
469 }
470
471 if (!is_bkgd && ldpid != 1) {
472 if (lduid == getuid()) {
473 return NULL;
474 }
475 /*
476 * Not all sessions can be moved.
477 * We should clean up this mess someday.
478 */
479 return (vproc_err_t)_vprocmgr_move_subset_to_user;
480 }
481
482 mach_port_t puc = 0, rootbs = get_root_bootstrap_port();
483
484 if (vproc_mig_lookup_per_user_context(rootbs, target_user, &puc) != 0) {
485 return (vproc_err_t)_vprocmgr_move_subset_to_user;
486 }
487
488 if( is_bkgd ) {
489 task_set_bootstrap_port(mach_task_self(), puc);
490 mach_port_deallocate(mach_task_self(), bootstrap_port);
491 bootstrap_port = puc;
492 } else {
493 kr = vproc_mig_move_subset(puc, bootstrap_port, (char *)session_type, _audit_session_self(), flags);
494 mach_port_deallocate(mach_task_self(), puc);
495 }
496
497 cached_pid = -1;
498
499 if (kr) {
500 return (vproc_err_t)_vprocmgr_move_subset_to_user;
501 }
502
503 return _vproc_post_fork_ping();
504}
505
506vproc_err_t
507_vprocmgr_switch_to_session(const char *target_session, vproc_flags_t flags __attribute__((unused)))
508{
509 mach_port_t new_bsport = MACH_PORT_NULL;
510 kern_return_t kr = KERN_FAILURE;
511
512 mach_port_t tnp = MACH_PORT_NULL;
513 task_name_for_pid(mach_task_self(), getpid(), &tnp);
514 if( (kr = vproc_mig_switch_to_session(bootstrap_port, tnp, (char *)target_session, _audit_session_self(), &new_bsport)) != KERN_SUCCESS ) {
515 _vproc_log(LOG_NOTICE, "_vprocmgr_switch_to_session(): kr = 0x%x", kr);
516 return (vproc_err_t)_vprocmgr_switch_to_session;
517 }
518
519 task_set_bootstrap_port(mach_task_self(), new_bsport);
520 mach_port_deallocate(mach_task_self(), bootstrap_port);
521 bootstrap_port = new_bsport;
522
523 return !issetugid() ? _vproc_post_fork_ping() : NULL;
524}
525
526vproc_err_t
527_vprocmgr_detach_from_console(vproc_flags_t flags __attribute__((unused)))
528{
529 return _vprocmgr_switch_to_session(VPROCMGR_SESSION_BACKGROUND, 0);
530}
531
532pid_t
533_spawn_via_launchd(const char *label, const char *const *argv, const struct spawn_via_launchd_attr *spawn_attrs, int struct_version)
534{
535 size_t i, good_enough_size = 10*1024*1024;
536 mach_msg_type_number_t indata_cnt = 0;
537 vm_offset_t indata = 0;
538 mach_port_t obsvr_port = MACH_PORT_NULL;
539 launch_data_t tmp, tmp_array, in_obj;
540 const char *const *tmpp;
541 kern_return_t kr = 1;
542 void *buf = NULL;
543 pid_t p = -1;
544
545 if ((in_obj = launch_data_alloc(LAUNCH_DATA_DICTIONARY)) == NULL) {
546 goto out;
547 }
548
549 if ((tmp = launch_data_new_string(label)) == NULL) {
550 goto out;
551 }
552
553 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_LABEL);
554
555 if ((tmp_array = launch_data_alloc(LAUNCH_DATA_ARRAY)) == NULL) {
556 goto out;
557 }
558
559 for (i = 0; *argv; i++, argv++) {
560 tmp = launch_data_new_string(*argv);
561 if (tmp == NULL) {
562 goto out;
563 }
564
565 launch_data_array_set_index(tmp_array, tmp, i);
566 }
567
568 launch_data_dict_insert(in_obj, tmp_array, LAUNCH_JOBKEY_PROGRAMARGUMENTS);
569
570 if (spawn_attrs) switch (struct_version) {
571 case 2:
572#if HAVE_QUARANTINE
573 if (spawn_attrs->spawn_quarantine) {
574 char qbuf[QTN_SERIALIZED_DATA_MAX];
575 size_t qbuf_sz = QTN_SERIALIZED_DATA_MAX;
576
577 if (qtn_proc_to_data(spawn_attrs->spawn_quarantine, qbuf, &qbuf_sz) == 0) {
578 tmp = launch_data_new_opaque(qbuf, qbuf_sz);
579 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_QUARANTINEDATA);
580 }
581 }
582#endif
583
584 if (spawn_attrs->spawn_seatbelt_profile) {
585 tmp = launch_data_new_string(spawn_attrs->spawn_seatbelt_profile);
586 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_SANDBOXPROFILE);
587 }
588
589 if (spawn_attrs->spawn_seatbelt_flags) {
590 tmp = launch_data_new_integer(*spawn_attrs->spawn_seatbelt_flags);
591 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_SANDBOXFLAGS);
592 }
593
594 /* fall through */
595 case 1:
596 if (spawn_attrs->spawn_binpref) {
597 tmp_array = launch_data_alloc(LAUNCH_DATA_ARRAY);
598 for (i = 0; i < spawn_attrs->spawn_binpref_cnt; i++) {
599 tmp = launch_data_new_integer(spawn_attrs->spawn_binpref[i]);
600 launch_data_array_set_index(tmp_array, tmp, i);
601 }
602 launch_data_dict_insert(in_obj, tmp_array, LAUNCH_JOBKEY_BINARYORDERPREFERENCE);
603 }
604 /* fall through */
605 case 0:
606 if (spawn_attrs->spawn_flags & SPAWN_VIA_LAUNCHD_STOPPED) {
607 tmp = launch_data_new_bool(true);
608 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_WAITFORDEBUGGER);
609 }
610
611 if (spawn_attrs->spawn_env) {
612 launch_data_t tmp_dict = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
613
614 for (tmpp = spawn_attrs->spawn_env; *tmpp; tmpp++) {
615 char *eqoff, tmpstr[strlen(*tmpp) + 1];
616
617 strcpy(tmpstr, *tmpp);
618
619 eqoff = strchr(tmpstr, '=');
620
621 if (!eqoff) {
622 goto out;
623 }
624
625 *eqoff = '\0';
626
627 launch_data_dict_insert(tmp_dict, launch_data_new_string(eqoff + 1), tmpstr);
628 }
629
630 launch_data_dict_insert(in_obj, tmp_dict, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES);
631 }
632
633 if (spawn_attrs->spawn_path) {
634 tmp = launch_data_new_string(spawn_attrs->spawn_path);
635 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_PROGRAM);
636 }
637
638 if (spawn_attrs->spawn_chdir) {
639 tmp = launch_data_new_string(spawn_attrs->spawn_chdir);
640 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_WORKINGDIRECTORY);
641 }
642
643 if (spawn_attrs->spawn_umask) {
644 tmp = launch_data_new_integer(*spawn_attrs->spawn_umask);
645 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_UMASK);
646 }
647
648 break;
649 default:
650 break;
651 }
652
653 if (!(buf = malloc(good_enough_size))) {
654 goto out;
655 }
656
657 if ((indata_cnt = launch_data_pack(in_obj, buf, good_enough_size, NULL, NULL)) == 0) {
658 goto out;
659 }
660
661 indata = (vm_offset_t)buf;
662
663 kr = vproc_mig_spawn(bootstrap_port, indata, indata_cnt, _audit_session_self(), &p, &obsvr_port);
664
665 if (kr == VPROC_ERR_TRY_PER_USER) {
666 mach_port_t puc;
667
668 if (vproc_mig_lookup_per_user_context(bootstrap_port, 0, &puc) == 0) {
669 kr = vproc_mig_spawn(puc, indata, indata_cnt, _audit_session_self(), &p, &obsvr_port);
670 mach_port_deallocate(mach_task_self(), puc);
671 }
672 }
673
674out:
675 if (in_obj) {
676 launch_data_free(in_obj);
677 }
678
679 if (buf) {
680 free(buf);
681 }
682
683 switch (kr) {
684 case BOOTSTRAP_SUCCESS:
685 if (spawn_attrs && spawn_attrs->spawn_observer_port) {
686 *spawn_attrs->spawn_observer_port = obsvr_port;
687 } else {
688 mach_port_deallocate(mach_task_self(), obsvr_port);
689 }
690 return p;
691 case BOOTSTRAP_NOT_PRIVILEGED:
692 errno = EPERM; break;
693 case BOOTSTRAP_NO_MEMORY:
694 errno = ENOMEM; break;
695 case BOOTSTRAP_NAME_IN_USE:
696 errno = EEXIST; break;
697 case 1:
698 errno = EIO; break;
699 default:
700 errno = EINVAL; break;
701 }
702
703 return -1;
704}
705
706kern_return_t
707mpm_wait(mach_port_t ajob, int *wstatus)
708{
709 return vproc_mig_wait(ajob, wstatus);
710}
711
712kern_return_t
713mpm_uncork_fork(mach_port_t ajob)
714{
715 return vproc_mig_uncork_fork(ajob);
716}
717
718kern_return_t
719_vprocmgr_getsocket(name_t sockpath)
720{
721 return vproc_mig_getsocket(bootstrap_port, sockpath);
722}
723
724vproc_err_t
725_vproc_get_last_exit_status(int *wstatus)
726{
727 int64_t val;
728
729 if (vproc_swap_integer(NULL, VPROC_GSK_LAST_EXIT_STATUS, 0, &val) == 0) {
730 *wstatus = (int)val;
731 return NULL;
732 }
733
734 return (vproc_err_t)_vproc_get_last_exit_status;
735}
736
737vproc_err_t
738_vproc_send_signal_by_label(const char *label, int sig)
739{
740 if (vproc_mig_send_signal(bootstrap_port, (char *)label, sig) == 0) {
741 return NULL;
742 }
743
744 return _vproc_send_signal_by_label;
745}
746
747vproc_err_t
748_vprocmgr_log_forward(mach_port_t mp, void *data, size_t len)
749{
750 if (vproc_mig_log_forward(mp, (vm_offset_t)data, len) == 0) {
751 return NULL;
752 }
753
754 return _vprocmgr_log_forward;
755}
756
757vproc_err_t
758_vprocmgr_log_drain(vproc_t vp __attribute__((unused)), pthread_mutex_t *mutex, _vprocmgr_log_drain_callback_t func)
759{
760 mach_msg_type_number_t outdata_cnt, tmp_cnt;
761 vm_offset_t outdata = 0;
762 struct timeval tv;
763 struct logmsg_s *lm;
764
765 if (!func) {
766 return _vprocmgr_log_drain;
767 }
768
769 if (vproc_mig_log_drain(bootstrap_port, &outdata, &outdata_cnt) != 0) {
770 return _vprocmgr_log_drain;
771 }
772
773 tmp_cnt = outdata_cnt;
774
775 if (mutex) {
776 pthread_mutex_lock(mutex);
777 }
778
779 for (lm = (struct logmsg_s *)outdata; tmp_cnt > 0; lm = ((void *)lm + lm->obj_sz)) {
780 lm->from_name = (char *)lm + lm->from_name_offset;
781 lm->about_name = (char *)lm + lm->about_name_offset;
782 lm->msg = (char *)lm + lm->msg_offset;
783 lm->session_name = (char *)lm + lm->session_name_offset;
784
785 tv.tv_sec = lm->when / USEC_PER_SEC;
786 tv.tv_usec = lm->when % USEC_PER_SEC;
787
788 func(&tv, lm->from_pid, lm->about_pid, lm->sender_uid, lm->sender_gid, lm->pri,
789 lm->from_name, lm->about_name, lm->session_name, lm->msg);
790
791 tmp_cnt -= lm->obj_sz;
792 }
793
794 if (mutex) {
795 pthread_mutex_unlock(mutex);
796 }
797
798 if (outdata) {
799 mig_deallocate(outdata, outdata_cnt);
800 }
801
802 return NULL;
803}
804
805vproc_err_t
806vproc_swap_integer(vproc_t vp, vproc_gsk_t key, int64_t *inval, int64_t *outval)
807{
808 static int64_t cached_is_managed = -1;
809 int64_t dummyval = 0;
810
811 switch (key) {
812 case VPROC_GSK_MGR_PID:
813 if (cached_pid != -1 && outval) {
814 *outval = cached_pid;
815 return NULL;
816 }
817 break;
818 case VPROC_GSK_IS_MANAGED:
819 if (cached_is_managed != -1 && outval) {
820 *outval = cached_is_managed;
821 return NULL;
822 }
823 break;
824 case VPROC_GSK_TRANSACTIONS_ENABLED:
825 /* Shared memory region is required for transactions. */
826 if( unlikely(vproc_shmem == NULL) ) {
827 int po_r = pthread_once(&shmem_inited, vproc_client_init);
828 if( po_r != 0 || vproc_shmem == NULL ) {
829 if( outval ) {
830 *outval = -1;
831 }
832 return (vproc_err_t)vproc_swap_integer;
833 }
834 }
835
836 if( s_cached_transactions_enabled && outval ) {
837 *outval = s_cached_transactions_enabled;
838 return NULL;
839 }
840 break;
841 default:
842 break;
843 }
844
845 kern_return_t kr = KERN_FAILURE;
846 mach_port_t mp = vp ? vp->j_port : bootstrap_port;
847 if ((kr = vproc_mig_swap_integer(mp, inval ? key : 0, outval ? key : 0, inval ? *inval : 0, outval ? outval : &dummyval)) == 0) {
848 switch (key) {
849 case VPROC_GSK_MGR_PID:
850 cached_pid = outval ? *outval : dummyval;
851 break;
852 case VPROC_GSK_IS_MANAGED:
853 cached_is_managed = outval ? *outval : dummyval;
854 break;
855 case VPROC_GSK_TRANSACTIONS_ENABLED:
856 /* Once you're in the transaction model, you're in for good. Like the Mafia. */
857 s_cached_transactions_enabled = 1;
858 break;
859 case VPROC_GSK_PERUSER_SUSPEND: {
860 char peruser_label[NAME_MAX];
861 snprintf(peruser_label, NAME_MAX - 1, "com.apple.launchd.peruser.%u", (uid_t)*inval);
862
863 vproc_t pu_vp = vprocmgr_lookup_vproc(peruser_label);
864 if( pu_vp ) {
865 int status = 0;
866 kr = vproc_mig_wait2(bootstrap_port, pu_vp->j_port, &status, false);
867 vproc_release(pu_vp);
868 }
869 break;
870 }
871 default:
872 break;
873 }
874 return NULL;
875 }
876
877 return (vproc_err_t)vproc_swap_integer;
878}
879
880mach_port_t
881get_root_bootstrap_port(void)
882{
883 mach_port_t parent_port = 0;
884 mach_port_t previous_port = 0;
885
886 do {
887 if (previous_port) {
888 if (previous_port != bootstrap_port) {
889 mach_port_deallocate(mach_task_self(), previous_port);
890 }
891 previous_port = parent_port;
892 } else {
893 previous_port = bootstrap_port;
894 }
895
896 if (bootstrap_parent(previous_port, &parent_port) != 0) {
897 return MACH_PORT_NULL;
898 }
899
900 } while (parent_port != previous_port);
901
902 return parent_port;
903}
904
905vproc_err_t
906vproc_swap_complex(vproc_t vp, vproc_gsk_t key, launch_data_t inval, launch_data_t *outval)
907{
908 size_t data_offset = 0, good_enough_size = 10*1024*1024;
909 mach_msg_type_number_t indata_cnt = 0, outdata_cnt;
910 vm_offset_t indata = 0, outdata = 0;
911 launch_data_t out_obj;
912 void *rval = vproc_swap_complex;
913 void *buf = NULL;
914
915 if (inval) {
916 if (!(buf = malloc(good_enough_size))) {
917 goto out;
918 }
919
920 if ((indata_cnt = launch_data_pack(inval, buf, good_enough_size, NULL, NULL)) == 0) {
921 goto out;
922 }
923
924 indata = (vm_offset_t)buf;
925 }
926
927 mach_port_t mp = vp ? vp->j_port : bootstrap_port;
928 if (vproc_mig_swap_complex(mp, inval ? key : 0, outval ? key : 0, indata, indata_cnt, &outdata, &outdata_cnt) != 0) {
929 goto out;
930 }
931
932 if (outval) {
933 if (!(out_obj = launch_data_unpack((void *)outdata, outdata_cnt, NULL, 0, &data_offset, NULL))) {
934 goto out;
935 }
936
937 if (!(*outval = launch_data_copy(out_obj))) {
938 goto out;
939 }
940 }
941
942 rval = NULL;
943out:
944 if (buf) {
945 free(buf);
946 }
947
948 if (outdata) {
949 mig_deallocate(outdata, outdata_cnt);
950 }
951
952 return rval;
953}
954
955vproc_err_t
956vproc_swap_string(vproc_t vp, vproc_gsk_t key, const char *instr, char **outstr)
957{
958 launch_data_t instr_data = instr ? launch_data_new_string(instr) : NULL;
959 launch_data_t outstr_data = NULL;
960
961 vproc_err_t verr = vproc_swap_complex(vp, key, instr_data, &outstr_data);
962 if( !verr && outstr ) {
963 if( launch_data_get_type(outstr_data) == LAUNCH_DATA_STRING ) {
964 *outstr = strdup(launch_data_get_string(outstr_data));
965 } else {
966 verr = (vproc_err_t)vproc_swap_string;
967 }
968 launch_data_free(outstr_data);
969 }
970 if( instr_data ) {
971 launch_data_free(instr_data);
972 }
973
974 return verr;
975}
976
977void *
978reboot2(uint64_t flags)
979{
980 if (vproc_mig_reboot2(get_root_bootstrap_port(), flags) == 0) {
981 return NULL;
982 }
983
984 return reboot2;
985}
986
987vproc_err_t
988_vproc_kickstart_by_label(const char *label, pid_t *out_pid, mach_port_t *out_port_name, mach_port_t *out_obsrvr_port, vproc_flags_t flags)
989{
990 mach_port_t junk = MACH_PORT_NULL;
991 mach_port_t junk2 = MACH_PORT_NULL;
992
993 kern_return_t kr = vproc_mig_kickstart(bootstrap_port, (char *)label, out_pid, out_port_name ?: &junk, out_obsrvr_port ?: &junk2, flags);
994 if( kr == KERN_SUCCESS ) {
995 if( !out_port_name ) {
996 mach_port_mod_refs(mach_task_self(), junk, MACH_PORT_RIGHT_SEND, -1);
997 }
998
999 if( !out_obsrvr_port ) {
1000 mach_port_mod_refs(mach_task_self(), junk2, MACH_PORT_RIGHT_SEND, -1);
1001 }
1002
1003 return NULL;
1004 }
1005
1006 return (vproc_err_t)_vproc_kickstart_by_label;
1007}
1008
1009vproc_err_t
1010_vproc_wait_by_label(const char *label, int *out_wstatus)
1011{
1012 if (vproc_mig_embedded_wait(bootstrap_port, (char *)label, out_wstatus) == 0) {
1013 return NULL;
1014 }
1015
1016 return (vproc_err_t)_vproc_wait_by_label;
1017}
1018
1019vproc_err_t
1020_vproc_set_global_on_demand(bool state)
1021{
1022 int64_t val = state ? ~0 : 0;
1023
1024 if (vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_ON_DEMAND, &val, NULL) == 0) {
1025 return NULL;
1026 }
1027
1028 return (vproc_err_t)_vproc_set_global_on_demand;
1029}
1030
1031void
1032_vproc_logv(int pri, int err, const char *msg, va_list ap)
1033{
1034 char flat_msg[3000];
1035
1036 vsnprintf(flat_msg, sizeof(flat_msg), msg, ap);
1037
1038 vproc_mig_log(bootstrap_port, pri, err, flat_msg);
1039}
1040
1041void
1042_vproc_log(int pri, const char *msg, ...)
1043{
1044 va_list ap;
1045
1046 va_start(ap, msg);
1047 _vproc_logv(pri, 0, msg, ap);
1048 va_end(ap);
1049}
1050
1051void
1052_vproc_log_error(int pri, const char *msg, ...)
1053{
1054 int saved_errno = errno;
1055 va_list ap;
1056
1057 va_start(ap, msg);
1058 _vproc_logv(pri, saved_errno, msg, ap);
1059 va_end(ap);
1060}