xref: /illumos-gate/usr/src/cmd/fs.d/autofs/autod_main.c (revision 2983dda76a6d296fdb560c88114fe41caad1b84f)
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 2008 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", res->rd_status);
350 }
351 
352 static void
353 autofs_readdir_1_free_r(struct autofs_rddirres *res)
354 {
355 	if (res->rd_status == AUTOFS_OK) {
356 		if (res->rd_rddir.rddir_entries)
357 			free(res->rd_rddir.rddir_entries);
358 	}
359 }
360 
361 
362 /* ARGSUSED */
363 static void
364 autofs_unmount_1_r(
365 	umntrequest *m,
366 	umntres *res)
367 {
368 	struct umntrequest *ul;
369 
370 	if (trace > 0) {
371 		char ctime_buf[CTIME_BUF_LEN];
372 		if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL)
373 			ctime_buf[0] = '\0';
374 
375 		trace_prt(1, "UNMOUNT REQUEST: %s", ctime_buf);
376 		for (ul = m; ul; ul = ul->next)
377 			trace_prt(1, " resource=%s fstype=%s mntpnt=%s"
378 			    " mntopts=%s %s\n",
379 			    ul->mntresource,
380 			    ul->fstype,
381 			    ul->mntpnt,
382 			    ul->mntopts,
383 			    ul->isdirect ? "direct" : "indirect");
384 	}
385 
386 
387 	res->status = do_unmount1(m);
388 
389 	if (trace > 0)
390 		trace_prt(1, "UNMOUNT REPLY: status=%d\n", res->status);
391 }
392 
393 static void
394 autofs_lookup_1_r(
395 	autofs_lookupargs *m,
396 	autofs_lookupres *res)
397 {
398 	autofs_action_t action;
399 	struct	linka link;
400 	int status;
401 
402 	if (trace > 0) {
403 		char ctime_buf[CTIME_BUF_LEN];
404 		if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL)
405 			ctime_buf[0] = '\0';
406 
407 		trace_prt(1, "LOOKUP REQUEST: %s", ctime_buf);
408 		trace_prt(1, "  name=%s[%s] map=%s opts=%s path=%s direct=%d\n",
409 		    m->name, m->subdir, m->map, m->opts, m->path, m->isdirect);
410 	}
411 
412 	bzero(&link, sizeof (struct linka));
413 
414 	status = do_lookup1(m->map, m->name, m->subdir, m->opts, m->path,
415 	    (uint_t)m->isdirect, m->uid, &action, &link);
416 	if (status == 0) {
417 		/*
418 		 * Return action list to kernel.
419 		 */
420 		res->lu_res = AUTOFS_OK;
421 		if ((res->lu_type.action = action) == AUTOFS_LINK_RQ) {
422 			res->lu_type.lookup_result_type_u.lt_linka = link;
423 		}
424 	} else {
425 		/*
426 		 * Entry not found
427 		 */
428 		res->lu_res = AUTOFS_NOENT;
429 	}
430 	res->lu_verbose = verbose;
431 
432 	if (trace > 0)
433 		trace_prt(1, "LOOKUP REPLY    : status=%d\n", res->lu_res);
434 }
435 
436 static void
437 autofs_mntinfo_1_r(
438 	autofs_lookupargs *m,
439 	autofs_mountres *res)
440 {
441 	int status;
442 	action_list		*alp = NULL;
443 
444 	if (trace > 0) {
445 		char ctime_buf[CTIME_BUF_LEN];
446 		if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL)
447 			ctime_buf[0] = '\0';
448 
449 		trace_prt(1, "MOUNT REQUEST:   %s", ctime_buf);
450 		trace_prt(1, "  name=%s[%s] map=%s opts=%s path=%s direct=%d\n",
451 		    m->name, m->subdir, m->map, m->opts, m->path, m->isdirect);
452 	}
453 
454 	status = do_mount1(m->map, m->name, m->subdir, m->opts, m->path,
455 	    (uint_t)m->isdirect, m->uid, &alp, DOMOUNT_USER);
456 	if (status != 0) {
457 		/*
458 		 * An error occurred, free action list if allocated.
459 		 */
460 		if (alp != NULL) {
461 			free_action_list(alp);
462 			alp = NULL;
463 		}
464 	}
465 	if (alp != NULL) {
466 		/*
467 		 * Return action list to kernel.
468 		 */
469 		res->mr_type.status = AUTOFS_ACTION;
470 		res->mr_type.mount_result_type_u.list = alp;
471 	} else {
472 		/*
473 		 * No work to do left for the kernel
474 		 */
475 		res->mr_type.status = AUTOFS_DONE;
476 		res->mr_type.mount_result_type_u.error = status;
477 	}
478 
479 	if (trace > 0) {
480 		switch (res->mr_type.status) {
481 		case AUTOFS_ACTION:
482 			trace_prt(1,
483 			    "MOUNT REPLY    : status=%d, AUTOFS_ACTION\n",
484 			    status);
485 			break;
486 		case AUTOFS_DONE:
487 			trace_prt(1,
488 			    "MOUNT REPLY    : status=%d, AUTOFS_DONE\n",
489 			    status);
490 			break;
491 		default:
492 			trace_prt(1, "MOUNT REPLY    : status=%d, UNKNOWN\n",
493 			    status);
494 		}
495 	}
496 
497 	if (status && verbose) {
498 		if (m->isdirect) {
499 			/* direct mount */
500 			syslog(LOG_ERR, "mount of %s failed", m->path);
501 		} else {
502 			/* indirect mount */
503 			syslog(LOG_ERR,
504 			    "mount of %s/%s failed", m->path, m->name);
505 		}
506 	}
507 }
508 
509 static void
510 autofs_mount_1_free_r(struct autofs_mountres *res)
511 {
512 	if (res->mr_type.status == AUTOFS_ACTION) {
513 		if (trace > 2)
514 			trace_prt(1, "freeing action list\n");
515 		free_action_list(res->mr_type.mount_result_type_u.list);
516 	}
517 }
518 
519 /*
520  * Used for reporting messages from code shared with automount command.
521  * Formats message into a buffer and calls syslog.
522  *
523  * Print an error.  Works like printf (fmt string and variable args)
524  * except that it will subsititute an error message for a "%m" string
525  * (like syslog).
526  */
527 void
528 pr_msg(const char *fmt, ...)
529 {
530 	va_list ap;
531 	char fmtbuff[BUFSIZ], buff[BUFSIZ];
532 	const char *p1;
533 	char *p2;
534 
535 	p2 = fmtbuff;
536 	fmt = gettext(fmt);
537 
538 	for (p1 = fmt; *p1; p1++) {
539 		if (*p1 == '%' && *(p1 + 1) == 'm') {
540 			(void) strcpy(p2, strerror(errno));
541 			p2 += strlen(p2);
542 			p1++;
543 		} else {
544 			*p2++ = *p1;
545 		}
546 	}
547 	if (p2 > fmtbuff && *(p2-1) != '\n')
548 		*p2++ = '\n';
549 	*p2 = '\0';
550 
551 	va_start(ap, fmt);
552 	(void) vsprintf(buff, fmtbuff, ap);
553 	va_end(ap);
554 	syslog(LOG_ERR, buff);
555 }
556 
557 static void
558 free_action_list(action_list *alp)
559 {
560 	action_list *p, *next = NULL;
561 	struct mounta *mp;
562 
563 	for (p = alp; p != NULL; p = next) {
564 		switch (p->action.action) {
565 		case AUTOFS_MOUNT_RQ:
566 			mp = &(p->action.action_list_entry_u.mounta);
567 			/* LINTED pointer alignment */
568 			if (mp->fstype) {
569 				if (strcmp(mp->fstype, "autofs") == 0) {
570 					free_autofs_args((autofs_args *)
571 					    mp->dataptr);
572 				} else if (strncmp(mp->fstype, "nfs", 3) == 0) {
573 					free_nfs_args((struct nfs_args *)
574 					    mp->dataptr);
575 				}
576 			}
577 			mp->dataptr = NULL;
578 			mp->datalen = 0;
579 			free_mounta(mp);
580 			break;
581 		case AUTOFS_LINK_RQ:
582 			syslog(LOG_ERR,
583 			    "non AUTOFS_MOUNT_RQ requests not implemented\n");
584 			break;
585 		default:
586 			syslog(LOG_ERR,
587 			    "non AUTOFS_MOUNT_RQ requests not implemented\n");
588 			break;
589 		}
590 		next = p->next;
591 		free(p);
592 	}
593 }
594 
595 static void
596 autofs_lookup_1_free_args(autofs_lookupargs *args)
597 {
598 	if (args->map)
599 		free(args->map);
600 	if (args->path)
601 		free(args->path);
602 	if (args->name)
603 		free(args->name);
604 	if (args->subdir)
605 		free(args->subdir);
606 	if (args->opts)
607 		free(args->opts);
608 }
609 
610 static void
611 autofs_unmount_1_free_args(umntrequest *args)
612 {
613 	if (args->mntresource)
614 		free(args->mntresource);
615 	if (args->mntpnt)
616 		free(args->mntpnt);
617 	if (args->fstype)
618 		free(args->fstype);
619 	if (args->mntopts)
620 		free(args->mntopts);
621 	if (args->next)
622 		autofs_unmount_1_free_args(args->next);
623 }
624 
625 static void
626 autofs_setdoor(int did)
627 {
628 
629 	if (did < 0) {
630 		did = 0;
631 	}
632 
633 	(void) _autofssys(AUTOFS_SETDOOR, &did);
634 }
635 
636 void *
637 autofs_get_buffer(size_t size)
638 {
639 	autofs_tsd_t *tsd = NULL;
640 
641 	/*
642 	 * Make sure the buffer size is aligned
643 	 */
644 	(void) thr_getspecific(s_thr_key, (void **)&tsd);
645 	if (tsd == NULL) {
646 		tsd = (autofs_tsd_t *)malloc(sizeof (autofs_tsd_t));
647 		if (tsd == NULL) {
648 			return (NULL);
649 		}
650 		tsd->atsd_buf = malloc(size);
651 		if (tsd->atsd_buf != NULL)
652 			tsd->atsd_len = size;
653 		else
654 			tsd->atsd_len = 0;
655 		(void) thr_setspecific(s_thr_key, tsd);
656 	} else {
657 		if (tsd->atsd_buf && (tsd->atsd_len < size)) {
658 			free(tsd->atsd_buf);
659 			tsd->atsd_buf = malloc(size);
660 			if (tsd->atsd_buf != NULL)
661 				tsd->atsd_len = size;
662 			else {
663 				tsd->atsd_len = 0;
664 			}
665 		}
666 	}
667 	if (tsd->atsd_buf) {
668 		bzero(tsd->atsd_buf, size);
669 		return (tsd->atsd_buf);
670 	} else {
671 		syslog(LOG_ERR,
672 		    gettext("Can't Allocate tsd buffer, size %d"), size);
673 		return (NULL);
674 	}
675 }
676 
677 /*
678  * Each request will automatically spawn a new thread with this
679  * as its entry point.
680  */
681 /* ARGUSED */
682 static void
683 autofs_doorfunc(
684 	void *cookie,
685 	char *argp,
686 	size_t arg_size,
687 	door_desc_t *dp,
688 	uint_t n_desc)
689 {
690 	char			*res;
691 	int			 res_size;
692 	int			 which;
693 	int			 error = 0;
694 	int			 srsz = 0;
695 	autofs_lookupargs	*xdrargs;
696 	autofs_lookupres	 lookup_res;
697 	autofs_rddirargs	*rddir_args;
698 	autofs_rddirres		 rddir_res;
699 	autofs_mountres		 mount_res;
700 	umntrequest		*umnt_args;
701 	umntres			 umount_res;
702 	autofs_door_res_t	*door_res;
703 	autofs_door_res_t	 failed_res;
704 
705 	if (arg_size < sizeof (autofs_door_args_t)) {
706 		failed_res.res_status = EINVAL;
707 		error = door_return((char *)&failed_res,
708 		    sizeof (autofs_door_res_t), NULL, 0);
709 		/*
710 		 * If we got here the door_return() failed.
711 		 */
712 		syslog(LOG_ERR, "Bad argument, door_return failure %d", error);
713 		return;
714 	}
715 
716 	timenow = time((time_t *)NULL);
717 
718 	which = ((autofs_door_args_t *)argp)->cmd;
719 	switch (which) {
720 	case AUTOFS_LOOKUP:
721 		if (error = decode_args(xdr_autofs_lookupargs,
722 		    (autofs_door_args_t *)argp, (caddr_t *)&xdrargs,
723 		    sizeof (autofs_lookupargs))) {
724 			syslog(LOG_ERR,
725 			    "error allocating lookup arguments buffer");
726 			failed_res.res_status = error;
727 			failed_res.xdr_len = 0;
728 			res = (caddr_t)&failed_res;
729 			res_size = 0;
730 			break;
731 		}
732 		bzero(&lookup_res, sizeof (autofs_lookupres));
733 
734 		autofs_lookup_1_r(xdrargs, &lookup_res);
735 
736 		autofs_lookup_1_free_args(xdrargs);
737 		free(xdrargs);
738 
739 		if (!encode_res(xdr_autofs_lookupres, &door_res,
740 		    (caddr_t)&lookup_res, &res_size)) {
741 			syslog(LOG_ERR,
742 			    "error allocating lookup results buffer");
743 			failed_res.res_status = EINVAL;
744 			failed_res.xdr_len = 0;
745 			res = (caddr_t)&failed_res;
746 		} else {
747 			door_res->res_status = 0;
748 			res = (caddr_t)door_res;
749 		}
750 		break;
751 
752 	case AUTOFS_MNTINFO:
753 		if (error = decode_args(xdr_autofs_lookupargs,
754 		    (autofs_door_args_t *)argp, (caddr_t *)&xdrargs,
755 		    sizeof (autofs_lookupargs))) {
756 			syslog(LOG_ERR,
757 			    "error allocating lookup arguments buffer");
758 			failed_res.res_status = error;
759 			failed_res.xdr_len = 0;
760 			res = (caddr_t)&failed_res;
761 			res_size = 0;
762 			break;
763 		}
764 
765 		autofs_mntinfo_1_r((autofs_lookupargs *)xdrargs, &mount_res);
766 
767 		autofs_lookup_1_free_args(xdrargs);
768 		free(xdrargs);
769 
770 		/*
771 		 * Only reason we would get a NULL res is because
772 		 * we could not allocate a results buffer.  Use
773 		 * a local one to return the error EAGAIN as has
774 		 * always been done when memory allocations fail.
775 		 */
776 		if (!encode_res(xdr_autofs_mountres, &door_res,
777 		    (caddr_t)&mount_res, &res_size)) {
778 			syslog(LOG_ERR,
779 			    "error allocating mount results buffer");
780 			failed_res.res_status = EAGAIN;
781 			failed_res.xdr_len = 0;
782 			res = (caddr_t)&failed_res;
783 		} else {
784 			door_res->res_status = 0;
785 			res = (caddr_t)door_res;
786 		}
787 		autofs_mount_1_free_r(&mount_res);
788 		break;
789 
790 	case AUTOFS_UNMOUNT:
791 		if (error = decode_args(xdr_umntrequest,
792 		    (autofs_door_args_t *)argp,
793 		    (caddr_t *)&umnt_args, sizeof (umntrequest))) {
794 			syslog(LOG_ERR,
795 			    "error allocating unmount argument buffer");
796 			failed_res.res_status = error;
797 			failed_res.xdr_len = 0;
798 			res = (caddr_t)&failed_res;
799 			res_size = sizeof (autofs_door_res_t);
800 			break;
801 		}
802 
803 		autofs_unmount_1_r(umnt_args, &umount_res);
804 
805 		error = umount_res.status;
806 
807 		autofs_unmount_1_free_args(umnt_args);
808 		free(umnt_args);
809 
810 		if (!encode_res(xdr_umntres, &door_res, (caddr_t)&umount_res,
811 		    &res_size)) {
812 			syslog(LOG_ERR,
813 			    "error allocating unmount results buffer");
814 			failed_res.res_status = EINVAL;
815 			failed_res.xdr_len = 0;
816 			res = (caddr_t)&failed_res;
817 			res_size = sizeof (autofs_door_res_t);
818 		} else {
819 			door_res->res_status = 0;
820 			res = (caddr_t)door_res;
821 		}
822 		break;
823 
824 	case AUTOFS_READDIR:
825 		if (error = decode_args(xdr_autofs_rddirargs,
826 		    (autofs_door_args_t *)argp,
827 		    (caddr_t *)&rddir_args,
828 		    sizeof (autofs_rddirargs))) {
829 			syslog(LOG_ERR,
830 			    "error allocating readdir argument buffer");
831 			failed_res.res_status = error;
832 			failed_res.xdr_len = 0;
833 			res = (caddr_t)&failed_res;
834 			res_size = sizeof (autofs_door_res_t);
835 			break;
836 		}
837 
838 		autofs_readdir_1_r(rddir_args, &rddir_res);
839 
840 		free(rddir_args->rda_map);
841 		free(rddir_args);
842 
843 		if (!encode_res(xdr_autofs_rddirres, &door_res,
844 		    (caddr_t)&rddir_res, &res_size)) {
845 			syslog(LOG_ERR,
846 			    "error allocating readdir results buffer");
847 			failed_res.res_status = ENOMEM;
848 			failed_res.xdr_len = 0;
849 			res = (caddr_t)&failed_res;
850 			res_size = sizeof (autofs_door_res_t);
851 		} else {
852 			door_res->res_status = 0;
853 			res = (caddr_t)door_res;
854 		}
855 		autofs_readdir_1_free_r(&rddir_res);
856 		break;
857 #ifdef MALLOC_DEBUG
858 	case AUTOFS_DUMP_DEBUG:
859 			check_leaks("/var/tmp/automountd.leak");
860 			error = door_return(NULL, 0, NULL, 0);
861 			/*
862 			 * If we got here, door_return() failed
863 			 */
864 			syslog(LOG_ERR, "dump debug door_return failure %d",
865 			    error);
866 			return;
867 #endif
868 	case NULLPROC:
869 			res = NULL;
870 			res_size = 0;
871 			break;
872 	default:
873 			failed_res.res_status = EINVAL;
874 			res = (char *)&failed_res;
875 			res_size = sizeof (autofs_door_res_t);
876 			break;
877 	}
878 
879 	srsz = res_size;
880 	errno = 0;
881 	error = door_return(res, res_size, NULL, 0);
882 
883 	if (errno == E2BIG) {
884 		/*
885 		 * Failed due to encoded results being bigger than the
886 		 * kernel expected bufsize. Passing actual results size
887 		 * back down to kernel.
888 		 */
889 		failed_res.res_status = EOVERFLOW;
890 		failed_res.xdr_len = srsz;
891 		res = (caddr_t)&failed_res;
892 		res_size = sizeof (autofs_door_res_t);
893 	} else {
894 		syslog(LOG_ERR, "door_return failed %d, buffer %p, "
895 		    "buffer size %d", error, (void *)res, res_size);
896 		res = NULL;
897 		res_size = 0;
898 	}
899 	(void) door_return(res, res_size, NULL, 0);
900 	/* NOTREACHED */
901 }
902 
903 static int
904 start_autofs_svcs(void)
905 {
906 	int doorfd;
907 #ifdef DEBUG
908 	int dfd;
909 #endif
910 
911 	if ((doorfd = door_create(autofs_doorfunc, NULL,
912 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
913 		syslog(LOG_ERR, gettext("Unable to create door\n"));
914 		return (1);
915 	}
916 
917 #ifdef DEBUG
918 	/*
919 	 * Create a file system path for the door
920 	 */
921 	if ((dfd = open(AUTOFS_DOOR, O_RDWR|O_CREAT|O_TRUNC,
922 	    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
923 		syslog(LOG_ERR, "Unable to open %s: %m\n", AUTOFS_DOOR);
924 		(void) close(doorfd);
925 		return (1);
926 	}
927 
928 	/*
929 	 * stale associations clean up
930 	 */
931 	(void) fdetach(AUTOFS_DOOR);
932 
933 	/*
934 	 * Register in the namespace to the kernel to door_ki_open.
935 	 */
936 	if (fattach(doorfd, AUTOFS_DOOR) == -1) {
937 		syslog(LOG_ERR, "Unable to fattach door %m\n", AUTOFS_DOOR);
938 		(void) close(dfd);
939 		(void) close(doorfd);
940 		return (1);
941 	}
942 #endif /* DEBUG */
943 
944 	/*
945 	 * Pass door name to kernel for door_ki_open
946 	 */
947 	autofs_setdoor(doorfd);
948 
949 	(void) thr_keycreate(&s_thr_key, NULL);
950 
951 	/*
952 	 * Wait for incoming calls
953 	 */
954 	/*CONSTCOND*/
955 	while (1)
956 		(void) pause();
957 
958 	/* NOTREACHED */
959 	syslog(LOG_ERR, gettext("Door server exited"));
960 	return (10);
961 }
962 
963 static int
964 decode_args(
965 	xdrproc_t xdrfunc,
966 	autofs_door_args_t *argp,
967 	caddr_t *xdrargs,
968 	int size)
969 {
970 	XDR xdrs;
971 
972 	caddr_t tmpargs = (caddr_t)&((autofs_door_args_t *)argp)->xdr_arg;
973 	size_t arg_size = ((autofs_door_args_t *)argp)->xdr_len;
974 
975 	xdrmem_create(&xdrs, tmpargs, arg_size, XDR_DECODE);
976 
977 	*xdrargs = malloc(size);
978 	if (*xdrargs == NULL) {
979 		syslog(LOG_ERR, "error allocating arguments buffer");
980 		return (ENOMEM);
981 	}
982 
983 	bzero(*xdrargs, size);
984 
985 	if (!(*xdrfunc)(&xdrs, *xdrargs)) {
986 		free(*xdrargs);
987 		*xdrargs = NULL;
988 		syslog(LOG_ERR, "error decoding arguments");
989 		return (EINVAL);
990 	}
991 
992 	return (0);
993 }
994 
995 
996 static bool_t
997 encode_res(
998 	xdrproc_t xdrfunc,
999 	autofs_door_res_t **results,
1000 	caddr_t resp,
1001 	int *size)
1002 {
1003 	XDR xdrs;
1004 
1005 	*size = xdr_sizeof((*xdrfunc), resp);
1006 	*results = autofs_get_buffer(
1007 	    sizeof (autofs_door_res_t) + *size);
1008 	if (*results == NULL) {
1009 		(*results)->res_status = ENOMEM;
1010 		return (FALSE);
1011 	}
1012 	(*results)->xdr_len = *size;
1013 	*size = sizeof (autofs_door_res_t) + (*results)->xdr_len;
1014 	xdrmem_create(&xdrs, (caddr_t)((*results)->xdr_res),
1015 	    (*results)->xdr_len, XDR_ENCODE);
1016 	if (!(*xdrfunc)(&xdrs, resp)) {
1017 		(*results)->res_status = EINVAL;
1018 		syslog(LOG_ERR, "error encoding results");
1019 		return (FALSE);
1020 	}
1021 	(*results)->res_status = 0;
1022 	return (TRUE);
1023 }
1024 
1025 static void
1026 automountd_wait_for_cleanup(pid_t pid)
1027 {
1028 	int status;
1029 	int child_exitval;
1030 
1031 	/*
1032 	 * Wait for the main automountd process to exit so we cleanup
1033 	 */
1034 	(void) waitpid(pid, &status, 0);
1035 
1036 	child_exitval = WEXITSTATUS(status);
1037 
1038 	/*
1039 	 * Shutdown the door server for mounting and unmounting
1040 	 * filesystems
1041 	 */
1042 	if (door_revoke(did_fork_exec) == -1) {
1043 		syslog(LOG_ERR, "failed to door_revoke(%d) %m", did_fork_exec);
1044 	}
1045 	if (door_revoke(did_exec_map) == -1) {
1046 		syslog(LOG_ERR, "failed to door_revoke(%d) %m", did_exec_map);
1047 	}
1048 	exit(child_exitval);
1049 }
1050