rpc_services.c   [plain text]


/*
 * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * bootstrap -- fundamental service initiator and port server
 * Mike DeMoney, NeXT, Inc.
 * Copyright, 1990.  All rights reserved.
 *
 * rpc_services.c -- implementation of bootstrap rpc services
 */

#include <mach/mach.h>
#include <mach/mach_error.h>
#include <syslog.h>
#include <string.h>

#include <bsm/audit.h>
#include <bsm/libbsm.h>

#include "bootstrap_internal.h"
#include "lists.h"
#include "bootstrap.h"

#ifndef ASSERT
#define ASSERT(p)
#endif

#ifndef NULL
#define	NULL	((void *)0)
#endif NULL
 
#define bsstatus(servicep) \
	(((servicep)->isActive) ? BOOTSTRAP_STATUS_ACTIVE : \
	 (((servicep)->server && (servicep)->server->servertype == DEMAND) ? \
		BOOTSTRAP_STATUS_ON_DEMAND : BOOTSTRAP_STATUS_INACTIVE))

/* extern port_all_t backup_port; */

/*
 * kern_return_t
 * bootstrap_create_server(mach_port_t bootstrap_port,
 *	 cmd_t server_cmd,
 *	 integer_t server_uid,
 *	 boolean_t on_demand,
 *	 mach_port_t *server_portp)
 *
 * Returns send rights to server_port of service.  At this point, the
 * server appears active, so nothing will try to launch it.  The server_port
 * can be used to delare services associated with this server by calling
 * bootstrap_create_service() and passing server_port as the bootstrap port.
 *
 * Errors:	Returns appropriate kernel errors on rpc failure.
 *		Returns BOOTSTRAP_NOT_PRIVILEGED, if bootstrap port invalid.
 */
__private_extern__ kern_return_t
x_bootstrap_create_server(
	mach_port_t bootstrapport,
	cmd_t server_cmd,
	uid_t server_uid,
	boolean_t on_demand,
	audit_token_t client_audit_token,
	mach_port_t *server_portp)
{
	server_t *serverp;
	struct auditinfo audit_info;
	bootstrap_info_t *bootstrap;

	uid_t client_euid;

	bootstrap = lookup_bootstrap_by_port(bootstrapport);
	syslog(LOG_DEBUG, "Server create attempt: \"%s\" bootstrap %x",
	      server_cmd, bootstrapport);

	/* No forwarding allowed for this call - security risk (we run as root) */
	if (!bootstrap || !active_bootstrap(bootstrap)) {
		syslog(LOG_DEBUG, "Server create: \"%s\": invalid bootstrap %x",
			server_cmd, bootstrapport);
		return BOOTSTRAP_NOT_PRIVILEGED;
	}

	/* get the identity of the requestor and set up audit_info of server */
	audit_token_to_au32(client_audit_token,
			    &audit_info.ai_auid,
			    &client_euid,
			    NULL /* egid */,
			    NULL /* ruid */,
			    NULL /* rgid */,
			    NULL /* pid */,
			    &audit_info.ai_asid,
			    &audit_info.ai_termid);

	if (client_euid != 0 && client_euid != server_uid) {
		syslog(LOG_NOTICE, "Server create: \"%s\": insufficient privilege for specified uid (euid-%d != requested-%d)",
			server_cmd, client_euid, server_uid);
		return BOOTSTRAP_NOT_PRIVILEGED;
	}

	serverp = new_server(
					bootstrap,
					server_cmd,
					server_uid,
					(on_demand) ? DEMAND : RESTARTABLE,
					audit_info);
	setup_server(serverp);

	syslog(LOG_INFO, "New server %x in bootstrap %x: \"%s\"",
					serverp->port, bootstrapport, server_cmd);
	*server_portp = serverp->port;
	return BOOTSTRAP_SUCCESS;
}

/*
 * kern_return_t
 * bootstrap_unprivileged(mach_port_t bootstrapport,
 *			  mach_port_t *unprivportp)
 *
 * Given a bootstrap port, return its unprivileged equivalent.  If
 * the port is already unprivileged, another reference to the same
 * port is returned.
 *
 * This is most often used by servers, which are launched with their
 * bootstrap port set to the privileged port for the server, to get
 * an unprivileged version of the same port for use by its unprivileged
 * children (or any offspring that it does not want to count as part
 * of the "server" for mach_init registration and re-launch purposes).
 */
__private_extern__ kern_return_t
x_bootstrap_unprivileged(
	mach_port_t bootstrapport,
	mach_port_t *unprivportp)
{
	bootstrap_info_t *bootstrap;

	syslog(LOG_DEBUG, "Get unprivileged attempt for bootstrap %x", bootstrapport);

	bootstrap = lookup_bootstrap_by_port(bootstrapport);
	if (!bootstrap) {
		syslog(LOG_DEBUG, "Get unprivileged: invalid bootstrap %x", bootstrapport);
		return BOOTSTRAP_NOT_PRIVILEGED;
	}

	*unprivportp = bootstrap->bootstrap_port;

	syslog(LOG_DEBUG, "Get unpriv bootstrap %x returned for bootstrap %x",
	       bootstrap->bootstrap_port, bootstrapport);
	return BOOTSTRAP_SUCCESS;
}

  
/*
 * kern_return_t
 * bootstrap_check_in(mach_port_t bootstrapport,
 *	 name_t servicename,
 *	 mach_port_t *serviceportp)
 *
 * 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.
 */
__private_extern__ kern_return_t
x_bootstrap_check_in(
	mach_port_t	bootstrapport,
	name_t		servicename,
	mach_port_t	*serviceportp)
{
	kern_return_t result;
	mach_port_t previous;
	service_t *servicep;
	server_t *serverp;
	bootstrap_info_t *bootstrap;

	serverp = lookup_server_by_port(bootstrapport);
	bootstrap = lookup_bootstrap_by_port(bootstrapport);
	syslog(LOG_DEBUG, "Service checkin attempt for service %s bootstrap %x",
	      servicename, bootstrapport);

	servicep = lookup_service_by_name(bootstrap, servicename);
	if (servicep == NULL || servicep->port == MACH_PORT_NULL) {
		syslog(LOG_DEBUG, "bootstrap_check_in service %s unknown%s", servicename,
			forward_ok ? " forwarding" : "");
		return  forward_ok ?
			bootstrap_check_in(
					inherited_bootstrap_port,
					servicename,
					serviceportp) :
			BOOTSTRAP_UNKNOWN_SERVICE;
	}
	if (servicep->server != NULL && servicep->server != serverp) {
		syslog(LOG_DEBUG, "bootstrap_check_in service %s not privileged",
			servicename);
		 return BOOTSTRAP_NOT_PRIVILEGED;
	}
	if (!canReceive(servicep->port)) {
		ASSERT(servicep->isActive);
		syslog(LOG_DEBUG, "bootstrap_check_in service %s already active",
			servicename);
		return BOOTSTRAP_SERVICE_ACTIVE;
	}
	syslog(LOG_DEBUG, "Checkin service %s for bootstrap %x", servicename,
	      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)
			panic("mach_port_request_notification(): %s", mach_error_string(result));
	} 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)
			panic("mach_port_request_notification(): %s", mach_error_string(result));
		else if (previous != MACH_PORT_NULL) {
			syslog(LOG_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)
				panic("mach_port_deallocate(): %s", mach_error_string(result));
		}
	}

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

	*serviceportp = servicep->port;
	return BOOTSTRAP_SUCCESS;
}

/*
 * kern_return_t
 * bootstrap_register(mach_port_t bootstrapport,
 *	name_t servicename,
 *	mach_port_t serviceport)
 *
 * 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.
 */
__private_extern__ kern_return_t
x_bootstrap_register(
	mach_port_t	bootstrapport,
	name_t	servicename,
	mach_port_t	serviceport)
{
	kern_return_t result;
	service_t *servicep;
	server_t *serverp;
	bootstrap_info_t *bootstrap;
	mach_port_t old_port;

	syslog(LOG_DEBUG, "Register attempt for service %s port %x",
	      servicename, serviceport);

	/*
	 * Validate the bootstrap.
	 */
	bootstrap = lookup_bootstrap_by_port(bootstrapport);
	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(bootstrapport);
	servicep = lookup_service_by_name(bootstrap, servicename);
	if (servicep && servicep->server && servicep->server != serverp)
		return BOOTSTRAP_NOT_PRIVILEGED;

	if (servicep == NULL || servicep->bootstrap != bootstrap) {
		servicep = new_service(bootstrap,
				       servicename,
				       serviceport,
				       ACTIVE,
				       REGISTERED,
				       NULL_SERVER);
		syslog(LOG_DEBUG, "Registered new service %s", servicename);
	} else {
		if (servicep->isActive) {
			syslog(LOG_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)
				panic("mach_port_mod_refs(): %s", mach_error_string(result));
		}
		result = mach_port_deallocate(
				mach_task_self(),
				old_port);
		if (result != KERN_SUCCESS)
			panic("mach_port_mod_refs(): %s", mach_error_string(result));
		
		servicep->port = serviceport;
		servicep->isActive = TRUE;
		syslog(LOG_DEBUG, "Re-registered inactive service %x bootstrap %x: %s",
			servicep->port, servicep->bootstrap->bootstrap_port, servicename);
	}

	/* detect the new service port going dead */
	result = mach_port_request_notification(
			mach_task_self(),
			serviceport,
			MACH_NOTIFY_DEAD_NAME,
			0,
			notify_port,
			MACH_MSG_TYPE_MAKE_SEND_ONCE,
			&old_port);
	if (result != KERN_SUCCESS) {
		syslog(LOG_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) {
		syslog(LOG_DEBUG, "deallocating old notification port (%x) for service %x",
		      old_port, serviceport);
		result = mach_port_deallocate(
				mach_task_self(),
				old_port);
		if (result != KERN_SUCCESS)
			panic("mach_port_deallocate(): %s", mach_error_string(result));
	}
	syslog(LOG_INFO, "Registered service %x bootstrap %x: %s",
	     servicep->port, servicep->bootstrap->bootstrap_port, servicep->name);
	return BOOTSTRAP_SUCCESS;
}

/*
 * kern_return_t
 * bootstrap_look_up(mach_port_t bootstrapport,
 *	name_t servicename,
 *	mach_port_t *serviceportp)
 *
 * Returns send rights for the service port of the service named by
 * service_name in *service_portp.  Service is not guaranteed to be active.
 *
 * Errors:	Returns appropriate kernel errors on rpc failure.
 *		Returns BOOTSTRAP_UNKNOWN_SERVICE, if service does not exist.
 */
__private_extern__ kern_return_t
x_bootstrap_look_up(
	mach_port_t	bootstrapport,
	name_t	servicename,
	mach_port_t	*serviceportp)
{
	service_t *servicep;
	bootstrap_info_t *bootstrap;

	bootstrap = lookup_bootstrap_by_port(bootstrapport);
	servicep = lookup_service_by_name(bootstrap, servicename);
	if (servicep == NULL || servicep->port == MACH_PORT_NULL) {
		if (forward_ok) {
			syslog(LOG_DEBUG, "bootstrap_look_up service %s forwarding",
				servicename);
			return bootstrap_look_up(inherited_bootstrap_port,
						servicename,
						serviceportp);
		} else {
			syslog(LOG_DEBUG, "bootstrap_look_up service %s unknown",
				servicename);
			return BOOTSTRAP_UNKNOWN_SERVICE;
		}
	}
	*serviceportp = servicep->port;
	syslog(LOG_DEBUG, "Lookup returns port %x for service %s", servicep->port, servicep->name);
	return BOOTSTRAP_SUCCESS;
}

/*
 * kern_return_t
 * bootstrap_look_up_array(mach_port_t bootstrapport,
 *	name_array_t	servicenames,
 *	int		servicenames_cnt,
 *	mach_port_array_t	*serviceports,
 *	int		*serviceports_cnt,
 *	boolean_t	*allservices_known)
 *
 * Returns port send rights in corresponding entries of the array service_ports
 * for all services named in the array service_names.  Service_ports_cnt is
 * returned and will always equal service_names_cnt (assuming service_names_cnt
 * is greater than or equal to zero).
 *
 * Errors:	Returns appropriate kernel errors on rpc failure.
 *		Returns BOOTSTRAP_NO_MEMORY, if server couldn't obtain memory
 *			for response.
 *		Unknown service names have the corresponding service
 *			port set to MACH_PORT_NULL.
 *		If all services are known, all_services_known is true on
 *			return,
 *		if any service is unknown, it's false.
 */
__private_extern__ kern_return_t
x_bootstrap_look_up_array(
	mach_port_t	bootstrapport,
	name_array_t	servicenames,
	unsigned int	servicenames_cnt,
	mach_port_array_t	*serviceportsp,
	unsigned int	*serviceports_cnt,
	boolean_t	*allservices_known)
{
	unsigned int i;
	static mach_port_t service_ports[BOOTSTRAP_MAX_LOOKUP_COUNT];
	
	if (servicenames_cnt > BOOTSTRAP_MAX_LOOKUP_COUNT)
		return BOOTSTRAP_BAD_COUNT;
	*serviceports_cnt = servicenames_cnt;
	*allservices_known = TRUE;
	for (i = 0; i < servicenames_cnt; i++) {
		if (   x_bootstrap_look_up(bootstrapport,
					  servicenames[i],
					  &service_ports[i])
		    != BOOTSTRAP_SUCCESS)
		{
			*allservices_known = FALSE;
			service_ports[i] = MACH_PORT_NULL;
		}
	}
	syslog(LOG_DEBUG, "bootstrap_look_up_array returns %d ports", servicenames_cnt);
	*serviceportsp = service_ports;
	return BOOTSTRAP_SUCCESS;
}

/*
 * kern_return_t
 * bootstrap_parent(mach_port_t bootstrapport,
 *		    mach_port_t *parentport);
 *
 * Given a bootstrap subset port, return the parent bootstrap port.
 * If the specified bootstrap port is already the root subset,
 * MACH_PORT_NULL will be returned.
 *
 * Errors:
 *	Returns BOOTSTRAP_NOT_PRIVILEGED if the caller is not running
 *	with an effective user id of root (as determined by the security
 *	token in the message trailer).
 */
__private_extern__ kern_return_t
x_bootstrap_parent(
	mach_port_t bootstrapport,
	security_token_t sectoken,
	mach_port_t *parentport)
{
	bootstrap_info_t *bootstrap;

	syslog(LOG_DEBUG, "Parent attempt for bootstrap %x", bootstrapport);

	bootstrap = lookup_bootstrap_by_port(bootstrapport);
	if (!bootstrap) { 
		syslog(LOG_DEBUG, "Parent attempt for bootstrap %x: invalid bootstrap",
		      bootstrapport);
		return BOOTSTRAP_NOT_PRIVILEGED;
	}
	if (sectoken.val[0]) {
		syslog(LOG_NOTICE, "Bootstrap parent for bootstrap %x: invalid security token (%d)",
		       bootstrapport, sectoken.val[0]);
		return BOOTSTRAP_NOT_PRIVILEGED;
	}
	syslog(LOG_DEBUG, "Returning bootstrap parent %x for bootstrap %x",
	      bootstrap->parent->bootstrap_port, bootstrapport);
	*parentport = bootstrap->parent->bootstrap_port;
	return BOOTSTRAP_SUCCESS;
}

/*
 * kern_return_t
 * bootstrap_status(mach_port_t bootstrapport,
 *	name_t servicename,
 *	bootstrap_status_t *serviceactive);
 *
 * Returns: service_active indicates if service is available.
 *			
 * Errors:	Returns appropriate kernel errors on rpc failure.
 *		Returns BOOTSTRAP_UNKNOWN_SERVICE, if service does not exist.
 */
__private_extern__ kern_return_t
x_bootstrap_status(
	mach_port_t		bootstrapport,
	name_t			servicename,
	bootstrap_status_t	*serviceactivep)
{
	service_t *servicep;
	bootstrap_info_t *bootstrap;

	bootstrap = lookup_bootstrap_by_port(bootstrapport);
	servicep = lookup_service_by_name(bootstrap, servicename);
	if (servicep == NULL) {
		if (forward_ok) {
			syslog(LOG_DEBUG, "bootstrap_status forwarding status, server %s",
				servicename);
			return bootstrap_status(inherited_bootstrap_port,
						servicename,
						serviceactivep);
		} else {
			syslog(LOG_DEBUG, "bootstrap_status service %s unknown",
				servicename);
			return BOOTSTRAP_UNKNOWN_SERVICE;
		}
	}
	*serviceactivep = bsstatus(servicep);

	syslog(LOG_DEBUG, "bootstrap_status server %s %sactive", servicename,
		servicep->isActive ? "" : "in");
	return BOOTSTRAP_SUCCESS;
}

/*
 * kern_return_t
 * bootstrap_info(mach_port_t bootstrapport,
 *	name_array_t *servicenamesp,
 *	int *servicenames_cnt,
 *	name_array_t *servernamesp,
 *	int *servernames_cnt,
 *	bootstrap_status_array_t *serviceactivesp,
 *	int *serviceactive_cnt);
 *
 * Returns bootstrap status for all known services.
 *			
 * Errors:	Returns appropriate kernel errors on rpc failure.
 */
__private_extern__ kern_return_t
x_bootstrap_info(
	mach_port_t			bootstrapport,
	name_array_t			*servicenamesp,
	unsigned int			*servicenames_cnt,
	name_array_t			*servernamesp,
	unsigned int			*servernames_cnt,
	bootstrap_status_array_t	*serviceactivesp,
	unsigned int			*serviceactives_cnt)
{
	kern_return_t result;
	unsigned int i, cnt;
	service_t *servicep;
	server_t *serverp;
	bootstrap_info_t *bootstrap;
	name_array_t service_names;
	name_array_t server_names;
	bootstrap_status_array_t service_actives;

	bootstrap = lookup_bootstrap_by_port(bootstrapport);

	for (   cnt = i = 0, servicep = services.next
	     ; i < nservices
	     ; servicep = servicep->next, i++)
	{
	    if (lookup_service_by_name(bootstrap, servicep->name) == servicep)
	    {
	    	cnt++;
	    }
	}
	result = vm_allocate(mach_task_self(),
			     (vm_address_t *)&service_names,
			     cnt * sizeof(service_names[0]),
			     ANYWHERE);
	if (result != KERN_SUCCESS)
		return BOOTSTRAP_NO_MEMORY;

	result = vm_allocate(mach_task_self(),
			     (vm_address_t *)&server_names,
			     cnt * sizeof(server_names[0]),
			     ANYWHERE);
	if (result != KERN_SUCCESS) {
		(void)vm_deallocate(mach_task_self(),
				    (vm_address_t)service_names,
				    cnt * sizeof(service_names[0]));
		return BOOTSTRAP_NO_MEMORY;
	}
	result = vm_allocate(mach_task_self(),
			     (vm_address_t *)&service_actives,
			     cnt * sizeof(service_actives[0]),
			     ANYWHERE);
	if (result != KERN_SUCCESS) {
		(void)vm_deallocate(mach_task_self(),
				    (vm_address_t)service_names,
				    cnt * sizeof(service_names[0]));
		(void)vm_deallocate(mach_task_self(),
				    (vm_address_t)server_names,
				    cnt * sizeof(server_names[0]));
		return BOOTSTRAP_NO_MEMORY;
	}

	for (  i = 0, servicep = services.next
	     ; i < cnt
	     ; servicep = servicep->next)
	{
	    if (   lookup_service_by_name(bootstrap, servicep->name)
		!= servicep)
		continue;
	    strncpy(service_names[i],
		    servicep->name,
		    sizeof(service_names[0]));
	    service_names[i][sizeof(service_names[0]) - 1] = '\0';
	    if (servicep->server) {
		    serverp = servicep->server;
		    strncpy(server_names[i],
			    serverp->cmd,
			    sizeof(server_names[0]));
		    server_names[i][sizeof(server_names[0]) - 1] = '\0';
		    syslog(LOG_DEBUG, "bootstrap info service %s server %s %sactive",
			servicep->name,
			serverp->cmd, servicep->isActive ? "" : "in"); 
	    } else {
		    server_names[i][0] = '\0';
		    syslog(LOG_DEBUG, "bootstrap info service %s %sactive",
			servicep->name, servicep->isActive ? "" : "in"); 
	    }
	    service_actives[i] = bsstatus(servicep);
	    i++;
	}
	*servicenamesp = service_names;
	*servernamesp = server_names;
	*serviceactivesp = service_actives;
	*servicenames_cnt = *servernames_cnt = *serviceactives_cnt = cnt;

	return BOOTSTRAP_SUCCESS;
}

/*
 * kern_return_t
 * bootstrap_subset(mach_port_t bootstrapport,
 *		    mach_port_t requestorport,
 *		    mach_port_t *subsetport);
 *
 * 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.
 */
__private_extern__ kern_return_t
x_bootstrap_subset(
	mach_port_t	bootstrapport,
	mach_port_t	requestorport,
	mach_port_t	*subsetportp)
{
	kern_return_t result;
	bootstrap_info_t *bootstrap;
	bootstrap_info_t *subset;
	mach_port_t new_bootstrapport;
	mach_port_t previous;

	syslog(LOG_DEBUG, "Subset create attempt: bootstrap %x, requestor: %x",
	      bootstrapport, requestorport);

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

	result = mach_port_allocate(
				mach_task_self(), 
				MACH_PORT_RIGHT_RECEIVE,
				&new_bootstrapport);
	if (result != KERN_SUCCESS)
		panic("mach_port_allocate(): %s", mach_error_string(result));

	result = mach_port_insert_right(
				mach_task_self(),
				new_bootstrapport,
				new_bootstrapport,
				MACH_MSG_TYPE_MAKE_SEND);
	if (result != KERN_SUCCESS)
		panic("failed to insert send right(): %s", mach_error_string(result));

	result = mach_port_insert_member(
				mach_task_self(),
				new_bootstrapport,
				bootstrap_port_set);
	if (result != KERN_SUCCESS)
		panic("port_set_add(): %s", mach_error_string(result));

	subset = new_bootstrap(bootstrap, new_bootstrapport, requestorport);

	result = mach_port_request_notification(
				mach_task_self(),
				requestorport,
				MACH_NOTIFY_DEAD_NAME,
				0,
				notify_port,
				MACH_MSG_TYPE_MAKE_SEND_ONCE,
				&previous); 
	if (result != KERN_SUCCESS) {
		syslog(LOG_ERR, "mach_port_request_notification(): %s", mach_error_string(result));
		mach_port_deallocate(mach_task_self(), requestorport);
		subset->requestor_port = MACH_PORT_NULL;
		deactivate_bootstrap(subset);
	} else if (previous != MACH_PORT_NULL) {
		syslog(LOG_DEBUG, "deallocating old notification port (%x) for requestor %x",
			  previous, requestorport);
		result = mach_port_deallocate(
				mach_task_self(),
				previous);
		if (result != KERN_SUCCESS)
			panic("mach_port_deallocate(): %s", mach_error_string(result));
	}

	syslog(LOG_INFO, "Created bootstrap subset %x parent %x requestor %x", 
		new_bootstrapport, bootstrapport, requestorport);
	*subsetportp = new_bootstrapport;
	return BOOTSTRAP_SUCCESS;
}

/*
 * kern_return_t
 * bootstrap_create_service(mach_port_t bootstrapport,
 *		      name_t servicename,
 *		      mach_port_t *serviceportp)
 *
 * Creates a service named "service_name" and returns send rights to that
 * port in "service_port."  The port may later be checked in as if this
 * port were configured in the bootstrap configuration file.
 *
 * Errors:	Returns appropriate kernel errors on rpc failure.
 *		Returns BOOTSTRAP_NAME_IN_USE, if service already exists.
 */
__private_extern__ kern_return_t
x_bootstrap_create_service(
	mach_port_t bootstrapport,
	name_t	servicename,
	mach_port_t *serviceportp)
{
	server_t *serverp;
	service_t *servicep;
	bootstrap_info_t *bootstrap;
	kern_return_t result;

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

	syslog(LOG_DEBUG, "Service creation attempt for service %s bootstrap %x",
	      servicename, bootstrapport);

	servicep = lookup_service_by_name(bootstrap, servicename);
	if (servicep) {
		syslog(LOG_DEBUG, "Service creation attempt for service %s failed, "
			"service already exists", servicename);
		return BOOTSTRAP_NAME_IN_USE;
	}

	serverp = lookup_server_by_port(bootstrapport);

	result = mach_port_allocate(mach_task_self(),
				    MACH_PORT_RIGHT_RECEIVE,
				    serviceportp);
	if (result != KERN_SUCCESS)
		panic("port_allocate(): %s", mach_error_string(result));
	result = mach_port_insert_right(mach_task_self(),
					*serviceportp, 
					*serviceportp,
					MACH_MSG_TYPE_MAKE_SEND);
	if (result != KERN_SUCCESS)
		panic("failed to insert send right(): %s", mach_error_string(result));

	if (serverp)
		serverp->activity++;

	servicep = new_service(bootstrap,
				servicename,
				*serviceportp,
				!ACTIVE,
				DECLARED,
				serverp);

	syslog(LOG_INFO, "Created new service %x in bootstrap %x: %s", 
	    servicep->port, bootstrap->bootstrap_port, servicename);

	return BOOTSTRAP_SUCCESS;
}