xref: /original-bsd/sbin/mountd/mountd.c (revision 86ed09e7)
1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Rick Macklem at The University of Guelph.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #ifndef lint
12 char copyright[] =
13 "@(#) Copyright (c) 1989 Regents of the University of California.\n\
14  All rights reserved.\n";
15 #endif not lint
16 
17 #ifndef lint
18 static char sccsid[] = "@(#)mountd.c	5.12 (Berkeley) 08/31/90";
19 #endif not lint
20 
21 #include <sys/param.h>
22 #include <sys/ioctl.h>
23 #include <sys/stat.h>
24 #include <sys/file.h>
25 #include <sys/mount.h>
26 #include <sys/socket.h>
27 #include <sys/errno.h>
28 #include <sys/signal.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <syslog.h>
32 #include <netdb.h>
33 #include <rpc/rpc.h>
34 #include <rpc/pmap_clnt.h>
35 #include <rpc/pmap_prot.h>
36 #include <nfs/rpcv2.h>
37 #include <nfs/nfsv2.h>
38 #include "pathnames.h"
39 
40 struct ufid {
41 	u_short	ufid_len;
42 	ino_t	ufid_ino;
43 	long	ufid_gen;
44 };
45 /*
46  * Structures for keeping the mount list and export list
47  */
48 struct mountlist {
49 	struct mountlist *ml_next;
50 	char	ml_host[RPCMNT_NAMELEN+1];
51 	char	ml_dirp[RPCMNT_PATHLEN+1];
52 };
53 
54 struct exportlist {
55 	struct exportlist *ex_next;
56 	struct exportlist *ex_prev;
57 	struct grouplist *ex_groups;
58 	int	ex_rootuid;
59 	int	ex_exflags;
60 	dev_t	ex_dev;
61 	char	ex_dirp[RPCMNT_PATHLEN+1];
62 };
63 
64 struct grouplist {
65 	struct grouplist *gr_next;
66 	struct hostent *gr_hp;
67 };
68 
69 /* Global defs */
70 int xdr_fhs(), xdr_mlist(), xdr_dir(), xdr_explist();
71 int mntsrv(), get_exportlist(), send_umntall(), umntall_each();
72 void get_mountlist(), add_mlist(), del_mlist();
73 struct exportlist exphead;
74 struct mountlist *mlhead;
75 char exname[MAXPATHLEN];
76 int def_rootuid = -2;
77 int root_only = 1;
78 extern int errno;
79 #ifdef DEBUG
80 int debug = 1;
81 #else
82 int debug = 0;
83 #endif
84 
85 /*
86  * Mountd server for NFS mount protocol as described in:
87  * NFS: Network File System Protocol Specification, RFC1094, Appendix A
88  * The optional arguments are the exports file name
89  * default: _PATH_EXPORTS
90  * and "-n" to allow nonroot mount.
91  */
92 main(argc, argv)
93 	int argc;
94 	char **argv;
95 {
96 	SVCXPRT *transp;
97 	int c;
98 	extern int optind;
99 	extern char *optarg;
100 
101 	while ((c = getopt(argc, argv, "n")) != EOF)
102 		switch (c) {
103 		case 'n':
104 			root_only = 0;
105 			break;
106 		default:
107 			fprintf(stderr, "Usage: mountd [-n] [export_file]\n");
108 			exit(1);
109 		};
110 	argc -= optind;
111 	argv += optind;
112 	exphead.ex_next = exphead.ex_prev = (struct exportlist *)0;
113 	mlhead = (struct mountlist *)0;
114 	if (argc == 1) {
115 		strncpy(exname, *argv, MAXPATHLEN-1);
116 		exname[MAXPATHLEN-1] = '\0';
117 	} else
118 		strcpy(exname, _PATH_EXPORTS);
119 	openlog("mountd:", LOG_PID, LOG_DAEMON);
120 	get_exportlist();
121 	get_mountlist();
122 	if (debug == 0) {
123 		daemon(0, 0);
124 		signal(SIGINT, SIG_IGN);
125 		signal(SIGQUIT, SIG_IGN);
126 	}
127 	signal(SIGHUP, get_exportlist);
128 	signal(SIGTERM, send_umntall);
129 	{ FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
130 	  if (pidfile != NULL) {
131 		fprintf(pidfile, "%d\n", getpid());
132 		fclose(pidfile);
133 	  }
134 	}
135 	if ((transp = svcudp_create(RPC_ANYSOCK)) == NULL) {
136 		syslog(LOG_ERR, "Can't create socket");
137 		exit(1);
138 	}
139 	pmap_unset(RPCPROG_MNT, RPCMNT_VER1);
140 	if (!svc_register(transp, RPCPROG_MNT, RPCMNT_VER1, mntsrv, IPPROTO_UDP)) {
141 		syslog(LOG_ERR, "Can't register mount");
142 		exit(1);
143 	}
144 	svc_run();
145 	syslog(LOG_ERR, "Mountd died");
146 	exit(1);
147 }
148 
149 /*
150  * The mount rpc service
151  */
152 mntsrv(rqstp, transp)
153 	register struct svc_req *rqstp;
154 	register SVCXPRT *transp;
155 {
156 	register struct grouplist *grp;
157 	register u_long **addrp;
158 	register struct exportlist *ep;
159 	nfsv2fh_t nfh;
160 	struct authunix_parms *ucr;
161 	struct stat stb;
162 	struct hostent *hp;
163 	u_long saddr;
164 	char dirpath[RPCMNT_PATHLEN+1];
165 	int bad = ENOENT;
166 	int omask;
167 	uid_t uid = -2;
168 
169 	/* Get authorization */
170 	switch (rqstp->rq_cred.oa_flavor) {
171 	case AUTH_UNIX:
172 		ucr = (struct authunix_parms *)rqstp->rq_clntcred;
173 		uid = ucr->aup_uid;
174 		break;
175 	case AUTH_NULL:
176 	default:
177 		break;
178 	}
179 
180 	saddr = transp->xp_raddr.sin_addr.s_addr;
181 	hp = (struct hostent *)0;
182 	switch (rqstp->rq_proc) {
183 	case NULLPROC:
184 		if (!svc_sendreply(transp, xdr_void, (caddr_t)0))
185 			syslog(LOG_ERR, "Can't send reply");
186 		return;
187 	case RPCMNT_MOUNT:
188 		if (uid != 0 && root_only) {
189 			svcerr_weakauth(transp);
190 			return;
191 		}
192 		if (!svc_getargs(transp, xdr_dir, dirpath)) {
193 			svcerr_decode(transp);
194 			return;
195 		}
196 
197 		/* Check to see if it's a valid dirpath */
198 		if (stat(dirpath, &stb) < 0 || (stb.st_mode&S_IFMT) !=
199 			S_IFDIR) {
200 			if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
201 				syslog(LOG_ERR, "Can't send reply");
202 			return;
203 		}
204 
205 		/* Check in the exports list */
206 		omask = sigblock(sigmask(SIGHUP));
207 		ep = exphead.ex_next;
208 		while (ep != NULL) {
209 			if (!strcmp(ep->ex_dirp, dirpath)) {
210 				grp = ep->ex_groups;
211 				if (grp == NULL)
212 					break;
213 
214 				/* Check for a host match */
215 				addrp = (u_long **)grp->gr_hp->h_addr_list;
216 				for (;;) {
217 					if (**addrp == saddr)
218 						break;
219 					if (*++addrp == NULL)
220 						if (grp = grp->gr_next) {
221 							addrp = (u_long **)
222 								grp->gr_hp->h_addr_list;
223 						} else {
224 							bad = EACCES;
225 							if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
226 								syslog(LOG_ERR, "Can't send reply");
227 							sigsetmask(omask);
228 							return;
229 						}
230 				}
231 				hp = grp->gr_hp;
232 				break;
233 			}
234 			ep = ep->ex_next;
235 		}
236 		sigsetmask(omask);
237 		if (ep == NULL) {
238 			bad = EACCES;
239 			if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
240 				syslog(LOG_ERR, "Can't send reply");
241 			return;
242 		}
243 
244 		/* Get the file handle */
245 		bzero((caddr_t)&nfh, sizeof(nfh));
246 		if (getfh(dirpath, (fhandle_t *)&nfh) < 0) {
247 			bad = errno;
248 			if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
249 				syslog(LOG_ERR, "Can't send reply");
250 			return;
251 		}
252 		if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&nfh))
253 			syslog(LOG_ERR, "Can't send reply");
254 		if (hp == NULL)
255 			hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
256 		if (hp)
257 			add_mlist(hp->h_name, dirpath);
258 		return;
259 	case RPCMNT_DUMP:
260 		if (!svc_sendreply(transp, xdr_mlist, (caddr_t)0))
261 			syslog(LOG_ERR, "Can't send reply");
262 		return;
263 	case RPCMNT_UMOUNT:
264 		if (uid != 0 && root_only) {
265 			svcerr_weakauth(transp);
266 			return;
267 		}
268 		if (!svc_getargs(transp, xdr_dir, dirpath)) {
269 			svcerr_decode(transp);
270 			return;
271 		}
272 		if (!svc_sendreply(transp, xdr_void, (caddr_t)0))
273 			syslog(LOG_ERR, "Can't send reply");
274 		hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
275 		if (hp)
276 			del_mlist(hp->h_name, dirpath);
277 		return;
278 	case RPCMNT_UMNTALL:
279 		if (uid != 0 && root_only) {
280 			svcerr_weakauth(transp);
281 			return;
282 		}
283 		if (!svc_sendreply(transp, xdr_void, (caddr_t)0))
284 			syslog(LOG_ERR, "Can't send reply");
285 		hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
286 		if (hp)
287 			del_mlist(hp->h_name, (char *)0);
288 		return;
289 	case RPCMNT_EXPORT:
290 		if (!svc_sendreply(transp, xdr_explist, (caddr_t)0))
291 			syslog(LOG_ERR, "Can't send reply");
292 		return;
293 	default:
294 		svcerr_noproc(transp);
295 		return;
296 	}
297 }
298 
299 /*
300  * Xdr conversion for a dirpath string
301  */
302 xdr_dir(xdrsp, dirp)
303 	XDR *xdrsp;
304 	char *dirp;
305 {
306 	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
307 }
308 
309 /*
310  * Xdr routine to generate fhstatus
311  */
312 xdr_fhs(xdrsp, nfh)
313 	XDR *xdrsp;
314 	nfsv2fh_t *nfh;
315 {
316 	int ok = 0;
317 
318 	if (!xdr_long(xdrsp, &ok))
319 		return (0);
320 	return (xdr_opaque(xdrsp, (caddr_t)nfh, NFSX_FH));
321 }
322 
323 xdr_mlist(xdrsp, cp)
324 	XDR *xdrsp;
325 	caddr_t cp;
326 {
327 	register struct mountlist *mlp;
328 	int true = 1;
329 	int false = 0;
330 	char *strp;
331 
332 	mlp = mlhead;
333 	while (mlp) {
334 		if (!xdr_bool(xdrsp, &true))
335 			return (0);
336 		strp = &mlp->ml_host[0];
337 		if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
338 			return (0);
339 		strp = &mlp->ml_dirp[0];
340 		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
341 			return (0);
342 		mlp = mlp->ml_next;
343 	}
344 	if (!xdr_bool(xdrsp, &false))
345 		return (0);
346 	return (1);
347 }
348 
349 /*
350  * Xdr conversion for export list
351  */
352 xdr_explist(xdrsp, cp)
353 	XDR *xdrsp;
354 	caddr_t cp;
355 {
356 	register struct exportlist *ep;
357 	register struct grouplist *grp;
358 	int true = 1;
359 	int false = 0;
360 	char *strp;
361 	int omask;
362 
363 	omask = sigblock(sigmask(SIGHUP));
364 	ep = exphead.ex_next;
365 	while (ep != NULL) {
366 		if (!xdr_bool(xdrsp, &true))
367 			goto errout;
368 		strp = &ep->ex_dirp[0];
369 		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
370 			goto errout;
371 		grp = ep->ex_groups;
372 		while (grp != NULL) {
373 			if (!xdr_bool(xdrsp, &true))
374 				goto errout;
375 			strp = grp->gr_hp->h_name;
376 			if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
377 				goto errout;
378 			grp = grp->gr_next;
379 		}
380 		if (!xdr_bool(xdrsp, &false))
381 			goto errout;
382 		ep = ep->ex_next;
383 	}
384 	sigsetmask(omask);
385 	if (!xdr_bool(xdrsp, &false))
386 		return (0);
387 	return (1);
388 errout:
389 	sigsetmask(omask);
390 	return (0);
391 }
392 
393 #define LINESIZ	10240
394 char line[LINESIZ];
395 
396 /*
397  * Get the export list
398  */
399 get_exportlist()
400 {
401 	register struct hostent *hp, *nhp;
402 	register char **addrp, **naddrp;
403 	register int i;
404 	register struct grouplist *grp;
405 	register struct exportlist *ep, *ep2;
406 	struct statfs stfsbuf;
407 	struct ufs_args args;
408 	struct stat sb;
409 	FILE *inf;
410 	char *cp, *endcp;
411 	char savedc;
412 	int len, dirplen;
413 	int rootuid, exflags;
414 	u_long saddr;
415 	struct exportlist *fep;
416 
417 	/*
418 	 * First, get rid of the old list
419 	 */
420 	ep = exphead.ex_next;
421 	while (ep != NULL) {
422 		ep2 = ep;
423 		ep = ep->ex_next;
424 		free_exp(ep2);
425 	}
426 
427 	/*
428 	 * Read in the exports file and build the list, calling
429 	 * exportfs() as we go along
430 	 */
431 	exphead.ex_next = exphead.ex_prev = (struct exportlist *)0;
432 	if ((inf = fopen(exname, "r")) == NULL) {
433 		syslog(LOG_ERR, "Can't open %s", exname);
434 		exit(2);
435 	}
436 	while (fgets(line, LINESIZ, inf)) {
437 		exflags = MNT_EXPORTED;
438 		rootuid = def_rootuid;
439 		cp = line;
440 		nextfield(&cp, &endcp);
441 
442 		/*
443 		 * Get file system devno and see if an entry for this
444 		 * file system already exists.
445 		 */
446 		savedc = *endcp;
447 		*endcp = '\0';
448 		if (stat(cp, &sb) < 0 || (sb.st_mode & S_IFMT) != S_IFDIR)
449 			goto err;
450 		fep = (struct exportlist *)0;
451 		ep = exphead.ex_next;
452 		while (ep) {
453 			if (ep->ex_dev == sb.st_dev) {
454 				fep = ep;
455 				break;
456 			}
457 			ep = ep->ex_next;
458 		}
459 		*endcp = savedc;
460 
461 		/*
462 		 * Create new exports list entry
463 		 */
464 		len = endcp-cp;
465 		if (len <= RPCMNT_PATHLEN && len > 0) {
466 			ep = (struct exportlist *)malloc(sizeof(*ep));
467 			ep->ex_next = ep->ex_prev = (struct exportlist *)0;
468 			ep->ex_groups = (struct grouplist *)0;
469 			bcopy(cp, ep->ex_dirp, len);
470 			ep->ex_dirp[len] = '\0';
471 			dirplen = len;
472 		} else
473 			goto err;
474 		cp = endcp;
475 		nextfield(&cp, &endcp);
476 		len = endcp-cp;
477 		while (len > 0) {
478 			savedc = *endcp;
479 			*endcp = '\0';
480 			if (len <= RPCMNT_NAMELEN) {
481 				if (*cp == '-') {
482 				    do_opt(cp+1, fep, ep, &exflags, &rootuid);
483 				} else {
484 				    if (isdigit(*cp)) {
485 					saddr = inet_addr(cp);
486 					if (saddr == -1 ||
487 					    (hp = gethostbyaddr((caddr_t)&saddr,
488 					     sizeof(saddr), AF_INET)) == NULL)
489 						goto err;
490 				    } else if ((hp = gethostbyname(cp)) == NULL)
491 					goto err;
492 				    grp = (struct grouplist *)
493 					    malloc(sizeof(struct grouplist));
494 				    if (grp == NULL)
495 					    goto err;
496 				    nhp = grp->gr_hp = (struct hostent *)
497 					    malloc(sizeof(struct hostent));
498 				    if (nhp == NULL)
499 					    goto err;
500 				    bcopy((caddr_t)hp, (caddr_t)nhp,
501 					    sizeof(struct hostent));
502 				    i = strlen(hp->h_name)+1;
503 				    nhp->h_name = (char *)malloc(i);
504 				    if (nhp->h_name == NULL)
505 					    goto err;
506 				    bcopy(hp->h_name, nhp->h_name, i);
507 				    addrp = hp->h_addr_list;
508 				    i = 1;
509 				    while (*addrp++)
510 					    i++;
511 				    naddrp = nhp->h_addr_list = (char **)
512 					    malloc(i*sizeof(char *));
513 				    if (naddrp == NULL)
514 					    goto err;
515 				    addrp = hp->h_addr_list;
516 				    while (*addrp) {
517 					    *naddrp = (char *)
518 					        malloc(hp->h_length);
519 					    if (*naddrp == NULL)
520 						goto err;
521 					    bcopy(*addrp, *naddrp,
522 						    hp->h_length);
523 					    addrp++;
524 					    naddrp++;
525 				    }
526 				    *naddrp = (char *)0;
527 				    grp->gr_next = ep->ex_groups;
528 				    ep->ex_groups = grp;
529 				}
530 			}
531 			cp = endcp;
532 			*cp = savedc;
533 			nextfield(&cp, &endcp);
534 			len = endcp-cp;
535 		}
536 		if (fep == NULL) {
537 			args.fspec = 0;
538 			args.exflags = exflags;
539 			args.exroot = rootuid;
540 			cp = (char *)0;
541 			while (statfs(ep->ex_dirp, &stfsbuf) < 0 ||
542 			       mount(MOUNT_UFS, ep->ex_dirp,
543 				     stfsbuf.f_flags|MNT_UPDATE, &args) < 0) {
544 				if (cp == NULL)
545 					cp = ep->ex_dirp + dirplen - 1;
546 				else
547 					*cp = savedc;
548 				/* back up over the last component */
549 				while (*cp == '/' && cp > ep->ex_dirp)
550 					cp--;
551 				while (*(cp - 1) != '/' && cp > ep->ex_dirp)
552 					cp--;
553 				if (cp == ep->ex_dirp) {
554 					syslog(LOG_WARNING,
555 					      "Can't export %s", ep->ex_dirp);
556 					free_exp(ep);
557 					goto nextline;
558 				}
559 				savedc = *cp;
560 				*cp = '\0';
561 			}
562 			if (cp)
563 				*cp = savedc;
564 			ep->ex_rootuid = rootuid;
565 			ep->ex_exflags = exflags;
566 		} else {
567 			ep->ex_rootuid = fep->ex_rootuid;
568 			ep->ex_exflags = fep->ex_exflags;
569 		}
570 		ep->ex_dev = sb.st_dev;
571 		ep->ex_next = exphead.ex_next;
572 		ep->ex_prev = &exphead;
573 		if (ep->ex_next != NULL)
574 			ep->ex_next->ex_prev = ep;
575 		exphead.ex_next = ep;
576 nextline:
577 		;
578 	}
579 	fclose(inf);
580 	return;
581 err:
582 	syslog(LOG_ERR, "Bad Exports File, mountd Failed");
583 	exit(2);
584 }
585 
586 /*
587  * Parse out the next white space separated field
588  */
589 nextfield(cp, endcp)
590 	char **cp;
591 	char **endcp;
592 {
593 	register char *p;
594 
595 	p = *cp;
596 	while (*p == ' ' || *p == '\t')
597 		p++;
598 	if (*p == '\n' || *p == '\0') {
599 		*cp = *endcp = p;
600 		return;
601 	}
602 	*cp = p++;
603 	while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
604 		p++;
605 	*endcp = p;
606 }
607 
608 /*
609  * Parse the option string
610  */
611 do_opt(cpopt, fep, ep, exflagsp, rootuidp)
612 	register char *cpopt;
613 	struct exportlist *fep, *ep;
614 	int *exflagsp, *rootuidp;
615 {
616 	register char *cpoptarg, *cpoptend;
617 
618 	while (cpopt && *cpopt) {
619 		if (cpoptend = index(cpopt, ','))
620 			*cpoptend++ = '\0';
621 		if (cpoptarg = index(cpopt, '='))
622 			*cpoptarg++ = '\0';
623 		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
624 			if (fep && (fep->ex_exflags & MNT_EXRDONLY) == 0)
625 				syslog(LOG_WARNING, "ro failed for %s",
626 				       ep->ex_dirp);
627 			else
628 				*exflagsp |= MNT_EXRDONLY;
629 		} else if (!strcmp(cpopt, "root") || !strcmp(cpopt, "r")) {
630 			if (cpoptarg && isdigit(*cpoptarg)) {
631 				*rootuidp = atoi(cpoptarg);
632 				if (fep && fep->ex_rootuid != *rootuidp)
633 					syslog(LOG_WARNING,
634 					       "uid failed for %s",
635 					       ep->ex_dirp);
636 			} else
637 				syslog(LOG_WARNING,
638 				       "uid failed for %s",
639 				       ep->ex_dirp);
640 		} else
641 			syslog(LOG_WARNING, "opt %s ignored for %s", cpopt,
642 			       ep->ex_dirp);
643 		cpopt = cpoptend;
644 	}
645 }
646 
647 #define	STRSIZ	(RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
648 /*
649  * Routines that maintain the remote mounttab
650  */
651 void get_mountlist()
652 {
653 	register struct mountlist *mlp, **mlpp;
654 	register char *eos, *dirp;
655 	int len;
656 	char str[STRSIZ];
657 	FILE *mlfile;
658 
659 	if (((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) &&
660 	    ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL)) {
661 		syslog(LOG_WARNING, "Can't open %s", _PATH_RMOUNTLIST);
662 		return;
663 	}
664 	mlpp = &mlhead;
665 	while (fgets(str, STRSIZ, mlfile) != NULL) {
666 		if ((dirp = index(str, '\t')) == NULL &&
667 		    (dirp = index(str, ' ')) == NULL)
668 			continue;
669 		mlp = (struct mountlist *)malloc(sizeof (*mlp));
670 		len = dirp-str;
671 		if (len > RPCMNT_NAMELEN)
672 			len = RPCMNT_NAMELEN;
673 		bcopy(str, mlp->ml_host, len);
674 		mlp->ml_host[len] = '\0';
675 		while (*dirp == '\t' || *dirp == ' ')
676 			dirp++;
677 		if ((eos = index(dirp, '\t')) == NULL &&
678 		    (eos = index(dirp, ' ')) == NULL &&
679 		    (eos = index(dirp, '\n')) == NULL)
680 			len = strlen(dirp);
681 		else
682 			len = eos-dirp;
683 		if (len > RPCMNT_PATHLEN)
684 			len = RPCMNT_PATHLEN;
685 		bcopy(dirp, mlp->ml_dirp, len);
686 		mlp->ml_dirp[len] = '\0';
687 		mlp->ml_next = (struct mountlist *)0;
688 		*mlpp = mlp;
689 		mlpp = &mlp->ml_next;
690 	}
691 	fclose(mlfile);
692 }
693 
694 void del_mlist(hostp, dirp)
695 	register char *hostp, *dirp;
696 {
697 	register struct mountlist *mlp, **mlpp;
698 	FILE *mlfile;
699 	int fnd = 0;
700 
701 	mlpp = &mlhead;
702 	mlp = mlhead;
703 	while (mlp) {
704 		if (!strcmp(mlp->ml_host, hostp) &&
705 		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
706 			fnd = 1;
707 			*mlpp = mlp->ml_next;
708 			free((caddr_t)mlp);
709 		}
710 		mlpp = &mlp->ml_next;
711 		mlp = mlp->ml_next;
712 	}
713 	if (fnd) {
714 		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
715 			syslog(LOG_WARNING, "Can't update %s", _PATH_RMOUNTLIST);
716 			return;
717 		}
718 		mlp = mlhead;
719 		while (mlp) {
720 			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
721 			mlp = mlp->ml_next;
722 		}
723 		fclose(mlfile);
724 	}
725 }
726 
727 void add_mlist(hostp, dirp)
728 	register char *hostp, *dirp;
729 {
730 	register struct mountlist *mlp, **mlpp;
731 	FILE *mlfile;
732 
733 	mlpp = &mlhead;
734 	mlp = mlhead;
735 	while (mlp) {
736 		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
737 			return;
738 		mlpp = &mlp->ml_next;
739 		mlp = mlp->ml_next;
740 	}
741 	mlp = (struct mountlist *)malloc(sizeof (*mlp));
742 	strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
743 	mlp->ml_host[RPCMNT_NAMELEN] = '\0';
744 	strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
745 	mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
746 	mlp->ml_next = (struct mountlist *)0;
747 	*mlpp = mlp;
748 	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
749 		syslog(LOG_WARNING, "Can't update %s", _PATH_RMOUNTLIST);
750 		return;
751 	}
752 	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
753 	fclose(mlfile);
754 }
755 
756 /*
757  * This function is called via. SIGTERM when the system is going down.
758  * It sends a broadcast RPCMNT_UMNTALL.
759  */
760 send_umntall()
761 {
762 	(void) clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL,
763 		xdr_void, (caddr_t)0, xdr_void, (caddr_t)0, umntall_each);
764 	exit();
765 }
766 
767 umntall_each(resultsp, raddr)
768 	caddr_t resultsp;
769 	struct sockaddr_in *raddr;
770 {
771 	return (1);
772 }
773 
774 /*
775  * Free up an exports list component
776  */
777 free_exp(ep)
778 	register struct exportlist *ep;
779 {
780 	register struct grouplist *grp;
781 	register char **addrp;
782 	struct grouplist *grp2;
783 
784 	grp = ep->ex_groups;
785 	while (grp != NULL) {
786 		addrp = grp->gr_hp->h_addr_list;
787 		while (*addrp)
788 			free(*addrp++);
789 		free((caddr_t)grp->gr_hp->h_addr_list);
790 		free(grp->gr_hp->h_name);
791 		free((caddr_t)grp->gr_hp);
792 		grp2 = grp;
793 		grp = grp->gr_next;
794 		free((caddr_t)grp2);
795 	}
796 	free((caddr_t)ep);
797 }
798