*/
#include "config.h"
-#include "libvproc_public.h"
-#include "libvproc_private.h"
-#include "libvproc_internal.h"
+#include "vproc.h"
+#include "vproc_priv.h"
+#include "vproc_internal.h"
#include <mach/mach.h>
#include <mach/vm_map.h>
#include <unistd.h>
#include <syslog.h>
#include <pthread.h>
+#include <signal.h>
+#include <assert.h>
+#include <libkern/OSAtomic.h>
+#include <sys/syscall.h>
+
#if HAVE_QUARANTINE
#include <quarantine.h>
#endif
-#include "liblaunch_public.h"
-#include "liblaunch_private.h"
-#include "liblaunch_internal.h"
+#include "launch.h"
+#include "launch_priv.h"
+#include "launch_internal.h"
+#include "launchd_ktrace.h"
#include "protocol_vproc.h"
#include "reboot2.h"
+#define likely(x) __builtin_expect((bool)(x), true)
+#define unlikely(x) __builtin_expect((bool)(x), false)
+
static mach_port_t get_root_bootstrap_port(void);
+const char *__crashreporter_info__; /* this should get merged with other versions of the symbol */
+
static int64_t cached_pid = -1;
+static struct vproc_shmem_s *vproc_shmem;
+static pthread_once_t shmem_inited = PTHREAD_ONCE_INIT;
+static uint64_t s_cached_transactions_enabled = 0;
+
+struct vproc_s {
+ int32_t refcount;
+ mach_port_t j_port;
+};
+
+vproc_t vprocmgr_lookup_vproc(const char *label)
+{
+ struct vproc_s *vp = NULL;
+
+ mach_port_t mp = MACH_PORT_NULL;
+ kern_return_t kr = vproc_mig_port_for_label(bootstrap_port, (char *)label, &mp);
+ if( kr == BOOTSTRAP_SUCCESS ) {
+ vp = (struct vproc_s *)calloc(1, sizeof(struct vproc_s));
+ if( vp ) {
+ vp->refcount = 1;
+ mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_SEND, 1);
+ vp->j_port = mp;
+ }
+ mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_SEND, -1);
+ }
+
+ return vp;
+}
+
+vproc_t vproc_retain(vproc_t vp)
+{
+ int32_t orig = OSAtomicAdd32(1, &vp->refcount) - 1;
+ if( orig <= 0 ) {
+ /* We've gone from 0 to 1, which means that this object was due to be freed. */
+ __crashreporter_info__ = "Under-retain / over-release of vproc_t.";
+ abort();
+ }
+
+ return vp;
+}
+
+void vproc_release(vproc_t vp)
+{
+ int32_t newval = OSAtomicAdd32(-1, &vp->refcount);
+ if( newval < 0 ) {
+ /* We're in negative numbers, which is bad. */
+ __crashreporter_info__ = "Over-release of vproc_t.";
+ abort();
+ } else if( newval == 0 ) {
+ mach_port_deallocate(mach_task_self(), vp->j_port);
+ free(vp);
+ }
+}
+
+static void
+vproc_shmem_init(void)
+{
+ vm_address_t vm_addr = 0;
+ mach_port_t shmem_port;
+ kern_return_t kr;
+
+ kr = vproc_mig_setup_shmem(bootstrap_port, &shmem_port);
+
+ //assert(kr == 0);
+ if (kr) {
+ /* rdar://problem/6416724
+ * If we fail to set up a shared memory page, just allocate a local chunk
+ * of memory. This way, processes can still introspect their own transaction
+ * counts if they're being run under a debugger. Moral of the story: Debug
+ * from the environment you intend to run in.
+ */
+ void *_vm_addr = calloc(1, sizeof(struct vproc_shmem_s));
+ if( !_vm_addr ) {
+ return;
+ }
+
+ vm_addr = (vm_address_t)_vm_addr;
+ } else {
+ kr = vm_map(mach_task_self(), &vm_addr, getpagesize(), 0, true, shmem_port, 0, false,
+ VM_PROT_READ|VM_PROT_WRITE, VM_PROT_READ|VM_PROT_WRITE, VM_INHERIT_NONE);
+
+ //assert(kr == 0);
+ if (kr) return;
+
+ kr = mach_port_deallocate(mach_task_self(), shmem_port);
+
+ //assert(kr == 0);
+ }
+
+ vproc_shmem = (struct vproc_shmem_s *)vm_addr;
+}
+
+static void
+vproc_client_init(void)
+{
+ char *val = getenv(LAUNCHD_DO_APPLE_INTERNAL_LOGGING);
+ if( val ) {
+ if( strncmp(val, "true", sizeof("true") - 1) == 0 ) {
+ do_apple_internal_logging = true;
+ }
+ }
+
+ vproc_shmem_init();
+}
+
+vproc_transaction_t
+vproc_transaction_begin(vproc_t vp __attribute__((unused)))
+{
+ vproc_transaction_t vpt = (vproc_transaction_t)vproc_shmem_init; /* we need a "random" variable that is testable */
+#if !TARGET_OS_EMBEDDED
+ _vproc_transaction_begin();
+#endif
+
+ return vpt;
+}
+
+void
+_vproc_transaction_begin(void)
+{
+#if !TARGET_OS_EMBEDDED
+ if (unlikely(vproc_shmem == NULL)) {
+ int po_r = pthread_once(&shmem_inited, vproc_client_init);
+ if (po_r != 0 || vproc_shmem == NULL) {
+ return;
+ }
+ }
+
+ typeof(vproc_shmem->vp_shmem_transaction_cnt) old = 0;
+ do {
+ old = vproc_shmem->vp_shmem_transaction_cnt;
+
+ if (unlikely(old < 0)) {
+ if (vproc_shmem->vp_shmem_flags & VPROC_SHMEM_EXITING) {
+ _exit(0);
+ } else {
+ __crashreporter_info__ = "Unbalanced: vproc_transaction_begin()";
+ }
+ abort();
+ }
+ } while( !__sync_bool_compare_and_swap(&vproc_shmem->vp_shmem_transaction_cnt, old, old + 1) );
+
+ runtime_ktrace(RTKT_VPROC_TRANSACTION_INCREMENT, old + 1, 0, 0);
+#endif
+}
+
+size_t
+_vproc_transaction_count(void)
+{
+ return likely(vproc_shmem) ? vproc_shmem->vp_shmem_transaction_cnt : INT32_MAX;
+}
+
+size_t
+_vproc_standby_count(void)
+{
+#ifdef VPROC_STANDBY_IMPLEMENTED
+ return likely(vproc_shmem) ? vproc_shmem->vp_shmem_standby_cnt : INT32_MAX;
+#else
+ return 0;
+#endif
+}
+
+size_t
+_vproc_standby_timeout(void)
+{
+ return likely(vproc_shmem) ? vproc_shmem->vp_shmem_standby_timeout : 0;
+}
+
+bool
+_vproc_pid_is_managed(pid_t p)
+{
+ boolean_t result = false;
+ vproc_mig_pid_is_managed(bootstrap_port, p, &result);
+
+ return result;
+}
+
+kern_return_t
+_vproc_transaction_count_for_pid(pid_t p, int32_t *count, bool *condemned)
+{
+ boolean_t _condemned = false;
+ kern_return_t kr = vproc_mig_transaction_count_for_pid(bootstrap_port, p, count, &_condemned);
+ if( kr == KERN_SUCCESS && condemned ) {
+ *condemned = _condemned ? true : false;
+ }
+
+ return kr;
+}
+
+void
+#if !TARGET_OS_EMBEDDED
+_vproc_transaction_try_exit(int status)
+{
+ if (unlikely(vproc_shmem == NULL)) {
+ return;
+ }
+
+ if (__sync_bool_compare_and_swap(&vproc_shmem->vp_shmem_transaction_cnt, 0, -1)) {
+ vproc_shmem->vp_shmem_flags |= VPROC_SHMEM_EXITING;
+ _exit(status);
+ }
+}
+#else
+_vproc_transaction_try_exit(int status __attribute__((unused)))
+{
+
+}
+#endif
+
+void
+vproc_transaction_end(vproc_t vp __attribute__((unused)), vproc_transaction_t vpt)
+{
+ if (unlikely(vpt != (vproc_transaction_t)vproc_shmem_init)) {
+ __crashreporter_info__ = "Bogus transaction handle passed to vproc_transaction_end() ";
+ abort();
+ }
+
+#if !TARGET_OS_EMBEDDED
+ _vproc_transaction_end();
+#endif
+}
+
+void
+_vproc_transaction_end(void)
+{
+#if !TARGET_OS_EMBEDDED
+ typeof(vproc_shmem->vp_shmem_transaction_cnt) newval;
+
+ if (unlikely(vproc_shmem == NULL)) {
+ return;
+ }
+
+ newval = __sync_sub_and_fetch(&vproc_shmem->vp_shmem_transaction_cnt, 1);
+
+ runtime_ktrace(RTKT_VPROC_TRANSACTION_DECREMENT, newval, 0, 0);
+ if (unlikely(newval < 0)) {
+ if (vproc_shmem->vp_shmem_flags & VPROC_SHMEM_EXITING) {
+ _exit(0);
+ } else {
+ __crashreporter_info__ = "Unbalanced: vproc_transaction_end()";
+ }
+ abort();
+ }
+#endif
+}
+
+vproc_standby_t
+vproc_standby_begin(vproc_t vp __attribute__((unused)))
+{
+#ifdef VPROC_STANDBY_IMPLEMENTED
+ vproc_standby_t vpsb = (vproc_standby_t)vproc_shmem_init; /* we need a "random" variable that is testable */
+
+ _vproc_standby_begin();
+
+ return vpsb;
+#else
+ return NULL;
+#endif
+}
+
+void
+_vproc_standby_begin(void)
+{
+#ifdef VPROC_STANDBY_IMPLEMENTED
+ typeof(vproc_shmem->vp_shmem_standby_cnt) newval;
+
+ if (unlikely(vproc_shmem == NULL)) {
+ int po_r = pthread_once(&shmem_inited, vproc_client_init);
+ if (po_r != 0 || vproc_shmem == NULL) {
+ return;
+ }
+ }
+
+ newval = __sync_add_and_fetch(&vproc_shmem->vp_shmem_standby_cnt, 1);
+
+ if (unlikely(newval < 1)) {
+ __crashreporter_info__ = "Unbalanced: vproc_standby_begin()";
+ abort();
+ }
+#else
+ return;
+#endif
+}
+
+void
+vproc_standby_end(vproc_t vp __attribute__((unused)), vproc_standby_t vpt __attribute__((unused)))
+{
+#ifdef VPROC_STANDBY_IMPLEMENTED
+ if (unlikely(vpt != (vproc_standby_t)vproc_shmem_init)) {
+ __crashreporter_info__ = "Bogus standby handle passed to vproc_standby_end() ";
+ abort();
+ }
+
+ _vproc_standby_end();
+#else
+ return;
+#endif
+}
+
+void
+_vproc_standby_end(void)
+{
+#ifdef VPROC_STANDBY_IMPLEMENTED
+ typeof(vproc_shmem->vp_shmem_standby_cnt) newval;
+
+ if( unlikely(vproc_shmem == NULL) ) {
+ __crashreporter_info__ = "Process called vproc_standby_end() when not enrolled in transaction model.";
+ abort();
+ }
+
+ newval = __sync_sub_and_fetch(&vproc_shmem->vp_shmem_standby_cnt, 1);
+
+ if (unlikely(newval < 0)) {
+ __crashreporter_info__ = "Unbalanced: vproc_standby_end()";
+ abort();
+ }
+#else
+ return;
+#endif
+}
kern_return_t
_vproc_grab_subset(mach_port_t bp, mach_port_t *reqport, mach_port_t *rcvright, launch_data_t *outval,
vproc_err_t
_vproc_post_fork_ping(void)
{
- return vproc_mig_post_fork_ping(bootstrap_port, mach_task_self()) == 0 ? NULL : _vproc_post_fork_ping;
-}
-
-static void
-setup_env_hack(const launch_data_t obj, const char *key, void *context __attribute__((unused)))
-{
- setenv(key, launch_data_get_string(obj), 1);
+#if !TARGET_OS_EMBEDDED
+ au_asid_t s = AU_DEFAUDITSID;
+ do {
+ mach_port_t session = MACH_PORT_NULL;
+ kern_return_t kr = vproc_mig_post_fork_ping(bootstrap_port, mach_task_self(), &session);
+ if( kr != KERN_SUCCESS ) {
+ /* If this happens, our bootstrap port probably got hosed. */
+ _vproc_log(LOG_ERR, "Post-fork ping failed!");
+ break;
+ }
+
+ /* If we get back MACH_PORT_NULL, that means we just stick with the session
+ * we inherited across fork(2).
+ */
+ if( session == MACH_PORT_NULL ) {
+ s = ~AU_DEFAUDITSID;
+ break;
+ }
+
+ s = _audit_session_join(session);
+ if( s == 0 ) {
+ _vproc_log_error(LOG_ERR, "Could not join security session!");
+ s = AU_DEFAUDITSID;
+ } else {
+ _vproc_log(LOG_DEBUG, "Joined session %d.", s);
+ }
+ } while( 0 );
+
+ return s != AU_DEFAUDITSID ? NULL : _vproc_post_fork_ping;
+#else
+ mach_port_t session = MACH_PORT_NULL;
+ return vproc_mig_post_fork_ping(bootstrap_port, mach_task_self(), &session) ? _vproc_post_fork_ping : NULL;
+#endif
}
vproc_err_t
_vprocmgr_init(const char *session_type)
{
- if (vproc_mig_move_subset(bootstrap_port, MACH_PORT_NULL, (char *)session_type) == 0) {
+ if (vproc_mig_init_session(bootstrap_port, (char *)session_type, _audit_session_self()) == 0) {
return NULL;
}
}
vproc_err_t
-_vprocmgr_move_subset_to_user(uid_t target_user, const char *session_type)
+_vprocmgr_move_subset_to_user(uid_t target_user, const char *session_type, uint64_t flags)
{
- launch_data_t output_obj;
kern_return_t kr = 0;
bool is_bkgd = (strcmp(session_type, VPROCMGR_SESSION_BACKGROUND) == 0);
int64_t ldpid, lduid;
return (vproc_err_t)_vprocmgr_move_subset_to_user;
}
- if (is_bkgd || target_user) {
- mach_port_t puc = 0, rootbs = get_root_bootstrap_port();
-
- if (vproc_mig_lookup_per_user_context(rootbs, target_user, &puc) != 0) {
- return (vproc_err_t)_vprocmgr_move_subset_to_user;
- }
-
- if (is_bkgd) {
- task_set_bootstrap_port(mach_task_self(), puc);
- mach_port_deallocate(mach_task_self(), bootstrap_port);
- bootstrap_port = puc;
- } else {
- kr = vproc_mig_move_subset(puc, bootstrap_port, (char *)session_type);
- mach_port_deallocate(mach_task_self(), puc);
- }
+ mach_port_t puc = 0, rootbs = get_root_bootstrap_port();
+
+ if (vproc_mig_lookup_per_user_context(rootbs, target_user, &puc) != 0) {
+ return (vproc_err_t)_vprocmgr_move_subset_to_user;
+ }
+
+ if( is_bkgd ) {
+ task_set_bootstrap_port(mach_task_self(), puc);
+ mach_port_deallocate(mach_task_self(), bootstrap_port);
+ bootstrap_port = puc;
} else {
- kr = _vprocmgr_init(session_type) ? 1 : 0;
+ kr = vproc_mig_move_subset(puc, bootstrap_port, (char *)session_type, _audit_session_self(), flags);
+ mach_port_deallocate(mach_task_self(), puc);
}
-
+
cached_pid = -1;
if (kr) {
return (vproc_err_t)_vprocmgr_move_subset_to_user;
}
- /* XXX We need to give 'nohup' a better API after Leopard ships */
- if (getprogname() && strcmp(getprogname(), "nohup") != 0) {
- if (vproc_swap_complex(NULL, VPROC_GSK_ENVIRONMENT, NULL, &output_obj) == NULL) {
- if (launch_data_get_type(output_obj) == LAUNCH_DATA_DICTIONARY) {
- launch_data_dict_iterate(output_obj, setup_env_hack, NULL);
- launch_data_free(output_obj);
- }
- }
- }
-
return _vproc_post_fork_ping();
}
+vproc_err_t
+_vprocmgr_switch_to_session(const char *target_session, vproc_flags_t flags __attribute__((unused)))
+{
+ mach_port_t new_bsport = MACH_PORT_NULL;
+ kern_return_t kr = KERN_FAILURE;
+
+ mach_port_t tnp = MACH_PORT_NULL;
+ task_name_for_pid(mach_task_self(), getpid(), &tnp);
+ if( (kr = vproc_mig_switch_to_session(bootstrap_port, tnp, (char *)target_session, _audit_session_self(), &new_bsport)) != KERN_SUCCESS ) {
+ _vproc_log(LOG_NOTICE, "_vprocmgr_switch_to_session(): kr = 0x%x", kr);
+ return (vproc_err_t)_vprocmgr_switch_to_session;
+ }
+
+ task_set_bootstrap_port(mach_task_self(), new_bsport);
+ mach_port_deallocate(mach_task_self(), bootstrap_port);
+ bootstrap_port = new_bsport;
+
+ return !issetugid() ? _vproc_post_fork_ping() : NULL;
+}
+
+vproc_err_t
+_vprocmgr_detach_from_console(vproc_flags_t flags __attribute__((unused)))
+{
+ return _vprocmgr_switch_to_session(VPROCMGR_SESSION_BACKGROUND, 0);
+}
pid_t
_spawn_via_launchd(const char *label, const char *const *argv, const struct spawn_via_launchd_attr *spawn_attrs, int struct_version)
indata = (vm_offset_t)buf;
- kr = vproc_mig_spawn(bootstrap_port, indata, indata_cnt, &p, &obsvr_port);
+ kr = vproc_mig_spawn(bootstrap_port, indata, indata_cnt, _audit_session_self(), &p, &obsvr_port);
if (kr == VPROC_ERR_TRY_PER_USER) {
mach_port_t puc;
if (vproc_mig_lookup_per_user_context(bootstrap_port, 0, &puc) == 0) {
- kr = vproc_mig_spawn(puc, indata, indata_cnt, &p, &obsvr_port);
+ kr = vproc_mig_spawn(puc, indata, indata_cnt, _audit_session_self(), &p, &obsvr_port);
mach_port_deallocate(mach_task_self(), puc);
}
}
}
kern_return_t
-mpm_wait(mach_port_t ajob __attribute__((unused)), int *wstatus)
+mpm_wait(mach_port_t ajob, int *wstatus)
{
return vproc_mig_wait(ajob, wstatus);
}
{
mach_msg_type_number_t outdata_cnt, tmp_cnt;
vm_offset_t outdata = 0;
+ struct timeval tv;
struct logmsg_s *lm;
if (!func) {
}
for (lm = (struct logmsg_s *)outdata; tmp_cnt > 0; lm = ((void *)lm + lm->obj_sz)) {
- lm->from_name += (size_t)lm;
- lm->about_name += (size_t)lm;
- lm->msg += (size_t)lm;
- lm->session_name += (size_t)lm;
+ lm->from_name = (char *)lm + lm->from_name_offset;
+ lm->about_name = (char *)lm + lm->about_name_offset;
+ lm->msg = (char *)lm + lm->msg_offset;
+ lm->session_name = (char *)lm + lm->session_name_offset;
+
+ tv.tv_sec = lm->when / USEC_PER_SEC;
+ tv.tv_usec = lm->when % USEC_PER_SEC;
- func(&lm->when, lm->from_pid, lm->about_pid, lm->sender_uid, lm->sender_gid, lm->pri,
+ func(&tv, lm->from_pid, lm->about_pid, lm->sender_uid, lm->sender_gid, lm->pri,
lm->from_name, lm->about_name, lm->session_name, lm->msg);
tmp_cnt -= lm->obj_sz;
}
vproc_err_t
-vproc_swap_integer(vproc_t vp __attribute__((unused)), vproc_gsk_t key, int64_t *inval, int64_t *outval)
+vproc_swap_integer(vproc_t vp, vproc_gsk_t key, int64_t *inval, int64_t *outval)
{
static int64_t cached_is_managed = -1;
int64_t dummyval = 0;
return NULL;
}
break;
+ case VPROC_GSK_TRANSACTIONS_ENABLED:
+ /* Shared memory region is required for transactions. */
+ if( unlikely(vproc_shmem == NULL) ) {
+ int po_r = pthread_once(&shmem_inited, vproc_client_init);
+ if( po_r != 0 || vproc_shmem == NULL ) {
+ if( outval ) {
+ *outval = -1;
+ }
+ return (vproc_err_t)vproc_swap_integer;
+ }
+ }
+
+ if( s_cached_transactions_enabled && outval ) {
+ *outval = s_cached_transactions_enabled;
+ return NULL;
+ }
+ break;
default:
break;
}
- if (vproc_mig_swap_integer(bootstrap_port, inval ? key : 0, outval ? key : 0, inval ? *inval : 0, outval ? outval : &dummyval) == 0) {
+ kern_return_t kr = KERN_FAILURE;
+ mach_port_t mp = vp ? vp->j_port : bootstrap_port;
+ if ((kr = vproc_mig_swap_integer(mp, inval ? key : 0, outval ? key : 0, inval ? *inval : 0, outval ? outval : &dummyval)) == 0) {
switch (key) {
case VPROC_GSK_MGR_PID:
cached_pid = outval ? *outval : dummyval;
case VPROC_GSK_IS_MANAGED:
cached_is_managed = outval ? *outval : dummyval;
break;
+ case VPROC_GSK_TRANSACTIONS_ENABLED:
+ /* Once you're in the transaction model, you're in for good. Like the Mafia. */
+ s_cached_transactions_enabled = 1;
+ break;
+ case VPROC_GSK_PERUSER_SUSPEND: {
+ char peruser_label[NAME_MAX];
+ snprintf(peruser_label, NAME_MAX - 1, "com.apple.launchd.peruser.%u", (uid_t)*inval);
+
+ vproc_t pu_vp = vprocmgr_lookup_vproc(peruser_label);
+ if( pu_vp ) {
+ int status = 0;
+ kr = vproc_mig_wait2(bootstrap_port, pu_vp->j_port, &status, false);
+ vproc_release(pu_vp);
+ }
+ break;
+ }
default:
break;
}
}
vproc_err_t
-vproc_swap_complex(vproc_t vp __attribute__((unused)), vproc_gsk_t key, launch_data_t inval, launch_data_t *outval)
+vproc_swap_complex(vproc_t vp, vproc_gsk_t key, launch_data_t inval, launch_data_t *outval)
{
size_t data_offset = 0, good_enough_size = 10*1024*1024;
mach_msg_type_number_t indata_cnt = 0, outdata_cnt;
indata = (vm_offset_t)buf;
}
- if (vproc_mig_swap_complex(bootstrap_port, inval ? key : 0, outval ? key : 0, indata, indata_cnt, &outdata, &outdata_cnt) != 0) {
+ mach_port_t mp = vp ? vp->j_port : bootstrap_port;
+ if (vproc_mig_swap_complex(mp, inval ? key : 0, outval ? key : 0, indata, indata_cnt, &outdata, &outdata_cnt) != 0) {
goto out;
}
return rval;
}
+vproc_err_t
+vproc_swap_string(vproc_t vp, vproc_gsk_t key, const char *instr, char **outstr)
+{
+ launch_data_t instr_data = instr ? launch_data_new_string(instr) : NULL;
+ launch_data_t outstr_data = NULL;
+
+ vproc_err_t verr = vproc_swap_complex(vp, key, instr_data, &outstr_data);
+ if( !verr && outstr ) {
+ if( launch_data_get_type(outstr_data) == LAUNCH_DATA_STRING ) {
+ *outstr = strdup(launch_data_get_string(outstr_data));
+ } else {
+ verr = (vproc_err_t)vproc_swap_string;
+ }
+ launch_data_free(outstr_data);
+ }
+ if( instr_data ) {
+ launch_data_free(instr_data);
+ }
+
+ return verr;
+}
+
void *
reboot2(uint64_t flags)
{
}
vproc_err_t
-_vproc_kickstart_by_label(const char *label, pid_t *out_pid, mach_port_t *out_port_name)
+_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)
{
- if (vproc_mig_embedded_kickstart(bootstrap_port, (char *)label, out_pid, out_port_name) == 0) {
+ mach_port_t junk = MACH_PORT_NULL;
+ mach_port_t junk2 = MACH_PORT_NULL;
+
+ kern_return_t kr = vproc_mig_kickstart(bootstrap_port, (char *)label, out_pid, out_port_name ?: &junk, out_obsrvr_port ?: &junk2, flags);
+ if( kr == KERN_SUCCESS ) {
+ if( !out_port_name ) {
+ mach_port_mod_refs(mach_task_self(), junk, MACH_PORT_RIGHT_SEND, -1);
+ }
+
+ if( !out_obsrvr_port ) {
+ mach_port_mod_refs(mach_task_self(), junk2, MACH_PORT_RIGHT_SEND, -1);
+ }
+
return NULL;
}