OpenSolaris_b135/lib/brand/lx/lx_thunk/common/lx_thunk.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 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

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

/*
 * The BrandZ Linux thunking library.
 *
 * The interfaces defined in this file form the client side of a bridge
 * to allow native Solaris process to access Linux services.  Currently
 * the Linux services that is made accessible by these interfaces here
 * are:
 *	- Linux host <-> address naming services
 *	- Linux service <-> port naming services
 *	- Linux syslog
 *
 * Currently, to use this library it must be LD_PRELOADed into the
 * application that needs to access Linux services.  Once loaded
 * Linux services are accessed by the client application in two
 * different ways:
 *
 * - Direct library calls:
 *	lxt_gethostbyname_r
 *	lxt_gethostbyaddr_r
 *	lxt_getservbyname_r
 *	lxt_getservbyport_r
 *	lxt_debug
 *
 *   These library functions are used by the BrandZ lx name services
 *   translation library (lx_nametoaddr.so) to handle libnsl.so name
 *   service requests.
 *
 * - Intercepted library calls:
 *	openlog(3c)
 *	syslog(3c)
 *	vsyslog(3c)
 *	closelog(3c)
 *
 *   Via the LD_PRELOAD mechanism this library interposes itself on
 *   these interfaces and when the application calls these interfaces
 *   (either directly or indirectly via any libraries the program may
 *   be linked against) this library intercepts the request and passes
 *   it onto a Linux process to handle the request.
 *
 * Once this library receives a request that needs to be serviced by a
 * Linux process, it packs up that request and attempts to send it
 * to a doors server.  The door server interfaces are defined in
 * lx_thunk_server.h.  If the doors server is not running or not
 * responding, this library will attempt to spawn a new doors server
 * by forking and executing the following shell script (which runs as
 * a native /bin/sh Linux process):
 *	/native/usr/lib/brand/lx/lx_thunk
 *
 * Notes:
 * - This library also intercepts the following system calls:
 *	close(2) - We intercept close(2) to prevent the caller from
 *		accidentally closing any of the file descriptors we
 *		need to do our work.
 *
 *	setppriv(2) - We intercept setppriv(2) to prevent a process
 *		from dropping any of the privileges we'll need to create
 *		a new lx_thunk server process and to deal with service
 *		requests.
 *
 * - To facilitate the running of native Solaris programs and libraries
 *   when this library is preloaded into an application it will chroot()
 *   into /native.  This way the Solaris application and libraries can
 *   access files via their expected paths and we can avoid having to
 *   either do path mapping or modifying all libraries to make them
 *   aware of "/native" so that they can pre-pend it to all their
 *   filesystem operations.
 *
 * - This library can only be used with processes that are initially
 *   run by root in a zone.  The reason is that we use the chroot()
 *   system call and this requires the PRIV_PROC_CHROOT privilege,
 *   which non-root users don't have.
 */

#include <alloca.h>
#include <assert.h>
#include <dlfcn.h>
#include <door.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netdir.h>
#include <priv.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <synch.h>
#include <sys/brand.h>
#include <sys/fcntl.h>
#include <sys/lx_thunk_server.h>
#include <sys/lx_thunk.h>
#include <sys/mman.h>
#include <sys/priv_impl.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <thread.h>
#include <unistd.h>
#include <sys/varargs.h>

#define	LXT_DOOR_DIR		"/tmp"
#define	LXT_DOOR_PREFIX		"lxt"
#define	LXT_MSG_MAXLEN		(128 + MAXPATHLEN)

#pragma init(init)

typedef uintptr_t (*fp1_t)(uintptr_t);
typedef uintptr_t (*fp3_t)(uintptr_t, uintptr_t, uintptr_t);

static char	*lxt_debug_path = NULL;		/* debug output file path */
static char	lxt_debug_path_buf[MAXPATHLEN];
static int	root_fd;
static int	debug_fd = -1;

void lxt_debug(const char *msg, ...);

void
init(void)
{
	if (getenv("LX_DEBUG") != NULL) {

		/* check if there's a debug log file specified */
		lxt_debug_path = getenv("LX_DEBUG_FILE");
		if (lxt_debug_path == NULL) {
			/* send all debugging output to /dev/tty */
			lxt_debug_path = "/dev/tty";
		}

		(void) strlcpy(lxt_debug_path_buf, lxt_debug_path,
		    sizeof (lxt_debug_path_buf));
		lxt_debug_path = lxt_debug_path_buf;

		/*
		 * Open the debugging output file.  We need to open it
		 * and hold it open because we're going to call chroot()
		 * in just a second, so we won't be able to open it later.
		 */
		if ((debug_fd = open(lxt_debug_path,
		    O_WRONLY|O_APPEND|O_CREAT|O_NDELAY|O_NOCTTY,
		    0666)) != -1) {
			(void) fchmod(debug_fd, 0666);
		}
	}
	lxt_debug("lxt_init: executing native process");

	/* Get a fd that points to the root directory */
	if ((root_fd = open("/", O_RDONLY)) < 0) {
		lxt_debug("lxt_init(): "
		    "failed to open root directory: %s", strerror(errno));
		exit(-1);
	}

	/*
	 * Now, so that we can avoid having to do path mapping,
	 * just chdir() and chroot() into /native.
	 */
	if (chdir("/native") != 0) {
		lxt_debug("lxt_init(): "
		    "failed to chdir to /native: %s", strerror(errno));
		exit(-1);
	}
	if (chroot("/native") != 0) {
		lxt_debug("lxt_init(): "
		    "failed to chroot to /native: %s", strerror(errno));
		exit(-1);
	}
}

/*
 * Linux Thunking Interfaces - Client Side
 */
static mutex_t	lxt_door_lock = DEFAULTMUTEX;
static int	lxt_door_fd = -1;

static void
lxt_server_exec(int fifo_wr, int fifo_rd)
{
	extern const char	**environ;
	char 			*nullist[] = { NULL };

	lxt_debug("lxt_server_exec: server starting");

	/*
	 * First we need to dup our fifos to the file descriptors
	 * the brand library is expecting them to be at.
	 */

	/* Check if the write fifo needs to be moved aside */
	if ((fifo_wr == LXT_SERVER_FIFO_RD_FD) &&
	    ((fifo_wr = dup(fifo_wr)) < 0))
		return;

	/* Check if the read fifo needs to be moved aside */
	if ((fifo_rd == LXT_SERVER_FIFO_WR_FD) &&
	    ((fifo_rd = dup(fifo_rd)) < 0))
		return;

	if ((fifo_wr != LXT_SERVER_FIFO_WR_FD) &&
	    (dup2(fifo_wr, LXT_SERVER_FIFO_WR_FD) < 0))
		return;
	if ((fifo_rd != LXT_SERVER_FIFO_RD_FD) &&
	    (dup2(fifo_rd, LXT_SERVER_FIFO_RD_FD) < 0))
		return;

	/*
	 * We're about to execute a native Linux process.
	 * Since we've been loaded into a Solaris process with
	 * LD_PRELOAD and LD_LIBRARY_PATH we should clear these
	 * variables from the environment before calling exec.
	 */
	(void) unsetenv("LD_PRELOAD");
	(void) unsetenv("LD_LIBRARY_PATH");

	/*
	 * Now we need to exec the thunk server process.  This is a
	 * branded Linux process that will act as a doors server and
	 * service our requests to perform native Linux operations.
	 * Since we're currently running as a native Solaris process
	 * to start up the server we'll use the brand system call to
	 * the kernel that the target of the exec will be a branded
	 * process.
	 */
	lxt_debug("lxt_server_exec: execing as Linux process");
	(void) syscall(SYS_brand, B_EXEC_BRAND,
	    LXT_SERVER_BINARY, nullist, environ);
}


static void *
lxt_door_waitpid(void *arg)
{
	pid_t	child_pid = (pid_t)(uintptr_t)arg;
	int	stat;

	(void) waitpid(child_pid, &stat, 0);
	return (NULL);
}

static char *
lxt_door_mkfifo()
{
	char	*path;

	for (;;) {
		path = tempnam(LXT_DOOR_DIR, LXT_DOOR_PREFIX);
		if (path == NULL)
			return (NULL);
		if (mkfifo(path, S_IWUSR | S_IRUSR) != 0) {
			if (errno != EEXIST) {
				free(path);
				return (NULL);
			}
			/* This file path exists, pick a new name. */
			free(path);
			continue;
		}
		/* We successfully created the fifo */
		break;
	}
	return (path);
}

static void
lxt_door_init()
{
	char		*fifo1_path = NULL, *fifo2_path;
	char		fifo1_path_native[MAXPATHLEN];
	int		fifo1_rd = -1, fifo1_wr = -1;
	int		fifo2_rd = -1, fifo2_wr = -1;
	int		junk;
	pid_t		child_pid;
	thread_t	tid;

	lxt_debug("lxt_door_init: preparint to start server");

	/* Create two new fifos. */
	if (((fifo1_path = lxt_door_mkfifo()) == NULL) ||
	    ((fifo2_path = lxt_door_mkfifo()) == NULL))
		goto fail;

	(void) snprintf(fifo1_path_native, sizeof (fifo1_path_native),
	    "/native%s", fifo1_path);

	/*
	 * Open both fifos for reading and writing.  We have to open
	 * the read side of the fifo first (because the write side will
	 * fail to open if there is no reader) and we have to use the
	 * O_NONBLOCK flag (because the read open with hang without it).
	 */
	if (((fifo1_rd = open(fifo1_path, O_RDONLY | O_NONBLOCK)) < 0) ||
	    ((fifo1_wr = open(fifo1_path, O_WRONLY)) < 0) ||
	    ((fifo2_rd = open(fifo2_path, O_RDONLY | O_NONBLOCK)) < 0) ||
	    ((fifo2_wr = open(fifo2_path, O_WRONLY)) < 0))
		goto fail;

	/*
	 * Now we have to close the read side of fifo1 and fifo2 and re-open
	 * them without the O_NONBLOCK flag.  This is because we're using
	 * the fifos for synchronization and when we actually try to read
	 * from them we want to block.
	 */
	(void) close(fifo1_rd);
	if ((fifo1_rd = open(fifo1_path, O_RDONLY)) < 0)
		goto fail;
	(void) close(fifo2_rd);
	if ((fifo2_rd = open(fifo2_path, O_RDONLY)) < 0)
		goto fail;

	/*
	 * Once fifo2 is opened no one will ever need to open it again
	 * so delete it now.
	 */
	(void) unlink(fifo2_path);
	free(fifo2_path);
	fifo2_path = NULL;

	/* Attempt to fork and start the door server */
	lxt_debug("lxt_door_init: starting server");
	switch (child_pid = fork1()) {
	case -1:
		/* fork1() failed. */
		goto fail;
	case 0:
		/* Child process - new door server. */
		(void) close(fifo1_rd);
		(void) close(fifo2_wr);

		/* Need to chroot back to the real root directory */
		if (fchroot(root_fd) != 0) {
			lxt_debug("lxt_server_exec: "
			    "failed fchroot(\"/\"): %s", strerror(errno));
			exit(-1);
		}
		(void) close(root_fd);

		/* Start the server */
		lxt_server_exec(fifo1_wr, fifo2_rd);
		lxt_debug("lxt_server_exec: server init failed");
		exit(-1);
		/*NOTREACHED*/
	}
	/* Parent process - door client. */

	/*
	 * fifo2 is used to send the door path to the child.
	 * (We can't simply pass it via the address space since the
	 * child will need to exec.)  We'll write the name of the door
	 * file to fifo2 before we close the read end of the fifo2 so
	 * that if the child has exited for some reason we won't get
	 * a SIGPIPE.  Note that we're reusing the name of fifo1 as
	 * the door path.  Also note that we've pre-pended /native
	 * to the fifo/door path.  The reason is that we're chroot'ed
	 * to /native, but when the thunking server executes it will
	 * be chroot'ed back to the real root directory.
	 */
	(void) write(fifo2_wr,
	    fifo1_path_native, strlen(fifo1_path_native) + 1);
	(void) close(fifo2_wr);
	(void) close(fifo2_rd);

	/*
	 * Start up a thread that will perfom a waitpid() on the child
	 * door server process.  We do this because if the calling
	 * application that is using our interfaces is forking it's own
	 * children and using wait(), then it won't expect to see our
	 * children.  We take advantage of the fact that if there are
	 * wait() and a waitpid() calls in progress at the same time
	 * when a child exists,  preference will be given to any
	 * waitpid() calls that are explicity waiting for that child.
	 * There is of course a window of time where the child could
	 * exit after we've forked it but before we've called waitpid()
	 * where another wait() in this process could collect the result.
	 * There's nothing we can really do to prevent this short of
	 * stopping all the other threads in this process.
	 */
	(void) thr_create(NULL, 0,
	    lxt_door_waitpid, (void *)(uintptr_t)child_pid, THR_DAEMON, &tid);

	/*
	 * fifo1 is used for the child process to signal us that the
	 * door server is ready to take requests.
	 */
	(void) close(fifo1_wr);
	(void) read(fifo1_rd, &junk, 1);
	(void) close(fifo1_rd);

	/* If there was a door that was open, close it now. */

	if (lxt_door_fd >= 0)
		(void) close(lxt_door_fd);
	/*
	 * The server should be started up by now and fattach()ed the door
	 * server to the fifo/door path.  so if we re-open that path now we
	 * should get a fd to the door server.
	 */
	lxt_door_fd = open(fifo1_path, O_RDWR);

	lxt_debug("lxt_door_init: new server door = %d", lxt_door_fd);

	/* We don't need the fifo/door anymore so delete it. */
	(void) unlink(fifo1_path);
	free(fifo1_path);
	return;

fail:
	if (fifo1_path != NULL)
		(void) unlink(fifo1_path);
	if (fifo2_path != NULL)
		(void) unlink(fifo2_path);
	if (fifo1_rd != -1)
		(void) close(fifo1_rd);
	if (fifo1_wr != -1)
		(void) close(fifo1_wr);
	if (fifo2_rd != -1)
		(void) close(fifo2_rd);
	if (fifo2_wr != -1)
		(void) close(fifo2_wr);
}

static int
lxt_door_call(door_arg_t *door_arg, int lock_held)
{
	int fd;

	if (!lock_held)
		(void) mutex_lock(&lxt_door_lock);

	/* Get a copy of lxt_door_fd */
	fd = lxt_door_fd;

	if (!lock_held)
		(void) mutex_unlock(&lxt_door_lock);

	if (fd == -1) {
		lxt_debug("lxt_door_call: no door available");
		return (-1);
	}

	if (door_call(fd, door_arg) != 0) {
		lxt_debug("lxt_door_call: call failed");
		return (-1);
	}
	if (door_arg->rbuf == NULL) {
		lxt_debug("lxt_door_call: call returned NULL");
		return (-1);
	}
	return (0);
}

static int
lxt_door_request(door_arg_t *door_arg)
{
	door_arg_t		door_ping;
	lxt_server_arg_t	ping_request, *ping_result;
	int			rv, ping_success = 0;

	/* First just try the door call. */
	lxt_debug("lxt_door_request: calling server");
	if (lxt_door_call(door_arg, 0) == 0)
		return (0);

	/* Prepare a door server ping request. */
	bzero(&door_ping, sizeof (door_ping));
	bzero(&ping_request, sizeof (ping_request));
	door_ping.data_ptr	= (char *)&ping_request;
	door_ping.data_size	= sizeof (ping_request);
	ping_request.lxt_sa_op = LXT_SERVER_OP_PING;

	(void) mutex_lock(&lxt_door_lock);

	/* Ping the doors server. */
	lxt_debug("lxt_door_request: pinging server");
	if (lxt_door_call(&door_ping, 1) == 0) {
		/*LINTED*/
		ping_result = (lxt_server_arg_t *)door_ping.rbuf;
		ping_success = ping_result->lxt_sa_success;
		(void) munmap(door_ping.rbuf, door_ping.rsize);
	}

	if (!ping_success) {
		/* The server is not responding so start up a new one. */
		lxt_door_init();
	}
	(void) mutex_unlock(&lxt_door_lock);

	/* Retry the original request */
	lxt_debug("lxt_door_request: calling server, retry");
	if ((rv = lxt_door_call(door_arg, 0)) == 0)
		return (0);
	return (rv);
}

static struct hostent *
lxt_gethost(int op, const char *token, int token_len, int type,
    struct hostent *result, char *buf, int buf_len, int *h_errnop)
{
	door_arg_t		door_arg;
	lxt_gethost_arg_t	*data;
	lxt_server_arg_t	*request;
	int			request_size, errno_tmp, i;

	lxt_debug("lxt_gethost: request caught");

	request_size = sizeof (*request) + sizeof (*data) +
	    token_len + buf_len - 1;
	if ((request = calloc(1, request_size)) == NULL) {
		lxt_debug("lxt_gethost: calloc() failed");
		*h_errnop = TRY_AGAIN;
		return (NULL);
	}
	/*LINTED*/
	data = (lxt_gethost_arg_t *)&request->lxt_sa_data[0];

	/* Initialize the server request. */
	request->lxt_sa_op = op;
	data->lxt_gh_type = type;
	data->lxt_gh_token_len = token_len;
	data->lxt_gh_buf_len = buf_len;
	data->lxt_gh_storage_len = token_len + token_len;
	bcopy(token, &data->lxt_gh_storage[0], token_len);

	/* Initialize door_call() arguments. */
	bzero(&door_arg, sizeof (door_arg));
	door_arg.data_ptr	= (char *)request;
	door_arg.data_size	= request_size;

	if (lxt_door_request(&door_arg) != 0) {
		lxt_debug("lxt_gethost: door_call() failed");
		/* Don't know what caused the error so clear errno. */
		errno = 0;
		*h_errnop = ND_SYSTEM;
		free(request);
		return (NULL);
	}

	free(request);

	if (door_arg.rbuf == NULL) {
		lxt_debug("lxt_gethost: door_call() returned NULL");
		/* Don't know what caused the error so clear errno. */
		errno = 0;
		*h_errnop = ND_SYSTEM;
		return (NULL);
	}

	/*LINTED*/
	request = (lxt_server_arg_t *)door_arg.rbuf;
	/*LINTED*/
	data = (lxt_gethost_arg_t *)&request->lxt_sa_data[0];

	/* Check if the remote procedure call failed */
	if (!request->lxt_sa_success) {
		lxt_debug("lxt_gethost: remote function call failed");
		errno_tmp = request->lxt_sa_errno;
		*h_errnop = data->lxt_gh_h_errno;
		(void) munmap(door_arg.rbuf, door_arg.rsize);
		errno = errno_tmp;
		return (NULL);
	}

	/* Copy out the results and output buffer. */
	bcopy(&data->lxt_gh_result, result, sizeof (*result));
	bcopy(&data->lxt_gh_storage[token_len], buf, buf_len);
	(void) munmap(door_arg.rbuf, door_arg.rsize);

	/* Now go through the results and convert all offsets to pointers */
	result->h_name = LXT_OFFSET_TO_PTR(result->h_name, buf);
	result->h_aliases = LXT_OFFSET_TO_PTR(result->h_aliases, buf);
	result->h_addr_list = LXT_OFFSET_TO_PTR(result->h_addr_list, buf);
	for (i = 0; result->h_aliases[i] != NULL; i++) {
		result->h_aliases[i] =
		    LXT_OFFSET_TO_PTR(result->h_aliases[i], buf);
	}
	for (i = 0; result->h_addr_list[i] != NULL; i++) {
		result->h_addr_list[i] =
		    LXT_OFFSET_TO_PTR(result->h_addr_list[i], buf);
	}

	return (result);
}

static struct servent *
lxt_getserv(int op, const char *token, const int token_len, const char *proto,
    struct servent *result, char *buf, int buf_len)
{
	door_arg_t		door_arg;
	lxt_getserv_arg_t	*data;
	lxt_server_arg_t	*request;
	int			request_size, errno_tmp, i;

	lxt_debug("lxt_getserv: request caught");

	request_size = sizeof (*request) + sizeof (*data) +
	    token_len + buf_len - 1;
	if ((request = calloc(1, request_size)) == NULL) {
		lxt_debug("lxt_getserv: calloc() failed");
		return (NULL);
	}
	/*LINTED*/
	data = (lxt_getserv_arg_t *)&request->lxt_sa_data[0];

	/* Initialize the server request. */
	request->lxt_sa_op = op;
	data->lxt_gs_token_len = token_len;
	data->lxt_gs_buf_len = buf_len;
	data->lxt_gs_storage_len = token_len + token_len;
	bcopy(token, &data->lxt_gs_storage[0], token_len);

	bzero(data->lxt_gs_proto, sizeof (data->lxt_gs_proto));
	if (proto != NULL)
		(void) strncpy(data->lxt_gs_proto, proto,
		    sizeof (data->lxt_gs_proto));

	/* Initialize door_call() arguments. */
	bzero(&door_arg, sizeof (door_arg));
	door_arg.data_ptr	= (char *)request;
	door_arg.data_size	= request_size;

	/* Call the doors server */
	if (lxt_door_request(&door_arg) != 0) {
		lxt_debug("lxt_getserv: door_call() failed");
		/* Don't know what caused the error so clear errno */
		errno = 0;
		free(request);
		return (NULL);
	}
	free(request);

	if (door_arg.rbuf == NULL) {
		lxt_debug("lxt_getserv: door_call() returned NULL");
		/* Don't know what caused the error so clear errno */
		errno = 0;
		return (NULL);
	}
	/*LINTED*/
	request = (lxt_server_arg_t *)door_arg.rbuf;
	/*LINTED*/
	data = (lxt_getserv_arg_t *)&request->lxt_sa_data[0];

	/* Check if the remote procedure call failed */
	if (!request->lxt_sa_success) {
		lxt_debug("lxt_getserv: remote function call failed");
		errno_tmp = request->lxt_sa_errno;
		(void) munmap(door_arg.rbuf, door_arg.rsize);
		errno = errno_tmp;
		return (NULL);
	}

	/* Copy out the results and output buffer. */
	bcopy(&data->lxt_gs_result, result, sizeof (*result));
	bcopy(&data->lxt_gs_storage[token_len], buf, buf_len);
	(void) munmap(door_arg.rbuf, door_arg.rsize);

	/*
	 * Now go through the results and convert all offsets to pointers.
	 * See the comments in lxt_server_getserv() for why we need
	 * to subtract 1 from each offset.
	 */
	result->s_name = LXT_OFFSET_TO_PTR(result->s_name, buf);
	result->s_proto = LXT_OFFSET_TO_PTR(result->s_proto, buf);
	result->s_aliases = LXT_OFFSET_TO_PTR(result->s_aliases, buf);
	for (i = 0; result->s_aliases[i] != NULL; i++) {
		result->s_aliases[i] =
		    LXT_OFFSET_TO_PTR(result->s_aliases[i], buf);
	}

	return (result);
}

static void
lxt_openlog(const char *ident, int logopt, int facility)
{
	door_arg_t		door_arg;
	lxt_openlog_arg_t	*data;
	lxt_server_arg_t	*request;
	int			request_size;

	request_size = sizeof (*request) + sizeof (*data);
	if ((request = calloc(1, request_size)) == NULL) {
		lxt_debug("lxt_openlog: calloc() failed");
		return;
	}
	/*LINTED*/
	data = (lxt_openlog_arg_t *)&request->lxt_sa_data[0];

	/* Initialize the server request. */
	request->lxt_sa_op = LXT_SERVER_OP_OPENLOG;
	data->lxt_ol_facility = facility;
	data->lxt_ol_logopt = logopt;
	(void) strlcpy(data->lxt_ol_ident, ident, sizeof (data->lxt_ol_ident));

	/* Initialize door_call() arguments. */
	bzero(&door_arg, sizeof (door_arg));
	door_arg.data_ptr	= (char *)request;
	door_arg.data_size	= request_size;

	/* Call the doors server */
	if (lxt_door_request(&door_arg) != 0) {
		lxt_debug("lxt_openlog: door_call() failed");
		free(request);
		return;
	}
	free(request);

	if (door_arg.rbuf == NULL) {
		lxt_debug("lxt_openlog: door_call() returned NULL");
		return;
	}

	/*LINTED*/
	request = (lxt_server_arg_t *)door_arg.rbuf;

	/* Check if the remote procedure call failed */
	if (!request->lxt_sa_success) {
		lxt_debug("lxt_openlog: remote function call failed");
	}
	(void) munmap(door_arg.rbuf, door_arg.rsize);
}

static void
lxt_vsyslog(int priority, const char *message, va_list va)
{
	door_arg_t		door_arg;
	lxt_syslog_arg_t	*data;
	lxt_server_arg_t	*request;
	psinfo_t		p;
	char			procfile[PRFNSZ], *buf, *estr;
	int			buf_len, buf_i, estr_len, request_size, procfd;
	int			i, key, err_count = 0, tok_count = 0;
	int			errno_backup = errno;

	/*
	 * Here we're going to use vsnprintf() to expand the message
	 * string passed in before we hand it off to a Linux process.
	 * Before we can call vsnprintf() we'll need to do modify the
	 * string to deal with certain special tokens.
	 *
	 * syslog() supports a special '%m' format token that expands to
	 * the error message string associated with the current value
	 * of errno.  Unfortunatly if we pass this token to vsnprintf()
	 * it will choke so we need to expand that token manually here.
	 *
	 * We also need to expand any "%%" characters into "%%%%".
	 * The reason is that we'll be calling vsnprintf() which will
	 * translate "%%%%" back to "%%", which is safe to pass to the
	 * Linux version if syslog.  If we didn't do this then vsnprintf()
	 * would translate "%%" to "%" and then the Linux syslog would
	 * attempt to intrepret "%" and whatever character follows it
	 * as a printf format style token.
	 */
	for (key = i = 0; message[i] != '\0'; i++) {
		if (!key && message[i] == '%') {
			key = 1;
			continue;
		}
		if (key && message[i] == '%')
			tok_count++;
		if (key && message[i] == 'm')
			err_count++;
		key = 0;
	}

	/* We found some tokens that we need to expand. */
	if (err_count || tok_count) {
		estr = strerror(errno_backup);
		estr_len = strlen(estr);
		assert(estr_len >= 2);

		/* Allocate a buffer to hold the expanded string. */
		buf_len = i + 1 +
		    (tok_count * 2) + (err_count * (estr_len - 2));
		if ((buf = calloc(1, buf_len)) == NULL) {
			lxt_debug("lxt_vsyslog: calloc() failed");
			return;
		}

		/* Finally, expand %% and %m. */
		for (key = buf_i = i = 0; message[i] != '\0'; i++) {
			assert(buf_i < buf_len);
			if (!key && message[i] == '%') {
				buf[buf_i++] = '%';
				key = 1;
				continue;
			}
			if (key && message[i] == 'm') {
				(void) bcopy(estr, &buf[buf_i - 1], estr_len);
				buf_i += estr_len - 1;
			} else if (key && message[i] == '%') {
				(void) bcopy("%%%%", &buf[buf_i - 1], 4);
				buf_i += 4 - 1;
			} else {
				buf[buf_i++] = message[i];
			}
			key = 0;
		}
		assert(buf[buf_i] == '\0');
		assert(buf_i == (buf_len - 1));

		/* Use the expanded buffer as our format string. */
		message = buf;
	}

	/* Allocate the request we're going to send to the server */
	request_size = sizeof (*request) + sizeof (*data);
	if ((request = calloc(1, request_size)) == NULL) {
		lxt_debug("lxt_vsyslog: calloc() failed");
		return;
	}

	/*LINTED*/
	data = (lxt_syslog_arg_t *)&request->lxt_sa_data[0];

	/* Initialize the server request. */
	request->lxt_sa_op = LXT_SERVER_OP_SYSLOG;
	data->lxt_sl_priority = priority;
	data->lxt_sl_pid = getpid();
	(void) vsnprintf(data->lxt_sl_message, sizeof (data->lxt_sl_message),
	    message, va);

	/* If we did token expansion then free the intermediate buffer. */
	if (err_count || tok_count)
		free(buf);

	/* Add the current program name into the request */
	(void) sprintf(procfile, "/proc/%u/psinfo", (int)getpid());
	/* (void) sprintf(procfile, "/native/proc/%u/psinfo", (int)getpid()); */
	if ((procfd = open(procfile, O_RDONLY)) >= 0) {
		if (read(procfd, &p, sizeof (psinfo_t)) >= 0) {
			(void) strncpy(data->lxt_sl_progname, p.pr_fname,
			    sizeof (data->lxt_sl_progname));
		}
		(void) close(procfd);
	}

	/* Initialize door_call() arguments. */
	bzero(&door_arg, sizeof (door_arg));
	door_arg.data_ptr	= (char *)request;
	door_arg.data_size	= request_size;

	/* Call the doors server */
	if (lxt_door_request(&door_arg) != 0) {
		lxt_debug("lxt_vsyslog: door_call() failed");
		free(request);
		return;
	}
	free(request);

	if (door_arg.rbuf == NULL) {
		lxt_debug("lxt_vsyslog: door_call() returned NULL");
		return;
	}

	/*LINTED*/
	request = (lxt_server_arg_t *)door_arg.rbuf;

	/* Check if the remote procedure call failed */
	if (!request->lxt_sa_success) {
		lxt_debug("lxt_vsyslog: remote function call failed");
	}
	(void) munmap(door_arg.rbuf, door_arg.rsize);
}

static void
lxt_closelog(void)
{
	door_arg_t		door_arg;
	lxt_server_arg_t	*request;
	int			request_size;

	request_size = sizeof (*request);
	if ((request = calloc(1, request_size)) == NULL) {
		lxt_debug("lxt_closelog: calloc() failed");
		return;
	}

	/* Initialize the server request. */
	request->lxt_sa_op = LXT_SERVER_OP_CLOSELOG;

	/* Initialize door_call() arguments. */
	bzero(&door_arg, sizeof (door_arg));
	door_arg.data_ptr	= (char *)request;
	door_arg.data_size	= request_size;

	/* Call the doors server */
	if (lxt_door_request(&door_arg) != 0) {
		lxt_debug("lxt_closelog: door_call() failed");
		free(request);
		return;
	}
	free(request);

	if (door_arg.rbuf == NULL) {
		lxt_debug("lxt_closelog: door_call() returned NULL");
		return;
	}

	/*LINTED*/
	request = (lxt_server_arg_t *)door_arg.rbuf;

	/* Check if the remote procedure call failed */
	if (!request->lxt_sa_success) {
		lxt_debug("lxt_closelog: remote function call failed");
	}
	(void) munmap(door_arg.rbuf, door_arg.rsize);
}

static void
lxt_pset_keep(priv_op_t op, priv_ptype_t type, priv_set_t *pset,
    const char *priv)
{
	if (priv_ismember(pset, priv) == B_TRUE) {
		if (op == PRIV_OFF) {
			(void) priv_delset(pset, priv);
			lxt_debug("lxt_pset_keep: "
			    "preventing drop of \"%s\" from \"%s\" set",
			    priv, type);
		}
	} else {
		if (op == PRIV_SET) {
			(void) priv_addset(pset, priv);
			lxt_debug("lxt_pset_keep: "
			    "preventing drop of \"%s\" from \"%s\" set",
			    priv, type);
		}
	}
}

/*
 * Public interfaces - used by lx_nametoaddr
 */
void
lxt_vdebug(const char *msg, va_list va)
{
	char		buf[LXT_MSG_MAXLEN + 1];
	int		rv, n;

	if (debug_fd == -1)
		return;

	/* Prefix the message with pid/tid. */
	if ((n = snprintf(buf, sizeof (buf), "%u/%u: ",
	    getpid(), thr_self())) == -1)
		return;

	/* Format the message. */
	if (vsnprintf(&buf[n], sizeof (buf) - n, msg, va) == -1)
		return;

	/* Add a carrige return if there isn't one already. */
	if ((buf[strlen(buf) - 1] != '\n') &&
	    (strlcat(buf, "\n", sizeof (buf)) >= sizeof (buf)))
		return;

	/* We retry in case of EINTR */
	do {
		rv = write(debug_fd, buf, strlen(buf));
	} while ((rv == -1) && (errno == EINTR));
}

void
lxt_debug(const char *msg, ...)
{
	va_list		va;
	int		errno_backup;

	if (debug_fd == -1)
		return;

	errno_backup = errno;
	va_start(va, msg);
	lxt_vdebug(msg, va);
	va_end(va);
	errno = errno_backup;
}

struct hostent *
lxt_gethostbyaddr_r(const char *addr, int addr_len, int type,
    struct hostent *result, char *buf, int buf_len, int *h_errnop)
{
	lxt_debug("lxt_gethostbyaddr_r: request recieved");
	return (lxt_gethost(LXT_SERVER_OP_ADDR2HOST,
	    addr, addr_len, type, result, buf, buf_len, h_errnop));
}

struct hostent *
lxt_gethostbyname_r(const char *name,
    struct hostent *result, char *buf, int buf_len, int *h_errnop)
{
	lxt_debug("lxt_gethostbyname_r: request recieved");
	return (lxt_gethost(LXT_SERVER_OP_NAME2HOST,
	    name, strlen(name) + 1, 0, result, buf, buf_len, h_errnop));
}

struct servent *
lxt_getservbyport_r(int port, const char *proto,
    struct servent *result, char *buf, int buf_len)
{
	lxt_debug("lxt_getservbyport_r: request recieved");
	return (lxt_getserv(LXT_SERVER_OP_PORT2SERV,
	    (const char *)&port, sizeof (int), proto, result, buf, buf_len));
}

struct servent *
lxt_getservbyname_r(const char *name, const char *proto,
    struct servent *result, char *buf, int buf_len)
{
	lxt_debug("lxt_getservbyname_r: request recieved");
	return (lxt_getserv(LXT_SERVER_OP_NAME2SERV,
	    name, strlen(name) + 1, proto, result, buf, buf_len));
}

/*
 * "Public" interfaces - used to override public existing interfaces
 */
#pragma weak _close = close
int
close(int fd)
{
	static fp1_t	fp = NULL;

	/*
	 * Don't let the process close our file descriptor that points
	 * back to the root directory.
	 */
	if (fd == root_fd)
		return (0);
	if (fd == debug_fd)
		return (0);

	if (fp == NULL)
		fp = (fp1_t)dlsym(RTLD_NEXT, "close");
	return (fp((uintptr_t)fd));
}

int
_setppriv(priv_op_t op, priv_ptype_t type, const priv_set_t *pset)
{
	static fp3_t	fp = NULL;
	priv_set_t	*pset_new;
	int		rv;

	lxt_debug("_setppriv: request caught");

	if (fp == NULL)
		fp = (fp3_t)dlsym(RTLD_NEXT, "_setppriv");

	while ((pset_new = priv_allocset()) == NULL)
		(void) sleep(1);

	priv_copyset(pset, pset_new);
	lxt_pset_keep(op, type, pset_new, PRIV_PROC_EXEC);
	lxt_pset_keep(op, type, pset_new, PRIV_PROC_FORK);
	lxt_pset_keep(op, type, pset_new, PRIV_PROC_CHROOT);
	lxt_pset_keep(op, type, pset_new, PRIV_FILE_DAC_READ);
	lxt_pset_keep(op, type, pset_new, PRIV_FILE_DAC_WRITE);
	lxt_pset_keep(op, type, pset_new, PRIV_FILE_DAC_SEARCH);

	rv = fp(op, (uintptr_t)type, (uintptr_t)pset_new);
	priv_freeset(pset_new);
	return (rv);
}

void
openlog(const char *ident, int logopt, int facility)
{
	lxt_debug("openlog: request caught");
	lxt_openlog(ident, logopt, facility);
}

void
syslog(int priority, const char *message, ...)
{
	va_list	va;

	lxt_debug("syslog: request caught");
	va_start(va, message);
	lxt_vsyslog(priority, message, va);
	va_end(va);
}

void
vsyslog(int priority, const char *message, va_list va)
{
	lxt_debug("vsyslog: request caught");
	lxt_vsyslog(priority, message, va);
}

void
closelog(void)
{
	lxt_debug("closelog: request caught");
	lxt_closelog();
}