2 * Copyright (c) 1999-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_APACHE_LICENSE_HEADER_START@
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * @APPLE_APACHE_LICENSE_HEADER_END@
23 #include "vproc_priv.h"
24 #include "vproc_internal.h"
26 #include <mach/mach.h>
27 #include <mach/vm_map.h>
28 #include <sys/param.h>
37 #include <libkern/OSAtomic.h>
38 #include <sys/syscall.h>
39 #include <sys/event.h>
42 #include <quarantine.h>
46 #include "launch_priv.h"
47 #include "launch_internal.h"
48 #include "launchd_ktrace.h"
50 #include "protocol_vproc.h"
52 #include "launchd_helper.h"
53 #include "launchd_helperServer.h"
57 #define likely(x) __builtin_expect((bool)(x), true)
58 #define unlikely(x) __builtin_expect((bool)(x), false)
60 static mach_port_t
get_root_bootstrap_port(void);
62 static int64_t cached_pid
= -1;
63 static struct vproc_shmem_s
*vproc_shmem
;
64 static pthread_once_t shmem_inited
= PTHREAD_ONCE_INIT
;
65 static uint64_t s_cached_transactions_enabled
= 0;
67 static _vproc_transaction_callout vproc_gone2zero
;
68 static _vproc_transaction_callout vproc_gonenonzero
;
70 static vproc_helper_recv_ping_t vprocmgr_helper_callout
;
77 /* These functions are a total nightmare to get to through headers.
78 * See rdar://problem/8223092.
80 typedef __darwin_mach_port_t fileport_t
;
81 #define FILEPORT_NULL ((fileport_t)0)
82 extern int fileport_makeport(int, fileport_t
*);
83 extern int fileport_makefd(fileport_t
);
85 vproc_t
vprocmgr_lookup_vproc(const char *label
)
87 struct vproc_s
*vp
= NULL
;
89 mach_port_t mp
= MACH_PORT_NULL
;
90 kern_return_t kr
= vproc_mig_port_for_label(bootstrap_port
, (char *)label
, &mp
);
91 if (kr
== BOOTSTRAP_SUCCESS
) {
92 vp
= (struct vproc_s
*)calloc(1, sizeof(struct vproc_s
));
95 mach_port_mod_refs(mach_task_self(), mp
, MACH_PORT_RIGHT_SEND
, 1);
98 (void)mach_port_deallocate(mach_task_self(), mp
);
104 vproc_t
vproc_retain(vproc_t vp
)
106 int32_t orig
= OSAtomicAdd32(1, &vp
->refcount
) - 1;
108 /* We've gone from 0 to 1, which means that this object was due to be freed. */
109 _vproc_set_crash_log_message("Under-retain / over-release of vproc_t.");
116 void vproc_release(vproc_t vp
)
118 int32_t newval
= OSAtomicAdd32(-1, &vp
->refcount
);
120 /* We're in negative numbers, which is bad. */
121 _vproc_set_crash_log_message("Over-release of vproc_t.");
123 } else if (newval
== 0) {
124 mach_port_deallocate(mach_task_self(), vp
->j_port
);
130 vproc_shmem_init(void)
132 vm_address_t vm_addr
= 0;
133 mach_port_t shmem_port
;
136 kr
= vproc_mig_setup_shmem(bootstrap_port
, &shmem_port
);
140 /* rdar://problem/6416724
141 * If we fail to set up a shared memory page, just allocate a local chunk
142 * of memory. This way, processes can still introspect their own transaction
143 * counts if they're being run under a debugger. Moral of the story: Debug
144 * from the environment you intend to run in.
146 void *_vm_addr
= calloc(1, sizeof(struct vproc_shmem_s
));
151 vm_addr
= (vm_address_t
)_vm_addr
;
153 kr
= vm_map(mach_task_self(), &vm_addr
, getpagesize(), 0, true, shmem_port
, 0, false,
154 VM_PROT_READ
|VM_PROT_WRITE
, VM_PROT_READ
|VM_PROT_WRITE
, VM_INHERIT_NONE
);
159 kr
= mach_port_deallocate(mach_task_self(), shmem_port
);
164 vproc_shmem
= (struct vproc_shmem_s
*)vm_addr
;
168 vproc_client_init(void)
170 char *val
= getenv(LAUNCHD_DO_APPLE_INTERNAL_LOGGING
);
172 if (strncmp(val
, "true", sizeof("true") - 1) == 0) {
173 do_apple_internal_logging
= true;
181 vproc_transaction_begin(vproc_t vp
__attribute__((unused
)))
183 vproc_transaction_t vpt
= (vproc_transaction_t
)vproc_shmem_init
; /* we need a "random" variable that is testable */
184 _vproc_transaction_begin();
190 _vproc_transaction_begin(void)
192 if (unlikely(vproc_shmem
== NULL
)) {
193 int po_r
= pthread_once(&shmem_inited
, vproc_client_init
);
194 if (po_r
!= 0 || vproc_shmem
== NULL
) {
199 /* We need to deal with the potential race condition of trying to open a
200 * transaction after launchd has marked the process for death. Consider if
201 * one thread closes the last transaction after marking the process for
202 * death. Then we call _exit(2). But exiting isn't instantaneous. So if some
203 * other threads come in very soon after and try to open transactions, we
204 * have to cut them off so that they can't begin their work. Because if they
205 * can manage to get to the point of, say, parking themselves in an
206 * uninterruptible wait, then the process won't exit.
208 * We loop here so that, if someone's calling vproc_transaction_end() at the
209 * same time, we can pick up the descent to -1 if launchd has marked us for
212 typeof(vproc_shmem
->vp_shmem_transaction_cnt
) old
= 0;
214 old
= vproc_shmem
->vp_shmem_transaction_cnt
;
216 if (unlikely(old
< 0)) {
217 /* No transactions should be opened after this point, so make sure
218 * this thread can't proceed. We don't crash here because it could
219 * be a legitimate race, as described above.
221 if (vproc_shmem
->vp_shmem_flags
& VPROC_SHMEM_EXITING
) {
224 _vproc_set_crash_log_message("Unbalanced: vproc_transaction_begin()");
227 } else if (old
== 0 && vproc_gonenonzero
) {
230 } while (!__sync_bool_compare_and_swap(&vproc_shmem
->vp_shmem_transaction_cnt
, old
, old
+ 1));
232 runtime_ktrace(RTKT_VPROC_TRANSACTION_INCREMENT
, old
+ 1, 0, 0);
236 _vproc_transaction_count(void)
238 return likely(vproc_shmem
) ? vproc_shmem
->vp_shmem_transaction_cnt
: INT32_MAX
;
242 _vproc_standby_count(void)
244 #ifdef VPROC_STANDBY_IMPLEMENTED
245 return likely(vproc_shmem
) ? vproc_shmem
->vp_shmem_standby_cnt
: INT32_MAX
;
252 _vproc_standby_timeout(void)
254 return likely(vproc_shmem
) ? vproc_shmem
->vp_shmem_standby_timeout
: 0;
258 _vproc_pid_is_managed(pid_t p
)
260 boolean_t result
= false;
261 vproc_mig_pid_is_managed(bootstrap_port
, p
, &result
);
267 _vproc_transaction_count_for_pid(pid_t p
, int32_t *count
, bool *condemned
)
269 boolean_t _condemned
= false;
270 kern_return_t kr
= vproc_mig_transaction_count_for_pid(bootstrap_port
, p
, count
, &_condemned
);
271 if (kr
== KERN_SUCCESS
&& condemned
) {
272 *condemned
= _condemned
? true : false;
279 _vproc_transaction_try_exit(int status
)
281 #if !TARGET_OS_EMBEDDED
282 if (unlikely(vproc_shmem
== NULL
)) {
286 if (__sync_bool_compare_and_swap(&vproc_shmem
->vp_shmem_transaction_cnt
, 0, -1)) {
287 vproc_shmem
->vp_shmem_flags
|= VPROC_SHMEM_EXITING
;
296 vproc_transaction_end(vproc_t vp
__attribute__((unused
)), vproc_transaction_t vpt
)
298 if (unlikely(vpt
!= (vproc_transaction_t
)vproc_shmem_init
)) {
299 _vproc_set_crash_log_message("Bogus transaction handle passed to vproc_transaction_end() ");
303 _vproc_transaction_end();
307 _vproc_transaction_end(void)
309 typeof(vproc_shmem
->vp_shmem_transaction_cnt
) newval
;
311 if (unlikely(vproc_shmem
== NULL
)) {
315 newval
= __sync_sub_and_fetch(&vproc_shmem
->vp_shmem_transaction_cnt
, 1);
317 runtime_ktrace(RTKT_VPROC_TRANSACTION_DECREMENT
, newval
, 0, 0);
318 if (unlikely(newval
< 0)) {
319 if (vproc_shmem
->vp_shmem_flags
& VPROC_SHMEM_EXITING
) {
322 _vproc_set_crash_log_message("Unbalanced: vproc_transaction_end()");
325 } else if (newval
== 0 && vproc_gone2zero
) {
331 vproc_standby_begin(vproc_t vp
__attribute__((unused
)))
333 #ifdef VPROC_STANDBY_IMPLEMENTED
334 vproc_standby_t vpsb
= (vproc_standby_t
)vproc_shmem_init
; /* we need a "random" variable that is testable */
336 _vproc_standby_begin();
345 _vproc_standby_begin(void)
347 #ifdef VPROC_STANDBY_IMPLEMENTED
348 typeof(vproc_shmem
->vp_shmem_standby_cnt
) newval
;
350 if (unlikely(vproc_shmem
== NULL
)) {
351 int po_r
= pthread_once(&shmem_inited
, vproc_client_init
);
352 if (po_r
!= 0 || vproc_shmem
== NULL
) {
357 newval
= __sync_add_and_fetch(&vproc_shmem
->vp_shmem_standby_cnt
, 1);
359 if (unlikely(newval
< 1)) {
360 _vproc_set_crash_log_message("Unbalanced: vproc_standby_begin()");
369 vproc_standby_end(vproc_t vp
__attribute__((unused
)), vproc_standby_t vpt
__attribute__((unused
)))
371 #ifdef VPROC_STANDBY_IMPLEMENTED
372 if (unlikely(vpt
!= (vproc_standby_t
)vproc_shmem_init
)) {
373 _vproc_set_crash_log_message("Bogus standby handle passed to vproc_standby_end() ");
377 _vproc_standby_end();
384 _vproc_standby_end(void)
386 #ifdef VPROC_STANDBY_IMPLEMENTED
387 typeof(vproc_shmem
->vp_shmem_standby_cnt
) newval
;
389 if (unlikely(vproc_shmem
== NULL
)) {
390 _vproc_set_crash_log_message("Process called vproc_standby_end() when not enrolled in transaction model.");
394 newval
= __sync_sub_and_fetch(&vproc_shmem
->vp_shmem_standby_cnt
, 1);
396 if (unlikely(newval
< 0)) {
397 _vproc_set_crash_log_message("Unbalanced: vproc_standby_end()");
406 _vproc_transaction_ptr(void)
412 _vproc_transaction_set_callouts(_vproc_transaction_callout gone2zero
, _vproc_transaction_callout gonenonzero
)
414 if (unlikely(vproc_shmem
== NULL
)) {
415 int po_r
= pthread_once(&shmem_inited
, vproc_client_init
);
416 if (po_r
!= 0 || vproc_shmem
== NULL
) {
421 static bool once
= false;
423 _vproc_set_crash_log_message("This SPI may only be called once. It is only meant for libxpc.");
429 vproc_gone2zero
= gone2zero
;
430 vproc_gonenonzero
= gonenonzero
;
434 _vproc_grab_subset(mach_port_t bp
, mach_port_t
*reqport
, mach_port_t
*rcvright
, launch_data_t
*outval
,
435 mach_port_array_t
*ports
, mach_msg_type_number_t
*portCnt
)
437 mach_msg_type_number_t outdata_cnt
;
438 vm_offset_t outdata
= 0;
439 size_t data_offset
= 0;
440 launch_data_t out_obj
;
443 if ((kr
= vproc_mig_take_subset(bp
, reqport
, rcvright
, &outdata
, &outdata_cnt
, ports
, portCnt
))) {
447 if ((out_obj
= launch_data_unpack((void *)outdata
, outdata_cnt
, NULL
, 0, &data_offset
, NULL
))) {
448 *outval
= launch_data_copy(out_obj
);
455 mig_deallocate(outdata
, outdata_cnt
);
462 _vproc_post_fork_ping(void)
464 #if !TARGET_OS_EMBEDDED
465 au_asid_t s
= AU_DEFAUDITSID
;
467 mach_port_t session
= MACH_PORT_NULL
;
468 kern_return_t kr
= vproc_mig_post_fork_ping(bootstrap_port
, mach_task_self(), &session
);
469 if (kr
!= KERN_SUCCESS
) {
470 /* If this happens, our bootstrap port probably got hosed. */
471 _vproc_log(LOG_ERR
, "Post-fork ping failed!");
475 /* If we get back MACH_PORT_NULL, that means we just stick with the session
476 * we inherited across fork(2).
478 if (session
== MACH_PORT_NULL
) {
483 s
= _audit_session_join(session
);
485 _vproc_log_error(LOG_ERR
, "Could not join security session!");
488 _vproc_log(LOG_DEBUG
, "Joined session %d.", s
);
492 return s
!= AU_DEFAUDITSID
? NULL
: _vproc_post_fork_ping
;
494 mach_port_t session
= MACH_PORT_NULL
;
495 return vproc_mig_post_fork_ping(bootstrap_port
, mach_task_self(), &session
) ? _vproc_post_fork_ping
: NULL
;
500 _vprocmgr_init(const char *session_type
)
502 if (vproc_mig_init_session(bootstrap_port
, (char *)session_type
, _audit_session_self()) == 0) {
506 return (vproc_err_t
)_vprocmgr_init
;
510 _vprocmgr_move_subset_to_user(uid_t target_user
, const char *session_type
, uint64_t flags
)
512 kern_return_t kr
= 0;
513 bool is_bkgd
= (strcmp(session_type
, VPROCMGR_SESSION_BACKGROUND
) == 0);
514 int64_t ldpid
, lduid
;
516 if (vproc_swap_integer(NULL
, VPROC_GSK_MGR_PID
, 0, &ldpid
) != 0) {
517 return (vproc_err_t
)_vprocmgr_move_subset_to_user
;
520 if (vproc_swap_integer(NULL
, VPROC_GSK_MGR_UID
, 0, &lduid
) != 0) {
521 return (vproc_err_t
)_vprocmgr_move_subset_to_user
;
524 if (!is_bkgd
&& ldpid
!= 1) {
525 if (lduid
== getuid()) {
529 * Not all sessions can be moved.
530 * We should clean up this mess someday.
532 return (vproc_err_t
)_vprocmgr_move_subset_to_user
;
535 mach_port_t puc
= 0, rootbs
= get_root_bootstrap_port();
537 if (vproc_mig_lookup_per_user_context(rootbs
, target_user
, &puc
) != 0) {
538 return (vproc_err_t
)_vprocmgr_move_subset_to_user
;
542 task_set_bootstrap_port(mach_task_self(), puc
);
543 mach_port_deallocate(mach_task_self(), bootstrap_port
);
544 bootstrap_port
= puc
;
546 kr
= vproc_mig_move_subset(puc
, bootstrap_port
, (char *)session_type
, _audit_session_self(), flags
);
547 mach_port_deallocate(mach_task_self(), puc
);
553 return (vproc_err_t
)_vprocmgr_move_subset_to_user
;
556 return _vproc_post_fork_ping();
560 _vprocmgr_switch_to_session(const char *target_session
, vproc_flags_t flags
__attribute__((unused
)))
562 mach_port_t new_bsport
= MACH_PORT_NULL
;
563 kern_return_t kr
= KERN_FAILURE
;
565 mach_port_t tnp
= MACH_PORT_NULL
;
566 task_name_for_pid(mach_task_self(), getpid(), &tnp
);
567 if ((kr
= vproc_mig_switch_to_session(bootstrap_port
, tnp
, (char *)target_session
, _audit_session_self(), &new_bsport
)) != KERN_SUCCESS
) {
568 _vproc_log(LOG_NOTICE
, "_vprocmgr_switch_to_session(): kr = 0x%x", kr
);
569 return (vproc_err_t
)_vprocmgr_switch_to_session
;
572 task_set_bootstrap_port(mach_task_self(), new_bsport
);
573 mach_port_deallocate(mach_task_self(), bootstrap_port
);
574 bootstrap_port
= new_bsport
;
576 return !issetugid() ? _vproc_post_fork_ping() : NULL
;
580 _vprocmgr_detach_from_console(vproc_flags_t flags
__attribute__((unused
)))
582 return _vprocmgr_switch_to_session(VPROCMGR_SESSION_BACKGROUND
, 0);
586 _spawn_via_launchd(const char *label
, const char *const *argv
, const struct spawn_via_launchd_attr
*spawn_attrs
, int struct_version
)
588 size_t i
, good_enough_size
= 10*1024*1024;
589 mach_msg_type_number_t indata_cnt
= 0;
590 vm_offset_t indata
= 0;
591 mach_port_t obsvr_port
= MACH_PORT_NULL
;
592 launch_data_t tmp
, tmp_array
, in_obj
;
593 const char *const *tmpp
;
594 kern_return_t kr
= 1;
598 if ((in_obj
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
)) == NULL
) {
602 if ((tmp
= launch_data_new_string(label
)) == NULL
) {
606 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_LABEL
);
608 if ((tmp_array
= launch_data_alloc(LAUNCH_DATA_ARRAY
)) == NULL
) {
612 for (i
= 0; *argv
; i
++, argv
++) {
613 tmp
= launch_data_new_string(*argv
);
618 launch_data_array_set_index(tmp_array
, tmp
, i
);
621 launch_data_dict_insert(in_obj
, tmp_array
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
623 if (spawn_attrs
) switch (struct_version
) {
627 if (spawn_attrs
->spawn_quarantine
) {
628 char qbuf
[QTN_SERIALIZED_DATA_MAX
];
629 size_t qbuf_sz
= QTN_SERIALIZED_DATA_MAX
;
631 if (qtn_proc_to_data(spawn_attrs
->spawn_quarantine
, qbuf
, &qbuf_sz
) == 0) {
632 tmp
= launch_data_new_opaque(qbuf
, qbuf_sz
);
633 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_QUARANTINEDATA
);
638 if (spawn_attrs
->spawn_seatbelt_profile
) {
639 tmp
= launch_data_new_string(spawn_attrs
->spawn_seatbelt_profile
);
640 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_SANDBOXPROFILE
);
643 if (spawn_attrs
->spawn_seatbelt_flags
) {
644 tmp
= launch_data_new_integer(*spawn_attrs
->spawn_seatbelt_flags
);
645 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_SANDBOXFLAGS
);
650 if (spawn_attrs
->spawn_binpref
) {
651 tmp_array
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
652 for (i
= 0; i
< spawn_attrs
->spawn_binpref_cnt
; i
++) {
653 tmp
= launch_data_new_integer(spawn_attrs
->spawn_binpref
[i
]);
654 launch_data_array_set_index(tmp_array
, tmp
, i
);
656 launch_data_dict_insert(in_obj
, tmp_array
, LAUNCH_JOBKEY_BINARYORDERPREFERENCE
);
660 if (spawn_attrs
->spawn_flags
& SPAWN_VIA_LAUNCHD_STOPPED
) {
661 tmp
= launch_data_new_bool(true);
662 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_WAITFORDEBUGGER
);
664 if (spawn_attrs
->spawn_flags
& SPAWN_VIA_LAUNCHD_TALAPP
) {
665 tmp
= launch_data_new_string(LAUNCH_KEY_POSIXSPAWNTYPE_TALAPP
);
666 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_POSIXSPAWNTYPE
);
668 if (spawn_attrs
->spawn_flags
& SPAWN_VIA_LAUNCHD_WIDGET
) {
669 tmp
= launch_data_new_string(LAUNCH_KEY_POSIXSPAWNTYPE_WIDGET
);
670 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_POSIXSPAWNTYPE
);
672 if (spawn_attrs
->spawn_flags
& SPAWN_VIA_LAUNCHD_DISABLE_ASLR
) {
673 tmp
= launch_data_new_bool(true);
674 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_DISABLEASLR
);
677 if (spawn_attrs
->spawn_env
) {
678 launch_data_t tmp_dict
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
680 for (tmpp
= spawn_attrs
->spawn_env
; *tmpp
; tmpp
++) {
681 char *eqoff
, tmpstr
[strlen(*tmpp
) + 1];
683 strcpy(tmpstr
, *tmpp
);
685 eqoff
= strchr(tmpstr
, '=');
693 launch_data_dict_insert(tmp_dict
, launch_data_new_string(eqoff
+ 1), tmpstr
);
696 launch_data_dict_insert(in_obj
, tmp_dict
, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES
);
699 if (spawn_attrs
->spawn_path
) {
700 tmp
= launch_data_new_string(spawn_attrs
->spawn_path
);
701 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_PROGRAM
);
704 if (spawn_attrs
->spawn_chdir
) {
705 tmp
= launch_data_new_string(spawn_attrs
->spawn_chdir
);
706 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_WORKINGDIRECTORY
);
709 if (spawn_attrs
->spawn_umask
) {
710 tmp
= launch_data_new_integer(*spawn_attrs
->spawn_umask
);
711 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_UMASK
);
719 if (!(buf
= malloc(good_enough_size
))) {
723 if ((indata_cnt
= launch_data_pack(in_obj
, buf
, good_enough_size
, NULL
, NULL
)) == 0) {
727 indata
= (vm_offset_t
)buf
;
729 if (struct_version
== 3) {
730 kr
= vproc_mig_spawn2(bootstrap_port
, indata
, indata_cnt
, _audit_session_self(), &p
, &obsvr_port
);
732 _vproc_set_crash_log_message("Bogus version passed to _spawn_via_launchd(). For this release, the only valid version is 3.");
735 if (kr
== VPROC_ERR_TRY_PER_USER
) {
738 if (vproc_mig_lookup_per_user_context(bootstrap_port
, 0, &puc
) == 0) {
739 if (struct_version
== 3) {
740 kr
= vproc_mig_spawn2(puc
, indata
, indata_cnt
, _audit_session_self(), &p
, &obsvr_port
);
742 mach_port_deallocate(mach_task_self(), puc
);
748 launch_data_free(in_obj
);
756 case BOOTSTRAP_SUCCESS
:
757 if (spawn_attrs
&& spawn_attrs
->spawn_observer_port
) {
758 *spawn_attrs
->spawn_observer_port
= obsvr_port
;
760 if (struct_version
== 3) {
761 mach_port_mod_refs(mach_task_self(), obsvr_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
763 mach_port_deallocate(mach_task_self(), obsvr_port
);
767 case BOOTSTRAP_NOT_PRIVILEGED
:
768 errno
= EPERM
; break;
769 case BOOTSTRAP_NO_MEMORY
:
770 errno
= ENOMEM
; break;
771 case BOOTSTRAP_NAME_IN_USE
:
772 errno
= EEXIST
; break;
776 errno
= EINVAL
; break;
783 mpm_wait(mach_port_t ajob
__attribute__((unused
)), int *wstatus
)
790 mpm_uncork_fork(mach_port_t ajob
__attribute__((unused
)))
796 _vprocmgr_getsocket(name_t sockpath
)
798 return vproc_mig_getsocket(bootstrap_port
, sockpath
);
802 _vproc_get_last_exit_status(int *wstatus
)
806 if (vproc_swap_integer(NULL
, VPROC_GSK_LAST_EXIT_STATUS
, 0, &val
) == 0) {
811 return (vproc_err_t
)_vproc_get_last_exit_status
;
815 _vproc_send_signal_by_label(const char *label
, int sig
)
817 if (vproc_mig_send_signal(bootstrap_port
, (char *)label
, sig
) == 0) {
821 return _vproc_send_signal_by_label
;
825 _vprocmgr_log_forward(mach_port_t mp
, void *data
, size_t len
)
827 if (vproc_mig_log_forward(mp
, (vm_offset_t
)data
, len
) == 0) {
831 return _vprocmgr_log_forward
;
835 _vprocmgr_log_drain(vproc_t vp
__attribute__((unused
)), pthread_mutex_t
*mutex
, _vprocmgr_log_drain_callback_t func
)
837 mach_msg_type_number_t outdata_cnt
, tmp_cnt
;
838 vm_offset_t outdata
= 0;
843 return _vprocmgr_log_drain
;
846 if (vproc_mig_log_drain(bootstrap_port
, &outdata
, &outdata_cnt
) != 0) {
847 return _vprocmgr_log_drain
;
850 tmp_cnt
= outdata_cnt
;
853 pthread_mutex_lock(mutex
);
856 for (lm
= (struct logmsg_s
*)outdata
; tmp_cnt
> 0; lm
= ((void *)lm
+ lm
->obj_sz
)) {
857 lm
->from_name
= (char *)lm
+ lm
->from_name_offset
;
858 lm
->about_name
= (char *)lm
+ lm
->about_name_offset
;
859 lm
->msg
= (char *)lm
+ lm
->msg_offset
;
860 lm
->session_name
= (char *)lm
+ lm
->session_name_offset
;
862 tv
.tv_sec
= lm
->when
/ USEC_PER_SEC
;
863 tv
.tv_usec
= lm
->when
% USEC_PER_SEC
;
865 func(&tv
, lm
->from_pid
, lm
->about_pid
, lm
->sender_uid
, lm
->sender_gid
, lm
->pri
,
866 lm
->from_name
, lm
->about_name
, lm
->session_name
, lm
->msg
);
868 tmp_cnt
-= lm
->obj_sz
;
872 pthread_mutex_unlock(mutex
);
876 mig_deallocate(outdata
, outdata_cnt
);
883 vproc_swap_integer(vproc_t vp
, vproc_gsk_t key
, int64_t *inval
, int64_t *outval
)
885 static int64_t cached_is_managed
= -1;
886 int64_t dummyval
= 0;
889 case VPROC_GSK_MGR_PID
:
890 if (cached_pid
!= -1 && outval
) {
891 *outval
= cached_pid
;
895 case VPROC_GSK_IS_MANAGED
:
896 if (cached_is_managed
!= -1 && outval
) {
897 *outval
= cached_is_managed
;
901 case VPROC_GSK_TRANSACTIONS_ENABLED
:
902 /* Shared memory region is required for transactions. */
903 if (unlikely(vproc_shmem
== NULL
)) {
904 int po_r
= pthread_once(&shmem_inited
, vproc_client_init
);
905 if (po_r
!= 0 || vproc_shmem
== NULL
) {
909 return (vproc_err_t
)vproc_swap_integer
;
913 if (s_cached_transactions_enabled
&& outval
) {
914 *outval
= s_cached_transactions_enabled
;
922 kern_return_t kr
= KERN_FAILURE
;
923 mach_port_t mp
= vp
? vp
->j_port
: bootstrap_port
;
924 if ((kr
= vproc_mig_swap_integer(mp
, inval
? key
: 0, outval
? key
: 0, inval
? *inval
: 0, outval
? outval
: &dummyval
)) == 0) {
926 case VPROC_GSK_MGR_PID
:
927 cached_pid
= outval
? *outval
: dummyval
;
929 case VPROC_GSK_IS_MANAGED
:
930 cached_is_managed
= outval
? *outval
: dummyval
;
932 case VPROC_GSK_TRANSACTIONS_ENABLED
:
933 s_cached_transactions_enabled
= 1;
935 case VPROC_GSK_PERUSER_SUSPEND
:
937 /* Wait for the per-user launchd to exit before returning. */
940 EV_SET(&kev
, dummyval
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, 0);
941 int r
= kevent(kq
, &kev
, 1, &kev
, 1, NULL
);
954 return (vproc_err_t
)vproc_swap_integer
;
958 get_root_bootstrap_port(void)
960 mach_port_t parent_port
= 0;
961 mach_port_t previous_port
= 0;
965 if (previous_port
!= bootstrap_port
) {
966 mach_port_deallocate(mach_task_self(), previous_port
);
968 previous_port
= parent_port
;
970 previous_port
= bootstrap_port
;
973 if (bootstrap_parent(previous_port
, &parent_port
) != 0) {
974 return MACH_PORT_NULL
;
977 } while (parent_port
!= previous_port
);
983 vproc_swap_complex(vproc_t vp
, vproc_gsk_t key
, launch_data_t inval
, launch_data_t
*outval
)
985 size_t data_offset
= 0, good_enough_size
= 10*1024*1024;
986 mach_msg_type_number_t indata_cnt
= 0, outdata_cnt
;
987 vm_offset_t indata
= 0, outdata
= 0;
988 launch_data_t out_obj
;
989 void *rval
= vproc_swap_complex
;
993 if (!(buf
= malloc(good_enough_size
))) {
997 if ((indata_cnt
= launch_data_pack(inval
, buf
, good_enough_size
, NULL
, NULL
)) == 0) {
1001 indata
= (vm_offset_t
)buf
;
1004 mach_port_t mp
= vp
? vp
->j_port
: bootstrap_port
;
1005 if (vproc_mig_swap_complex(mp
, inval
? key
: 0, outval
? key
: 0, indata
, indata_cnt
, &outdata
, &outdata_cnt
) != 0) {
1010 if (!(out_obj
= launch_data_unpack((void *)outdata
, outdata_cnt
, NULL
, 0, &data_offset
, NULL
))) {
1014 if (!(*outval
= launch_data_copy(out_obj
))) {
1026 mig_deallocate(outdata
, outdata_cnt
);
1033 vproc_swap_string(vproc_t vp
, vproc_gsk_t key
, const char *instr
, char **outstr
)
1035 launch_data_t instr_data
= instr
? launch_data_new_string(instr
) : NULL
;
1036 launch_data_t outstr_data
= NULL
;
1038 vproc_err_t verr
= vproc_swap_complex(vp
, key
, instr_data
, &outstr_data
);
1039 if (!verr
&& outstr
) {
1040 if (launch_data_get_type(outstr_data
) == LAUNCH_DATA_STRING
) {
1041 *outstr
= strdup(launch_data_get_string(outstr_data
));
1043 verr
= (vproc_err_t
)vproc_swap_string
;
1045 launch_data_free(outstr_data
);
1048 launch_data_free(instr_data
);
1055 reboot2(uint64_t flags
)
1057 if (vproc_mig_reboot2(get_root_bootstrap_port(), flags
) == 0) {
1065 _vproc_kickstart_by_label(const char *label
, pid_t
*out_pid
, mach_port_t
*out_port_name
__attribute__((unused
)), mach_port_t
*out_obsrvr_port
__attribute__((unused
)), vproc_flags_t flags
)
1067 /* Ignore the two port parameters. This SPI isn't long for this world, and
1068 * all the current clients just leak them anyway.
1070 kern_return_t kr
= vproc_mig_kickstart(bootstrap_port
, (char *)label
, out_pid
, flags
);
1071 if (kr
== KERN_SUCCESS
) {
1075 return (vproc_err_t
)_vproc_kickstart_by_label
;
1079 _vproc_set_global_on_demand(bool state
)
1081 int64_t val
= state
? ~0 : 0;
1083 if (vproc_swap_integer(NULL
, VPROC_GSK_GLOBAL_ON_DEMAND
, &val
, NULL
) == 0) {
1087 return (vproc_err_t
)_vproc_set_global_on_demand
;
1091 _vproc_logv(int pri
, int err
, const char *msg
, va_list ap
)
1093 char flat_msg
[3000];
1095 vsnprintf(flat_msg
, sizeof(flat_msg
), msg
, ap
);
1097 vproc_mig_log(bootstrap_port
, pri
, err
, flat_msg
);
1101 _vproc_log(int pri
, const char *msg
, ...)
1106 _vproc_logv(pri
, 0, msg
, ap
);
1111 _vproc_log_error(int pri
, const char *msg
, ...)
1113 int saved_errno
= errno
;
1117 _vproc_logv(pri
, saved_errno
, msg
, ap
);
1122 vprocmgr_helper_check_in(const char *name
, mach_port_t rp
, launch_data_t
*events
, uint64_t *tokens
)
1124 vm_offset_t events_packed
= 0;
1125 mach_msg_type_number_t sz
= 0;
1126 size_t data_off
= 0;
1128 kern_return_t kr
= vproc_mig_event_source_check_in(bootstrap_port
, (char *)name
, rp
, &events_packed
, &sz
, tokens
);
1130 launch_data_t _events
= launch_data_unpack((void *)events_packed
, sz
, NULL
, 0, &data_off
, 0);
1131 *events
= launch_data_copy(_events
);
1136 mig_deallocate(events_packed
, sz
);
1143 vprocmgr_helper_event_set_state(const char *sysname
, uint64_t token
, bool state
)
1145 kern_return_t kr
= vproc_mig_event_set_state(bootstrap_port
, (char *)sysname
, token
, state
);
1150 vprocmgr_helper_register(vproc_helper_recv_ping_t callout
)
1152 vprocmgr_helper_callout
= callout
;
1155 /* The type naming convention is as follows:
1157 * union __RequestUnion__<userprefix><subsystem>_subsystem
1159 * union __ReplyUnion__<userprefix><subsystem>_subsystem
1162 union __RequestUnion__helper_downcall_launchd_helper_subsystem req
;
1163 union __ReplyUnion__helper_downcall_launchd_helper_subsystem rep
;
1166 size_t vprocmgr_helper_maxmsgsz
= sizeof(union maxmsgsz
);
1169 helper_recv_ping(mach_port_t p
, audit_token_t autok
)
1171 return vprocmgr_helper_callout(p
, autok
);
1175 vprocmgr_helper_server_routine_for_dispatch(mach_msg_header_t
*message
, mach_msg_header_t
*reply
)
1177 return launchd_helper_server(message
, reply
);
1181 helper_recv_wait(mach_port_t p
, int status
)
1184 return (errno
= mach_port_set_context(mach_task_self(), p
, (mach_vm_address_t
)status
));
1188 launch_wait(mach_port_t port
)
1191 errno
= mach_msg_server_once(launchd_helper_server
, vprocmgr_helper_maxmsgsz
, port
, 0);
1192 if (errno
== MACH_MSG_SUCCESS
) {
1193 mach_vm_address_t ctx
= 0;
1194 if ((errno
= mach_port_get_context(mach_task_self(), port
, &ctx
)) == KERN_SUCCESS
) {