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