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