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