xref: /illumos-gate/usr/src/cmd/fs.d/autofs/autod_main.c (revision 35551380472894a564e057962b701af78f719377)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.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/rpc.h>
44 #include <rpc/xdr.h>
45 #include <rpcsvc/nfs_prot.h>
46 #include <net/if.h>
47 #include <netdir.h>
48 #include <string.h>
49 #include <thread.h>
50 #include <locale.h>
51 #include "automount.h"
52 #include <sys/vfs.h>
53 #include <sys/mnttab.h>
54 #include <arpa/inet.h>
55 #include <rpc/svc.h>			/* for private dupcache routines */
56 #include <rpcsvc/daemon_utils.h>
57 #include <deflt.h>
58 #include <strings.h>
59 
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 	if (!rpc_control(RPC_SVC_MTMODE_SET, &rpc_svc_mode)) {
326 		syslog(LOG_ERR, "unable to set automatic MT mode");
327 		exit(1);
328 	}
329 	if (svc_create_local_service(autofs_prog,
330 		AUTOFS_PROG, AUTOFS_VERS, "netpath", "autofs") == 0) {
331 		syslog(LOG_ERR, "unable to create service");
332 		exit(1);
333 	}
334 
335 	(void) signal(SIGHUP, warn_hup);
336 
337 	svc_run();
338 	syslog(LOG_ERR, "svc_run returned");
339 	return (1);
340 }
341 
342 /*
343  * The old automounter supported a SIGHUP
344  * to allow it to resynchronize internal
345  * state with the /etc/mnttab.
346  * This is no longer relevant, but we
347  * need to catch the signal and warn
348  * the user.
349  */
350 /* ARGSUSED */
351 static void
352 warn_hup(i)
353 	int i;
354 {
355 	syslog(LOG_ERR, "SIGHUP received: ignored");
356 	(void) signal(SIGHUP, warn_hup);
357 }
358 
359 static void
360 usage()
361 {
362 	(void) fprintf(stderr, "Usage: automountd\n"
363 	    "\t[-T]\t\t(trace requests)\n"
364 	    "\t[-v]\t\t(verbose error msgs)\n"
365 	    "\t[-D n=s]\t(define env variable)\n");
366 	exit(1);
367 	/* NOTREACHED */
368 }
369 
370 /*
371  * dupreq_nonidemp(struct svc_req *rqstp, SVCXPRT *transp, int res_sz,
372  *		bool_t (*xdr_result)(), void (*local_free)())
373  * check the status of nonidempotent requests in the duplicate request cache.
374  * Get result of done requests and send a reply to the kernel. Return status.
375  */
376 static int
377 dupreq_nonidemp(struct svc_req *rqstp, SVCXPRT *transp, int res_sz,
378 		bool_t (*xdr_result)(), void (*local_free)())
379 {
380 	caddr_t resp_buf;
381 	uint_t resp_bufsz;
382 	int dupstat;
383 	XDR xdrs;
384 	caddr_t res;
385 
386 	dupstat = __svc_vc_dup(rqstp, &resp_buf, &resp_bufsz);
387 	switch (dupstat) {
388 	case DUP_NEW:
389 		break;
390 	case DUP_DONE:
391 		if (!resp_buf) {
392 			if (verbose) {
393 				syslog(LOG_ERR,
394 				"dupreq_nonidemp: done, no cached result");
395 			}
396 			break;
397 		}
398 		/* buffer contains xdr encoded results - decode and sendreply */
399 		if (verbose) {
400 			syslog(LOG_ERR,
401 			"dupreq_nonidemp: done, send reply to kernel");
402 		}
403 
404 		memset((caddr_t)&xdrs, 0, sizeof (XDR));
405 		xdrmem_create(&xdrs, resp_buf, resp_bufsz, XDR_DECODE);
406 
407 		if ((res = (caddr_t)malloc(res_sz)) == NULL) {
408 			syslog(LOG_ERR, "dupreq_nonidemp: out of memory");
409 			xdr_destroy(&xdrs);
410 			free(resp_buf);
411 			break;
412 		}
413 		memset(res, 0, res_sz);
414 
415 		if ((*xdr_result)(&xdrs, res) == FALSE) {
416 			if (verbose)
417 				syslog(LOG_ERR,
418 				"dupreq_nonidemp: cannot xdr decode result");
419 			xdr_destroy(&xdrs);
420 			free(resp_buf);
421 			free(res);
422 			break;
423 		}
424 
425 		if (!svc_sendreply(transp, xdr_result, (caddr_t)res)) {
426 			xdr_destroy(&xdrs);
427 			free(resp_buf);
428 			(void) (*local_free)(res);
429 			free(res);
430 			svcerr_systemerr(transp);
431 			return (DUP_ERROR);
432 		}
433 		xdr_destroy(&xdrs);
434 		free(resp_buf);
435 		(void) (*local_free)(res);
436 		free(res);
437 		break;
438 
439 	/* all other cases log the case and drop the request */
440 	case DUP_INPROGRESS:
441 		if (verbose) {
442 			syslog(LOG_ERR,
443 			"dupreq_nonidemp: duplicate request in progress\n");
444 		}
445 		break;
446 	case DUP_DROP:	/* should never be called in automountd */
447 		if (verbose)
448 			syslog(LOG_ERR,
449 			"dupreq_nonidemp: dropped duplicate request error");
450 		break;
451 	case DUP_ERROR: /* fall through */
452 	default:
453 		if (verbose)
454 			syslog(LOG_ERR,
455 			"dupreq_nonidemp: duplicate request cache error");
456 		break;
457 	}
458 	return (dupstat);
459 }
460 
461 /*
462  * dupdonereq_nonidemp(struct svc_req *rqstp, caddr_t res,
463  *		bool_t (*xdr_result)())
464  * call the cache to indicate we are done with the nonidempotent request.
465  * xdr_result will write the encoded xdr form of results into the buffer
466  * provided in xdrmem_create. Makes a best effort to update the cache
467  * first with a buffer containing the results, and then with a NULL buffer.
468  * Return status.
469  */
470 static int
471 dupdonereq_nonidemp(struct svc_req *rqstp, caddr_t res, bool_t (*xdr_result)())
472 {
473 	caddr_t resp_buf;
474 	ulong_t resp_bufsz;
475 	XDR xdrs;
476 	int dupstat;
477 
478 	/*
479 	 * create a results buffer and write into the cache
480 	 * continue with a NULL buffer on errors.
481 	 */
482 	if ((resp_bufsz = xdr_sizeof(xdr_result, (void *)res)) == 0) {
483 		if (verbose)
484 			syslog(LOG_ERR, "dupdonereq_nonidemp: xdr error");
485 		resp_buf = NULL;
486 		resp_bufsz = 0;
487 	} else {
488 		if ((resp_buf = (caddr_t)malloc(resp_bufsz)) == NULL) {
489 			syslog(LOG_ERR, "dupdonereq_nonidemp: out of memory");
490 			resp_bufsz = 0;
491 		} else {
492 			memset(resp_buf, 0, resp_bufsz);
493 			memset((caddr_t)&xdrs, 0, sizeof (XDR));
494 			xdrmem_create(&xdrs, resp_buf, (uint_t)resp_bufsz,
495 			    XDR_ENCODE);
496 			if ((*xdr_result)(&xdrs, res) == FALSE) {
497 				if (verbose)
498 					syslog(LOG_ERR,
499 					"cannot xdr encode results");
500 				xdr_destroy(&xdrs);
501 				free(resp_buf);
502 				resp_buf = NULL;
503 				resp_bufsz = 0;
504 			} else
505 				xdr_destroy(&xdrs);
506 		}
507 	}
508 
509 	dupstat = __svc_vc_dupdone(rqstp, resp_buf, (uint_t)resp_bufsz,
510 	    DUP_DONE);
511 	if (dupstat == DUP_ERROR) {
512 		if (verbose)
513 			syslog(LOG_ERR, "dupdonereq_nonidemp: cache error");
514 		if (resp_buf != NULL) {
515 			if (verbose)
516 				syslog(LOG_ERR, "dupdonereq_nonidemp: retry");
517 			dupstat = __svc_vc_dupdone(rqstp, NULL, 0, DUP_DONE);
518 			if ((dupstat == DUP_ERROR) && verbose)
519 				syslog(LOG_ERR,
520 				"dupdonereq_nonidemp: retry failed");
521 		}
522 	}
523 	if (resp_buf)
524 		free(resp_buf);
525 	return (dupstat);
526 }
527 
528 /*
529  * dupreq_idemp(struct svc_req *rqstp, SVCXPRT *transp, int res_sz;
530  *		bool_t (*xdr_result)(), void (*local_free)())
531  * check the status of idempotent requests in the duplicate request cache.
532  * treat a idempotent request like a new one if its done, but do workavoids
533  * if its a request in progress. Return status.
534  */
535 static int
536 dupreq_idemp(struct svc_req *rqstp, SVCXPRT *transp, int res_sz,
537 		bool_t (*xdr_result)(), void (*local_free)())
538 {
539 	int dupstat;
540 
541 #ifdef lint
542 	transp = transp;
543 	res_sz = res_sz;
544 	local_free = local_free;
545 	xdr_result = xdr_result;
546 #endif /* lint */
547 
548 	/*
549 	 * call the cache to check the status of the request. don't care
550 	 * about results in the cache.
551 	 */
552 	dupstat = __svc_vc_dup(rqstp, NULL, NULL);
553 	switch (dupstat) {
554 	case DUP_NEW:
555 		break;
556 	case DUP_DONE:
557 		if (verbose)
558 			syslog(LOG_ERR, "dupreq_idemp: done request, redo");
559 		dupstat = DUP_NEW;
560 		break;
561 
562 	/* all other cases log the case and drop the request */
563 	case DUP_INPROGRESS:
564 		if (verbose)
565 			syslog(LOG_ERR,
566 			"dupreq_idemp: duplicate request in progress\n");
567 		break;
568 	case DUP_DROP:	/* should never be called in automountd */
569 		if (verbose)
570 			syslog(LOG_ERR,
571 			"dupreq_idemp: dropped duplicate request error");
572 		break;
573 	case DUP_ERROR:	/* fall through */
574 	default:
575 		if (verbose)
576 			syslog(LOG_ERR,
577 			"dupreq_idemp: duplicate request cache error");
578 		break;
579 	}
580 	return (dupstat);
581 }
582 
583 /*
584  * dupdonereq_idemp(struct svc_req *rqstp, caddr_t res,	bool_t (*xdr_result)())
585  * call the cache to indicate we are done with the idempotent request - we do
586  * this to allow work avoids for in progress requests. don't bother to store
587  * any results in the cache. Return status.
588  */
589 static int
590 dupdonereq_idemp(struct svc_req *rqstp, caddr_t res, bool_t (*xdr_result)())
591 {
592 	int dupstat;
593 
594 #ifdef lint
595 	res = res;
596 	xdr_result = xdr_result;
597 #endif /* lint */
598 
599 	dupstat = __svc_vc_dupdone(rqstp, NULL, (uint_t)0, DUP_DONE);
600 	if ((dupstat == DUP_ERROR) && verbose)
601 		syslog(LOG_ERR, "dupdonereq_idemp: cannot cache result");
602 	return (dupstat);
603 }
604 
605 /*
606  * Returns the UID of the caller
607  */
608 static uid_t
609 getowner(transp)
610 	SVCXPRT *transp;
611 {
612 	uid_t uid;
613 
614 	if (__rpc_get_local_uid(transp, &uid) < 0) {
615 		char *err_msg = "Could not get local uid - request ignored\n";
616 
617 		if (trace > 1)
618 			trace_prt(1, err_msg);
619 		if (verbose)
620 			pr_msg(err_msg);
621 		return (-1);
622 	}
623 	if (uid != 0) {
624 		char *err_msg =
625 			"Illegal access attempt by uid=%ld - request ignored\n";
626 
627 		if (trace > 1)
628 			trace_prt(1, err_msg, uid);
629 		pr_msg(err_msg, uid);
630 	}
631 	return (uid);
632 }
633 
634 /*
635  * Each RPC request will automatically spawn a new thread with this
636  * as its entry point.
637  * XXX - the switch statement should be changed to a table of procedures
638  * similar to that used by rfs_dispatch() in uts/common/fs/nfs/nfs_server.c.
639  * duplicate request handling should also be synced with rfs_dispatch().
640  */
641 static void
642 autofs_prog(rqstp, transp)
643 	struct svc_req *rqstp;
644 	register SVCXPRT *transp;
645 {
646 	union {
647 		autofs_lookupargs autofs_mount_1_arg;
648 		autofs_lookupargs autofs_lookup_1_arg;
649 		umntrequest autofs_umount_1_arg;
650 		autofs_rddirargs autofs_readdir_1_arg;
651 	} argument;
652 
653 	union {
654 		autofs_mountres mount_res;
655 		autofs_lookupres lookup_res;
656 		umntres umount_res;
657 		autofs_rddirres readdir_res;
658 	} res;
659 
660 	bool_t (*xdr_argument)();
661 	bool_t (*xdr_result)();
662 	void   (*local)();
663 	void   (*local_free)();
664 	int    (*dup_request)();
665 	int    (*dupdone_request)();
666 
667 	timenow = time((time_t *)NULL);
668 
669 	if (rqstp->rq_proc != NULLPROC && getowner(transp) != 0) {
670 		/*
671 		 * Drop request
672 		 */
673 		return;
674 	}
675 
676 	switch (rqstp->rq_proc) {
677 	case NULLPROC:
678 		(void) svc_sendreply(transp, xdr_void, (char *)NULL);
679 		return;
680 
681 #ifdef MALLOC_DEBUG
682 	case AUTOFS_DUMP_DEBUG:
683 		(void) svc_sendreply(transp, xdr_void, (char *)NULL);
684 		check_leaks("/var/tmp/automountd.leak");
685 		return;
686 #endif
687 
688 	case AUTOFS_LOOKUP:
689 		xdr_argument = xdr_autofs_lookupargs;
690 		xdr_result = xdr_autofs_lookupres;
691 		local = autofs_lookup_1_r;
692 		local_free = autofs_lookup_1_free_r;
693 		dup_request = dupreq_nonidemp;
694 		dupdone_request = dupdonereq_nonidemp;
695 		break;
696 
697 	case AUTOFS_MOUNT:
698 		xdr_argument = xdr_autofs_lookupargs;
699 		xdr_result = xdr_autofs_mountres;
700 		local = autofs_mount_1_r;
701 		local_free = autofs_mount_1_free_r;
702 		dup_request = dupreq_nonidemp;
703 		dupdone_request = dupdonereq_nonidemp;
704 		break;
705 
706 	case AUTOFS_UNMOUNT:
707 		xdr_argument = xdr_umntrequest;
708 		xdr_result = xdr_umntres;
709 		local = autofs_unmount_1_r;
710 		local_free = autofs_unmount_1_free_r;
711 		dup_request = dupreq_nonidemp;
712 		dupdone_request = dupdonereq_nonidemp;
713 		break;
714 
715 	case AUTOFS_READDIR:
716 		xdr_argument = xdr_autofs_rddirargs;
717 		xdr_result = xdr_autofs_rddirres;
718 		local = autofs_readdir_1_r;
719 		local_free = autofs_readdir_1_free_r;
720 		dup_request = dupreq_idemp;
721 		dupdone_request = dupdonereq_idemp;
722 		break;
723 
724 	default:
725 		svcerr_noproc(transp);
726 		return;
727 	}
728 
729 
730 	if ((*dup_request)(rqstp, transp, sizeof (res), xdr_result,
731 				local_free) != DUP_NEW)
732 		return;
733 
734 	(void) memset((char *)&argument, 0, sizeof (argument));
735 	if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
736 		svcerr_decode(transp);
737 		return;
738 	}
739 
740 	(void) memset((char *)&res, 0, sizeof (res));
741 	(*local)(&argument, &res, rqstp->rq_clntcred);
742 
743 	/* update cache with done request results */
744 	(void) (*dupdone_request)(rqstp, (caddr_t)&res, xdr_result);
745 
746 	if (!svc_sendreply(transp, xdr_result, (caddr_t)&res)) {
747 		svcerr_systemerr(transp);
748 	}
749 
750 	if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) {
751 		syslog(LOG_ERR, "unable to free arguments");
752 	}
753 
754 	(*local_free)(&res);
755 
756 }
757 
758 static void
759 autofs_readdir_1_r(req, res, cred)
760 	struct autofs_rddirargs *req;
761 	struct autofs_rddirres *res;
762 	struct authunix_parms *cred;
763 {
764 	if (trace > 0)
765 		trace_prt(1, "READDIR REQUEST	: %s @ %ld\n",
766 		req->rda_map, req->rda_offset);
767 
768 	(void) do_readdir(req, res, cred);
769 
770 	if (trace > 0)
771 		trace_prt(1, "READDIR REPLY	: status=%d\n", res->rd_status);
772 }
773 
774 static void
775 autofs_readdir_1_free_r(res)
776 	struct autofs_rddirres *res;
777 {
778 	if (res->rd_status == AUTOFS_OK) {
779 		if (res->rd_rddir.rddir_entries)
780 			free(res->rd_rddir.rddir_entries);
781 	}
782 }
783 
784 /* ARGSUSED */
785 static void
786 autofs_unmount_1_r(m, res, cred)
787 	struct umntrequest *m;
788 	struct umntres *res;
789 	struct authunix_parms *cred;
790 {
791 	struct umntrequest *ul;
792 
793 	if (trace > 0) {
794 		char ctime_buf[CTIME_BUF_LEN];
795 		if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL)
796 			ctime_buf[0] = '\0';
797 
798 		trace_prt(1, "UNMOUNT REQUEST: %s", ctime_buf);
799 		for (ul = m; ul; ul = ul->next)
800 			trace_prt(1, " resource=%s fstype=%s mntpnt=%s"
801 				" mntopts=%s %s\n",
802 				ul->mntresource,
803 				ul->fstype,
804 				ul->mntpnt,
805 				ul->mntopts,
806 				ul->isdirect ? "direct" : "indirect");
807 	}
808 
809 	res->status = do_unmount1(m);
810 
811 	if (trace > 0)
812 		trace_prt(1, "UNMOUNT REPLY: status=%d\n", res->status);
813 }
814 
815 static void
816 autofs_unmount_1_free_r(res)
817 	struct umntres *res;
818 {
819 #ifdef lint
820 	res = res;
821 #endif /* lint */
822 }
823 
824 static void
825 autofs_lookup_1_r(m, res, cred)
826 	struct autofs_lookupargs *m;
827 	struct autofs_lookupres *res;
828 	struct authunix_parms *cred;
829 {
830 	enum autofs_action action;
831 	struct linka link;
832 	int status;
833 
834 	if (trace > 0) {
835 		char ctime_buf[CTIME_BUF_LEN];
836 		if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL)
837 			ctime_buf[0] = '\0';
838 
839 		trace_prt(1, "LOOKUP REQUEST: %s", ctime_buf);
840 		trace_prt(1, "  name=%s[%s] map=%s opts=%s path=%s direct=%d\n",
841 			m->name, m->subdir, m->map, m->opts,
842 			m->path, m->isdirect);
843 	}
844 
845 	status = do_lookup1(m->map, m->name, m->subdir, m->opts, m->path,
846 			(uint_t)m->isdirect, &action, &link, cred);
847 	if (status == 0) {
848 		/*
849 		 * Return action list to kernel.
850 		 */
851 		res->lu_res = AUTOFS_OK;
852 		if ((res->lu_type.action = action) == AUTOFS_LINK_RQ)
853 			res->lu_type.lookup_result_type_u.lt_linka = link;
854 	} else {
855 		/*
856 		 * Entry not found
857 		 */
858 		res->lu_res = AUTOFS_NOENT;
859 	}
860 	res->lu_verbose = verbose;
861 
862 	if (trace > 0)
863 		trace_prt(1, "LOOKUP REPLY    : status=%d\n", res->lu_res);
864 }
865 
866 static void
867 autofs_lookup_1_free_r(res)
868 	struct autofs_lookupres *res;
869 {
870 	struct linka link;
871 
872 	if ((res->lu_res == AUTOFS_OK) &&
873 	    (res->lu_type.action == AUTOFS_LINK_RQ)) {
874 		/*
875 		 * Free link information
876 		 */
877 		link = res->lu_type.lookup_result_type_u.lt_linka;
878 		if (link.dir)
879 			free(link.dir);
880 		if (link.link)
881 			free(link.link);
882 	}
883 }
884 
885 static void
886 autofs_mount_1_r(m, res, cred)
887 	struct autofs_lookupargs *m;
888 	struct autofs_mountres *res;
889 	struct authunix_parms *cred;
890 {
891 	int status;
892 	action_list *alp = NULL;
893 
894 	if (trace > 0) {
895 		char ctime_buf[CTIME_BUF_LEN];
896 		if (ctime_r(&timenow, ctime_buf, CTIME_BUF_LEN) == NULL)
897 			ctime_buf[0] = '\0';
898 
899 		trace_prt(1, "MOUNT REQUEST:   %s", ctime_buf);
900 		trace_prt(1, "  name=%s[%s] map=%s opts=%s path=%s direct=%d\n",
901 			m->name, m->subdir, m->map, m->opts,
902 			m->path, m->isdirect);
903 	}
904 
905 	status = do_mount1(m->map, m->name, m->subdir, m->opts, m->path,
906 			(uint_t)m->isdirect, &alp, cred);
907 	if (status != 0) {
908 		/*
909 		 * An error occurred, free action list if allocated.
910 		 */
911 		if (alp != NULL) {
912 			free_action_list(alp);
913 			alp = NULL;
914 		}
915 	}
916 	if (alp != NULL) {
917 		/*
918 		 * Return action list to kernel.
919 		 */
920 		res->mr_type.status = AUTOFS_ACTION;
921 		res->mr_type.mount_result_type_u.list = alp;
922 	} else {
923 		/*
924 		 * No work to do left for the kernel
925 		 */
926 		res->mr_type.status = AUTOFS_DONE;
927 		res->mr_type.mount_result_type_u.error = status;
928 	}
929 	res->mr_verbose = verbose;
930 
931 	if (trace > 0) {
932 		switch (res->mr_type.status) {
933 		case AUTOFS_ACTION:
934 			trace_prt(1,
935 				"MOUNT REPLY    : status=%d, AUTOFS_ACTION\n",
936 				status);
937 			break;
938 		case AUTOFS_DONE:
939 			trace_prt(1,
940 				"MOUNT REPLY    : status=%d, AUTOFS_DONE\n",
941 				status);
942 			break;
943 		default:
944 			trace_prt(1, "MOUNT REPLY    : status=%d, UNKNOWN\n",
945 				status);
946 		}
947 	}
948 
949 	if (status && verbose) {
950 		if (m->isdirect) {
951 			/* direct mount */
952 			syslog(LOG_ERR, "mount of %s failed", m->path);
953 		} else {
954 			/* indirect mount */
955 			syslog(LOG_ERR,
956 				"mount of %s/%s failed", m->path, m->name);
957 		}
958 	}
959 }
960 
961 static void
962 autofs_mount_1_free_r(res)
963 	struct autofs_mountres *res;
964 {
965 	if (res->mr_type.status == AUTOFS_ACTION) {
966 		if (trace > 2)
967 			trace_prt(1, "freeing action list\n");
968 
969 		free_action_list(res->mr_type.mount_result_type_u.list);
970 	}
971 }
972 
973 /*
974  * Used for reporting messages from code shared with automount command.
975  * Formats message into a buffer and calls syslog.
976  *
977  * Print an error.  Works like printf (fmt string and variable args)
978  * except that it will subsititute an error message for a "%m" string
979  * (like syslog).
980  */
981 void
982 pr_msg(const char *fmt, ...)
983 {
984 	va_list ap;
985 	char fmtbuff[BUFSIZ], buff[BUFSIZ];
986 	const char *p1;
987 	char *p2;
988 
989 	p2 = fmtbuff;
990 	fmt = gettext(fmt);
991 
992 	for (p1 = fmt; *p1; p1++) {
993 		if (*p1 == '%' && *(p1 + 1) == 'm') {
994 			(void) strcpy(p2, strerror(errno));
995 			p2 += strlen(p2);
996 			p1++;
997 		} else {
998 			*p2++ = *p1;
999 		}
1000 	}
1001 	if (p2 > fmtbuff && *(p2-1) != '\n')
1002 		*p2++ = '\n';
1003 	*p2 = '\0';
1004 
1005 	va_start(ap, fmt);
1006 	(void) vsprintf(buff, fmtbuff, ap);
1007 	va_end(ap);
1008 	syslog(LOG_ERR, buff);
1009 }
1010 
1011 static void
1012 free_action_list(action_list *alp)
1013 {
1014 	action_list *p, *next = NULL;
1015 	struct mounta *mp;
1016 
1017 	for (p = alp; p != NULL; p = next) {
1018 		switch (p->action.action) {
1019 		case AUTOFS_MOUNT_RQ:
1020 			mp = &(p->action.action_list_entry_u.mounta);
1021 			/* LINTED pointer alignment */
1022 			free_autofs_args((autofs_args *)mp->dataptr);
1023 			mp->dataptr = NULL;
1024 			mp->datalen = 0;
1025 			free_mounta(mp);
1026 			break;
1027 		case AUTOFS_LINK_RQ:
1028 			syslog(LOG_ERR,
1029 			    "non AUTOFS_MOUNT_RQ requests not implemented\n");
1030 			break;
1031 		default:
1032 			syslog(LOG_ERR,
1033 			    "non AUTOFS_MOUNT_RQ requests not implemented\n");
1034 			break;
1035 		}
1036 		next = p->next;
1037 		free(p);
1038 	}
1039 }
1040