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