view usr/src/cmd/fs.d/autofs/autod_main.c @ 4:1a15d5aaf794

synchronized with onnv_86 (6202) in onnv-gate
author Koji Uno <koji.uno@sun.com>
date Mon, 31 Aug 2009 14:38:03 +0900
parents c9caec207d52
children
line wrap: on
line source

/*
 * 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"

#include <stdio.h>
#include <stdio_ext.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <memory.h>
#include <stropts.h>
#include <netconfig.h>
#include <stdarg.h>
#include <sys/resource.h>
#include <sys/systeminfo.h>
#include <syslog.h>
#include <errno.h>
#include <sys/sockio.h>
#include <rpc/xdr.h>
#include <net/if.h>
#include <netdir.h>
#include <string.h>
#include <thread.h>
#include <locale.h>
#include <door.h>
#include "automount.h"
#include <sys/vfs.h>
#include <sys/mnttab.h>
#include <arpa/inet.h>
#include <rpcsvc/daemon_utils.h>
#include <deflt.h>
#include <strings.h>
#include <priv.h>
#include <tsol/label.h>
#include <sys/utsname.h>
#include <sys/thread.h>
#include <nfs/rnode.h>
#include <nfs/nfs.h>
#include <wait.h>

static void autofs_doorfunc(void *, char *, size_t, door_desc_t *, uint_t);
static void autofs_setdoor(int);
static void autofs_mntinfo_1_r(autofs_lookupargs *, autofs_mountres *);
static void autofs_mount_1_free_r(struct autofs_mountres *);
static void autofs_lookup_1_r(autofs_lookupargs *, autofs_lookupres *);
static void autofs_lookup_1_free_args(autofs_lookupargs *);
static void autofs_unmount_1_r(umntrequest *, umntres *);
static void autofs_unmount_1_free_args(umntrequest *);
static void autofs_readdir_1_r(autofs_rddirargs *, autofs_rddirres *);
static void autofs_readdir_1_free_r(struct autofs_rddirres *);
static int decode_args(xdrproc_t, autofs_door_args_t *, caddr_t *, int);
static bool_t encode_res(xdrproc_t, autofs_door_res_t **, caddr_t, int *);
static void usage();
static void warn_hup(int);
static void free_action_list();
static int start_autofs_svcs();
static void automountd_wait_for_cleanup(pid_t);

/*
 * Private autofs system call
 */
extern int _autofssys(int, void *);

#define	CTIME_BUF_LEN 26

#define	RESOURCE_FACTOR 8
#ifdef DEBUG
#define	AUTOFS_DOOR	"/var/run/autofs_door"
#endif /* DEBUG */

static thread_key_t	s_thr_key;

struct autodir *dir_head;
struct autodir *dir_tail;
char self[64];

time_t timenow;
int verbose = 0;
int trace = 0;
int automountd_nobrowse = 0;

int
main(argc, argv)
	int argc;
	char *argv[];

{
	pid_t pid;
	int c, error;
	struct rlimit rlset;
	char *defval;

	if (geteuid() != 0) {
		(void) fprintf(stderr, "%s must be run as root\n", argv[0]);
		exit(1);
	}

	/*
	 * Read in the values from config file first before we check
	 * commandline options so the options override the file.
	 */
	if ((defopen(AUTOFSADMIN)) == 0) {
		if ((defval = defread("AUTOMOUNTD_VERBOSE=")) != NULL) {
			if (strncasecmp("true", defval, 4) == 0)
				verbose = TRUE;
			else
				verbose = FALSE;
		}
		if ((defval = defread("AUTOMOUNTD_NOBROWSE=")) != NULL) {
			if (strncasecmp("true", defval, 4) == 0)
				automountd_nobrowse = TRUE;
			else
				automountd_nobrowse = FALSE;
		}
		if ((defval = defread("AUTOMOUNTD_TRACE=")) != NULL) {
			errno = 0;
			trace = strtol(defval, (char **)NULL, 10);
			if (errno != 0)
				trace = 0;
		}
		put_automountd_env();

		/* close defaults file */
		defopen(NULL);
	}

	while ((c = getopt(argc, argv, "vnTD:")) != EOF) {
		switch (c) {
		case 'v':
			verbose++;
			break;
		case 'n':
			automountd_nobrowse++;
			break;
		case 'T':
			trace++;
			break;
		case 'D':
			(void) putenv(optarg);
			break;
		default:
			usage();
		}
	}

	if (sysinfo(SI_HOSTNAME, self, sizeof (self)) == -1) {
		error = errno;
		(void) fprintf(stderr,
			"automountd: can't determine hostname, error: %d\n",
			error);
		exit(1);
	}

#ifndef DEBUG
	pid = fork();
	if (pid < 0) {
		perror("cannot fork");
		exit(1);
	}
	if (pid)
		exit(0);
#endif

	(void) setsid();
	openlog("automountd", LOG_PID, LOG_DAEMON);
	(void) setlocale(LC_ALL, "");

	/*
	 * Create the door_servers to manage fork/exec requests for
	 * mounts and executable automount maps
	 */
	if ((did_fork_exec = door_create(automountd_do_fork_exec,
	    NULL, NULL)) == -1) {
		syslog(LOG_ERR, "door_create failed: %m, Exiting.");
		exit(errno);
	}
	if ((did_exec_map = door_create(automountd_do_exec_map,
	    NULL, NULL)) == -1) {
		syslog(LOG_ERR, "door_create failed: %m, Exiting.");
		if (door_revoke(did_fork_exec) == -1) {
			syslog(LOG_ERR, "failed to door_revoke(%d) %m",
			    did_fork_exec);
		}
		exit(errno);
	}
	/*
	 * Before we become multithreaded we fork allowing the parent
	 * to become a door server to handle all mount and unmount
	 * requests. This works around a potential hang in using
	 * fork1() within a multithreaded environment
	 */

	pid = fork1();
	if (pid < 0) {
		syslog(LOG_ERR,
			"can't fork the automountd mount process %m");
		if (door_revoke(did_fork_exec) == -1) {
			syslog(LOG_ERR, "failed to door_revoke(%d) %m",
				did_fork_exec);
		}
		if (door_revoke(did_exec_map) == -1) {
			syslog(LOG_ERR, "failed to door_revoke(%d) %m",
				did_exec_map);
		}
		exit(1);
	} else if (pid > 0) {
		/* this is the door server process */
		automountd_wait_for_cleanup(pid);
	}


	(void) rwlock_init(&cache_lock, USYNC_THREAD, NULL);
	(void) rwlock_init(&autofs_rddir_cache_lock, USYNC_THREAD, NULL);

	/*
	 * initialize the name services, use NULL arguments to ensure
	 * we don't initialize the stack of files used in file service
	 */
	(void) ns_setup(NULL, NULL);

	/*
	 * we're using doors and its thread management now so we need to
	 * make sure we have more than the default of 256 file descriptors
	 * available.
	 */
	rlset.rlim_cur = RLIM_INFINITY;
	rlset.rlim_max = RLIM_INFINITY;
	if (setrlimit(RLIMIT_NOFILE, &rlset) == -1)
		syslog(LOG_ERR, "setrlimit failed for %s: %s", AUTOMOUNTD,
		    strerror(errno));

	(void) enable_extended_FILE_stdio(-1, -1);

	/*
	 * establish our lock on the lock file and write our pid to it.
	 * exit if some other process holds the lock, or if there's any
	 * error in writing/locking the file.
	 */
	pid = _enter_daemon_lock(AUTOMOUNTD);
	switch (pid) {
	case 0:
		break;
	case -1:
		syslog(LOG_ERR, "error locking for %s: %m", AUTOMOUNTD);
		exit(2);
	default:
		/* daemon was already running */
		exit(0);
	}

	/*
	 * If we coredump it'll be /core.
	 */
	if (chdir("/") < 0)
		syslog(LOG_ERR, "chdir /: %m");

	/*
	 * Create cache_cleanup thread
	 */
	if (thr_create(NULL, 0, (void *(*)(void *))cache_cleanup, NULL,
			THR_DETACHED | THR_DAEMON | THR_NEW_LWP, NULL)) {
		syslog(LOG_ERR, "unable to create cache_cleanup thread");
		exit(1);
	}

	/* other initializations */
	(void) rwlock_init(&portmap_cache_lock, USYNC_THREAD, NULL);

	/*
	 * On a labeled system, allow read-down nfs mounts if privileged
	 * (PRIV_NET_MAC_AWARE) to do so.  Otherwise, ignore the error
	 * and "mount equal label only" behavior will result.
	 */
	if (is_system_labeled()) {
		(void) setpflags(NET_MAC_AWARE, 1);
		(void) setpflags(NET_MAC_AWARE_INHERIT, 1);
	}

	(void) signal(SIGHUP, warn_hup);

	/* start services */
	return (start_autofs_svcs());

}

/*
 * The old automounter supported a SIGHUP
 * to allow it to resynchronize internal
 * state with the /etc/mnttab.
 * This is no longer relevant, but we
 * need to catch the signal and warn
 * the user.
 */
/* ARGSUSED */
static void
warn_hup(i)
	int i;
{
	syslog(LOG_ERR, "SIGHUP received: ignored");
	(void) signal(SIGHUP, warn_hup);
}

static void
usage()
{
	(void) fprintf(stderr, "Usage: automountd\n"
	    "\t[-T]\t\t(trace requests)\n"
	    "\t[-v]\t\t(verbose error msgs)\n"
	    "\t[-D n=s]\t(define env variable)\n");
	exit(1);
	/* NOTREACHED */
}

static void
autofs_readdir_1_r(
	autofs_rddirargs *req,
	autofs_rddirres *res)
{
	if (trace > 0)
		trace_prt(1, "READDIR REQUEST	: %s @ %ld\n",
		    req->rda_map, req->rda_offset);

	do_readdir(req, res);
	if (trace > 0)
		trace_prt(1, "READDIR REPLY	: status=%d\n", res->rd_status);
}

static void
autofs_readdir_1_free_r(struct autofs_rddirres *res)
{
	if (res->rd_status == AUTOFS_OK) {
		if (res->rd_rddir.rddir_entries)
			free(res->rd_rddir.rddir_entries);
	}
}


/* ARGSUSED */
static void
autofs_unmount_1_r(
	umntrequest *m,
	umntres *res)
{
	struct umntrequest *ul;

	if (trace > 0) {
		char ctime_buf[CTIME_BUF_LEN];
		if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL)
			ctime_buf[0] = '\0';

		trace_prt(1, "UNMOUNT REQUEST: %s", ctime_buf);
		for (ul = m; ul; ul = ul->next)
			trace_prt(1, " resource=%s fstype=%s mntpnt=%s"
			    " mntopts=%s %s\n",
			    ul->mntresource,
			    ul->fstype,
			    ul->mntpnt,
			    ul->mntopts,
			    ul->isdirect ? "direct" : "indirect");
	}


	res->status = do_unmount1(m);

	if (trace > 0)
		trace_prt(1, "UNMOUNT REPLY: status=%d\n", res->status);
}

static void
autofs_lookup_1_r(
	autofs_lookupargs *m,
	autofs_lookupres *res)
{
	autofs_action_t action;
	struct	linka link;
	int status;

	if (trace > 0) {
		char ctime_buf[CTIME_BUF_LEN];
		if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL)
			ctime_buf[0] = '\0';

		trace_prt(1, "LOOKUP REQUEST: %s", ctime_buf);
		trace_prt(1, "  name=%s[%s] map=%s opts=%s path=%s direct=%d\n",
		    m->name, m->subdir, m->map, m->opts, m->path, m->isdirect);
	}

	bzero(&link, sizeof (struct linka));

	status = do_lookup1(m->map, m->name, m->subdir, m->opts, m->path,
	    (uint_t)m->isdirect, m->uid, &action, &link);
	if (status == 0) {
		/*
		 * Return action list to kernel.
		 */
		res->lu_res = AUTOFS_OK;
		if ((res->lu_type.action = action) == AUTOFS_LINK_RQ) {
			res->lu_type.lookup_result_type_u.lt_linka = link;
		}
	} else {
		/*
		 * Entry not found
		 */
		res->lu_res = AUTOFS_NOENT;
	}
	res->lu_verbose = verbose;

	if (trace > 0)
		trace_prt(1, "LOOKUP REPLY    : status=%d\n", res->lu_res);
}

static void
autofs_mntinfo_1_r(
	autofs_lookupargs *m,
	autofs_mountres *res)
{
	int status;
	action_list		*alp = NULL;

	if (trace > 0) {
		char ctime_buf[CTIME_BUF_LEN];
		if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL)
			ctime_buf[0] = '\0';

		trace_prt(1, "MOUNT REQUEST:   %s", ctime_buf);
		trace_prt(1, "  name=%s[%s] map=%s opts=%s path=%s direct=%d\n",
		    m->name, m->subdir, m->map, m->opts, m->path, m->isdirect);
	}

	status = do_mount1(m->map, m->name, m->subdir, m->opts, m->path,
	    (uint_t)m->isdirect, m->uid, &alp, DOMOUNT_USER);
	if (status != 0) {
		/*
		 * An error occurred, free action list if allocated.
		 */
		if (alp != NULL) {
			free_action_list(alp);
			alp = NULL;
		}
	}
	if (alp != NULL) {
		/*
		 * Return action list to kernel.
		 */
		res->mr_type.status = AUTOFS_ACTION;
		res->mr_type.mount_result_type_u.list = alp;
	} else {
		/*
		 * No work to do left for the kernel
		 */
		res->mr_type.status = AUTOFS_DONE;
		res->mr_type.mount_result_type_u.error = status;
	}

	if (trace > 0) {
		switch (res->mr_type.status) {
		case AUTOFS_ACTION:
			trace_prt(1,
			    "MOUNT REPLY    : status=%d, AUTOFS_ACTION\n",
			    status);
			break;
		case AUTOFS_DONE:
			trace_prt(1,
			    "MOUNT REPLY    : status=%d, AUTOFS_DONE\n",
			    status);
			break;
		default:
			trace_prt(1, "MOUNT REPLY    : status=%d, UNKNOWN\n",
			    status);
		}
	}

	if (status && verbose) {
		if (m->isdirect) {
			/* direct mount */
			syslog(LOG_ERR, "mount of %s failed", m->path);
		} else {
			/* indirect mount */
			syslog(LOG_ERR,
			    "mount of %s/%s failed", m->path, m->name);
		}
	}
}

static void
autofs_mount_1_free_r(struct autofs_mountres *res)
{
	if (res->mr_type.status == AUTOFS_ACTION) {
		if (trace > 2)
			trace_prt(1, "freeing action list\n");
		free_action_list(res->mr_type.mount_result_type_u.list);
	}
}

/*
 * Used for reporting messages from code shared with automount command.
 * Formats message into a buffer and calls syslog.
 *
 * Print an error.  Works like printf (fmt string and variable args)
 * except that it will subsititute an error message for a "%m" string
 * (like syslog).
 */
void
pr_msg(const char *fmt, ...)
{
	va_list ap;
	char fmtbuff[BUFSIZ], buff[BUFSIZ];
	const char *p1;
	char *p2;

	p2 = fmtbuff;
	fmt = gettext(fmt);

	for (p1 = fmt; *p1; p1++) {
		if (*p1 == '%' && *(p1 + 1) == 'm') {
			(void) strcpy(p2, strerror(errno));
			p2 += strlen(p2);
			p1++;
		} else {
			*p2++ = *p1;
		}
	}
	if (p2 > fmtbuff && *(p2-1) != '\n')
		*p2++ = '\n';
	*p2 = '\0';

	va_start(ap, fmt);
	(void) vsprintf(buff, fmtbuff, ap);
	va_end(ap);
	syslog(LOG_ERR, buff);
}

static void
free_action_list(action_list *alp)
{
	action_list *p, *next = NULL;
	struct mounta *mp;

	for (p = alp; p != NULL; p = next) {
		switch (p->action.action) {
		case AUTOFS_MOUNT_RQ:
			mp = &(p->action.action_list_entry_u.mounta);
			/* LINTED pointer alignment */
			if (mp->fstype) {
				if (strcmp(mp->fstype, "autofs") == 0) {
					free_autofs_args((autofs_args *)
					    mp->dataptr);
				} else if (strncmp(mp->fstype, "nfs", 3) == 0) {
					free_nfs_args((struct nfs_args *)
					    mp->dataptr);
				}
			}
			mp->dataptr = NULL;
			mp->datalen = 0;
			free_mounta(mp);
			break;
		case AUTOFS_LINK_RQ:
			syslog(LOG_ERR,
			    "non AUTOFS_MOUNT_RQ requests not implemented\n");
			break;
		default:
			syslog(LOG_ERR,
			    "non AUTOFS_MOUNT_RQ requests not implemented\n");
			break;
		}
		next = p->next;
		free(p);
	}
}

static void
autofs_lookup_1_free_args(autofs_lookupargs *args)
{
	if (args->map)
		free(args->map);
	if (args->path)
		free(args->path);
	if (args->name)
		free(args->name);
	if (args->subdir)
		free(args->subdir);
	if (args->opts)
		free(args->opts);
}

static void
autofs_unmount_1_free_args(umntrequest *args)
{
	if (args->mntresource)
		free(args->mntresource);
	if (args->mntpnt)
		free(args->mntpnt);
	if (args->fstype)
		free(args->fstype);
	if (args->mntopts)
		free(args->mntopts);
	if (args->next)
		autofs_unmount_1_free_args(args->next);
}

static void
autofs_setdoor(int did)
{

	if (did < 0) {
		did = 0;
	}

	(void) _autofssys(AUTOFS_SETDOOR, &did);
}

void *
autofs_get_buffer(size_t size)
{
	autofs_tsd_t *tsd = NULL;

	/*
	 * Make sure the buffer size is aligned
	 */
	(void) thr_getspecific(s_thr_key, (void **)&tsd);
	if (tsd == NULL) {
		tsd = (autofs_tsd_t *)malloc(sizeof (autofs_tsd_t));
		if (tsd == NULL) {
			return (NULL);
		}
		tsd->atsd_buf = malloc(size);
		if (tsd->atsd_buf != NULL)
			tsd->atsd_len = size;
		else
			tsd->atsd_len = 0;
		(void) thr_setspecific(s_thr_key, tsd);
	} else {
		if (tsd->atsd_buf && (tsd->atsd_len < size)) {
			free(tsd->atsd_buf);
			tsd->atsd_buf = malloc(size);
			if (tsd->atsd_buf != NULL)
				tsd->atsd_len = size;
			else {
				tsd->atsd_len = 0;
			}
		}
	}
	if (tsd->atsd_buf) {
		bzero(tsd->atsd_buf, size);
		return (tsd->atsd_buf);
	} else {
		syslog(LOG_ERR,
		    gettext("Can't Allocate tsd buffer, size %d"), size);
		return (NULL);
	}
}

/*
 * Each request will automatically spawn a new thread with this
 * as its entry point.
 */
/* ARGUSED */
static void
autofs_doorfunc(
	void *cookie,
	char *argp,
	size_t arg_size,
	door_desc_t *dp,
	uint_t n_desc)
{
	char			*res;
	int			 res_size;
	int			 which;
	int			 error = 0;
	int			 srsz = 0;
	autofs_lookupargs	*xdrargs;
	autofs_lookupres	 lookup_res;
	autofs_rddirargs	*rddir_args;
	autofs_rddirres		 rddir_res;
	autofs_mountres		 mount_res;
	umntrequest		*umnt_args;
	umntres			 umount_res;
	autofs_door_res_t	*door_res;
	autofs_door_res_t	 failed_res;

	if (arg_size < sizeof (autofs_door_args_t)) {
		failed_res.res_status = EINVAL;
		error = door_return((char *)&failed_res,
		    sizeof (autofs_door_res_t), NULL, 0);
		/*
		 * If we got here the door_return() failed.
		 */
		syslog(LOG_ERR, "Bad argument, door_return failure %d", error);
		return;
	}

	timenow = time((time_t *)NULL);

	which = ((autofs_door_args_t *)argp)->cmd;
	switch (which) {
	case AUTOFS_LOOKUP:
		if (error = decode_args(xdr_autofs_lookupargs,
		    (autofs_door_args_t *)argp, (caddr_t *)&xdrargs,
		    sizeof (autofs_lookupargs))) {
			syslog(LOG_ERR,
			    "error allocating lookup arguments buffer");
			failed_res.res_status = error;
			failed_res.xdr_len = 0;
			res = (caddr_t)&failed_res;
			res_size = 0;
			break;
		}
		bzero(&lookup_res, sizeof (autofs_lookupres));

		autofs_lookup_1_r(xdrargs, &lookup_res);

		autofs_lookup_1_free_args(xdrargs);
		free(xdrargs);

		if (!encode_res(xdr_autofs_lookupres, &door_res,
		    (caddr_t)&lookup_res, &res_size)) {
			syslog(LOG_ERR,
			    "error allocating lookup results buffer");
			failed_res.res_status = EINVAL;
			failed_res.xdr_len = 0;
			res = (caddr_t)&failed_res;
		} else {
			door_res->res_status = 0;
			res = (caddr_t)door_res;
		}
		break;

	case AUTOFS_MNTINFO:
		if (error = decode_args(xdr_autofs_lookupargs,
		    (autofs_door_args_t *)argp, (caddr_t *)&xdrargs,
		    sizeof (autofs_lookupargs))) {
			syslog(LOG_ERR,
			    "error allocating lookup arguments buffer");
			failed_res.res_status = error;
			failed_res.xdr_len = 0;
			res = (caddr_t)&failed_res;
			res_size = 0;
			break;
		}

		autofs_mntinfo_1_r((autofs_lookupargs *)xdrargs, &mount_res);

		autofs_lookup_1_free_args(xdrargs);
		free(xdrargs);

		/*
		 * Only reason we would get a NULL res is because
		 * we could not allocate a results buffer.  Use
		 * a local one to return the error EAGAIN as has
		 * always been done when memory allocations fail.
		 */
		if (!encode_res(xdr_autofs_mountres, &door_res,
		    (caddr_t)&mount_res, &res_size)) {
			syslog(LOG_ERR,
			    "error allocating mount results buffer");
			failed_res.res_status = EAGAIN;
			failed_res.xdr_len = 0;
			res = (caddr_t)&failed_res;
		} else {
			door_res->res_status = 0;
			res = (caddr_t)door_res;
		}
		autofs_mount_1_free_r(&mount_res);
		break;

	case AUTOFS_UNMOUNT:
		if (error = decode_args(xdr_umntrequest,
		    (autofs_door_args_t *)argp,
		    (caddr_t *)&umnt_args, sizeof (umntrequest))) {
			syslog(LOG_ERR,
			    "error allocating unmount argument buffer");
			failed_res.res_status = error;
			failed_res.xdr_len = 0;
			res = (caddr_t)&failed_res;
			res_size = sizeof (autofs_door_res_t);
			break;
		}

		autofs_unmount_1_r(umnt_args, &umount_res);

		error = umount_res.status;

		autofs_unmount_1_free_args(umnt_args);
		free(umnt_args);

		if (!encode_res(xdr_umntres, &door_res, (caddr_t)&umount_res,
		    &res_size)) {
			syslog(LOG_ERR,
			    "error allocating unmount results buffer");
			failed_res.res_status = EINVAL;
			failed_res.xdr_len = 0;
			res = (caddr_t)&failed_res;
			res_size = sizeof (autofs_door_res_t);
		} else {
			door_res->res_status = 0;
			res = (caddr_t)door_res;
		}
		break;

	case AUTOFS_READDIR:
		if (error = decode_args(xdr_autofs_rddirargs,
		    (autofs_door_args_t *)argp,
		    (caddr_t *)&rddir_args,
		    sizeof (autofs_rddirargs))) {
			syslog(LOG_ERR,
			    "error allocating readdir argument buffer");
			failed_res.res_status = error;
			failed_res.xdr_len = 0;
			res = (caddr_t)&failed_res;
			res_size = sizeof (autofs_door_res_t);
			break;
		}

		autofs_readdir_1_r(rddir_args, &rddir_res);

		free(rddir_args->rda_map);
		free(rddir_args);

		if (!encode_res(xdr_autofs_rddirres, &door_res,
		    (caddr_t)&rddir_res, &res_size)) {
			syslog(LOG_ERR,
			    "error allocating readdir results buffer");
			failed_res.res_status = ENOMEM;
			failed_res.xdr_len = 0;
			res = (caddr_t)&failed_res;
			res_size = sizeof (autofs_door_res_t);
		} else {
			door_res->res_status = 0;
			res = (caddr_t)door_res;
		}
		autofs_readdir_1_free_r(&rddir_res);
		break;
#ifdef MALLOC_DEBUG
	case AUTOFS_DUMP_DEBUG:
			check_leaks("/var/tmp/automountd.leak");
			error = door_return(NULL, 0, NULL, 0);
			/*
			 * If we got here, door_return() failed
			 */
			syslog(LOG_ERR, "dump debug door_return failure %d",
			    error);
			return;
#endif
	case NULLPROC:
			res = NULL;
			res_size = 0;
			break;
	default:
			failed_res.res_status = EINVAL;
			res = (char *)&failed_res;
			res_size = sizeof (autofs_door_res_t);
			break;
	}

	srsz = res_size;
	errno = 0;
	error = door_return(res, res_size, NULL, 0);

	if (errno == E2BIG) {
		/*
		 * Failed due to encoded results being bigger than the
		 * kernel expected bufsize. Passing actual results size
		 * back down to kernel.
		 */
		failed_res.res_status = EOVERFLOW;
		failed_res.xdr_len = srsz;
		res = (caddr_t)&failed_res;
		res_size = sizeof (autofs_door_res_t);
	} else {
		syslog(LOG_ERR, "door_return failed %d, buffer %p, "
		    "buffer size %d", error, (void *)res, res_size);
		res = NULL;
		res_size = 0;
	}
	(void) door_return(res, res_size, NULL, 0);
	/* NOTREACHED */
}

static int
start_autofs_svcs(void)
{
	int doorfd;
#ifdef DEBUG
	int dfd;
#endif

	if ((doorfd = door_create(autofs_doorfunc, NULL,
	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
		syslog(LOG_ERR, gettext("Unable to create door\n"));
		return (1);
	}

#ifdef DEBUG
	/*
	 * Create a file system path for the door
	 */
	if ((dfd = open(AUTOFS_DOOR, O_RDWR|O_CREAT|O_TRUNC,
	    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
		syslog(LOG_ERR, "Unable to open %s: %m\n", AUTOFS_DOOR);
		(void) close(doorfd);
		return (1);
	}

	/*
	 * stale associations clean up
	 */
	(void) fdetach(AUTOFS_DOOR);

	/*
	 * Register in the namespace to the kernel to door_ki_open.
	 */
	if (fattach(doorfd, AUTOFS_DOOR) == -1) {
		syslog(LOG_ERR, "Unable to fattach door %m\n", AUTOFS_DOOR);
		(void) close(dfd);
		(void) close(doorfd);
		return (1);
	}
#endif /* DEBUG */

	/*
	 * Pass door name to kernel for door_ki_open
	 */
	autofs_setdoor(doorfd);

	(void) thr_keycreate(&s_thr_key, NULL);

	/*
	 * Wait for incoming calls
	 */
	/*CONSTCOND*/
	while (1)
		(void) pause();

	/* NOTREACHED */
	syslog(LOG_ERR, gettext("Door server exited"));
	return (10);
}

static int
decode_args(
	xdrproc_t xdrfunc,
	autofs_door_args_t *argp,
	caddr_t *xdrargs,
	int size)
{
	XDR xdrs;

	caddr_t tmpargs = (caddr_t)&((autofs_door_args_t *)argp)->xdr_arg;
	size_t arg_size = ((autofs_door_args_t *)argp)->xdr_len;

	xdrmem_create(&xdrs, tmpargs, arg_size, XDR_DECODE);

	*xdrargs = malloc(size);
	if (*xdrargs == NULL) {
		syslog(LOG_ERR, "error allocating arguments buffer");
		return (ENOMEM);
	}

	bzero(*xdrargs, size);

	if (!(*xdrfunc)(&xdrs, *xdrargs)) {
		free(*xdrargs);
		*xdrargs = NULL;
		syslog(LOG_ERR, "error decoding arguments");
		return (EINVAL);
	}

	return (0);
}


static bool_t
encode_res(
	xdrproc_t xdrfunc,
	autofs_door_res_t **results,
	caddr_t resp,
	int *size)
{
	XDR xdrs;

	*size = xdr_sizeof((*xdrfunc), resp);
	*results = autofs_get_buffer(
	    sizeof (autofs_door_res_t) + *size);
	if (*results == NULL) {
		(*results)->res_status = ENOMEM;
		return (FALSE);
	}
	(*results)->xdr_len = *size;
	*size = sizeof (autofs_door_res_t) + (*results)->xdr_len;
	xdrmem_create(&xdrs, (caddr_t)((*results)->xdr_res),
	    (*results)->xdr_len, XDR_ENCODE);
	if (!(*xdrfunc)(&xdrs, resp)) {
		(*results)->res_status = EINVAL;
		syslog(LOG_ERR, "error encoding results");
		return (FALSE);
	}
	(*results)->res_status = 0;
	return (TRUE);
}

static void
automountd_wait_for_cleanup(pid_t pid)
{
	int status;
	int child_exitval;

	/*
	 * Wait for the main automountd process to exit so we cleanup
	 */
	(void) waitpid(pid, &status, 0);

	child_exitval = WEXITSTATUS(status);

	/*
	 * Shutdown the door server for mounting and unmounting
	 * filesystems
	 */
	if (door_revoke(did_fork_exec) == -1) {
		syslog(LOG_ERR, "failed to door_revoke(%d) %m", did_fork_exec);
	}
	if (door_revoke(did_exec_map) == -1) {
		syslog(LOG_ERR, "failed to door_revoke(%d) %m", did_exec_map);
	}
	exit(child_exitval);
}