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 <stdlib.h>
30 #include <unistd.h>
31 #include <signal.h>
32 #include <sys/types.h>
33 #include <memory.h>
34 #include <stropts.h>
35 #include <netconfig.h>
36 #include <stdarg.h>
37 #include <sys/resource.h>
38 #include <sys/systeminfo.h>
39 #include <syslog.h>
40 #include <errno.h>
41 #include <sys/sockio.h>
42 #include <rpc/rpc.h>
43 #include <rpc/xdr.h>
44 #include <rpcsvc/nfs_prot.h>
45 #include <net/if.h>
46 #include <netdir.h>
47 #include <string.h>
48 #include <thread.h>
49 #include <locale.h>
50 #include "automount.h"
51 #include <sys/vfs.h>
52 #include <sys/mnttab.h>
53 #include <arpa/inet.h>
54 #include <rpc/svc.h>			/* for private dupcache routines */
55 #include <rpcsvc/daemon_utils.h>
56 #include <deflt.h>
57 #include <strings.h>
58 #include <priv.h>
59 #include <tsol/label.h>
60 
61 static void autofs_prog(struct svc_req *, SVCXPRT *);
62 static void autofs_mount_1_r(struct autofs_lookupargs *,
63 		struct autofs_mountres *, struct authunix_parms *);
64 static void autofs_mount_1_free_r(struct autofs_mountres *);
65 static void autofs_lookup_1_r(struct autofs_lookupargs *,
66 		struct autofs_lookupres *, struct authunix_parms *);
67 static void autofs_lookup_1_free_r(struct autofs_lookupres *);
68 static void autofs_unmount_1_r(struct umntrequest *, struct umntres *,
69 		struct authunix_parms *);
70 static void autofs_unmount_1_free_r(struct umntres *);
71 static void autofs_readdir_1_r(struct autofs_rddirargs *,
72 		struct autofs_rddirres *, struct authunix_parms *);
73 static void autofs_readdir_1_free_r(struct autofs_rddirres *);
74 static void usage();
75 static void warn_hup(int);
76 static void free_action_list();
77 
78 static int dupreq_nonidemp(struct svc_req *, SVCXPRT *, int, bool_t (*)(),
79 		void (*)());
80 static int dupdonereq_nonidemp(struct svc_req *, caddr_t, bool_t (*)());
81 static int dupreq_idemp(struct svc_req *, SVCXPRT *, int, bool_t (*)(),
82 		void (*)());
83 static int dupdonereq_idemp(struct svc_req *, caddr_t, bool_t (*)());
84 
85 #define	CTIME_BUF_LEN 26
86 
87 /*
88  * XXX - this limit was imposed due to resource problems - even though
89  * we can and do try and set the rlimit to be able to handle more threads,
90  * fopen() doesn't allow more than 256 fp's.
91  */
92 #define	MAXTHREADS 64
93 
94 #define	RESOURCE_FACTOR 8
95 
96 static char str_arch[32];
97 static char str_cpu[32];
98 
99 struct autodir *dir_head;
100 struct autodir *dir_tail;
101 char self[64];
102 
103 time_t timenow;
104 int verbose = 0;
105 int trace = 0;
106 int automountd_nobrowse = 0;
107 
108 int
109 main(argc, argv)
110 	int argc;
111 	char *argv[];
112 
113 {
114 	pid_t pid;
115 	int c, i, error;
116 	struct rlimit rlset;
117 	int rpc_svc_mode = RPC_SVC_MT_AUTO;
118 	int maxthreads = MAXTHREADS;
119 	int prevthreads = 0;
120 	char *defval;
121 	int defflags;
122 
123 	if (geteuid() != 0) {
124 		(void) fprintf(stderr, "%s must be run as root\n", argv[0]);
125 		exit(1);
126 	}
127 
128 	/*
129 	 * Read in the values from config file first before we check
130 	 * commandline options so the options override the file.
131 	 */
132 	if ((defopen(AUTOFSADMIN)) == 0) {
133 		if ((defval = defread("AUTOMOUNTD_VERBOSE=")) != NULL) {
134 			if (strncasecmp("true", defval, 4) == 0)
135 				verbose = TRUE;
136 			else
137 				verbose = FALSE;
138 		}
139 		if ((defval = defread("AUTOMOUNTD_NOBROWSE=")) != NULL) {
140 			if (strncasecmp("true", defval, 4) == 0)
141 				automountd_nobrowse = TRUE;
142 			else
143 				automountd_nobrowse = FALSE;
144 		}
145 		if ((defval = defread("AUTOMOUNTD_TRACE=")) != NULL) {
146 			errno = 0;
147 			trace = strtol(defval, (char **)NULL, 10);
148 			if (errno != 0)
149 				trace = 0;
150 		}
151 		if ((defval = defread("AUTOMOUNTD_ENV=")) != NULL) {
152 			(void) putenv(strdup(defval));
153 			defflags = defcntl(DC_GETFLAGS, 0);
154 			TURNON(defflags, DC_NOREWIND);
155 			defflags = defcntl(DC_SETFLAGS, defflags);
156 			while ((defval = defread("AUTOMOUNTD_ENV=")) != NULL)
157 				(void) putenv(strdup(defval));
158 			(void) defcntl(DC_SETFLAGS, defflags);
159 		}
160 
161 		/* close defaults file */
162 		defopen(NULL);
163 	}
164 
165 	while ((c = getopt(argc, argv, "vnTD:")) != EOF) {
166 		switch (c) {
167 		case 'v':
168 			verbose++;
169 			break;
170 		case 'n':
171 			automountd_nobrowse++;
172 			break;
173 		case 'T':
174 			trace++;
175 			break;
176 		case 'D':
177 			(void) putenv(optarg);
178 			break;
179 		default:
180 			usage();
181 		}
182 	}
183 
184 	if (sysinfo(SI_HOSTNAME, self, sizeof (self)) == -1) {
185 		error = errno;
186 		fprintf(stderr,
187 			"automountd: can't determine hostname, error: %d\n",
188 			error);
189 		exit(1);
190 	}
191 
192 #ifndef DEBUG
193 	pid = fork();
194 	if (pid < 0) {
195 		perror("cannot fork");
196 		exit(1);
197 	}
198 	if (pid)
199 		exit(0);
200 #endif
201 
202 	(void) setsid();
203 	openlog("automountd", LOG_PID, LOG_DAEMON);
204 	(void) setlocale(LC_ALL, "");
205 
206 	/*
207 	 * Since the "arch" command no longer exists we
208 	 * have to rely on sysinfo(SI_MACHINE) to return the closest
209 	 * approximation.  For backward compatibility we
210 	 * need to substitute "sun4" for "sun4m", "sun4c", ...
211 	 */
212 	if (getenv("ARCH") == NULL) {
213 		char buf[16];
214 
215 		if (sysinfo(SI_MACHINE, buf, sizeof (buf)) != -1) {
216 			if (strncmp(buf, "sun4", 4) == 0)
217 				(void) strcpy(buf, "sun4");
218 			(void) sprintf(str_arch, "ARCH=%s", buf);
219 			(void) putenv(str_arch);
220 		} else {
221 			syslog(LOG_ERR,
222 				"can't determine machine type, error: %m");
223 		}
224 	}
225 	if (getenv("CPU") == NULL) {
226 		char buf[16];
227 
228 		if (sysinfo(SI_ARCHITECTURE, buf, sizeof (buf)) != -1) {
229 			(void) sprintf(str_cpu, "CPU=%s", buf);
230 			(void) putenv(str_cpu);
231 		} else {
232 			syslog(LOG_ERR,
233 				"can't determine processor type, error: %m");
234 		}
235 	}
236 
237 	(void) rwlock_init(&cache_lock, USYNC_THREAD, NULL);
238 	(void) rwlock_init(&rddir_cache_lock, USYNC_THREAD, NULL);
239 
240 	/*
241 	 * initialize the name services, use NULL arguments to ensure
242 	 * we don't initialize the stack of files used in file service
243 	 */
244 	(void) ns_setup(NULL, NULL);
245 
246 	/*
247 	 * set the maximum number of threads to be used. If it succeeds
248 	 * increase the number of resources the threads need. If the
249 	 * the resource allocation fails, return the threads value back
250 	 * to the default value
251 	 */
252 	if (((rpc_control(RPC_SVC_THRMAX_GET, &prevthreads)) == TRUE) &&
253 		((rpc_control(RPC_SVC_THRMAX_SET, &maxthreads)) == TRUE)) {
254 		rlset.rlim_max = RESOURCE_FACTOR * maxthreads;
255 		rlset.rlim_cur = RESOURCE_FACTOR * maxthreads;
256 		if ((setrlimit(RLIMIT_NOFILE, &rlset)) != 0) {
257 			syslog(LOG_ERR,
258 				"unable to increase system resource limit");
259 
260 			/* back off changes to threads */
261 			if ((rpc_control(RPC_SVC_THRMAX_SET, &prevthreads))
262 				== FALSE) {
263 				/*
264 				 * Exit if we have more threads than resources.
265 				 */
266 				syslog(LOG_ERR,
267 				"unable to match threads to system resources");
268 				exit(1);
269 			}
270 			syslog(LOG_ERR,
271 				"decreased threads to match low resources");
272 		} else {
273 			/*
274 			 * Both are successful. Note that setrlimit
275 			 * allows a max setting of 1024
276 			 */
277 			if (trace > 3) {
278 				trace_prt(1,
279 				"  maxthreads: %d rlim_max: %d rlim_cur: %d\n",
280 				maxthreads, rlset.rlim_max, rlset.rlim_cur);
281 			}
282 			closefrom(3);
283 		}
284 	} else {
285 		syslog(LOG_ERR,
286 			"unable to increase threads - continue with default");
287 	}
288 
289 	/*
290 	 * establish our lock on the lock file and write our pid to it.
291 	 * exit if some other process holds the lock, or if there's any
292 	 * error in writing/locking the file.
293 	 */
294 	pid = _enter_daemon_lock(AUTOMOUNTD);
295 	switch (pid) {
296 	case 0:
297 		break;
298 	case -1:
299 		syslog(LOG_ERR, "error locking for %s: %s", AUTOMOUNTD,
300 		    strerror(errno));
301 		exit(2);
302 	default:
303 		/* daemon was already running */
304 		exit(0);
305 	}
306 
307 	/*
308 	 * If we coredump it'll be /core.
309 	 */
310 	if (chdir("/") < 0)
311 		syslog(LOG_ERR, "chdir /: %m");
312 
313 	/*
314 	 * Create cache_cleanup thread
315 	 */
316 	if (thr_create(NULL, 0, (void *(*)(void *))cache_cleanup, NULL,
317 			THR_DETACHED | THR_DAEMON | THR_NEW_LWP, NULL)) {
318 		syslog(LOG_ERR, "unable to create cache_cleanup thread");
319 		exit(1);
320 	}
321 
322 	/* other initializations */
323 	(void) rwlock_init(&portmap_cache_lock, USYNC_THREAD, NULL);
324 
325 	/* on a labeled system, the automounter implements read-down policy */
326 	if (is_system_labeled()) {
327 		if ((setpflags(NET_MAC_AWARE, 1) == -1) ||
328 		    (setpflags(NET_MAC_AWARE_INHERIT, 1) == -1))
329 			syslog(LOG_ERR, "ignored failure to set MAC-aware "
330 			    "mode: %m");
331 	}
332 
333 	if (!rpc_control(RPC_SVC_MTMODE_SET, &rpc_svc_mode)) {
334 		syslog(LOG_ERR, "unable to set automatic MT mode");
335 		exit(1);
336 	}
337 	if (svc_create_local_service(autofs_prog,
338 		AUTOFS_PROG, AUTOFS_VERS, "netpath", "autofs") == 0) {
339 		syslog(LOG_ERR, "unable to create service");
340 		exit(1);
341 	}
342 
343 	(void) signal(SIGHUP, warn_hup);
344 
345 	svc_run();
346 	syslog(LOG_ERR, "svc_run returned");
347 	return (1);
348 }
349 
350 /*
351  * The old automounter supported a SIGHUP
352  * to allow it to resynchronize internal
353  * state with the /etc/mnttab.
354  * This is no longer relevant, but we
355  * need to catch the signal and warn
356  * the user.
357  */
358 /* ARGSUSED */
359 static void
360 warn_hup(i)
361 	int i;
362 {
363 	syslog(LOG_ERR, "SIGHUP received: ignored");
364 	(void) signal(SIGHUP, warn_hup);
365 }
366 
367 static void
368 usage()
369 {
370 	(void) fprintf(stderr, "Usage: automountd\n"
371 	    "\t[-T]\t\t(trace requests)\n"
372 	    "\t[-v]\t\t(verbose error msgs)\n"
373 	    "\t[-D n=s]\t(define env variable)\n");
374 	exit(1);
375 	/* NOTREACHED */
376 }
377 
378 /*
379  * dupreq_nonidemp(struct svc_req *rqstp, SVCXPRT *transp, int res_sz,
380  *		bool_t (*xdr_result)(), void (*local_free)())
381  * check the status of nonidempotent requests in the duplicate request cache.
382  * Get result of done requests and send a reply to the kernel. Return status.
383  */
384 static int
385 dupreq_nonidemp(struct svc_req *rqstp, SVCXPRT *transp, int res_sz,
386 		bool_t (*xdr_result)(), void (*local_free)())
387 {
388 	caddr_t resp_buf;
389 	uint_t resp_bufsz;
390 	int dupstat;
391 	XDR xdrs;
392 	caddr_t res;
393 
394 	dupstat = __svc_vc_dup(rqstp, &resp_buf, &resp_bufsz);
395 	switch (dupstat) {
396 	case DUP_NEW:
397 		break;
398 	case DUP_DONE:
399 		if (!resp_buf) {
400 			if (verbose) {
401 				syslog(LOG_ERR,
402 				"dupreq_nonidemp: done, no cached result");
403 			}
404 			break;
405 		}
406 		/* buffer contains xdr encoded results - decode and sendreply */
407 		if (verbose) {
408 			syslog(LOG_ERR,
409 			"dupreq_nonidemp: done, send reply to kernel");
410 		}
411 
412 		memset((caddr_t)&xdrs, 0, sizeof (XDR));
413 		xdrmem_create(&xdrs, resp_buf, resp_bufsz, XDR_DECODE);
414 
415 		if ((res = (caddr_t)malloc(res_sz)) == NULL) {
416 			syslog(LOG_ERR, "dupreq_nonidemp: out of memory");
417 			xdr_destroy(&xdrs);
418 			free(resp_buf);
419 			break;
420 		}
421 		memset(res, 0, res_sz);
422 
423 		if ((*xdr_result)(&xdrs, res) == FALSE) {
424 			if (verbose)
425 				syslog(LOG_ERR,
426 				"dupreq_nonidemp: cannot xdr decode result");
427 			xdr_destroy(&xdrs);
428 			free(resp_buf);
429 			free(res);
430 			break;
431 		}
432 
433 		if (!svc_sendreply(transp, xdr_result, (caddr_t)res)) {
434 			xdr_destroy(&xdrs);
435 			free(resp_buf);
436 			(void) (*local_free)(res);
437 			free(res);
438 			svcerr_systemerr(transp);
439 			return (DUP_ERROR);
440 		}
441 		xdr_destroy(&xdrs);
442 		free(resp_buf);
443 		(void) (*local_free)(res);
444 		free(res);
445 		break;
446 
447 	/* all other cases log the case and drop the request */
448 	case DUP_INPROGRESS:
449 		if (verbose) {
450 			syslog(LOG_ERR,
451 			"dupreq_nonidemp: duplicate request in progress\n");
452 		}
453 		break;
454 	case DUP_DROP:	/* should never be called in automountd */
455 		if (verbose)
456 			syslog(LOG_ERR,
457 			"dupreq_nonidemp: dropped duplicate request error");
458 		break;
459 	case DUP_ERROR: /* fall through */
460 	default:
461 		if (verbose)
462 			syslog(LOG_ERR,
463 			"dupreq_nonidemp: duplicate request cache error");
464 		break;
465 	}
466 	return (dupstat);
467 }
468 
469 /*
470  * dupdonereq_nonidemp(struct svc_req *rqstp, caddr_t res,
471  *		bool_t (*xdr_result)())
472  * call the cache to indicate we are done with the nonidempotent request.
473  * xdr_result will write the encoded xdr form of results into the buffer
474  * provided in xdrmem_create. Makes a best effort to update the cache
475  * first with a buffer containing the results, and then with a NULL buffer.
476  * Return status.
477  */
478 static int
479 dupdonereq_nonidemp(struct svc_req *rqstp, caddr_t res, bool_t (*xdr_result)())
480 {
481 	caddr_t resp_buf;
482 	ulong_t resp_bufsz;
483 	XDR xdrs;
484 	int dupstat;
485 
486 	/*
487 	 * create a results buffer and write into the cache
488 	 * continue with a NULL buffer on errors.
489 	 */
490 	if ((resp_bufsz = xdr_sizeof(xdr_result, (void *)res)) == 0) {
491 		if (verbose)
492 			syslog(LOG_ERR, "dupdonereq_nonidemp: xdr error");
493 		resp_buf = NULL;
494 		resp_bufsz = 0;
495 	} else {
496 		if ((resp_buf = (caddr_t)malloc(resp_bufsz)) == NULL) {
497 			syslog(LOG_ERR, "dupdonereq_nonidemp: out of memory");
498 			resp_bufsz = 0;
499 		} else {
500 			memset(resp_buf, 0, resp_bufsz);
501 			memset((caddr_t)&xdrs, 0, sizeof (XDR));
502 			xdrmem_create(&xdrs, resp_buf, (uint_t)resp_bufsz,
503 			    XDR_ENCODE);
504 			if ((*xdr_result)(&xdrs, res) == FALSE) {
505 				if (verbose)
506 					syslog(LOG_ERR,
507 					"cannot xdr encode results");
508 				xdr_destroy(&xdrs);
509 				free(resp_buf);
510 				resp_buf = NULL;
511 				resp_bufsz = 0;
512 			} else
513 				xdr_destroy(&xdrs);
514 		}
515 	}
516 
517 	dupstat = __svc_vc_dupdone(rqstp, resp_buf, (uint_t)resp_bufsz,
518 	    DUP_DONE);
519 	if (dupstat == DUP_ERROR) {
520 		if (verbose)
521 			syslog(LOG_ERR, "dupdonereq_nonidemp: cache error");
522 		if (resp_buf != NULL) {
523 			if (verbose)
524 				syslog(LOG_ERR, "dupdonereq_nonidemp: retry");
525 			dupstat = __svc_vc_dupdone(rqstp, NULL, 0, DUP_DONE);
526 			if ((dupstat == DUP_ERROR) && verbose)
527 				syslog(LOG_ERR,
528 				"dupdonereq_nonidemp: retry failed");
529 		}
530 	}
531 	if (resp_buf)
532 		free(resp_buf);
533 	return (dupstat);
534 }
535 
536 /*
537  * dupreq_idemp(struct svc_req *rqstp, SVCXPRT *transp, int res_sz;
538  *		bool_t (*xdr_result)(), void (*local_free)())
539  * check the status of idempotent requests in the duplicate request cache.
540  * treat a idempotent request like a new one if its done, but do workavoids
541  * if its a request in progress. Return status.
542  */
543 static int
544 dupreq_idemp(struct svc_req *rqstp, SVCXPRT *transp, int res_sz,
545 		bool_t (*xdr_result)(), void (*local_free)())
546 {
547 	int dupstat;
548 
549 #ifdef lint
550 	transp = transp;
551 	res_sz = res_sz;
552 	local_free = local_free;
553 	xdr_result = xdr_result;
554 #endif /* lint */
555 
556 	/*
557 	 * call the cache to check the status of the request. don't care
558 	 * about results in the cache.
559 	 */
560 	dupstat = __svc_vc_dup(rqstp, NULL, NULL);
561 	switch (dupstat) {
562 	case DUP_NEW:
563 		break;
564 	case DUP_DONE:
565 		if (verbose)
566 			syslog(LOG_ERR, "dupreq_idemp: done request, redo");
567 		dupstat = DUP_NEW;
568 		break;
569 
570 	/* all other cases log the case and drop the request */
571 	case DUP_INPROGRESS:
572 		if (verbose)
573 			syslog(LOG_ERR,
574 			"dupreq_idemp: duplicate request in progress\n");
575 		break;
576 	case DUP_DROP:	/* should never be called in automountd */
577 		if (verbose)
578 			syslog(LOG_ERR,
579 			"dupreq_idemp: dropped duplicate request error");
580 		break;
581 	case DUP_ERROR:	/* fall through */
582 	default:
583 		if (verbose)
584 			syslog(LOG_ERR,
585 			"dupreq_idemp: duplicate request cache error");
586 		break;
587 	}
588 	return (dupstat);
589 }
590 
591 /*
592  * dupdonereq_idemp(struct svc_req *rqstp, caddr_t res,	bool_t (*xdr_result)())
593  * call the cache to indicate we are done with the idempotent request - we do
594  * this to allow work avoids for in progress requests. don't bother to store
595  * any results in the cache. Return status.
596  */
597 static int
598 dupdonereq_idemp(struct svc_req *rqstp, caddr_t res, bool_t (*xdr_result)())
599 {
600 	int dupstat;
601 
602 #ifdef lint
603 	res = res;
604 	xdr_result = xdr_result;
605 #endif /* lint */
606 
607 	dupstat = __svc_vc_dupdone(rqstp, NULL, (uint_t)0, DUP_DONE);
608 	if ((dupstat == DUP_ERROR) && verbose)
609 		syslog(LOG_ERR, "dupdonereq_idemp: cannot cache result");
610 	return (dupstat);
611 }
612 
613 /*
614  * Returns the UID of the caller
615  */
616 static uid_t
617 getowner(transp)
618 	SVCXPRT *transp;
619 {
620 	uid_t uid;
621 
622 	if (__rpc_get_local_uid(transp, &uid) < 0) {
623 		char *err_msg = "Could not get local uid - request ignored\n";
624 
625 		if (trace > 1)
626 			trace_prt(1, err_msg);
627 		if (verbose)
628 			pr_msg(err_msg);
629 		return (-1);
630 	}
631 	if (uid != 0) {
632 		char *err_msg =
633 			"Illegal access attempt by uid=%ld - request ignored\n";
634 
635 		if (trace > 1)
636 			trace_prt(1, err_msg, uid);
637 		pr_msg(err_msg, uid);
638 	}
639 	return (uid);
640 }
641 
642 /*
643  * Each RPC request will automatically spawn a new thread with this
644  * as its entry point.
645  * XXX - the switch statement should be changed to a table of procedures
646  * similar to that used by rfs_dispatch() in uts/common/fs/nfs/nfs_server.c.
647  * duplicate request handling should also be synced with rfs_dispatch().
648  */
649 static void
650 autofs_prog(rqstp, transp)
651 	struct svc_req *rqstp;
652 	register SVCXPRT *transp;
653 {
654 	union {
655 		autofs_lookupargs autofs_mount_1_arg;
656 		autofs_lookupargs autofs_lookup_1_arg;
657 		umntrequest autofs_umount_1_arg;
658 		autofs_rddirargs autofs_readdir_1_arg;
659 	} argument;
660 
661 	union {
662 		autofs_mountres mount_res;
663 		autofs_lookupres lookup_res;
664 		umntres umount_res;
665 		autofs_rddirres readdir_res;
666 	} res;
667 
668 	bool_t (*xdr_argument)();
669 	bool_t (*xdr_result)();
670 	void   (*local)();
671 	void   (*local_free)();
672 	int    (*dup_request)();
673 	int    (*dupdone_request)();
674 
675 	timenow = time((time_t *)NULL);
676 
677 	if (rqstp->rq_proc != NULLPROC && getowner(transp) != 0) {
678 		/*
679 		 * Drop request
680 		 */
681 		return;
682 	}
683 
684 	switch (rqstp->rq_proc) {
685 	case NULLPROC:
686 		(void) svc_sendreply(transp, xdr_void, (char *)NULL);
687 		return;
688 
689 #ifdef MALLOC_DEBUG
690 	case AUTOFS_DUMP_DEBUG:
691 		(void) svc_sendreply(transp, xdr_void, (char *)NULL);
692 		check_leaks("/var/tmp/automountd.leak");
693 		return;
694 #endif
695 
696 	case AUTOFS_LOOKUP:
697 		xdr_argument = xdr_autofs_lookupargs;
698 		xdr_result = xdr_autofs_lookupres;
699 		local = autofs_lookup_1_r;
700 		local_free = autofs_lookup_1_free_r;
701 		dup_request = dupreq_nonidemp;
702 		dupdone_request = dupdonereq_nonidemp;
703 		break;
704 
705 	case AUTOFS_MOUNT:
706 		xdr_argument = xdr_autofs_lookupargs;
707 		xdr_result = xdr_autofs_mountres;
708 		local = autofs_mount_1_r;
709 		local_free = autofs_mount_1_free_r;
710 		dup_request = dupreq_nonidemp;
711 		dupdone_request = dupdonereq_nonidemp;
712 		break;
713 
714 	case AUTOFS_UNMOUNT:
715 		xdr_argument = xdr_umntrequest;
716 		xdr_result = xdr_umntres;
717 		local = autofs_unmount_1_r;
718 		local_free = autofs_unmount_1_free_r;
719 		dup_request = dupreq_nonidemp;
720 		dupdone_request = dupdonereq_nonidemp;
721 		break;
722 
723 	case AUTOFS_READDIR:
724 		xdr_argument = xdr_autofs_rddirargs;
725 		xdr_result = xdr_autofs_rddirres;
726 		local = autofs_readdir_1_r;
727 		local_free = autofs_readdir_1_free_r;
728 		dup_request = dupreq_idemp;
729 		dupdone_request = dupdonereq_idemp;
730 		break;
731 
732 	default:
733 		svcerr_noproc(transp);
734 		return;
735 	}
736 
737 
738 	if ((*dup_request)(rqstp, transp, sizeof (res), xdr_result,
739 				local_free) != DUP_NEW)
740 		return;
741 
742 	(void) memset((char *)&argument, 0, sizeof (argument));
743 	if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
744 		svcerr_decode(transp);
745 		return;
746 	}
747 
748 	(void) memset((char *)&res, 0, sizeof (res));
749 	(*local)(&argument, &res, rqstp->rq_clntcred);
750 
751 	/* update cache with done request results */
752 	(void) (*dupdone_request)(rqstp, (caddr_t)&res, xdr_result);
753 
754 	if (!svc_sendreply(transp, xdr_result, (caddr_t)&res)) {
755 		svcerr_systemerr(transp);
756 	}
757 
758 	if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) {
759 		syslog(LOG_ERR, "unable to free arguments");
760 	}
761 
762 	(*local_free)(&res);
763 
764 }
765 
766 static void
767 autofs_readdir_1_r(req, res, cred)
768 	struct autofs_rddirargs *req;
769 	struct autofs_rddirres *res;
770 	struct authunix_parms *cred;
771 {
772 	if (trace > 0)
773 		trace_prt(1, "READDIR REQUEST	: %s @ %ld\n",
774 		req->rda_map, req->rda_offset);
775 
776 	(void) do_readdir(req, res, cred);
777 
778 	if (trace > 0)
779 		trace_prt(1, "READDIR REPLY	: status=%d\n", res->rd_status);
780 }
781 
782 static void
783 autofs_readdir_1_free_r(res)
784 	struct autofs_rddirres *res;
785 {
786 	if (res->rd_status == AUTOFS_OK) {
787 		if (res->rd_rddir.rddir_entries)
788 			free(res->rd_rddir.rddir_entries);
789 	}
790 }
791 
792 /* ARGSUSED */
793 static void
794 autofs_unmount_1_r(m, res, cred)
795 	struct umntrequest *m;
796 	struct umntres *res;
797 	struct authunix_parms *cred;
798 {
799 	struct umntrequest *ul;
800 
801 	if (trace > 0) {
802 		char ctime_buf[CTIME_BUF_LEN];
803 		if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL)
804 			ctime_buf[0] = '\0';
805 
806 		trace_prt(1, "UNMOUNT REQUEST: %s", ctime_buf);
807 		for (ul = m; ul; ul = ul->next)
808 			trace_prt(1, " resource=%s fstype=%s mntpnt=%s"
809 				" mntopts=%s %s\n",
810 				ul->mntresource,
811 				ul->fstype,
812 				ul->mntpnt,
813 				ul->mntopts,
814 				ul->isdirect ? "direct" : "indirect");
815 	}
816 
817 	res->status = do_unmount1(m);
818 
819 	if (trace > 0)
820 		trace_prt(1, "UNMOUNT REPLY: status=%d\n", res->status);
821 }
822 
823 static void
824 autofs_unmount_1_free_r(res)
825 	struct umntres *res;
826 {
827 #ifdef lint
828 	res = res;
829 #endif /* lint */
830 }
831 
832 static void
833 autofs_lookup_1_r(m, res, cred)
834 	struct autofs_lookupargs *m;
835 	struct autofs_lookupres *res;
836 	struct authunix_parms *cred;
837 {
838 	enum autofs_action action;
839 	struct linka link;
840 	int status;
841 
842 	if (trace > 0) {
843 		char ctime_buf[CTIME_BUF_LEN];
844 		if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL)
845 			ctime_buf[0] = '\0';
846 
847 		trace_prt(1, "LOOKUP REQUEST: %s", ctime_buf);
848 		trace_prt(1, "  name=%s[%s] map=%s opts=%s path=%s direct=%d\n",
849 			m->name, m->subdir, m->map, m->opts,
850 			m->path, m->isdirect);
851 	}
852 
853 	status = do_lookup1(m->map, m->name, m->subdir, m->opts, m->path,
854 			(uint_t)m->isdirect, &action, &link, cred);
855 	if (status == 0) {
856 		/*
857 		 * Return action list to kernel.
858 		 */
859 		res->lu_res = AUTOFS_OK;
860 		if ((res->lu_type.action = action) == AUTOFS_LINK_RQ)
861 			res->lu_type.lookup_result_type_u.lt_linka = link;
862 	} else {
863 		/*
864 		 * Entry not found
865 		 */
866 		res->lu_res = AUTOFS_NOENT;
867 	}
868 	res->lu_verbose = verbose;
869 
870 	if (trace > 0)
871 		trace_prt(1, "LOOKUP REPLY    : status=%d\n", res->lu_res);
872 }
873 
874 static void
875 autofs_lookup_1_free_r(res)
876 	struct autofs_lookupres *res;
877 {
878 	struct linka link;
879 
880 	if ((res->lu_res == AUTOFS_OK) &&
881 	    (res->lu_type.action == AUTOFS_LINK_RQ)) {
882 		/*
883 		 * Free link information
884 		 */
885 		link = res->lu_type.lookup_result_type_u.lt_linka;
886 		if (link.dir)
887 			free(link.dir);
888 		if (link.link)
889 			free(link.link);
890 	}
891 }
892 
893 static void
894 autofs_mount_1_r(m, res, cred)
895 	struct autofs_lookupargs *m;
896 	struct autofs_mountres *res;
897 	struct authunix_parms *cred;
898 {
899 	int status;
900 	action_list *alp = NULL;
901 
902 	if (trace > 0) {
903 		char ctime_buf[CTIME_BUF_LEN];
904 		if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL)
905 			ctime_buf[0] = '\0';
906 
907 		trace_prt(1, "MOUNT REQUEST:   %s", ctime_buf);
908 		trace_prt(1, "  name=%s[%s] map=%s opts=%s path=%s direct=%d\n",
909 			m->name, m->subdir, m->map, m->opts,
910 			m->path, m->isdirect);
911 	}
912 
913 	status = do_mount1(m->map, m->name, m->subdir, m->opts, m->path,
914 			(uint_t)m->isdirect, &alp, cred);
915 	if (status != 0) {
916 		/*
917 		 * An error occurred, free action list if allocated.
918 		 */
919 		if (alp != NULL) {
920 			free_action_list(alp);
921 			alp = NULL;
922 		}
923 	}
924 	if (alp != NULL) {
925 		/*
926 		 * Return action list to kernel.
927 		 */
928 		res->mr_type.status = AUTOFS_ACTION;
929 		res->mr_type.mount_result_type_u.list = alp;
930 	} else {
931 		/*
932 		 * No work to do left for the kernel
933 		 */
934 		res->mr_type.status = AUTOFS_DONE;
935 		res->mr_type.mount_result_type_u.error = status;
936 	}
937 	res->mr_verbose = verbose;
938 
939 	if (trace > 0) {
940 		switch (res->mr_type.status) {
941 		case AUTOFS_ACTION:
942 			trace_prt(1,
943 				"MOUNT REPLY    : status=%d, AUTOFS_ACTION\n",
944 				status);
945 			break;
946 		case AUTOFS_DONE:
947 			trace_prt(1,
948 				"MOUNT REPLY    : status=%d, AUTOFS_DONE\n",
949 				status);
950 			break;
951 		default:
952 			trace_prt(1, "MOUNT REPLY    : status=%d, UNKNOWN\n",
953 				status);
954 		}
955 	}
956 
957 	if (status && verbose) {
958 		if (m->isdirect) {
959 			/* direct mount */
960 			syslog(LOG_ERR, "mount of %s failed", m->path);
961 		} else {
962 			/* indirect mount */
963 			syslog(LOG_ERR,
964 				"mount of %s/%s failed", m->path, m->name);
965 		}
966 	}
967 }
968 
969 static void
970 autofs_mount_1_free_r(res)
971 	struct autofs_mountres *res;
972 {
973 	if (res->mr_type.status == AUTOFS_ACTION) {
974 		if (trace > 2)
975 			trace_prt(1, "freeing action list\n");
976 
977 		free_action_list(res->mr_type.mount_result_type_u.list);
978 	}
979 }
980 
981 /*
982  * Used for reporting messages from code shared with automount command.
983  * Formats message into a buffer and calls syslog.
984  *
985  * Print an error.  Works like printf (fmt string and variable args)
986  * except that it will subsititute an error message for a "%m" string
987  * (like syslog).
988  */
989 void
990 pr_msg(const char *fmt, ...)
991 {
992 	va_list ap;
993 	char fmtbuff[BUFSIZ], buff[BUFSIZ];
994 	const char *p1;
995 	char *p2;
996 
997 	p2 = fmtbuff;
998 	fmt = gettext(fmt);
999 
1000 	for (p1 = fmt; *p1; p1++) {
1001 		if (*p1 == '%' && *(p1 + 1) == 'm') {
1002 			(void) strcpy(p2, strerror(errno));
1003 			p2 += strlen(p2);
1004 			p1++;
1005 		} else {
1006 			*p2++ = *p1;
1007 		}
1008 	}
1009 	if (p2 > fmtbuff && *(p2-1) != '\n')
1010 		*p2++ = '\n';
1011 	*p2 = '\0';
1012 
1013 	va_start(ap, fmt);
1014 	(void) vsprintf(buff, fmtbuff, ap);
1015 	va_end(ap);
1016 	syslog(LOG_ERR, buff);
1017 }
1018 
1019 static void
1020 free_action_list(action_list *alp)
1021 {
1022 	action_list *p, *next = NULL;
1023 	struct mounta *mp;
1024 
1025 	for (p = alp; p != NULL; p = next) {
1026 		switch (p->action.action) {
1027 		case AUTOFS_MOUNT_RQ:
1028 			mp = &(p->action.action_list_entry_u.mounta);
1029 			/* LINTED pointer alignment */
1030 			free_autofs_args((autofs_args *)mp->dataptr);
1031 			mp->dataptr = NULL;
1032 			mp->datalen = 0;
1033 			free_mounta(mp);
1034 			break;
1035 		case AUTOFS_LINK_RQ:
1036 			syslog(LOG_ERR,
1037 			    "non AUTOFS_MOUNT_RQ requests not implemented\n");
1038 			break;
1039 		default:
1040 			syslog(LOG_ERR,
1041 			    "non AUTOFS_MOUNT_RQ requests not implemented\n");
1042 			break;
1043 		}
1044 		next = p->next;
1045 		free(p);
1046 	}
1047 }
1048