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