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