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 #define _vproc_set_crash_log_message(x)
64 static int64_t cached_pid
= -1;
65 static struct vproc_shmem_s
*vproc_shmem
;
66 static pthread_once_t shmem_inited
= PTHREAD_ONCE_INIT
;
67 static uint64_t s_cached_transactions_enabled
= 0;
69 static _vproc_transaction_callout vproc_gone2zero
;
70 static _vproc_transaction_callout vproc_gonenonzero
;
72 static vproc_helper_recv_ping_t vprocmgr_helper_callout
;
79 /* These functions are a total nightmare to get to through headers.
80 * See rdar://problem/8223092.
82 typedef __darwin_mach_port_t fileport_t
;
83 #define FILEPORT_NULL ((fileport_t)0)
84 extern int fileport_makeport(int, fileport_t
*);
85 extern int fileport_makefd(fileport_t
);
87 vproc_t
vprocmgr_lookup_vproc(const char *label
)
89 struct vproc_s
*vp
= NULL
;
91 mach_port_t mp
= MACH_PORT_NULL
;
92 kern_return_t kr
= vproc_mig_port_for_label(bootstrap_port
, (char *)label
, &mp
);
93 if (kr
== BOOTSTRAP_SUCCESS
) {
94 vp
= (struct vproc_s
*)calloc(1, sizeof(struct vproc_s
));
97 mach_port_mod_refs(mach_task_self(), mp
, MACH_PORT_RIGHT_SEND
, 1);
100 (void)mach_port_deallocate(mach_task_self(), mp
);
106 vproc_t
vproc_retain(vproc_t vp
)
108 int32_t orig
= OSAtomicAdd32(1, &vp
->refcount
) - 1;
110 /* We've gone from 0 to 1, which means that this object was due to be freed. */
111 _vproc_set_crash_log_message("Under-retain / over-release of vproc_t.");
118 void vproc_release(vproc_t vp
)
120 int32_t newval
= OSAtomicAdd32(-1, &vp
->refcount
);
122 /* We're in negative numbers, which is bad. */
123 _vproc_set_crash_log_message("Over-release of vproc_t.");
125 } else if (newval
== 0) {
126 mach_port_deallocate(mach_task_self(), vp
->j_port
);
132 vproc_shmem_init(void)
134 vm_address_t vm_addr
= 0;
135 mach_port_t shmem_port
;
138 kr
= vproc_mig_setup_shmem(bootstrap_port
, &shmem_port
);
142 /* rdar://problem/6416724
143 * If we fail to set up a shared memory page, just allocate a local chunk
144 * of memory. This way, processes can still introspect their own transaction
145 * counts if they're being run under a debugger. Moral of the story: Debug
146 * from the environment you intend to run in.
148 void *_vm_addr
= calloc(1, sizeof(struct vproc_shmem_s
));
153 vm_addr
= (vm_address_t
)_vm_addr
;
155 kr
= vm_map(mach_task_self(), &vm_addr
, getpagesize(), 0, true, shmem_port
, 0, false,
156 VM_PROT_READ
|VM_PROT_WRITE
, VM_PROT_READ
|VM_PROT_WRITE
, VM_INHERIT_NONE
);
161 kr
= mach_port_deallocate(mach_task_self(), shmem_port
);
166 vproc_shmem
= (struct vproc_shmem_s
*)vm_addr
;
170 vproc_client_init(void)
172 char *val
= getenv(LAUNCHD_DO_APPLE_INTERNAL_LOGGING
);
174 if (strncmp(val
, "true", sizeof("true") - 1) == 0) {
175 do_apple_internal_logging
= true;
183 vproc_transaction_begin(vproc_t vp
__attribute__((unused
)))
185 vproc_transaction_t vpt
= (vproc_transaction_t
)vproc_shmem_init
; /* we need a "random" variable that is testable */
186 _vproc_transaction_begin();
192 _vproc_transaction_begin(void)
194 if (unlikely(vproc_shmem
== NULL
)) {
195 int po_r
= pthread_once(&shmem_inited
, vproc_client_init
);
196 if (po_r
!= 0 || vproc_shmem
== NULL
) {
201 /* We need to deal with the potential race condition of trying to open a
202 * transaction after launchd has marked the process for death. Consider if
203 * one thread closes the last transaction after marking the process for
204 * death. Then we call _exit(2). But exiting isn't instantaneous. So if some
205 * other threads come in very soon after and try to open transactions, we
206 * have to cut them off so that they can't begin their work. Because if they
207 * can manage to get to the point of, say, parking themselves in an
208 * uninterruptible wait, then the process won't exit.
210 * We loop here so that, if someone's calling vproc_transaction_end() at the
211 * same time, we can pick up the descent to -1 if launchd has marked us for
214 typeof(vproc_shmem
->vp_shmem_transaction_cnt
) old
= 0;
216 old
= vproc_shmem
->vp_shmem_transaction_cnt
;
218 if (unlikely(old
< 0)) {
219 /* No transactions should be opened after this point, so make sure
220 * this thread can't proceed. We don't crash here because it could
221 * be a legitimate race, as described above.
223 if (vproc_shmem
->vp_shmem_flags
& VPROC_SHMEM_EXITING
) {
226 _vproc_set_crash_log_message("Unbalanced: vproc_transaction_begin()");
229 } else if (old
== 0 && vproc_gonenonzero
) {
232 } while (!__sync_bool_compare_and_swap(&vproc_shmem
->vp_shmem_transaction_cnt
, old
, old
+ 1));
234 runtime_ktrace(RTKT_VPROC_TRANSACTION_INCREMENT
, old
+ 1, 0, 0);
238 _vproc_transaction_count(void)
240 return likely(vproc_shmem
) ? vproc_shmem
->vp_shmem_transaction_cnt
: INT32_MAX
;
244 _vproc_standby_count(void)
246 #ifdef VPROC_STANDBY_IMPLEMENTED
247 return likely(vproc_shmem
) ? vproc_shmem
->vp_shmem_standby_cnt
: INT32_MAX
;
254 _vproc_standby_timeout(void)
256 return likely(vproc_shmem
) ? vproc_shmem
->vp_shmem_standby_timeout
: 0;
260 _vproc_pid_is_managed(pid_t p
)
262 boolean_t result
= false;
263 vproc_mig_pid_is_managed(bootstrap_port
, p
, &result
);
269 _vproc_transaction_count_for_pid(pid_t p
, int32_t *count
, bool *condemned
)
271 boolean_t _condemned
= false;
272 kern_return_t kr
= vproc_mig_transaction_count_for_pid(bootstrap_port
, p
, count
, &_condemned
);
273 if (kr
== KERN_SUCCESS
&& condemned
) {
274 *condemned
= _condemned
? true : false;
281 _vproc_transaction_try_exit(int status
)
283 #if !TARGET_OS_EMBEDDED
284 if (unlikely(vproc_shmem
== NULL
)) {
288 if (__sync_bool_compare_and_swap(&vproc_shmem
->vp_shmem_transaction_cnt
, 0, -1)) {
289 vproc_shmem
->vp_shmem_flags
|= VPROC_SHMEM_EXITING
;
298 vproc_transaction_end(vproc_t vp
__attribute__((unused
)), vproc_transaction_t vpt
)
300 if (unlikely(vpt
!= (vproc_transaction_t
)vproc_shmem_init
)) {
301 _vproc_set_crash_log_message("Bogus transaction handle passed to vproc_transaction_end() ");
305 _vproc_transaction_end();
309 _vproc_transaction_end(void)
311 typeof(vproc_shmem
->vp_shmem_transaction_cnt
) newval
;
313 if (unlikely(vproc_shmem
== NULL
)) {
317 newval
= __sync_sub_and_fetch(&vproc_shmem
->vp_shmem_transaction_cnt
, 1);
319 runtime_ktrace(RTKT_VPROC_TRANSACTION_DECREMENT
, newval
, 0, 0);
320 if (unlikely(newval
< 0)) {
321 if (vproc_shmem
->vp_shmem_flags
& VPROC_SHMEM_EXITING
) {
324 _vproc_set_crash_log_message("Unbalanced: vproc_transaction_end()");
327 } else if (newval
== 0 && vproc_gone2zero
) {
333 vproc_standby_begin(vproc_t vp
__attribute__((unused
)))
335 #ifdef VPROC_STANDBY_IMPLEMENTED
336 vproc_standby_t vpsb
= (vproc_standby_t
)vproc_shmem_init
; /* we need a "random" variable that is testable */
338 _vproc_standby_begin();
347 _vproc_standby_begin(void)
349 #ifdef VPROC_STANDBY_IMPLEMENTED
350 typeof(vproc_shmem
->vp_shmem_standby_cnt
) newval
;
352 if (unlikely(vproc_shmem
== NULL
)) {
353 int po_r
= pthread_once(&shmem_inited
, vproc_client_init
);
354 if (po_r
!= 0 || vproc_shmem
== NULL
) {
359 newval
= __sync_add_and_fetch(&vproc_shmem
->vp_shmem_standby_cnt
, 1);
361 if (unlikely(newval
< 1)) {
362 _vproc_set_crash_log_message("Unbalanced: vproc_standby_begin()");
371 vproc_standby_end(vproc_t vp
__attribute__((unused
)), vproc_standby_t vpt
__attribute__((unused
)))
373 #ifdef VPROC_STANDBY_IMPLEMENTED
374 if (unlikely(vpt
!= (vproc_standby_t
)vproc_shmem_init
)) {
375 _vproc_set_crash_log_message("Bogus standby handle passed to vproc_standby_end() ");
379 _vproc_standby_end();
386 _vproc_standby_end(void)
388 #ifdef VPROC_STANDBY_IMPLEMENTED
389 typeof(vproc_shmem
->vp_shmem_standby_cnt
) newval
;
391 if (unlikely(vproc_shmem
== NULL
)) {
392 _vproc_set_crash_log_message("Process called vproc_standby_end() when not enrolled in transaction model.");
396 newval
= __sync_sub_and_fetch(&vproc_shmem
->vp_shmem_standby_cnt
, 1);
398 if (unlikely(newval
< 0)) {
399 _vproc_set_crash_log_message("Unbalanced: vproc_standby_end()");
408 _vproc_transaction_ptr(void)
414 _vproc_transaction_set_callouts(_vproc_transaction_callout gone2zero
, _vproc_transaction_callout gonenonzero
)
416 if (unlikely(vproc_shmem
== NULL
)) {
417 int po_r
= pthread_once(&shmem_inited
, vproc_client_init
);
418 if (po_r
!= 0 || vproc_shmem
== NULL
) {
423 static bool once
= false;
425 _vproc_set_crash_log_message("This SPI may only be called once. It is only meant for libxpc.");
431 vproc_gone2zero
= gone2zero
;
432 vproc_gonenonzero
= gonenonzero
;
436 _vproc_grab_subset(mach_port_t bp
, mach_port_t
*reqport
, mach_port_t
*rcvright
, launch_data_t
*outval
,
437 mach_port_array_t
*ports
, mach_msg_type_number_t
*portCnt
)
439 mach_msg_type_number_t outdata_cnt
;
440 vm_offset_t outdata
= 0;
441 size_t data_offset
= 0;
442 launch_data_t out_obj
;
445 if ((kr
= vproc_mig_take_subset(bp
, reqport
, rcvright
, &outdata
, &outdata_cnt
, ports
, portCnt
))) {
449 if ((out_obj
= launch_data_unpack((void *)outdata
, outdata_cnt
, NULL
, 0, &data_offset
, NULL
))) {
450 *outval
= launch_data_copy(out_obj
);
457 mig_deallocate(outdata
, outdata_cnt
);
464 _vproc_post_fork_ping(void)
466 #if !TARGET_OS_EMBEDDED
467 au_asid_t s
= AU_DEFAUDITSID
;
469 mach_port_t session
= MACH_PORT_NULL
;
470 kern_return_t kr
= vproc_mig_post_fork_ping(bootstrap_port
, mach_task_self(), &session
);
471 if (kr
!= KERN_SUCCESS
) {
472 /* If this happens, our bootstrap port probably got hosed. */
473 _vproc_log(LOG_ERR
, "Post-fork ping failed!");
477 /* If we get back MACH_PORT_NULL, that means we just stick with the session
478 * we inherited across fork(2).
480 if (session
== MACH_PORT_NULL
) {
485 s
= _audit_session_join(session
);
487 _vproc_log_error(LOG_ERR
, "Could not join security session!");
490 _vproc_log(LOG_DEBUG
, "Joined session %d.", s
);
494 return s
!= AU_DEFAUDITSID
? NULL
: _vproc_post_fork_ping
;
496 mach_port_t session
= MACH_PORT_NULL
;
497 return vproc_mig_post_fork_ping(bootstrap_port
, mach_task_self(), &session
) ? _vproc_post_fork_ping
: NULL
;
502 _vprocmgr_init(const char *session_type
)
504 if (vproc_mig_init_session(bootstrap_port
, (char *)session_type
, _audit_session_self()) == 0) {
508 return (vproc_err_t
)_vprocmgr_init
;
512 _vprocmgr_move_subset_to_user(uid_t target_user
, const char *session_type
, uint64_t flags
)
514 kern_return_t kr
= 0;
515 bool is_bkgd
= (strcmp(session_type
, VPROCMGR_SESSION_BACKGROUND
) == 0);
516 int64_t ldpid
, lduid
;
518 if (vproc_swap_integer(NULL
, VPROC_GSK_MGR_PID
, 0, &ldpid
) != 0) {
519 return (vproc_err_t
)_vprocmgr_move_subset_to_user
;
522 if (vproc_swap_integer(NULL
, VPROC_GSK_MGR_UID
, 0, &lduid
) != 0) {
523 return (vproc_err_t
)_vprocmgr_move_subset_to_user
;
526 if (!is_bkgd
&& ldpid
!= 1) {
527 if (lduid
== getuid()) {
531 * Not all sessions can be moved.
532 * We should clean up this mess someday.
534 return (vproc_err_t
)_vprocmgr_move_subset_to_user
;
537 mach_port_t puc
= 0, rootbs
= get_root_bootstrap_port();
539 if (vproc_mig_lookup_per_user_context(rootbs
, target_user
, &puc
) != 0) {
540 return (vproc_err_t
)_vprocmgr_move_subset_to_user
;
544 task_set_bootstrap_port(mach_task_self(), puc
);
545 mach_port_deallocate(mach_task_self(), bootstrap_port
);
546 bootstrap_port
= puc
;
548 kr
= vproc_mig_move_subset(puc
, bootstrap_port
, (char *)session_type
, _audit_session_self(), flags
);
549 mach_port_deallocate(mach_task_self(), puc
);
555 return (vproc_err_t
)_vprocmgr_move_subset_to_user
;
558 return _vproc_post_fork_ping();
562 _vprocmgr_switch_to_session(const char *target_session
, vproc_flags_t flags
__attribute__((unused
)))
564 mach_port_t new_bsport
= MACH_PORT_NULL
;
565 kern_return_t kr
= KERN_FAILURE
;
567 mach_port_t tnp
= MACH_PORT_NULL
;
568 task_name_for_pid(mach_task_self(), getpid(), &tnp
);
569 if ((kr
= vproc_mig_switch_to_session(bootstrap_port
, tnp
, (char *)target_session
, _audit_session_self(), &new_bsport
)) != KERN_SUCCESS
) {
570 _vproc_log(LOG_NOTICE
, "_vprocmgr_switch_to_session(): kr = 0x%x", kr
);
571 return (vproc_err_t
)_vprocmgr_switch_to_session
;
574 task_set_bootstrap_port(mach_task_self(), new_bsport
);
575 mach_port_deallocate(mach_task_self(), bootstrap_port
);
576 bootstrap_port
= new_bsport
;
578 return !issetugid() ? _vproc_post_fork_ping() : NULL
;
582 _vprocmgr_detach_from_console(vproc_flags_t flags
__attribute__((unused
)))
584 return _vprocmgr_switch_to_session(VPROCMGR_SESSION_BACKGROUND
, 0);
588 _spawn_via_launchd(const char *label
, const char *const *argv
, const struct spawn_via_launchd_attr
*spawn_attrs
, int struct_version
)
590 size_t i
, good_enough_size
= 10*1024*1024;
591 mach_msg_type_number_t indata_cnt
= 0;
592 vm_offset_t indata
= 0;
593 mach_port_t obsvr_port
= MACH_PORT_NULL
;
594 launch_data_t tmp
, tmp_array
, in_obj
;
595 const char *const *tmpp
;
596 kern_return_t kr
= 1;
600 if ((in_obj
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
)) == NULL
) {
604 if ((tmp
= launch_data_new_string(label
)) == NULL
) {
608 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_LABEL
);
610 if ((tmp_array
= launch_data_alloc(LAUNCH_DATA_ARRAY
)) == NULL
) {
614 for (i
= 0; *argv
; i
++, argv
++) {
615 tmp
= launch_data_new_string(*argv
);
620 launch_data_array_set_index(tmp_array
, tmp
, i
);
623 launch_data_dict_insert(in_obj
, tmp_array
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
625 if (spawn_attrs
) switch (struct_version
) {
629 if (spawn_attrs
->spawn_quarantine
) {
630 char qbuf
[QTN_SERIALIZED_DATA_MAX
];
631 size_t qbuf_sz
= QTN_SERIALIZED_DATA_MAX
;
633 if (qtn_proc_to_data(spawn_attrs
->spawn_quarantine
, qbuf
, &qbuf_sz
) == 0) {
634 tmp
= launch_data_new_opaque(qbuf
, qbuf_sz
);
635 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_QUARANTINEDATA
);
640 if (spawn_attrs
->spawn_seatbelt_profile
) {
641 tmp
= launch_data_new_string(spawn_attrs
->spawn_seatbelt_profile
);
642 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_SANDBOXPROFILE
);
645 if (spawn_attrs
->spawn_seatbelt_flags
) {
646 tmp
= launch_data_new_integer(*spawn_attrs
->spawn_seatbelt_flags
);
647 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_SANDBOXFLAGS
);
652 if (spawn_attrs
->spawn_binpref
) {
653 tmp_array
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
654 for (i
= 0; i
< spawn_attrs
->spawn_binpref_cnt
; i
++) {
655 tmp
= launch_data_new_integer(spawn_attrs
->spawn_binpref
[i
]);
656 launch_data_array_set_index(tmp_array
, tmp
, i
);
658 launch_data_dict_insert(in_obj
, tmp_array
, LAUNCH_JOBKEY_BINARYORDERPREFERENCE
);
662 if (spawn_attrs
->spawn_flags
& SPAWN_VIA_LAUNCHD_STOPPED
) {
663 tmp
= launch_data_new_bool(true);
664 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_WAITFORDEBUGGER
);
666 if (spawn_attrs
->spawn_flags
& SPAWN_VIA_LAUNCHD_TALAPP
) {
667 tmp
= launch_data_new_string(LAUNCH_KEY_POSIXSPAWNTYPE_TALAPP
);
668 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_POSIXSPAWNTYPE
);
670 if (spawn_attrs
->spawn_flags
& SPAWN_VIA_LAUNCHD_WIDGET
) {
671 tmp
= launch_data_new_string(LAUNCH_KEY_POSIXSPAWNTYPE_WIDGET
);
672 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_POSIXSPAWNTYPE
);
674 if (spawn_attrs
->spawn_flags
& SPAWN_VIA_LAUNCHD_DISABLE_ASLR
) {
675 tmp
= launch_data_new_bool(true);
676 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_DISABLEASLR
);
679 if (spawn_attrs
->spawn_env
) {
680 launch_data_t tmp_dict
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
682 for (tmpp
= spawn_attrs
->spawn_env
; *tmpp
; tmpp
++) {
683 char *eqoff
, tmpstr
[strlen(*tmpp
) + 1];
685 strcpy(tmpstr
, *tmpp
);
687 eqoff
= strchr(tmpstr
, '=');
695 launch_data_dict_insert(tmp_dict
, launch_data_new_string(eqoff
+ 1), tmpstr
);
698 launch_data_dict_insert(in_obj
, tmp_dict
, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES
);
701 if (spawn_attrs
->spawn_path
) {
702 tmp
= launch_data_new_string(spawn_attrs
->spawn_path
);
703 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_PROGRAM
);
706 if (spawn_attrs
->spawn_chdir
) {
707 tmp
= launch_data_new_string(spawn_attrs
->spawn_chdir
);
708 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_WORKINGDIRECTORY
);
711 if (spawn_attrs
->spawn_umask
) {
712 tmp
= launch_data_new_integer(*spawn_attrs
->spawn_umask
);
713 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_UMASK
);
721 if (!(buf
= malloc(good_enough_size
))) {
725 if ((indata_cnt
= launch_data_pack(in_obj
, buf
, good_enough_size
, NULL
, NULL
)) == 0) {
729 indata
= (vm_offset_t
)buf
;
731 if (struct_version
== 3) {
732 kr
= vproc_mig_spawn2(bootstrap_port
, indata
, indata_cnt
, _audit_session_self(), &p
, &obsvr_port
);
734 _vproc_set_crash_log_message("Bogus version passed to _spawn_via_launchd(). For this release, the only valid version is 3.");
737 if (kr
== VPROC_ERR_TRY_PER_USER
) {
740 if (vproc_mig_lookup_per_user_context(bootstrap_port
, 0, &puc
) == 0) {
741 if (struct_version
== 3) {
742 kr
= vproc_mig_spawn2(puc
, indata
, indata_cnt
, _audit_session_self(), &p
, &obsvr_port
);
744 mach_port_deallocate(mach_task_self(), puc
);
750 launch_data_free(in_obj
);
758 case BOOTSTRAP_SUCCESS
:
759 if (spawn_attrs
&& spawn_attrs
->spawn_observer_port
) {
760 *spawn_attrs
->spawn_observer_port
= obsvr_port
;
762 if (struct_version
== 3) {
763 mach_port_mod_refs(mach_task_self(), obsvr_port
, MACH_PORT_RIGHT_RECEIVE
, -1);
765 mach_port_deallocate(mach_task_self(), obsvr_port
);
769 case BOOTSTRAP_NOT_PRIVILEGED
:
770 errno
= EPERM
; break;
771 case BOOTSTRAP_NO_MEMORY
:
772 errno
= ENOMEM
; break;
773 case BOOTSTRAP_NAME_IN_USE
:
774 errno
= EEXIST
; break;
778 errno
= EINVAL
; break;
785 mpm_wait(mach_port_t ajob
__attribute__((unused
)), int *wstatus
)
792 mpm_uncork_fork(mach_port_t ajob
__attribute__((unused
)))
798 _vprocmgr_getsocket(name_t sockpath
)
800 return vproc_mig_getsocket(bootstrap_port
, sockpath
);
804 _vproc_get_last_exit_status(int *wstatus
)
808 if (vproc_swap_integer(NULL
, VPROC_GSK_LAST_EXIT_STATUS
, 0, &val
) == 0) {
813 return (vproc_err_t
)_vproc_get_last_exit_status
;
817 _vproc_send_signal_by_label(const char *label
, int sig
)
819 if (vproc_mig_send_signal(bootstrap_port
, (char *)label
, sig
) == 0) {
823 return _vproc_send_signal_by_label
;
827 _vprocmgr_log_forward(mach_port_t mp
, void *data
, size_t len
)
829 if (vproc_mig_log_forward(mp
, (vm_offset_t
)data
, len
) == 0) {
833 return _vprocmgr_log_forward
;
837 _vprocmgr_log_drain(vproc_t vp
__attribute__((unused
)), pthread_mutex_t
*mutex
, _vprocmgr_log_drain_callback_t func
)
839 mach_msg_type_number_t outdata_cnt
, tmp_cnt
;
840 vm_offset_t outdata
= 0;
845 return _vprocmgr_log_drain
;
848 if (vproc_mig_log_drain(bootstrap_port
, &outdata
, &outdata_cnt
) != 0) {
849 return _vprocmgr_log_drain
;
852 tmp_cnt
= outdata_cnt
;
855 pthread_mutex_lock(mutex
);
858 for (lm
= (struct logmsg_s
*)outdata
; tmp_cnt
> 0; lm
= ((void *)lm
+ lm
->obj_sz
)) {
859 lm
->from_name
= (char *)lm
+ lm
->from_name_offset
;
860 lm
->about_name
= (char *)lm
+ lm
->about_name_offset
;
861 lm
->msg
= (char *)lm
+ lm
->msg_offset
;
862 lm
->session_name
= (char *)lm
+ lm
->session_name_offset
;
864 tv
.tv_sec
= lm
->when
/ USEC_PER_SEC
;
865 tv
.tv_usec
= lm
->when
% USEC_PER_SEC
;
867 func(&tv
, lm
->from_pid
, lm
->about_pid
, lm
->sender_uid
, lm
->sender_gid
, lm
->pri
,
868 lm
->from_name
, lm
->about_name
, lm
->session_name
, lm
->msg
);
870 tmp_cnt
-= lm
->obj_sz
;
874 pthread_mutex_unlock(mutex
);
878 mig_deallocate(outdata
, outdata_cnt
);
885 vproc_swap_integer(vproc_t vp
, vproc_gsk_t key
, int64_t *inval
, int64_t *outval
)
887 static int64_t cached_is_managed
= -1;
888 int64_t dummyval
= 0;
891 case VPROC_GSK_MGR_PID
:
892 if (cached_pid
!= -1 && outval
) {
893 *outval
= cached_pid
;
897 case VPROC_GSK_IS_MANAGED
:
898 if (cached_is_managed
!= -1 && outval
) {
899 *outval
= cached_is_managed
;
903 case VPROC_GSK_TRANSACTIONS_ENABLED
:
904 /* Shared memory region is required for transactions. */
905 if (unlikely(vproc_shmem
== NULL
)) {
906 int po_r
= pthread_once(&shmem_inited
, vproc_client_init
);
907 if (po_r
!= 0 || vproc_shmem
== NULL
) {
911 return (vproc_err_t
)vproc_swap_integer
;
915 if (s_cached_transactions_enabled
&& outval
) {
916 *outval
= s_cached_transactions_enabled
;
924 kern_return_t kr
= KERN_FAILURE
;
925 mach_port_t mp
= vp
? vp
->j_port
: bootstrap_port
;
926 if ((kr
= vproc_mig_swap_integer(mp
, inval
? key
: 0, outval
? key
: 0, inval
? *inval
: 0, outval
? outval
: &dummyval
)) == 0) {
928 case VPROC_GSK_MGR_PID
:
929 cached_pid
= outval
? *outval
: dummyval
;
931 case VPROC_GSK_IS_MANAGED
:
932 cached_is_managed
= outval
? *outval
: dummyval
;
934 case VPROC_GSK_TRANSACTIONS_ENABLED
:
935 s_cached_transactions_enabled
= 1;
937 case VPROC_GSK_PERUSER_SUSPEND
:
939 /* Wait for the per-user launchd to exit before returning. */
942 EV_SET(&kev
, dummyval
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, 0);
943 int r
= kevent(kq
, &kev
, 1, &kev
, 1, NULL
);
956 return (vproc_err_t
)vproc_swap_integer
;
960 get_root_bootstrap_port(void)
962 mach_port_t parent_port
= 0;
963 mach_port_t previous_port
= 0;
967 if (previous_port
!= bootstrap_port
) {
968 mach_port_deallocate(mach_task_self(), previous_port
);
970 previous_port
= parent_port
;
972 previous_port
= bootstrap_port
;
975 if (bootstrap_parent(previous_port
, &parent_port
) != 0) {
976 return MACH_PORT_NULL
;
979 } while (parent_port
!= previous_port
);
985 vproc_swap_complex(vproc_t vp
, vproc_gsk_t key
, launch_data_t inval
, launch_data_t
*outval
)
987 size_t data_offset
= 0, good_enough_size
= 10*1024*1024;
988 mach_msg_type_number_t indata_cnt
= 0, outdata_cnt
;
989 vm_offset_t indata
= 0, outdata
= 0;
990 launch_data_t out_obj
;
991 void *rval
= vproc_swap_complex
;
995 if (!(buf
= malloc(good_enough_size
))) {
999 if ((indata_cnt
= launch_data_pack(inval
, buf
, good_enough_size
, NULL
, NULL
)) == 0) {
1003 indata
= (vm_offset_t
)buf
;
1006 mach_port_t mp
= vp
? vp
->j_port
: bootstrap_port
;
1007 if (vproc_mig_swap_complex(mp
, inval
? key
: 0, outval
? key
: 0, indata
, indata_cnt
, &outdata
, &outdata_cnt
) != 0) {
1012 if (!(out_obj
= launch_data_unpack((void *)outdata
, outdata_cnt
, NULL
, 0, &data_offset
, NULL
))) {
1016 if (!(*outval
= launch_data_copy(out_obj
))) {
1028 mig_deallocate(outdata
, outdata_cnt
);
1035 vproc_swap_string(vproc_t vp
, vproc_gsk_t key
, const char *instr
, char **outstr
)
1037 launch_data_t instr_data
= instr
? launch_data_new_string(instr
) : NULL
;
1038 launch_data_t outstr_data
= NULL
;
1040 vproc_err_t verr
= vproc_swap_complex(vp
, key
, instr_data
, &outstr_data
);
1041 if (!verr
&& outstr
) {
1042 if (launch_data_get_type(outstr_data
) == LAUNCH_DATA_STRING
) {
1043 *outstr
= strdup(launch_data_get_string(outstr_data
));
1045 verr
= (vproc_err_t
)vproc_swap_string
;
1047 launch_data_free(outstr_data
);
1050 launch_data_free(instr_data
);
1057 reboot2(uint64_t flags
)
1059 if (vproc_mig_reboot2(get_root_bootstrap_port(), flags
) == 0) {
1067 _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
)
1069 /* Ignore the two port parameters. This SPI isn't long for this world, and
1070 * all the current clients just leak them anyway.
1072 kern_return_t kr
= vproc_mig_kickstart(bootstrap_port
, (char *)label
, out_pid
, flags
);
1073 if (kr
== KERN_SUCCESS
) {
1077 return (vproc_err_t
)_vproc_kickstart_by_label
;
1081 _vproc_set_global_on_demand(bool state
)
1083 int64_t val
= state
? ~0 : 0;
1085 if (vproc_swap_integer(NULL
, VPROC_GSK_GLOBAL_ON_DEMAND
, &val
, NULL
) == 0) {
1089 return (vproc_err_t
)_vproc_set_global_on_demand
;
1093 _vproc_logv(int pri
, int err
, const char *msg
, va_list ap
)
1095 char flat_msg
[3000];
1097 vsnprintf(flat_msg
, sizeof(flat_msg
), msg
, ap
);
1099 vproc_mig_log(bootstrap_port
, pri
, err
, flat_msg
);
1103 _vproc_log(int pri
, const char *msg
, ...)
1108 _vproc_logv(pri
, 0, msg
, ap
);
1113 _vproc_log_error(int pri
, const char *msg
, ...)
1115 int saved_errno
= errno
;
1119 _vproc_logv(pri
, saved_errno
, msg
, ap
);
1124 vprocmgr_helper_check_in(const char *name
, mach_port_t rp
, launch_data_t
*events
, uint64_t *tokens
)
1126 vm_offset_t events_packed
= 0;
1127 mach_msg_type_number_t sz
= 0;
1128 size_t data_off
= 0;
1130 kern_return_t kr
= vproc_mig_event_source_check_in(bootstrap_port
, (char *)name
, rp
, &events_packed
, &sz
, tokens
);
1132 launch_data_t _events
= launch_data_unpack((void *)events_packed
, sz
, NULL
, 0, &data_off
, 0);
1133 *events
= launch_data_copy(_events
);
1138 mig_deallocate(events_packed
, sz
);
1145 vprocmgr_helper_event_set_state(const char *sysname
, uint64_t token
, bool state
)
1147 kern_return_t kr
= vproc_mig_event_set_state(bootstrap_port
, (char *)sysname
, token
, state
);
1152 vprocmgr_helper_register(vproc_helper_recv_ping_t callout
)
1154 vprocmgr_helper_callout
= callout
;
1157 /* The type naming convention is as follows:
1159 * union __RequestUnion__<userprefix><subsystem>_subsystem
1161 * union __ReplyUnion__<userprefix><subsystem>_subsystem
1164 union __RequestUnion__helper_downcall_launchd_helper_subsystem req
;
1165 union __ReplyUnion__helper_downcall_launchd_helper_subsystem rep
;
1168 size_t vprocmgr_helper_maxmsgsz
= sizeof(union maxmsgsz
);
1171 helper_recv_ping(mach_port_t p
, audit_token_t autok
)
1173 return vprocmgr_helper_callout(p
, autok
);
1177 vprocmgr_helper_server_routine_for_dispatch(mach_msg_header_t
*message
, mach_msg_header_t
*reply
)
1179 return launchd_helper_server(message
, reply
);
1183 helper_recv_wait(mach_port_t p
, int status
)
1186 return (errno
= mach_port_set_context(mach_task_self(), p
, (mach_vm_address_t
)status
));
1190 launch_wait(mach_port_t port
)
1193 errno
= mach_msg_server_once(launchd_helper_server
, vprocmgr_helper_maxmsgsz
, port
, 0);
1194 if (errno
== MACH_MSG_SUCCESS
) {
1195 mach_vm_address_t ctx
= 0;
1196 if ((errno
= mach_port_get_context(mach_task_self(), port
, &ctx
)) == KERN_SUCCESS
) {