OpenSolaris_b135/cmd/nscd/nscd_door.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <sys/param.h>
#include <string.h>
#include <door.h>
#include <sys/mman.h>
#include "nscd_door.h"
#include "nscd_log.h"
#include <getxby_door.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>

static void
initdoor(void *buf, int *doorfd)
{
	nss_pheader_t	*phdr = (nss_pheader_t *)buf;
	door_info_t 	doori;
	char		*me = "initdoor";

	*doorfd = open64(NAME_SERVICE_DOOR, O_RDONLY, 0);

	_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
		(me, "door is %s (fd is %d)\n", NAME_SERVICE_DOOR,
		*doorfd);

	if (*doorfd == -1)
		NSCD_RETURN_STATUS(phdr, NSS_ERROR, errno);

	if (door_info(*doorfd, &doori) < 0 ||
		(doori.di_attributes & DOOR_REVOKED) ||
		doori.di_data != (uintptr_t)NAME_SERVICE_DOOR_COOKIE) {

		/*
		 * we should close doorfd because we just opened it
		 */
		(void) close(*doorfd);

		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
		(me, "door %d not valid\n", *doorfd);

		NSCD_RETURN_STATUS(phdr, NSS_ERROR, ECONNREFUSED);
	}

	NSCD_RETURN_STATUS_SUCCESS(phdr);
}

/* general door call functions used by nscd */

static nss_status_t
copy_output(void *outdata, int outdlen,
	nss_pheader_t *phdr, nss_pheader_t *outphdr)
{
	void		*dp;
	nss_status_t	ret = NSS_SUCCESS;
	char		*me = "copy_output";

	if (outdata != NULL && phdr->data_off > 0 && phdr->data_len > 0) {
		if (phdr->data_len <= outdlen) {
			dp = (char *)phdr + phdr->data_off;
			(void) memmove(outdata, dp, phdr->data_len);
		} else {

			_NSCD_LOG(NSCD_LOG_FRONT_END,
				NSCD_LOG_LEVEL_DEBUG)
			(me, "output buffer not large enough "
			" should be > %d but is %d\n",
			phdr->data_len, outdlen);

			if (outphdr != NULL) {
				NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV,
				0, NSCD_INVALID_ARGUMENT);
				NSCD_COPY_STATUS(outphdr, phdr);
			}
			ret = NSS_NSCD_PRIV;
		}
	}

	return (ret);
}

nss_status_t
_nscd_doorcall(int callnum)
{
	size_t		buflen;
	nss_pheader_t	*phdr;
	void		*dptr;
	size_t		ndata;
	size_t		adata;
	int		ret;
	char		*me = "_nscd_doorcall";

	_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
	(me, "processing door call %d ...\n", callnum);

	/* allocate door buffer from the stack */
	NSCD_ALLOC_DOORBUF(callnum, 0, dptr, buflen);
	ndata = buflen;
	adata = buflen;

	ret = _nsc_trydoorcall(&dptr, &ndata, &adata);

	if (ret != NSS_SUCCESS) {
		phdr = (nss_pheader_t *)dptr;
		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
		(me, "door call (%d) failed (status = %d, error = %s)\n",
			callnum, ret, strerror(NSCD_GET_ERRNO(phdr)));
	}

	return (ret);
}


nss_status_t
_nscd_doorcall_data(int callnum, void *indata, int indlen,
	void *outdata, int outdlen, nss_pheader_t *phdr)
{
	void		*uptr;
	size_t		buflen;
	void		*dptr;
	void		*datap;
	size_t		ndata;
	size_t		adata;
	nss_pheader_t	*phdr_d;
	int		ret;
	char		*me = "_nscd_doorcall_data";

	_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
	(me, "processing door call %d ...\n", callnum);

	/* allocate door buffer from the stack */
	NSCD_ALLOC_DOORBUF(callnum, indlen, uptr, buflen);
	dptr = uptr;
	ndata = buflen;
	adata = buflen;
	datap = NSCD_N2N_DOOR_DATA(void, dptr);
	if (indata != NULL)
		(void) memmove(datap, indata, indlen);

	ret = _nsc_trydoorcall(&dptr, &ndata, &adata);

	phdr_d = (nss_pheader_t *)dptr;
	if (ret != NSS_SUCCESS) {
		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
		(me, "door call (%d) failed (status = %d, error = %s)\n",
			callnum, ret, strerror(NSCD_GET_ERRNO(phdr_d)));
	} else {
		if (phdr != NULL) {
			NSCD_COPY_STATUS(phdr, phdr_d);
		}
		ret = copy_output(outdata, outdlen, phdr_d, phdr);
	}

	/* if new buffer allocated for this door call, free it */
	if (dptr != uptr)
		(void) munmap(dptr, ndata);

	return (ret);
}

nss_status_t
_nscd_doorcall_fd(int fd, int callnum, void *indata, int indlen,
	void *outdata, int outdlen, nss_pheader_t *phdr)
{
	void		*uptr;
	void		*dptr;
	void		*datap;
	size_t		ndata;
	size_t		adata;
	size_t		buflen;
	door_arg_t	param;
	int		ret, errnum;
	nss_pheader_t	*phdr_d;
	char		*me = "_nscd_doorcall_fd";

	_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
	(me, "processing door call %d (fd = %d)...\n", callnum, fd);

	/* allocate door buffer from the stack */
	NSCD_ALLOC_DOORBUF(callnum, indlen, uptr, buflen);
	dptr = uptr;
	ndata = buflen;
	adata = buflen;
	datap = NSCD_N2N_DOOR_DATA(void, dptr);
	if (indata != NULL)
		(void) memmove(datap, indata, indlen);

	param.rbuf = (char *)dptr;
	param.rsize = ndata;
	param.data_ptr = (char *)dptr;
	param.data_size = adata;
	param.desc_ptr = NULL;
	param.desc_num = 0;
	ret = door_call(fd, &param);
	if (ret < 0) {
		errnum = errno;
		/*
		 * door call did not get through, return errno
		 * if requested
		 */
		if (phdr != NULL) {
			NSCD_SET_STATUS(phdr, NSS_ERROR, errnum);
		}

		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
		(me, "door call (%d to %d) did not get through (%s)\n",
			callnum, fd, strerror(errnum));

		return (NSS_ERROR);
	}
	ndata = param.rsize;
	dptr = (void *)param.data_ptr;

	/*
	 * door call got through, check if operation failed.
	 * if so, return error info if requested
	 */
	phdr_d = (nss_pheader_t *)dptr;
	ret = NSCD_GET_STATUS(phdr_d);
	if (ret != NSS_SUCCESS) {
		if (phdr != NULL) {
			NSCD_COPY_STATUS(phdr, phdr_d);
		}

		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
		(me, "door call (%d to %d) failed: p_status = %d, "
		"p_errno = %s, nscd status = %d\n", callnum, fd,
		ret, strerror(NSCD_GET_ERRNO(phdr_d)),
		NSCD_GET_NSCD_STATUS(phdr_d));
	} else
		ret = copy_output(outdata, outdlen, phdr_d, phdr);

	/* if new buffer allocated for this door call, free it */
	if (dptr != uptr)
		(void) munmap(dptr, param.rsize);


	return (ret);
}

static void
send_doorfd(void **dptr, size_t *ndata, size_t *adata,
	door_desc_t *pdesc)
{
	nss_pheader_t	*phdr = (nss_pheader_t *)*dptr;
	door_arg_t	param;
	int		ret;
	int		doorfd;
	int		errnum;
	char		*me = "send_doorfd";

	initdoor(*dptr, &doorfd);
	if (NSCD_STATUS_IS_NOT_OK(phdr))
		return;

	param.rbuf = (char *)*dptr;
	param.rsize = *ndata;
	param.data_ptr = (char *)*dptr;
	param.data_size = *adata;
	param.desc_ptr = pdesc;
	param.desc_num = 1;
	ret = door_call(doorfd, &param);
	if (ret < 0) {
		errnum = errno;

		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
		(me, "door call (to fd %d) failed (%s)\n",
			doorfd, strerror(errnum));
		(void) close(doorfd);
		NSCD_RETURN_STATUS(phdr, NSS_ERROR, errnum);
	}
	*adata = param.data_size;
	*ndata = param.rsize;
	*dptr = (void *)param.data_ptr;

	if (*adata == 0 || *dptr == NULL) {
		(void) close(doorfd);

		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
		(me, "no data\n");

		NSCD_RETURN_STATUS(phdr, NSS_ERROR, ENOTCONN);
	}

	(void) close(doorfd);
}

nss_status_t
_nscd_doorcall_sendfd(int fd, int callnum, void *indata, int indlen,
	nss_pheader_t *phdr)
{
	void		*uptr;
	void		*dptr;
	void		*datap;
	size_t		ndata;
	size_t		adata;
	size_t		buflen;
	nss_pheader_t	*phdr_d;
	door_desc_t	desc;
	char		*me = "_nscd_doorcall_sendfd";

	_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
	(me, "processing door call %d (fd = %d)...\n", callnum, fd);

	/* allocate door buffer from the stack */
	NSCD_ALLOC_DOORBUF(callnum, indlen, uptr, buflen);
	dptr = uptr;
	ndata = buflen;
	adata = buflen;
	datap = NSCD_N2N_DOOR_DATA(void, dptr);
	if (indata != NULL)
		(void) memmove(datap, indata, indlen);
	desc.d_attributes = DOOR_DESCRIPTOR;
	desc.d_data.d_desc.d_descriptor = fd;

	send_doorfd(&dptr, &ndata, &adata, &desc);

	phdr_d = (nss_pheader_t *)dptr;
	if (NSCD_STATUS_IS_NOT_OK(phdr_d)) {
		if (phdr != NULL)
			*phdr = *phdr_d;

		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
		(me, "door call (%d) failed (status = %d, error = %s)\n",
			callnum, NSCD_GET_STATUS(phdr_d),
			strerror(NSCD_GET_ERRNO(phdr_d)));
	}

	return (NSCD_GET_STATUS(phdr_d));
}