Esempio n. 1
0
/* Implement proc_execdata_notify as described in <hurd/process.defs>. */
kern_return_t
S_proc_execdata_notify (struct proc *p,
			mach_port_t notify)
{
  struct execdata_notify *n;
  mach_port_t foo;

  /* No need to check P here; we don't use it. */

  n = malloc (sizeof (struct execdata_notify));
  if (! n)
    return ENOMEM;

  n->notify_port = notify;
  n->next = execdata_notifys;
  execdata_notifys = n;

  mach_port_request_notification (mach_task_self (), notify,
				  MACH_NOTIFY_DEAD_NAME, 1,
				  generic_port, MACH_MSG_TYPE_MAKE_SEND_ONCE,
				  &foo);

  if (foo)
    mach_port_deallocate (mach_task_self (), foo);

  if (std_port_array)
    exec_setexecdata (n->notify_port, std_port_array, MACH_MSG_TYPE_COPY_SEND,
		      n_std_ports, std_int_array, n_std_ints);
  return 0;
}
Esempio n. 2
0
void InstallExceptionHandler()
{
	mach_port_t port;
	CheckKR("mach_port_allocate", mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port));
	std::thread exc_thread(ExceptionThread, port);
	exc_thread.detach();
	// Obtain a send right for thread_set_exception_ports to copy...
	CheckKR("mach_port_insert_right", mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND));
	// Mach tries the following exception ports in order: thread, task, host.
	// Debuggers set the task port, so we grab the thread port.
	CheckKR("thread_set_exception_ports", thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS, port, EXCEPTION_STATE | MACH_EXCEPTION_CODES, x86_THREAD_STATE64));
	// ...and get rid of our copy so that MACH_NOTIFY_NO_SENDERS works.
	CheckKR("mach_port_mod_refs", mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1));
	mach_port_t previous;
	CheckKR("mach_port_request_notification", mach_port_request_notification(mach_task_self(), port, MACH_NOTIFY_NO_SENDERS, 0, port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous));
}
Esempio n. 3
0
bool xnu_create_exception_thread(RDebug *dbg) {
#if __POWERPC__
	return false;
#else
	kern_return_t kr;
	mach_port_t exception_port = MACH_PORT_NULL;
	mach_port_t req_port;
        // Got the mach port for the current process
	mach_port_t task_self = mach_task_self ();
	task_t task = pid_to_task (dbg->pid);
	if (!task) {
		eprintf ("error to get task for the debuggee process"
			" xnu_start_exception_thread\n");
		return false;
	}
	if (!MACH_PORT_VALID (task_self)) {
		eprintf ("error to get the task for the current process"
			" xnu_start_exception_thread\n");
		return false;
	}
        // Allocate an exception port that we will use to track our child process
        kr = mach_port_allocate (task_self, MACH_PORT_RIGHT_RECEIVE,
				&exception_port);
	RETURN_ON_MACH_ERROR ("error to allocate mach_port exception\n", false);
        // Add the ability to send messages on the new exception port
	kr = mach_port_insert_right (task_self, exception_port, exception_port,
				     MACH_MSG_TYPE_MAKE_SEND);
	RETURN_ON_MACH_ERROR ("error to allocate insert right\n", false);
	// Atomically swap out (and save) the child process's exception ports
	// for the one we just created. We'll want to receive all exceptions.
	ex.count = (sizeof (ex.ports) / sizeof (*ex.ports));
	kr = task_swap_exception_ports (task, EXC_MASK_ALL, exception_port,
		EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE,
		ex.masks, &ex.count, ex.ports, ex.behaviors, ex.flavors);
	RETURN_ON_MACH_ERROR ("failed to swap exception ports\n", false);
	//get notification when process die
	kr = mach_port_request_notification (task_self, task, MACH_NOTIFY_DEAD_NAME,
		 0, exception_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &req_port);
	if (kr != KERN_SUCCESS) {
		eprintf ("Termination notification request failed\n");
	}
	ex.exception_port = exception_port;
	return true;
#endif
}
Esempio n. 4
0
void
ports_no_senders (void *portstruct,
		  mach_port_mscount_t mscount)
{
  struct port_info *pi = portstruct;
  int dealloc;
  mach_port_t old;

  pthread_mutex_lock (&_ports_lock);
  if ((pi->flags & PORT_HAS_SENDRIGHTS) == 0)
    {
      pthread_mutex_unlock (&_ports_lock);
      return;
    }
  if (mscount >= pi->mscount)
    {
      dealloc = 1;
      pi->flags &= ~PORT_HAS_SENDRIGHTS;
    }
  else
    {
      /* Request a new notification.  The sync value is because
       we might have accounted for a new sender but not actually
       made the send right yet.  */
      mach_port_request_notification (mach_task_self (), pi->port_right,
				      MACH_NOTIFY_NO_SENDERS, pi->mscount,
				      pi->port_right,
				      MACH_MSG_TYPE_MAKE_SEND_ONCE, &old);
      if (old)
	mach_port_deallocate (mach_task_self (), old);
      dealloc = 0;
    }
  pthread_mutex_unlock (&_ports_lock);
  
  if (dealloc)
    {
      ports_interrupt_notified_rpcs (portstruct, pi->port_right,
				     MACH_NOTIFY_NO_SENDERS);
      ports_interrupt_rpcs (pi);
      ports_port_deref (pi);
    }
}
Esempio n. 5
0
kern_return_t k5_ipc_server_create_client_connection (mach_port_t    in_server_port,
                                                      mach_port_t   *out_connection_port)
{
    kern_return_t err = KERN_SUCCESS;
    mach_port_t connection_port = MACH_PORT_NULL;
    mach_port_t old_notification_target = MACH_PORT_NULL;

    if (!err) {
        err = mach_port_allocate (mach_task_self (),
                                  MACH_PORT_RIGHT_RECEIVE, &connection_port);
    }

    if (!err) {
        err = mach_port_move_member (mach_task_self (),
                                     connection_port, g_listen_port_set);
    }

    if (!err) {
        /* request no-senders notification so we can tell when client quits/crashes */
        err = mach_port_request_notification (mach_task_self (),
                                              connection_port,
                                              MACH_NOTIFY_NO_SENDERS, 1,
                                              connection_port,
                                              MACH_MSG_TYPE_MAKE_SEND_ONCE,
                                              &old_notification_target );
    }

    if (!err) {
        err = k5_ipc_server_add_client (connection_port);
    }

    if (!err) {
        *out_connection_port = connection_port;
        connection_port = MACH_PORT_NULL;
    }

    if (MACH_PORT_VALID (connection_port)) { mach_port_deallocate (mach_task_self (), connection_port); }

    return err;
}
Esempio n. 6
0
int main(void) {
    ERR_FILE = fopen("/tmp/FortunateSonErrorFile.txt", "a");
    fprintf(ERR_FILE, "Started with pid %d\n", getpid());
    
    mach_port_t parent_recv_port = get_hex_fiend_receive_port();
    
    // Get notified when the parent receive port dies
    mach_port_t my_task = mach_task_self();
    mach_port_t notificationPort = MACH_PORT_NULL;
    CHECK_MACH_ERROR(mach_port_allocate(my_task, MACH_PORT_RIGHT_RECEIVE, &notificationPort));
    mach_port_t old;
    CHECK_MACH_ERROR(mach_port_request_notification(my_task, parent_recv_port, MACH_NOTIFY_NO_SENDERS, 0/*sync*/, notificationPort, MACH_MSG_TYPE_MAKE_SEND_ONCE, &old));
    
    /* Make a port set */
    mach_port_t portSet = MACH_PORT_NULL;
    CHECK_MACH_ERROR(mach_port_allocate(my_task, MACH_PORT_RIGHT_PORT_SET, &portSet));
    CHECK_MACH_ERROR(mach_port_insert_member(my_task, parent_recv_port, portSet));
    CHECK_MACH_ERROR(mach_port_insert_member(my_task, notificationPort, portSet));
    run_server(portSet, notificationPort);
    
    /* Once run_server returns, we're done, so exit */
    return 0;
}
Esempio n. 7
0
/* Try and start the translator for CTL_PORT on NODE.  If successful, this
   call will not return until the translator is stopped; otherwise it returns
   an error code describing the reason why it couldn't start.  */
static error_t
start_translator(file_t node, fsys_t ctl_port)
{
  mach_port_t prev;
  error_t err =
    file_set_translator(node, 0, FS_TRANS_EXCL, 0, NULL, 0, ctl_port);

  if (err)
    return err;

  /* Set up a watch on the process we're interested in.  */
  err =
    mach_port_request_notification(mach_task_self(),
				   watched_process, MACH_NOTIFY_DEAD_NAME, 1,
				   node,
				   MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev);
  if (prev)
    mach_port_deallocate(mach_task_self(), prev);

  /* Boogie. */
  ports_manage_port_operations_onethread();

  return 0;
}
Esempio n. 8
0
mach_port_t Port::requestNotify(mach_port_t notify, mach_msg_id_t type, mach_port_mscount_t sync)
{
    mach_port_t previous;
    check(mach_port_request_notification(self(), mPort, type, sync, notify,
        MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous));

#if !defined(NDEBUG)
	const char *typeName;
	switch (type) {
	case MACH_NOTIFY_PORT_DELETED:	typeName = "port deleted"; break;
	case MACH_NOTIFY_PORT_DESTROYED:typeName = "port destroyed"; break;
	case MACH_NOTIFY_NO_SENDERS:	typeName = "no senders"; break;
	case MACH_NOTIFY_SEND_ONCE:		typeName = "send once"; break;
	case MACH_NOTIFY_DEAD_NAME:		typeName = "dead name"; break;
	default:						typeName = "???"; break;
	}
	if (notify == MACH_PORT_NULL)
		secdebug("port", "%d cancel notify %s", port(), typeName);
	else
		secdebug("port", "%d request notify %s to %d (sync %d)", port(), typeName, notify, sync);
#endif //!NDEBUG

    return previous;
}
/* Arrange for hurd_cancel to be called on RPC's thread if OBJECT gets notified
   that any of the things in COND have happened to PORT.  RPC should be an
   rpc on OBJECT.  */
error_t
ports_interrupt_rpc_on_notification (void *object,
				     struct rpc_info *rpc,
				     mach_port_t port, mach_msg_id_t what)
{
  int req_notify;
  struct ports_notify *pn;
  struct rpc_notify *new_req, *req;
  struct port_info *pi = object;

  mutex_lock (&_ports_lock);

  if (! MACH_PORT_VALID (port))
    /* PORT is already dead or bogus, so interrupt the rpc immediately.  */
    {
      hurd_thread_cancel (rpc->thread);
      mutex_unlock (&_ports_lock);
      return 0;
    }

  new_req = _ports_free_rpc_notifies;
  if (new_req)
    /* We got a req off the free list.  */
    _ports_free_rpc_notifies = new_req->next;
  else
    /* No free notify structs, allocate one; it's expected that 99% of the
       time we'll add a new structure, so we malloc while we don't have the
       lock, and free it if we're wrong.  */
    {
      mutex_unlock (&_ports_lock); /* Don't hold the lock during malloc. */
      new_req = malloc (sizeof (struct rpc_notify));
      if (! new_req)
	return ENOMEM;
      mutex_lock (&_ports_lock);
    }

  /* Find any existing entry for PORT/WHAT.  */
  for (pn = _ports_notifications; pn; pn = pn->next)
    if (pn->port == port && pn->what == what)
      break;

  if (! pn)
    /* A notification on a new port.  */
    {
      pn = _ports_free_ports_notifies;

      if (pn)
	_ports_free_ports_notifies = pn->next;
      else
	{
	  pn = malloc (sizeof (struct ports_notify));
	  if (! pn)
	    /* sigh.  Free what we've alloced and return.  */
	    {
	      new_req->next = _ports_free_rpc_notifies;
	      _ports_free_rpc_notifies = new_req;
	      mutex_unlock (&_ports_lock);
	      return ENOMEM;
	    }
	}

      pn->reqs = 0;
      pn->port = port;
      pn->what = what;
      pn->pending = 0;
      mutex_init (&pn->lock);

      pn->next = _ports_notifications;
      pn->prevp = &_ports_notifications;
      if (_ports_notifications)
	_ports_notifications->prevp = &pn->next;
      _ports_notifications = pn;
    }

  for (req = rpc->notifies; req; req = req->next)
    if (req->notify == pn)
      break;

  if (req)
    /* REQ is already pending for PORT/WHAT on RPC, so free NEW_REQ.  */
    {
      new_req->next = _ports_free_rpc_notifies;
      _ports_free_rpc_notifies = new_req;
    }
  else
    /* Add a new request for PORT/WHAT on RPC.  */
    {
      req = new_req;

      req->rpc = rpc;
      req->notify = pn;
      req->pending = 0;

      req->next_req = pn->reqs;
      req->prev_req_p = &pn->reqs;
      if (pn->reqs)
	pn->reqs->prev_req_p = &req->next_req;
      pn->reqs = req;

      req->next = rpc->notifies;
      rpc->notifies = req;
    }

  /* Make sure that this request results in an interrupt.  */
  req->pending++;

  /* Find out whether we should request a new notification (after we release
     _PORTS_LOCK) -- PN may be new, or left over after a previous
     notification (in which case our new request is likely to trigger an
     immediate notification).  */
  req_notify = !pn->pending;
  if (req_notify)
    mutex_lock (&pn->lock);

  mutex_unlock (&_ports_lock);

  if (req_notify)
    {
      mach_port_t old;
      error_t err =
	mach_port_request_notification (mach_task_self (), port,
					what, 1, pi->port_right,
					MACH_MSG_TYPE_MAKE_SEND_ONCE, &old);

      if (! err && old != MACH_PORT_NULL)
	mach_port_deallocate (mach_task_self (), old);

      pn->pending = 1;
      mutex_unlock (&pn->lock);

      return err;
    }
  else
    return 0;
}
Esempio n. 10
0
void
deactivate_bootstrap(bootstrap_info_t *bootstrap)
{
	bootstrap_info_t *deactivating_bootstraps;
	bootstrap_info_t *query_bootstrap;
	bootstrap_info_t *next_limit;
	bootstrap_info_t *limit;

	/*
	 * we need to recursively deactivate the whole subset tree below
	 * this point.  But we don't want to do real recursion because
	 * we don't have a limit on the depth.  So, build up a chain of
	 * active bootstraps anywhere underneath this one.
	 */
	deactivating_bootstraps = bootstrap;
	bootstrap->deactivate = NULL;
	for (next_limit = deactivating_bootstraps, limit = NULL
			 ; deactivating_bootstraps != limit
			 ; limit = next_limit, next_limit = deactivating_bootstraps)
	{
		for (bootstrap = deactivating_bootstraps
				 ; bootstrap != limit
				 ; bootstrap = bootstrap->deactivate)
		{
			for (  query_bootstrap = FIRST(bootstraps)
				   ; !IS_END(query_bootstrap, bootstraps)
				   ; query_bootstrap = NEXT(query_bootstrap))
			{
				if (query_bootstrap->parent == bootstrap &&
					query_bootstrap->requestor_port != MACH_PORT_NULL) {
					mach_port_deallocate(
										 mach_task_self(),
										 query_bootstrap->requestor_port);
					query_bootstrap->requestor_port = MACH_PORT_NULL;
					query_bootstrap->deactivate = deactivating_bootstraps;
					deactivating_bootstraps = query_bootstrap;
				}
			}
		}
	}

	/*
	 * The list is ordered with the furthest away progeny being
	 * at the front, and concluding with the one we started with.
	 * This allows us to safely deactivate and remove the reference
	 * each holds on their parent without fear of the chain getting
	 * corrupted (because each active parent holds a reference on
	 * itself and that doesn't get removed until we reach its spot
	 * in the list).
	 */
	do {
		bootstrap = deactivating_bootstraps;
		deactivating_bootstraps = bootstrap->deactivate;

		info("deactivating bootstrap %x", bootstrap->bootstrap_port);

		delete_bootstrap_services(bootstrap);
		
		mach_port_deallocate(mach_task_self(), bootstrap->bootstrap_port);

		{
			mach_port_t previous;
			mach_port_request_notification(
					mach_task_self(),
					bootstrap->bootstrap_port,
					MACH_NOTIFY_NO_SENDERS,
					1,
					bootstrap->bootstrap_port,
					MACH_MSG_TYPE_MAKE_SEND_ONCE,
					&previous);
		}
	} while (deactivating_bootstraps != NULL);
}
Esempio n. 11
0
int32_t k5_ipc_send_request (const char    *in_service_id,
                             int32_t        in_launch_server,
                             k5_ipc_stream  in_request_stream,
                             k5_ipc_stream *out_reply_stream)
{
    int err = 0;
    int32_t done = 0;
    int32_t try_count = 0;
    mach_port_t server_port = MACH_PORT_NULL;
    k5_ipc_connection_info cinfo = NULL;
    k5_ipc_connection connection = NULL;
    mach_port_t reply_port = MACH_PORT_NULL;
    const char *inl_request = NULL; /* char * so we can pass the buffer in directly */
    mach_msg_type_number_t inl_request_length = 0;
    k5_ipc_ool_request_t ool_request = NULL;
    mach_msg_type_number_t ool_request_length = 0;

    if (!in_request_stream) { err = EINVAL; }
    if (!out_reply_stream ) { err = EINVAL; }
    
    if (!err) {
        err = CALL_INIT_FUNCTION (k5_cli_ipc_thread_init);
    }    
    
    if (!err) {
        /* depending on how big the message is, use the fast inline buffer or  
         * the slow dynamically allocated buffer */
        mach_msg_type_number_t request_length = k5_ipc_stream_size (in_request_stream);
        
        if (request_length > K5_IPC_MAX_INL_MSG_SIZE) {
            /*dprintf ("%s choosing out of line buffer (size is %d)", 
             *                  __FUNCTION__, request_length); */
            
            err = vm_read (mach_task_self (), 
                           (vm_address_t) k5_ipc_stream_data (in_request_stream), 
                           request_length, 
                           (vm_address_t *) &ool_request, 
                           &ool_request_length);        
        } else {
            /*dprintf ("%s choosing in line buffer (size is %d)",
             *                  __FUNCTION__, request_length); */
            
            inl_request_length = request_length;
            inl_request = k5_ipc_stream_data (in_request_stream);
        }
    }

    if (!err) {
        cinfo = k5_getspecific (K5_KEY_IPC_CONNECTION_INFO);

        if (!cinfo) {
            err = k5_ipc_client_cinfo_allocate (&cinfo);

            if (!err) {
                err = k5_setspecific (K5_KEY_IPC_CONNECTION_INFO, cinfo);
            }
        }
        
        if (!err) {
            int i, found = 0;

            for (i = 0; i < KIPC_SERVICE_COUNT; i++) {
                if (!strcmp (in_service_id, cinfo->connections[i].service_id)) {
                    found = 1;
                    connection = &cinfo->connections[i];
                    break;
                }
            }
            
            if (!found) { err = EINVAL; }
        }
    }
    
    if (!err) {
        err = k5_ipc_client_lookup_server (in_service_id, in_launch_server, 
                                           TRUE, &server_port);
    }

    if (!err) {
        err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, 
                                  &reply_port);
    }
    
    while (!err && !done) {
        if (!err && !MACH_PORT_VALID (connection->port)) {
            err = k5_ipc_client_create_client_connection (server_port, 
                                                          &connection->port);
        }
        
        if (!err) {
            err = k5_ipc_client_request (connection->port, reply_port,
                                         inl_request, inl_request_length,
                                         ool_request, ool_request_length);
            
        }
        
        if (err == MACH_SEND_INVALID_DEST) {
            if (try_count < 2) { 
                try_count++;
                err = 0;
            }

            if (MACH_PORT_VALID (connection->port)) {
                mach_port_mod_refs (mach_task_self(), connection->port, 
                                    MACH_PORT_RIGHT_SEND, -1 );
                connection->port = MACH_PORT_NULL;
            }    
            
            /* Look up server name again without using the cached copy */
            err = k5_ipc_client_lookup_server (in_service_id,  
                                               in_launch_server, 
                                               FALSE, &server_port);
            
        } else {
            /* Talked to server, though we may have gotten an error */
            done = 1;
            
            /* Because we use ",dealloc" ool_request will be freed by mach. 
            * Don't double free it. */
            ool_request = NULL; 
            ool_request_length = 0;                
        }            
    }
    
    if (!err) {
        err = k5_ipc_stream_new (&cinfo->reply_stream);
    }
    
    if (!err) {
        mach_port_t old_notification_target = MACH_PORT_NULL;

        /* request no-senders notification so we know when server dies */
        err = mach_port_request_notification (mach_task_self (), reply_port, 
                                              MACH_NOTIFY_NO_SENDERS, 1, 
                                              reply_port, 
                                              MACH_MSG_TYPE_MAKE_SEND_ONCE, 
                                              &old_notification_target);
        
        if (!err && old_notification_target != MACH_PORT_NULL) {
            mach_port_deallocate (mach_task_self (), old_notification_target);
        }
    }
    
    if (!err) {
        cinfo->server_died = 0;
        
        err = mach_msg_server_once (k5_ipc_reply_demux, K5_IPC_MAX_MSG_SIZE, 
                                    reply_port, MACH_MSG_TIMEOUT_NONE);
        
        if (!err && cinfo->server_died) {
            err = ENOTCONN;
        }
    }
    
    if (err == BOOTSTRAP_UNKNOWN_SERVICE && !in_launch_server) {
        err = 0;  /* If server is not running just return an empty stream. */
    }
    
    if (!err) {
        *out_reply_stream = cinfo->reply_stream;
        cinfo->reply_stream = NULL;
    }
 
    if (reply_port != MACH_PORT_NULL) { 
        mach_port_destroy (mach_task_self (), reply_port); 
    }
    if (ool_request_length) { 
        vm_deallocate (mach_task_self (), 
                       (vm_address_t) ool_request, ool_request_length); 
    }
    if (cinfo && cinfo->reply_stream) { 
        k5_ipc_stream_release (cinfo->reply_stream); 
        cinfo->reply_stream = NULL;
    }
    
    return err;    
}
Esempio n. 12
0
int32_t k5_ipc_server_listen_loop (void)
{
    /* Run the Mach IPC listen loop.
     * This will call k5_ipc_server_create_client_connection for new clients
     * and k5_ipc_server_request for existing clients */

    kern_return_t  err = KERN_SUCCESS;
    char          *service = NULL;
    char          *lookup = NULL;
    mach_port_t    lookup_port = MACH_PORT_NULL;
    mach_port_t    boot_port = MACH_PORT_NULL;
    mach_port_t    previous_notify_port = MACH_PORT_NULL;

    if (!err) {
        err = k5_ipc_server_get_lookup_and_service_names (&lookup, &service);
    }

    if (!err) {
        /* Get the bootstrap port */
        err = task_get_bootstrap_port (mach_task_self (), &boot_port);
    }

    if (!err) {
        /* We are an on-demand server so our lookup port already exists. */
        err = bootstrap_check_in (boot_port, lookup, &lookup_port);
    }

    if (!err) {
        /* We are an on-demand server so our service port already exists. */
        err = bootstrap_check_in (boot_port, service, &g_service_port);
    }

    if (!err) {
        /* Create the port set that the server will listen on */
        err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
                                  &g_notify_port);
    }

    if (!err) {
        /* Ask for notification when the server port has no more senders
         * A send-once right != a send right so our send-once right will
         * not interfere with the notification */
        err = mach_port_request_notification (mach_task_self (), g_service_port,
                                              MACH_NOTIFY_NO_SENDERS, true,
                                              g_notify_port,
                                              MACH_MSG_TYPE_MAKE_SEND_ONCE,
                                              &previous_notify_port);
    }

    if (!err) {
        /* Create the port set that the server will listen on */
        err = mach_port_allocate (mach_task_self (),
                                  MACH_PORT_RIGHT_PORT_SET, &g_listen_port_set);
    }

    if (!err) {
        /* Add the lookup port to the port set */
        err = mach_port_move_member (mach_task_self (),
                                     lookup_port, g_listen_port_set);
    }

    if (!err) {
        /* Add the service port to the port set */
        err = mach_port_move_member (mach_task_self (),
                                     g_service_port, g_listen_port_set);
    }

    if (!err) {
        /* Add the notify port to the port set */
        err = mach_port_move_member (mach_task_self (),
                                     g_notify_port, g_listen_port_set);
    }

    while (!err && !g_ready_to_quit) {
        /* Handle one message at a time so we can check to see if
         * the server wants to quit */
        err = mach_msg_server_once (k5_ipc_request_demux, K5_IPC_MAX_MSG_SIZE,
                                    g_listen_port_set, MACH_MSG_OPTION_NONE);
    }

    /* Clean up the ports and strings */
    if (MACH_PORT_VALID (g_notify_port)) {
        mach_port_destroy (mach_task_self (), g_notify_port);
        g_notify_port = MACH_PORT_NULL;
    }
    if (MACH_PORT_VALID (g_listen_port_set)) {
        mach_port_destroy (mach_task_self (), g_listen_port_set);
        g_listen_port_set = MACH_PORT_NULL;
    }
    if (MACH_PORT_VALID (boot_port)) {
        mach_port_deallocate (mach_task_self (), boot_port);
    }

    free (service);
    free (lookup);

    return err;
}
Esempio n. 13
0
__private_extern__
kern_return_t
_configopen(mach_port_t			server,
	    xmlData_t			nameRef,		/* raw XML bytes */
	    mach_msg_type_number_t	nameLen,
	    xmlData_t			optionsRef,		/* raw XML bytes */
	    mach_msg_type_number_t	optionsLen,
	    mach_port_t			*newServer,
	    int				*sc_status,
	    audit_token_t		audit_token)
{
	CFDictionaryRef			info;
	serverSessionRef		mySession;
	CFStringRef			name		= NULL;	/* name (un-serialized) */
	CFMutableDictionaryRef		newInfo;
	mach_port_t			oldNotify;
	CFDictionaryRef			options		= NULL;	/* options (un-serialized) */
	CFStringRef			sessionKey;
	kern_return_t 			status;
	SCDynamicStorePrivateRef	storePrivate;
	CFBooleanRef			useSessionKeys	= NULL;

	*sc_status = kSCStatusOK;

	/* un-serialize the name */
	if (!_SCUnserializeString(&name, NULL, (void *)nameRef, nameLen)) {
		*sc_status = kSCStatusFailed;
	}

	if ((optionsRef != NULL) && (optionsLen > 0)) {
		/* un-serialize the [session] options */
		if (!_SCUnserialize((CFPropertyListRef *)&options, NULL, (void *)optionsRef, optionsLen)) {
			*sc_status = kSCStatusFailed;
		}
	}

	if (*sc_status != kSCStatusOK) {
		goto done;
	}

	if (!isA_CFString(name)) {
		*sc_status = kSCStatusInvalidArgument;
		goto done;
	}

	if (options != NULL) {
		if (!isA_CFDictionary(options)) {
			*sc_status = kSCStatusInvalidArgument;
			goto done;
		}

		/*
		 * [pre-]process any provided options
		 */
		useSessionKeys = CFDictionaryGetValue(options, kSCDynamicStoreUseSessionKeys);
		if (useSessionKeys != NULL) {
			if (!isA_CFBoolean(useSessionKeys)) {
				*sc_status = kSCStatusInvalidArgument;
				goto done;
			}
		}
	}

	/*
	 * establish the new session
	 */
	mySession = addSession(server, openMPCopyDescription);
	if (mySession == NULL) {
#ifdef	DEBUG
		SCLog(TRUE, LOG_DEBUG, CFSTR("_configopen(): session is already open."));
#endif	/* DEBUG */
		*sc_status = kSCStatusFailed;	/* you can't re-open an "open" session */
		goto done;
	}

	*newServer = mySession->key;
	__MACH_PORT_DEBUG(TRUE, "*** _configopen (after addSession)", *newServer);

	/* save the audit_token in case we need to check the callers credentials */
	mySession->auditToken = audit_token;

	/* Create and add a run loop source for the port */
	mySession->serverRunLoopSource = CFMachPortCreateRunLoopSource(NULL, mySession->serverPort, 0);
	CFRunLoopAddSource(CFRunLoopGetCurrent(),
			   mySession->serverRunLoopSource,
			   kCFRunLoopDefaultMode);

	if (_configd_trace) {
		SCTrace(TRUE, _configd_trace,
			CFSTR("open    : %5d : %@\n"),
			*newServer,
			name);
	}

	*sc_status = __SCDynamicStoreOpen(&mySession->store, name);
	storePrivate = (SCDynamicStorePrivateRef)mySession->store;

	/*
	 * Make the server port accessible to the framework routines.
	 * ... and be sure to clear before calling CFRelease(store)
	 */
	storePrivate->server = *newServer;

	/*
	 * Process any provided [session] options
	 */
	if (useSessionKeys != NULL) {
		storePrivate->useSessionKeys = CFBooleanGetValue(useSessionKeys);
	}

	/* Request a notification when/if the client dies */
	status = mach_port_request_notification(mach_task_self(),
						*newServer,
						MACH_NOTIFY_NO_SENDERS,
						1,
						*newServer,
						MACH_MSG_TYPE_MAKE_SEND_ONCE,
						&oldNotify);
	if (status != KERN_SUCCESS) {
		SCLog(TRUE, LOG_ERR, CFSTR("_configopen() mach_port_request_notification() failed: %s"), mach_error_string(status));
		cleanupSession(*newServer);
		*newServer = MACH_PORT_NULL;
		*sc_status = kSCStatusFailed;
		goto done;
	}
	__MACH_PORT_DEBUG(TRUE, "*** _configopen (after mach_port_request_notification)", *newServer);

	if (oldNotify != MACH_PORT_NULL) {
		SCLog(TRUE, LOG_ERR, CFSTR("_configopen(): oldNotify != MACH_PORT_NULL"));
	}

	/*
	 * Save the name of the calling application / plug-in with the session data.
	 */
	sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), *newServer);
	info = CFDictionaryGetValue(sessionData, sessionKey);
	if (info != NULL) {
		newInfo = CFDictionaryCreateMutableCopy(NULL, 0, info);
	} else {
		newInfo = CFDictionaryCreateMutable(NULL,
						    0,
						    &kCFTypeDictionaryKeyCallBacks,
						    &kCFTypeDictionaryValueCallBacks);
	}
	CFDictionarySetValue(newInfo, kSCDName, name);
	CFDictionarySetValue(sessionData, sessionKey, newInfo);
	CFRelease(newInfo);
	CFRelease(sessionKey);

	/*
	 * Note: at this time we should be holding ONE send right and
	 *       ONE receive right to the server.  The send right is
	 *       moved to the caller.
	 */

    done :

	if (name != NULL)	CFRelease(name);
	if (options != NULL)	CFRelease(options);
	return KERN_SUCCESS;
}
Esempio n. 14
0
/*
 * kern_return_t
 * bootstrap_check_in(mach_port_t bootstrap_port,
 *	 name_t service_name,
 *	 mach_port_t *service_portp)
 *
 * Returns receive rights to service_port of service named by service_name.
 *
 * Errors:	Returns appropriate kernel errors on rpc failure.
 *		Returns BOOTSTRAP_UNKNOWN_SERVICE, if service does not exist.
 *		Returns BOOTSTRAP_SERVICE_NOT_DECLARED, if service not declared
 *			in /etc/bootstrap.conf.
 *		Returns BOOTSTRAP_SERVICE_ACTIVE, if service has already been
 *			registered or checked-in.
 */
kern_return_t
x_bootstrap_check_in(
	mach_port_t	bootstrap_port,
	name_t		service_name,
	mach_port_t	*service_portp)
{
	kern_return_t result;
	mach_port_t previous;
	service_t *servicep;
	server_t *serverp;
	bootstrap_info_t *bootstrap;

	serverp = lookup_server_by_port(bootstrap_port);
	bootstrap = lookup_bootstrap_by_port(bootstrap_port);
	debug("Service checkin attempt for service %s bootstrap %x",
	      service_name, bootstrap_port);

	servicep = lookup_service_by_name(bootstrap, service_name);
	if (servicep == NULL || servicep->port == MACH_PORT_NULL) {
		debug("bootstrap_check_in service %s unknown%s", service_name,
			forward_ok ? " forwarding" : "");
		return  forward_ok ?
			bootstrap_check_in(
					inherited_bootstrap_port,
					service_name,
					service_portp) :
			BOOTSTRAP_UNKNOWN_SERVICE;
	}
	if (servicep->server != NULL && servicep->server != serverp) {
		debug("bootstrap_check_in service %s not privileged",
			service_name);
		 return BOOTSTRAP_NOT_PRIVILEGED;
	}
	if (!canReceive(servicep->port)) {
		ASSERT(servicep->isActive);
		debug("bootstrap_check_in service %s already active",
			service_name);
		return BOOTSTRAP_SERVICE_ACTIVE;
	}
	debug("Checkin service %s for bootstrap %x", service_name,
	      bootstrap->bootstrap_port);
	ASSERT(servicep->isActive == FALSE);
	servicep->isActive = TRUE;

	if (servicep->server != NULL_SERVER) {
		/* registered server - service needs backup */
		serverp->activity++;
		serverp->active_services++;
		result = mach_port_request_notification(
					mach_task_self(),
					servicep->port,
					MACH_NOTIFY_PORT_DESTROYED,
					0,
					backup_port,
					MACH_MSG_TYPE_MAKE_SEND_ONCE,
					&previous);
		if (result != KERN_SUCCESS)
			kern_fatal(result, "mach_port_request_notification");
	} else {
		/* one time use/created service */
		servicep->servicetype = REGISTERED;
		result = mach_port_request_notification(
					mach_task_self(),
					servicep->port,
					MACH_NOTIFY_DEAD_NAME,
					0,
					notify_port,
					MACH_MSG_TYPE_MAKE_SEND_ONCE,
					&previous);
		if (result != KERN_SUCCESS)
			kern_fatal(result, "mach_port_request_notification");
		else if (previous != MACH_PORT_NULL) {
			debug("deallocating old notification port (%x) for checked in service %x",
				previous, servicep->port);
			result = mach_port_deallocate(
						mach_task_self(),
						previous);
			if (result != KERN_SUCCESS)
				kern_fatal(result, "mach_port_deallocate");
		}
	}

	info("Check-in service %x in bootstrap %x: %s",
	      servicep->port, servicep->bootstrap->bootstrap_port, servicep->name);

	*service_portp = servicep->port;
	return BOOTSTRAP_SUCCESS;
}
static void
rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode)
{
	SCDynamicStoreRef		store		= (SCDynamicStoreRef)info;
	SCDynamicStorePrivateRef	storePrivate	= (SCDynamicStorePrivateRef)store;

#ifdef	DEBUG
	SCLog(_sc_verbose, LOG_DEBUG,
	      CFSTR("schedule notifications for mode %@"),
	      (rl != NULL) ? mode : CFSTR("libdispatch"));
#endif	/* DEBUG */

	if (storePrivate->rlList == NULL) {
		CFMachPortContext	context		= { 0
							  , (void *)store
							  , CFRetain
							  , CFRelease
							  , notifyMPCopyDescription
							  };
		mach_port_t		oldNotify;
		mach_port_t		port;
		int			sc_status;
		kern_return_t		status;

#ifdef	DEBUG
		SCLog(_sc_verbose, LOG_DEBUG, CFSTR("  activate callback runloop source"));
#endif	/* DEBUG */

		/* Allocating port (for server response) */
		status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
		if (status != KERN_SUCCESS) {
			SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule mach_port_allocate(): %s"), mach_error_string(status));
			return;
		}

		status = mach_port_insert_right(mach_task_self(),
						port,
						port,
						MACH_MSG_TYPE_MAKE_SEND);
		if (status != KERN_SUCCESS) {
			/*
			 * We can't insert a send right into our own port!  This should
			 * only happen if someone stomped on OUR port (so let's leave
			 * the port alone).
			 */
			SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule mach_port_insert_right(): %s"), mach_error_string(status));
			return;
		}

		/* Request a notification when/if the server dies */
		status = mach_port_request_notification(mach_task_self(),
							port,
							MACH_NOTIFY_NO_SENDERS,
							1,
							port,
							MACH_MSG_TYPE_MAKE_SEND_ONCE,
							&oldNotify);
		if (status != KERN_SUCCESS) {
			/*
			 * We can't request a notification for our own port!  This should
			 * only happen if someone stomped on OUR port (so let's leave
			 * the port alone).
			 */
			SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule mach_port_request_notification(): %s"), mach_error_string(status));
			return;
		}

		if (oldNotify != MACH_PORT_NULL) {
			SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule(): oldNotify != MACH_PORT_NULL"));
		}

	    retry :

		__MACH_PORT_DEBUG(TRUE, "*** rlsSchedule", port);
		status = notifyviaport(storePrivate->server, port, 0, (int *)&sc_status);

		if (__SCDynamicStoreCheckRetryAndHandleError(store,
							     status,
							     &sc_status,
							     "rlsSchedule notifyviaport()")) {
			goto retry;
		}

		if (status != KERN_SUCCESS) {
			if ((status == MACH_SEND_INVALID_DEST) || (status == MIG_SERVER_DIED)) {
				/* remove the send right that we tried (but failed) to pass to the server */
				(void) mach_port_deallocate(mach_task_self(), port);
			}

			/* remove our receive right  */
			(void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
			return;
		}

		if (sc_status != kSCStatusOK) {
			/* something [else] didn't work, remove our receive right  */
			(void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
			return;
		}

		__MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after notifyviaport)", port);
		storePrivate->rlsNotifyPort = _SC_CFMachPortCreateWithPort("SCDynamicStore",
									   port,
									   rlsCallback,
									   &context);
		storePrivate->rlsNotifyRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->rlsNotifyPort, 0);

		storePrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
	}

	if ((rl != NULL) && (storePrivate->rlsNotifyRLS != NULL)) {
		if (!_SC_isScheduled(store, rl, mode, storePrivate->rlList)) {
			/*
			 * if we are not already scheduled with this runLoop / runLoopMode
			 */
			CFRunLoopAddSource(rl, storePrivate->rlsNotifyRLS, mode);
			__MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after CFRunLoopAddSource)", CFMachPortGetPort(storePrivate->rlsNotifyPort));
		}

		_SC_schedule(store, rl, mode, storePrivate->rlList);
	}

	return;
}
Esempio n. 16
0
/* -----------------------------------------------------------------------------
----------------------------------------------------------------------------- */
__private_extern__
kern_return_t
_pppcontroller_attach_proxy(mach_port_t server,
							xmlData_t nameRef,		/* raw XML bytes */
							mach_msg_type_number_t nameLen,
							mach_port_t bootstrap,
							mach_port_t notify,
							mach_port_t au_session,
							int uid,
							int gid,
							int pid,
							mach_port_t *session,
							int * result,
							audit_token_t audit_token)
{
	CFStringRef			serviceID = NULL;
	CFMachPortRef		port = NULL;
	CFRunLoopSourceRef  rls = NULL;
	struct client		*client = NULL;
	mach_port_t			oldport;
	uid_t				audit_euid = -1;
	gid_t				audit_egid = -1;
	pid_t				audit_pid = -1;
	
	*session = MACH_PORT_NULL;
	/* un-serialize the serviceID */
	if (!_SCUnserializeString(&serviceID, NULL, (void *)nameRef, nameLen)) {
		*result = kSCStatusFailed;
		goto failed;
	}

	if (!isA_CFString(serviceID)) {
		*result = kSCStatusInvalidArgument;
		goto failed;
	}

	/* only allow "root" callers to change the client uid/gid/pid */
	audit_token_to_au32(audit_token,
						NULL,			// auidp
						&audit_euid,	// euid
						&audit_egid,	// egid
						NULL,			// ruid
						NULL,			// rgid
						&audit_pid,		// pid
						NULL,			// asid
						NULL);			// tid

    if ((audit_euid != 0) &&
        ((uid != audit_euid) || (gid != audit_egid) || (pid != audit_pid))) {
        /*
         * the caller is NOT "root" and is trying to masquerade
         * as some other user/process.
         */
        
        /* does caller has the right entitlement */
        if (!(hasEntitlement(audit_token, kSCVPNConnectionEntitlementName, NULL))){
           *result = kSCStatusAccessError;
            goto failed;
        }
    }
    
	
	//if ((findbyserviceID(serviceID)) == 0) {
	//	*result = kSCStatusInvalidArgument;
	//	goto failed;
	//}

	/* allocate session port */
	(void) mach_port_allocate(mach_task_self(),
							  MACH_PORT_RIGHT_RECEIVE,
							  session);

    /*
     * Note: we create the CFMachPort *before* we insert a send
     *       right present to ensure that CF does not establish
     *       it's dead name notification.
     */
	port = _SC_CFMachPortCreateWithPort("PPPController/PPP", *session, server_handle_request, NULL);

    /* insert send right that will be moved to the client */
	(void) mach_port_insert_right(mach_task_self(),
								  *session,
								  *session,
								  MACH_MSG_TYPE_MAKE_SEND);

	/* Request a notification when/if the client dies */
	(void) mach_port_request_notification(mach_task_self(),
										  *session,
										  MACH_NOTIFY_NO_SENDERS,
										  1,
										  *session,
										  MACH_MSG_TYPE_MAKE_SEND_ONCE,
										  &oldport);

	/* add to runloop */
	rls = CFMachPortCreateRunLoopSource(NULL, port, 0);
	CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);

	if (au_session != MACH_PORT_NULL) {
		if ((audit_session_join(au_session)) == AU_DEFAUDITSID) {
			SCLog(TRUE, LOG_ERR, CFSTR("_pppcontroller_attach audit_session_join fails"));
		}
	}else {
		SCLog(TRUE, LOG_ERR, CFSTR("_pppcontroller_attach au_session == NULL"));
	}

	client = client_new_mach(port, rls, serviceID, uid, gid, pid, bootstrap, notify, au_session);
	if (client == 0) {
		*result = kSCStatusFailed;
		goto failed;
	}

	*result = kSCStatusOK;
	
	my_CFRelease(&serviceID);
	my_CFRelease(&port);
	my_CFRelease(&rls);
    return KERN_SUCCESS;
	
 failed:
	my_CFRelease(&serviceID);
	if (port) {
		CFMachPortInvalidate(port);
		my_CFRelease(&port);
	}
	if (rls) {
		CFRunLoopRemoveSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
		my_CFRelease(&rls);
	}
	if (*session != MACH_PORT_NULL) {
		mach_port_mod_refs(mach_task_self(), *session, MACH_PORT_RIGHT_SEND   , -1);
		mach_port_mod_refs(mach_task_self(), *session, MACH_PORT_RIGHT_RECEIVE, -1);
		*session = MACH_PORT_NULL;
	}
	if (client) {
		client_dispose(client);
	} else {
		if (bootstrap != MACH_PORT_NULL)
			mach_port_deallocate(mach_task_self(), bootstrap);
		if (notify != MACH_PORT_NULL)
			mach_port_deallocate(mach_task_self(), notify);
	}
    return KERN_SUCCESS;
}
Esempio n. 17
0
/*
 * kern_return_t
 * bootstrap_subset(mach_port_t bootstrap_port,
 *		    mach_port_t requestor_port,
 *		    mach_port_t *subset_port);
 *
 * Returns a new port to use as a bootstrap port.  This port behaves
 * exactly like the previous bootstrap_port, except that ports dynamically
 * registered via bootstrap_register() are available only to users of this
 * specific subset_port.  Lookups on the subset_port will return ports
 * registered with this port specifically, and ports registered with
 * ancestors of this subset_port.  Duplications of services already
 * registered with an ancestor port may be registered with the subset port
 * are allowed.  Services already advertised may then be effectively removed
 * by registering MACH_PORT_NULL for the service.
 * When it is detected that the requestor_port is destroied the subset
 * port and all services advertized by it are destroied as well.
 *
 * Errors:	Returns appropriate kernel errors on rpc failure.
 */
kern_return_t
x_bootstrap_subset(
	mach_port_t	bootstrap_port,
	mach_port_t	requestor_port,
	mach_port_t	*subset_port)
{
	kern_return_t result;
	bootstrap_info_t *bootstrap;
	bootstrap_info_t *subset;
	mach_port_t new_bootstrap_port;
	mach_port_t previous;

	debug("Subset create attempt: bootstrap %x, requestor: %x",
	      bootstrap_port, requestor_port);

	bootstrap = lookup_bootstrap_by_port(bootstrap_port);
	if (!bootstrap || !active_bootstrap(bootstrap))
		return BOOTSTRAP_NOT_PRIVILEGED;

	result = mach_port_allocate(
				mach_task_self(), 
				MACH_PORT_RIGHT_RECEIVE,
				&new_bootstrap_port);
	if (result != KERN_SUCCESS)
		kern_fatal(result, "mach_port_allocate");

	result = mach_port_insert_right(
				mach_task_self(),
				new_bootstrap_port,
				new_bootstrap_port,
				MACH_MSG_TYPE_MAKE_SEND);
	if (result != KERN_SUCCESS)
		kern_fatal(result, "failed to insert send right");

	result = mach_port_insert_member(
				mach_task_self(),
				new_bootstrap_port,
				bootstrap_port_set);
	if (result != KERN_SUCCESS)
		kern_fatal(result, "port_set_add");

	subset = new_bootstrap(bootstrap, new_bootstrap_port, requestor_port);

	result = mach_port_request_notification(
				mach_task_self(),
				requestor_port,
				MACH_NOTIFY_DEAD_NAME,
				0,
				notify_port,
				MACH_MSG_TYPE_MAKE_SEND_ONCE,
				&previous); 
	if (result != KERN_SUCCESS) {
		kern_error(result, "mach_port_request_notification");
		mach_port_deallocate(mach_task_self(), requestor_port);
		subset->requestor_port = MACH_PORT_NULL;
		deactivate_bootstrap(subset);
	} else if (previous != MACH_PORT_NULL) {
		debug("deallocating old notification port (%x) for requestor %x",
			  previous, requestor_port);
		result = mach_port_deallocate(
				mach_task_self(),
				previous);
		if (result != KERN_SUCCESS)
			kern_fatal(result, "mach_port_deallocate");
	}

	info("Created bootstrap subset %x parent %x requestor %x", 
		new_bootstrap_port, bootstrap_port, requestor_port);
	*subset_port = new_bootstrap_port;
	return BOOTSTRAP_SUCCESS;
}
Esempio n. 18
0
/*
 * kern_return_t
 * bootstrap_register(mach_port_t bootstrap_port,
 *	name_t service_name,
 *	mach_port_t service_port)
 *
 * Registers send rights for the port service_port for the service named by
 * service_name.  Registering a declared service or registering a service for
 * which bootstrap has receive rights via a port backup notification is
 * allowed.
 * The previous service port will be deallocated.  Restarting services wishing
 * to resume service for previous clients must first attempt to checkin to the
 * service.
 *
 * Errors:	Returns appropriate kernel errors on rpc failure.
 *		Returns BOOTSTRAP_NOT_PRIVILEGED, if request directed to
 *			unprivileged bootstrap port.
 *		Returns BOOTSTRAP_SERVICE_ACTIVE, if service has already been
 *			register or checked-in.
 */
kern_return_t
x_bootstrap_register(
	mach_port_t	bootstrap_port,
	name_t	service_name,
	mach_port_t	service_port)
{
	kern_return_t result;
	service_t *servicep;
	server_t *serverp;
	bootstrap_info_t *bootstrap;
	mach_port_t old_port;

	debug("Register attempt for service %s port %x",
	      service_name, service_port);

	/*
	 * Validate the bootstrap.
	 */
	bootstrap = lookup_bootstrap_by_port(bootstrap_port);
	if (!bootstrap || !active_bootstrap(bootstrap))
		return BOOTSTRAP_NOT_PRIVILEGED;
	  
	/*
	 * If this bootstrap port is for a server, or it's an unprivileged
	 * bootstrap can't register the port.
	 */
	serverp = lookup_server_by_port(bootstrap_port);
	servicep = lookup_service_by_name(bootstrap, service_name);
	if (servicep && servicep->server && servicep->server != serverp)
		return BOOTSTRAP_NOT_PRIVILEGED;

	if (servicep == NULL || servicep->bootstrap != bootstrap) {
		servicep = new_service(bootstrap,
				       service_name,
				       service_port,
				       ACTIVE,
				       REGISTERED,
				       NULL_SERVER);
		debug("Registered new service %s", service_name);
	} else {
		if (servicep->isActive) {
			debug("Register: service %s already active, port %x",
		 	      servicep->name, servicep->port);
			ASSERT(!canReceive(servicep->port));
			return BOOTSTRAP_SERVICE_ACTIVE;
		}
		old_port = servicep->port;
		if (servicep->servicetype == DECLARED) {
			servicep->servicetype = REGISTERED;

			if (servicep->server) {
				ASSERT(servicep->server == serverp);
				ASSERT(active_server(serverp));
				servicep->server = NULL_SERVER;
				serverp->activity++;
			}

			result = mach_port_mod_refs(
					mach_task_self(),
					old_port,
					MACH_PORT_RIGHT_RECEIVE, 
					-1);
			if (result != KERN_SUCCESS)
				kern_fatal(result, "mach_port_mod_refs");
		}
		result = mach_port_deallocate(
				mach_task_self(),
				old_port);
		if (result != KERN_SUCCESS)
			kern_fatal(result, "mach_port_mod_refs");
		
		servicep->port = service_port;
		servicep->isActive = TRUE;
		debug("Re-registered inactive service %x bootstrap %x: %s",
			servicep->port, servicep->bootstrap->bootstrap_port, service_name);
	}

	/* detect the new service port going dead */
	result = mach_port_request_notification(
			mach_task_self(),
			service_port,
			MACH_NOTIFY_DEAD_NAME,
			0,
			notify_port,
			MACH_MSG_TYPE_MAKE_SEND_ONCE,
			&old_port);
	if (result != KERN_SUCCESS) {
		debug("Can't request notification on service %x bootstrap %x: %s",
		       service_port, servicep->bootstrap->bootstrap_port, "must be dead");
		delete_service(servicep);
		return BOOTSTRAP_SUCCESS;
	} else if (old_port != MACH_PORT_NULL) {
		debug("deallocating old notification port (%x) for service %x",
		      old_port, service_port);
		result = mach_port_deallocate(
				mach_task_self(),
				old_port);
		if (result != KERN_SUCCESS)
			kern_fatal(result, "mach_port_deallocate");
	}
	info("Registered service %x bootstrap %x: %s",
	     servicep->port, servicep->bootstrap->bootstrap_port, servicep->name);
	return BOOTSTRAP_SUCCESS;
}