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