xref: /illumos-gate/usr/src/cmd/fs.d/autofs/autod_main.c (revision 40cb5e5daa7b80bb70fcf8dadfb20f9281566331)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdio.h>
29 #include <stdio_ext.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <signal.h>
33 #include <sys/types.h>
34 #include <memory.h>
35 #include <stropts.h>
36 #include <netconfig.h>
37 #include <stdarg.h>
38 #include <sys/resource.h>
39 #include <sys/systeminfo.h>
40 #include <syslog.h>
41 #include <errno.h>
42 #include <sys/sockio.h>
43 #include <rpc/xdr.h>
44 #include <net/if.h>
45 #include <netdir.h>
46 #include <string.h>
47 #include <thread.h>
48 #include <locale.h>
49 #include <ucred.h>
50 #include <door.h>
51 #include "automount.h"
52 #include <sys/vfs.h>
53 #include <sys/mnttab.h>
54 #include <arpa/inet.h>
55 #include <rpcsvc/daemon_utils.h>
56 #include <deflt.h>
57 #include <strings.h>
58 #include <priv.h>
59 #include <tsol/label.h>
60 #include <sys/utsname.h>
61 #include <sys/thread.h>
62 #include <nfs/rnode.h>
63 #include <nfs/nfs.h>
64 
65 static void autofs_doorfunc(void *, char *, size_t, door_desc_t *, uint_t);
66 static void autofs_setdoor(int);
67 static void autofs_mntinfo_1_r(autofs_lookupargs *,
68 		autofs_mountres *, ucred_t *);
69 static void autofs_mount_1_free_r(struct autofs_mountres *);
70 static void autofs_lookup_1_r(autofs_lookupargs *,
71 		autofs_lookupres *, ucred_t *);
72 static void autofs_lookup_1_free_args(autofs_lookupargs *);
73 static void autofs_unmount_1_r(umntrequest *, umntres *,
74 		ucred_t *);
75 static void autofs_unmount_1_free_args(umntrequest *);
76 static void autofs_readdir_1_r(autofs_rddirargs *,
77 		autofs_rddirres *, ucred_t *);
78 static void autofs_readdir_1_free_r(struct autofs_rddirres *);
79 static int decode_args(xdrproc_t, autofs_door_args_t *, caddr_t *, int);
80 static bool_t encode_res(xdrproc_t, autofs_door_res_t **, caddr_t, int *);
81 static void usage();
82 static void warn_hup(int);
83 static void free_action_list();
84 static int start_autofs_svcs();
85 
86 /*
87  * Private autofs system call
88  */
89 extern int _autofssys(int, void *);
90 
91 #define	CTIME_BUF_LEN 26
92 
93 /*
94  * XXX - this limit was imposed due to resource problems - even though
95  * we can and do try and set the rlimit to be able to handle more threads,
96  * fopen() doesn't allow more than 256 fp's.
97  */
98 #define	MAXTHREADS 64
99 
100 #define	RESOURCE_FACTOR 8
101 #ifdef DEBUG
102 #define	AUTOFS_DOOR	"/var/run/autofs_door"
103 #endif /* DEBUG */
104 
105 
106 static thread_key_t	s_thr_key;
107 
108 struct autodir *dir_head;
109 struct autodir *dir_tail;
110 char self[64];
111 
112 time_t timenow;
113 int verbose = 0;
114 int trace = 0;
115 int automountd_nobrowse = 0;
116 
117 int
118 main(argc, argv)
119 	int argc;
120 	char *argv[];
121 
122 {
123 	pid_t pid;
124 	int c, error;
125 	struct rlimit rlset;
126 	char *defval;
127 
128 	if (geteuid() != 0) {
129 		(void) fprintf(stderr, "%s must be run as root\n", argv[0]);
130 		exit(1);
131 	}
132 
133 	/*
134 	 * Read in the values from config file first before we check
135 	 * commandline options so the options override the file.
136 	 */
137 	if ((defopen(AUTOFSADMIN)) == 0) {
138 		if ((defval = defread("AUTOMOUNTD_VERBOSE=")) != NULL) {
139 			if (strncasecmp("true", defval, 4) == 0)
140 				verbose = TRUE;
141 			else
142 				verbose = FALSE;
143 		}
144 		if ((defval = defread("AUTOMOUNTD_NOBROWSE=")) != NULL) {
145 			if (strncasecmp("true", defval, 4) == 0)
146 				automountd_nobrowse = TRUE;
147 			else
148 				automountd_nobrowse = FALSE;
149 		}
150 		if ((defval = defread("AUTOMOUNTD_TRACE=")) != NULL) {
151 			errno = 0;
152 			trace = strtol(defval, (char **)NULL, 10);
153 			if (errno != 0)
154 				trace = 0;
155 		}
156 		put_automountd_env();
157 
158 		/* close defaults file */
159 		defopen(NULL);
160 	}
161 
162 	while ((c = getopt(argc, argv, "vnTD:")) != EOF) {
163 		switch (c) {
164 		case 'v':
165 			verbose++;
166 			break;
167 		case 'n':
168 			automountd_nobrowse++;
169 			break;
170 		case 'T':
171 			trace++;
172 			break;
173 		case 'D':
174 			(void) putenv(optarg);
175 			break;
176 		default:
177 			usage();
178 		}
179 	}
180 
181 	if (sysinfo(SI_HOSTNAME, self, sizeof (self)) == -1) {
182 		error = errno;
183 		(void) fprintf(stderr,
184 			"automountd: can't determine hostname, error: %d\n",
185 			error);
186 		exit(1);
187 	}
188 
189 #ifndef DEBUG
190 	pid = fork();
191 	if (pid < 0) {
192 		perror("cannot fork");
193 		exit(1);
194 	}
195 	if (pid)
196 		exit(0);
197 #endif
198 
199 	(void) setsid();
200 	openlog("automountd", LOG_PID, LOG_DAEMON);
201 	(void) setlocale(LC_ALL, "");
202 
203 	(void) rwlock_init(&cache_lock, USYNC_THREAD, NULL);
204 	(void) rwlock_init(&autofs_rddir_cache_lock, USYNC_THREAD, NULL);
205 
206 	/*
207 	 * initialize the name services, use NULL arguments to ensure
208 	 * we don't initialize the stack of files used in file service
209 	 */
210 	(void) ns_setup(NULL, NULL);
211 
212 	/*
213 	 * establish our lock on the lock file and write our pid to it.
214 	 * exit if some other process holds the lock, or if there's any
215 	 * error in writing/locking the file.
216 	 */
217 	pid = _enter_daemon_lock(AUTOMOUNTD);
218 	switch (pid) {
219 	case 0:
220 		break;
221 	case -1:
222 		syslog(LOG_ERR, "error locking for %s: %s", AUTOMOUNTD,
223 		    strerror(errno));
224 		exit(2);
225 	default:
226 		/* daemon was already running */
227 		exit(0);
228 	}
229 
230 	/*
231 	 * If we coredump it'll be /core.
232 	 */
233 	if (chdir("/") < 0)
234 		syslog(LOG_ERR, "chdir /: %m");
235 
236 	/*
237 	 * Create cache_cleanup thread
238 	 */
239 	if (thr_create(NULL, 0, (void *(*)(void *))cache_cleanup, NULL,
240 			THR_DETACHED | THR_DAEMON | THR_NEW_LWP, NULL)) {
241 		syslog(LOG_ERR, "unable to create cache_cleanup thread");
242 		exit(1);
243 	}
244 
245 	/* other initializations */
246 	(void) rwlock_init(&portmap_cache_lock, USYNC_THREAD, NULL);
247 
248 	/* on a labeled system, the automounter implements read-down policy */
249 	if (is_system_labeled()) {
250 		if ((setpflags(NET_MAC_AWARE, 1) == -1) ||
251 		    (setpflags(NET_MAC_AWARE_INHERIT, 1) == -1))
252 			syslog(LOG_ERR, "ignored failure to set MAC-aware "
253 			    "mode: %m");
254 	}
255 
256 	(void) signal(SIGHUP, warn_hup);
257 
258 	/* start services */
259 	return (start_autofs_svcs());
260 
261 }
262 
263 /*
264  * The old automounter supported a SIGHUP
265  * to allow it to resynchronize internal
266  * state with the /etc/mnttab.
267  * This is no longer relevant, but we
268  * need to catch the signal and warn
269  * the user.
270  */
271 /* ARGSUSED */
272 static void
273 warn_hup(i)
274 	int i;
275 {
276 	syslog(LOG_ERR, "SIGHUP received: ignored");
277 	(void) signal(SIGHUP, warn_hup);
278 }
279 
280 static void
281 usage()
282 {
283 	(void) fprintf(stderr, "Usage: automountd\n"
284 	    "\t[-T]\t\t(trace requests)\n"
285 	    "\t[-v]\t\t(verbose error msgs)\n"
286 	    "\t[-D n=s]\t(define env variable)\n");
287 	exit(1);
288 	/* NOTREACHED */
289 }
290 
291 static void
292 autofs_readdir_1_r(
293 	autofs_rddirargs *req,
294 	autofs_rddirres *res,
295 	ucred_t	*autofs_cred)
296 {
297 	if (trace > 0)
298 		trace_prt(1, "READDIR REQUEST	: %s @ %ld\n",
299 		req->rda_map, req->rda_offset);
300 
301 	do_readdir(req, res, autofs_cred);
302 	if (trace > 0)
303 		trace_prt(1, "READDIR REPLY	: status=%d\n",
304 			res->rd_status);
305 }
306 
307 static void
308 autofs_readdir_1_free_r(struct autofs_rddirres *res)
309 {
310 	if (res->rd_status == AUTOFS_OK) {
311 		if (res->rd_rddir.rddir_entries)
312 			free(res->rd_rddir.rddir_entries);
313 	}
314 }
315 
316 
317 /* ARGSUSED */
318 static void
319 autofs_unmount_1_r(
320 	umntrequest *m,
321 	umntres *res,
322 	ucred_t	*autofs_cred)
323 {
324 	struct umntrequest *ul;
325 
326 	if (trace > 0) {
327 		char ctime_buf[CTIME_BUF_LEN];
328 		if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL)
329 		    ctime_buf[0] = '\0';
330 
331 		trace_prt(1, "UNMOUNT REQUEST: %s", ctime_buf);
332 		for (ul = m; ul; ul = ul->next)
333 			trace_prt(1, " resource=%s fstype=%s mntpnt=%s"
334 				" mntopts=%s %s\n",
335 				ul->mntresource,
336 				ul->fstype,
337 				ul->mntpnt,
338 				ul->mntopts,
339 				ul->isdirect ? "direct" : "indirect");
340 	}
341 
342 
343 	res->status = do_unmount1(m);
344 
345 	if (trace > 0)
346 		trace_prt(1, "UNMOUNT REPLY: status=%d\n", res->status);
347 }
348 
349 static void
350 autofs_lookup_1_r(
351 	autofs_lookupargs *m,
352 	autofs_lookupres *res,
353 	ucred_t	*autofs_cred)
354 {
355 	autofs_action_t action;
356 	struct	linka link;
357 	int status;
358 
359 	if (trace > 0) {
360 		char ctime_buf[CTIME_BUF_LEN];
361 		if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL)
362 			ctime_buf[0] = '\0';
363 
364 		trace_prt(1, "LOOKUP REQUEST: %s", ctime_buf);
365 		trace_prt(1, "  name=%s[%s] map=%s opts=%s path=%s direct=%d\n",
366 			m->name, m->subdir, m->map, m->opts,
367 			m->path, m->isdirect);
368 	}
369 
370 	bzero(&link, sizeof (struct linka));
371 
372 	status = do_lookup1(m->map, m->name, m->subdir, m->opts, m->path,
373 			(uint_t)m->isdirect, &action, &link, autofs_cred);
374 	if (status == 0) {
375 		/*
376 		 * Return action list to kernel.
377 		 */
378 		res->lu_res = AUTOFS_OK;
379 		if ((res->lu_type.action = action) == AUTOFS_LINK_RQ) {
380 			res->lu_type.lookup_result_type_u.lt_linka = link;
381 		}
382 	} else {
383 		/*
384 		 * Entry not found
385 		 */
386 		res->lu_res = AUTOFS_NOENT;
387 	}
388 	res->lu_verbose = verbose;
389 
390 	if (trace > 0)
391 		trace_prt(1, "LOOKUP REPLY    : status=%d\n", res->lu_res);
392 }
393 
394 static void
395 autofs_mntinfo_1_r(
396 	autofs_lookupargs *m,
397 	autofs_mountres *res,
398 	ucred_t	*autofs_cred)
399 {
400 	int status;
401 	action_list		*alp = NULL;
402 
403 	if (trace > 0) {
404 		char ctime_buf[CTIME_BUF_LEN];
405 		if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL)
406 			ctime_buf[0] = '\0';
407 
408 		trace_prt(1, "MOUNT REQUEST:   %s", ctime_buf);
409 		trace_prt(1, "  name=%s[%s] map=%s opts=%s path=%s direct=%d\n",
410 			m->name, m->subdir, m->map, m->opts,
411 			m->path, m->isdirect);
412 	}
413 
414 	status = do_mount1(m->map, m->name, m->subdir, m->opts, m->path,
415 			(uint_t)m->isdirect, &alp, autofs_cred, DOMOUNT_USER);
416 	if (status != 0) {
417 		/*
418 		 * An error occurred, free action list if allocated.
419 		 */
420 		if (alp != NULL) {
421 			free_action_list(alp);
422 			alp = NULL;
423 		}
424 	}
425 	if (alp != NULL) {
426 		/*
427 		 * Return action list to kernel.
428 		 */
429 		res->mr_type.status = AUTOFS_ACTION;
430 		res->mr_type.mount_result_type_u.list = alp;
431 	} else {
432 		/*
433 		 * No work to do left for the kernel
434 		 */
435 		res->mr_type.status = AUTOFS_DONE;
436 		res->mr_type.mount_result_type_u.error = status;
437 	}
438 
439 	if (trace > 0) {
440 		switch (res->mr_type.status) {
441 		case AUTOFS_ACTION:
442 			trace_prt(1,
443 				"MOUNT REPLY    : status=%d, AUTOFS_ACTION\n",
444 				status);
445 			break;
446 		case AUTOFS_DONE:
447 			trace_prt(1,
448 				"MOUNT REPLY    : status=%d, AUTOFS_DONE\n",
449 				status);
450 			break;
451 		default:
452 			trace_prt(1, "MOUNT REPLY    : status=%d, UNKNOWN\n",
453 				status);
454 		}
455 	}
456 
457 	if (status && verbose) {
458 		if (m->isdirect) {
459 			/* direct mount */
460 			syslog(LOG_ERR, "mount of %s failed", m->path);
461 		} else {
462 			/* indirect mount */
463 			syslog(LOG_ERR,
464 				"mount of %s/%s failed", m->path, m->name);
465 		}
466 	}
467 }
468 
469 static void
470 autofs_mount_1_free_r(struct autofs_mountres *res)
471 {
472 	if (res->mr_type.status == AUTOFS_ACTION) {
473 		if (trace > 2)
474 			trace_prt(1, "freeing action list\n");
475 		free_action_list(res->mr_type.mount_result_type_u.list);
476 	}
477 }
478 
479 /*
480  * Used for reporting messages from code shared with automount command.
481  * Formats message into a buffer and calls syslog.
482  *
483  * Print an error.  Works like printf (fmt string and variable args)
484  * except that it will subsititute an error message for a "%m" string
485  * (like syslog).
486  */
487 void
488 pr_msg(const char *fmt, ...)
489 {
490 	va_list ap;
491 	char fmtbuff[BUFSIZ], buff[BUFSIZ];
492 	const char *p1;
493 	char *p2;
494 
495 	p2 = fmtbuff;
496 	fmt = gettext(fmt);
497 
498 	for (p1 = fmt; *p1; p1++) {
499 		if (*p1 == '%' && *(p1 + 1) == 'm') {
500 			(void) strcpy(p2, strerror(errno));
501 			p2 += strlen(p2);
502 			p1++;
503 		} else {
504 			*p2++ = *p1;
505 		}
506 	}
507 	if (p2 > fmtbuff && *(p2-1) != '\n')
508 		*p2++ = '\n';
509 	*p2 = '\0';
510 
511 	va_start(ap, fmt);
512 	(void) vsprintf(buff, fmtbuff, ap);
513 	va_end(ap);
514 	syslog(LOG_ERR, buff);
515 }
516 
517 static void
518 free_action_list(action_list *alp)
519 {
520 	action_list *p, *next = NULL;
521 	struct mounta *mp;
522 
523 	for (p = alp; p != NULL; p = next) {
524 		switch (p->action.action) {
525 		case AUTOFS_MOUNT_RQ:
526 			mp = &(p->action.action_list_entry_u.mounta);
527 			/* LINTED pointer alignment */
528 			if (mp->fstype) {
529 				if (strcmp(mp->fstype, "autofs") == 0) {
530 					free_autofs_args((autofs_args *)
531 						mp->dataptr);
532 				} else if (strncmp(mp->fstype,
533 					"nfs", 3) == 0) {
534 					free_nfs_args((struct nfs_args *)
535 						mp->dataptr);
536 				}
537 			}
538 			mp->dataptr = NULL;
539 			mp->datalen = 0;
540 			free_mounta(mp);
541 			break;
542 		case AUTOFS_LINK_RQ:
543 			syslog(LOG_ERR,
544 			    "non AUTOFS_MOUNT_RQ requests not implemented\n");
545 			break;
546 		default:
547 			syslog(LOG_ERR,
548 			    "non AUTOFS_MOUNT_RQ requests not implemented\n");
549 			break;
550 		}
551 		next = p->next;
552 		free(p);
553 	}
554 }
555 
556 static void
557 autofs_lookup_1_free_args(autofs_lookupargs *args)
558 {
559 	if (args->map)
560 		free(args->map);
561 	if (args->path)
562 		free(args->path);
563 	if (args->name)
564 		free(args->name);
565 	if (args->subdir)
566 		free(args->subdir);
567 	if (args->opts)
568 		free(args->opts);
569 }
570 
571 static void
572 autofs_unmount_1_free_args(umntrequest *args)
573 {
574 	if (args->mntresource)
575 		free(args->mntresource);
576 	if (args->mntpnt)
577 		free(args->mntpnt);
578 	if (args->fstype)
579 		free(args->fstype);
580 	if (args->mntopts)
581 		free(args->mntopts);
582 	if (args->next)
583 		autofs_unmount_1_free_args(args->next);
584 }
585 
586 static void
587 autofs_setdoor(int did)
588 {
589 
590 	if (did < 0) {
591 		did = 0;
592 	}
593 
594 	(void) _autofssys(AUTOFS_SETDOOR, &did);
595 }
596 
597 void *
598 autofs_get_buffer(size_t size)
599 {
600 	autofs_tsd_t *tsd = NULL;
601 
602 	/*
603 	 * Make sure the buffer size is aligned
604 	 */
605 	(void) thr_getspecific(s_thr_key, (void **)&tsd);
606 	if (tsd == NULL) {
607 		tsd = (autofs_tsd_t *)malloc(sizeof (autofs_tsd_t));
608 		if (tsd == NULL) {
609 			return (NULL);
610 		}
611 		tsd->atsd_buf = malloc(size);
612 		if (tsd->atsd_buf != NULL)
613 			tsd->atsd_len = size;
614 		else
615 			tsd->atsd_len = 0;
616 		(void) thr_setspecific(s_thr_key, tsd);
617 	} else {
618 		if (tsd->atsd_buf && (tsd->atsd_len < size)) {
619 			free(tsd->atsd_buf);
620 			tsd->atsd_buf = malloc(size);
621 			if (tsd->atsd_buf != NULL)
622 				tsd->atsd_len = size;
623 			else {
624 				tsd->atsd_len = 0;
625 			}
626 		}
627 	}
628 	if (tsd->atsd_buf) {
629 		bzero(tsd->atsd_buf, size);
630 		return (tsd->atsd_buf);
631 	} else {
632 		syslog(LOG_ERR, gettext("Can't Allocate tsd buffer, size %d"),
633 			size);
634 		return (NULL);
635 	}
636 }
637 
638 /*
639  * Each request will automatically spawn a new thread with this
640  * as its entry point.
641  */
642 /* ARGUSED */
643 static void
644 autofs_doorfunc(
645 	void *cookie,
646 	char *argp,
647 	size_t arg_size,
648 	door_desc_t *dp,
649 	uint_t n_desc)
650 {
651 
652 	char			*res;
653 	int			res_size;
654 	int			which, error = 0;
655 
656 	autofs_lookupargs	*xdrargs;
657 	autofs_lookupres	lookup_res;
658 	autofs_rddirargs	*rddir_args;
659 	autofs_rddirres		rddir_res;
660 	autofs_mountres		mount_res;
661 	umntrequest		*umnt_args;
662 	umntres			umount_res;
663 	autofs_door_res_t	*door_res;
664 	autofs_door_res_t	failed_res;
665 
666 	/*
667 	 * autofs_cred is nulled because door_cred assumes non-null
668 	 * to have been previously allocated.
669 	 */
670 	ucred_t 		*autofs_cred = NULL;
671 
672 	if (arg_size < sizeof (autofs_door_args_t)) {
673 		failed_res.res_status = EINVAL;
674 		error = door_return((char *)&failed_res,
675 		    sizeof (autofs_door_res_t), NULL, 0);
676 		/*
677 		 * If we got here the door_return() failed.
678 		 */
679 		syslog(LOG_ERR, "Bad argument, door_return failure %d",
680 			error);
681 		return;
682 	}
683 
684 	error = door_ucred(&autofs_cred);
685 	if (error) {
686 		failed_res.res_status = error;
687 		error = door_return((char *)&failed_res,
688 		    sizeof (autofs_door_res_t), NULL, 0);
689 		/*
690 		 * If we got here, door_return() failed
691 		 */
692 		syslog(LOG_ERR, "Bad cred, door_return() failed, %d",
693 			error);
694 		return;
695 	}
696 
697 	timenow = time((time_t *)NULL);
698 
699 	which = ((autofs_door_args_t *)argp)->cmd;
700 	switch (which) {
701 	case AUTOFS_LOOKUP:
702 		if (error = decode_args(xdr_autofs_lookupargs,
703 				(autofs_door_args_t *)argp, (caddr_t *)&xdrargs,
704 				sizeof (autofs_lookupargs))) {
705 			syslog(LOG_ERR, "error allocating lookup arguments"
706 				" buffer");
707 			failed_res.res_status = error;
708 			failed_res.xdr_len = 0;
709 			res = (caddr_t)&failed_res;
710 			res_size = 0;
711 			break;
712 		}
713 		bzero(&lookup_res, sizeof (autofs_lookupres));
714 
715 		autofs_lookup_1_r(xdrargs, &lookup_res, autofs_cred);
716 
717 		autofs_lookup_1_free_args(xdrargs);
718 		free(xdrargs);
719 
720 		if (!encode_res(xdr_autofs_lookupres, &door_res,
721 					(caddr_t)&lookup_res, &res_size)) {
722 			syslog(LOG_ERR, "error allocating lookup"
723 			"results buffer");
724 			failed_res.res_status = EINVAL;
725 			failed_res.xdr_len = 0;
726 			res = (caddr_t)&failed_res;
727 		} else {
728 			door_res->res_status = 0;
729 			res = (caddr_t)door_res;
730 		}
731 		break;
732 
733 	case AUTOFS_MNTINFO:
734 		if (error = decode_args(xdr_autofs_lookupargs,
735 				(autofs_door_args_t *)argp, (caddr_t *)&xdrargs,
736 				sizeof (autofs_lookupargs))) {
737 			syslog(LOG_ERR, "error allocating lookup arguments"
738 				" buffer");
739 			failed_res.res_status = error;
740 			failed_res.xdr_len = 0;
741 			res = (caddr_t)&failed_res;
742 			res_size = 0;
743 			break;
744 		}
745 
746 		autofs_mntinfo_1_r((autofs_lookupargs *)xdrargs,
747 					&mount_res, autofs_cred);
748 
749 		autofs_lookup_1_free_args(xdrargs);
750 		free(xdrargs);
751 
752 		/*
753 		 * Only reason we would get a NULL res is because
754 		 * we could not allocate a results buffer.  Use
755 		 * a local one to return the error EAGAIN as has
756 		 * always been done when memory allocations fail.
757 		 */
758 		if (!encode_res(xdr_autofs_mountres, &door_res,
759 					(caddr_t)&mount_res, &res_size)) {
760 			syslog(LOG_ERR, "error allocating mount"
761 				"results buffer");
762 			failed_res.res_status = EAGAIN;
763 			failed_res.xdr_len = 0;
764 			res = (caddr_t)&failed_res;
765 		} else {
766 			door_res->res_status = 0;
767 			res = (caddr_t)door_res;
768 		}
769 		autofs_mount_1_free_r(&mount_res);
770 		break;
771 
772 	case AUTOFS_UNMOUNT:
773 		if (error = decode_args(xdr_umntrequest,
774 			    (autofs_door_args_t *)argp,
775 			    (caddr_t *)&umnt_args, sizeof (umntrequest))) {
776 			syslog(LOG_ERR, "error allocating unmount "
777 			    "argument buffer");
778 			failed_res.res_status = error;
779 			failed_res.xdr_len = 0;
780 			res = (caddr_t)&failed_res;
781 			res_size = sizeof (autofs_door_res_t);
782 			break;
783 		}
784 
785 		autofs_unmount_1_r(umnt_args,
786 		    &umount_res, autofs_cred);
787 
788 		error = umount_res.status;
789 
790 		autofs_unmount_1_free_args(umnt_args);
791 		free(umnt_args);
792 
793 		if (!encode_res(xdr_umntres, &door_res, (caddr_t)&umount_res,
794 				&res_size)) {
795 			syslog(LOG_ERR, "error allocating unmount"
796 			    "results buffer");
797 			failed_res.res_status = EINVAL;
798 			failed_res.xdr_len = 0;
799 			res = (caddr_t)&failed_res;
800 			res_size = sizeof (autofs_door_res_t);
801 		} else {
802 			door_res->res_status = 0;
803 			res = (caddr_t)door_res;
804 		}
805 		break;
806 
807 	case AUTOFS_READDIR:
808 		if (error = decode_args(xdr_autofs_rddirargs,
809 			(autofs_door_args_t *)argp,
810 			(caddr_t *)&rddir_args,
811 			sizeof (autofs_rddirargs))) {
812 			syslog(LOG_ERR,
813 				"error allocating readdir argument buffer");
814 			failed_res.res_status = error;
815 			failed_res.xdr_len = 0;
816 			res = (caddr_t)&failed_res;
817 			res_size = sizeof (autofs_door_res_t);
818 			break;
819 		}
820 
821 		autofs_readdir_1_r(rddir_args, &rddir_res, autofs_cred);
822 
823 		free(rddir_args->rda_map);
824 		free(rddir_args);
825 
826 		if (!encode_res(xdr_autofs_rddirres, &door_res,
827 		    (caddr_t)&rddir_res, &res_size)) {
828 			syslog(LOG_ERR, "error allocating readdir"
829 			    "results buffer");
830 			failed_res.res_status = ENOMEM;
831 			failed_res.xdr_len = 0;
832 			res = (caddr_t)&failed_res;
833 			res_size = sizeof (autofs_door_res_t);
834 		} else {
835 			door_res->res_status = 0;
836 			res = (caddr_t)door_res;
837 		}
838 		autofs_readdir_1_free_r(&rddir_res);
839 		break;
840 #ifdef MALLOC_DEBUG
841 	case AUTOFS_DUMP_DEBUG:
842 			ucred_free(autofs_cred);
843 			check_leaks("/var/tmp/automountd.leak");
844 			error = door_return(NULL, 0, NULL, 0);
845 			/*
846 			 * If we got here, door_return() failed
847 			 */
848 			syslog(LOG_ERR, "dump debug door_return failure %d",
849 				error);
850 			return;
851 #endif
852 	case NULLPROC:
853 			res = NULL;
854 			res_size = 0;
855 			break;
856 	default:
857 			failed_res.res_status = EINVAL;
858 			res = (char *)&failed_res;
859 			res_size = sizeof (autofs_door_res_t);
860 			break;
861 	}
862 	ucred_free(autofs_cred);
863 	error = door_return(res, res_size, NULL, 0);
864 	/*
865 	 * If we got here, door_return failed.
866 	 */
867 	syslog(LOG_ERR, "door_return failed %d, buffer %p, buffer size %d",
868 		error, (void *)res, res_size);
869 }
870 
871 static int
872 start_autofs_svcs(void)
873 {
874 	int doorfd;
875 #ifdef DEBUG
876 	int dfd;
877 #endif
878 
879 	if ((doorfd = door_create(autofs_doorfunc, NULL,
880 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
881 		syslog(LOG_ERR, gettext("Unable to create door\n"));
882 		return (1);
883 	}
884 
885 #ifdef DEBUG
886 	/*
887 	 * Create a file system path for the door
888 	 */
889 	if ((dfd = open(AUTOFS_DOOR, O_RDWR|O_CREAT|O_TRUNC,
890 	    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
891 		syslog(LOG_ERR, "Unable to open %s: %m\n", AUTOFS_DOOR);
892 		(void) close(doorfd);
893 		return (1);
894 	}
895 
896 	/*
897 	 * stale associations clean up
898 	 */
899 	(void) fdetach(AUTOFS_DOOR);
900 
901 	/*
902 	 * Register in the namespace to the kernel to door_ki_open.
903 	 */
904 	if (fattach(doorfd, AUTOFS_DOOR) == -1) {
905 		syslog(LOG_ERR, "Unable to fattach door %m\n", AUTOFS_DOOR);
906 		(void) close(dfd);
907 		(void) close(doorfd);
908 		return (1);
909 	}
910 #endif /* DEBUG */
911 
912 	/*
913 	 * Pass door name to kernel for door_ki_open
914 	 */
915 	autofs_setdoor(doorfd);
916 
917 	(void) thr_keycreate(&s_thr_key, NULL);
918 
919 	/*
920 	 * Wait for incoming calls
921 	 */
922 	/*CONSTCOND*/
923 	while (1)
924 		(void) pause();
925 
926 	/* NOTREACHED */
927 	syslog(LOG_ERR, gettext("Door server exited"));
928 	return (10);
929 }
930 
931 static int
932 decode_args(
933 	xdrproc_t xdrfunc,
934 	autofs_door_args_t *argp,
935 	caddr_t *xdrargs,
936 	int size)
937 {
938 	XDR xdrs;
939 
940 	caddr_t tmpargs = (caddr_t)&((autofs_door_args_t *)argp)->xdr_arg;
941 	size_t arg_size = ((autofs_door_args_t *)argp)->xdr_len;
942 
943 	xdrmem_create(&xdrs, tmpargs, arg_size, XDR_DECODE);
944 
945 	*xdrargs = malloc(size);
946 	if (*xdrargs == NULL) {
947 		syslog(LOG_ERR, "error allocating arguments"
948 				" buffer");
949 		return (ENOMEM);
950 	}
951 
952 	bzero(*xdrargs, size);
953 
954 	if (!(*xdrfunc)(&xdrs, *xdrargs)) {
955 		free(*xdrargs);
956 		*xdrargs = NULL;
957 		syslog(LOG_ERR, "error decoding arguments");
958 		return (EINVAL);
959 	}
960 
961 	return (0);
962 }
963 
964 
965 static bool_t
966 encode_res(
967 	xdrproc_t xdrfunc,
968 	autofs_door_res_t **results,
969 	caddr_t resp,
970 	int *size)
971 {
972 	XDR xdrs;
973 
974 	*size = xdr_sizeof((*xdrfunc), resp);
975 	*results = autofs_get_buffer(
976 	    sizeof (autofs_door_res_t) + *size);
977 	if (*results == NULL) {
978 		(*results)->res_status = ENOMEM;
979 		return (FALSE);
980 	}
981 	(*results)->xdr_len = *size;
982 	*size = sizeof (autofs_door_res_t) + (*results)->xdr_len;
983 	xdrmem_create(&xdrs, (caddr_t)((*results)->xdr_res),
984 		(*results)->xdr_len,
985 		XDR_ENCODE);
986 	if (!(*xdrfunc)(&xdrs, resp)) {
987 		(*results)->res_status = EINVAL;
988 		syslog(LOG_ERR, "error encoding results");
989 		return (FALSE);
990 	}
991 	(*results)->res_status = 0;
992 	return (TRUE);
993 }
994