mountd.c revision 349756
1331722Seadler/*
21558Srgrimes * Copyright (c) 1989, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes *
51558Srgrimes * This code is derived from software contributed to Berkeley by
61558Srgrimes * Herb Hasler and Rick Macklem at The University of Guelph.
71558Srgrimes *
81558Srgrimes * Redistribution and use in source and binary forms, with or without
91558Srgrimes * modification, are permitted provided that the following conditions
101558Srgrimes * are met:
111558Srgrimes * 1. Redistributions of source code must retain the above copyright
121558Srgrimes *    notice, this list of conditions and the following disclaimer.
131558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141558Srgrimes *    notice, this list of conditions and the following disclaimer in the
151558Srgrimes *    documentation and/or other materials provided with the distribution.
161558Srgrimes * 4. Neither the name of the University nor the names of its contributors
171558Srgrimes *    may be used to endorse or promote products derived from this software
181558Srgrimes *    without specific prior written permission.
191558Srgrimes *
201558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301558Srgrimes * SUCH DAMAGE.
311558Srgrimes */
321558Srgrimes
331558Srgrimes#ifndef lint
3437663Scharnierstatic const char copyright[] =
351558Srgrimes"@(#) Copyright (c) 1989, 1993\n\
361558Srgrimes	The Regents of the University of California.  All rights reserved.\n";
372999Swollman#endif /*not lint*/
381558Srgrimes
39105267Scharnier#if 0
401558Srgrimes#ifndef lint
4137663Scharnierstatic char sccsid[] = "@(#)mountd.c	8.15 (Berkeley) 5/1/95";
42105267Scharnier#endif /*not lint*/
4337663Scharnier#endif
441558Srgrimes
45105267Scharnier#include <sys/cdefs.h>
46105267Scharnier__FBSDID("$FreeBSD: stable/11/usr.sbin/mountd/mountd.c 349756 2019-07-05 01:04:58Z rmacklem $");
47105267Scharnier
481558Srgrimes#include <sys/param.h>
49192934Srmacklem#include <sys/fcntl.h>
50349756Srmacklem#include <sys/fnv_hash.h>
51192934Srmacklem#include <sys/linker.h>
52192934Srmacklem#include <sys/module.h>
531558Srgrimes#include <sys/mount.h>
54324955Smanu#include <sys/queue.h>
551558Srgrimes#include <sys/stat.h>
56192934Srmacklem#include <sys/sysctl.h>
571558Srgrimes#include <sys/syslog.h>
581558Srgrimes
591558Srgrimes#include <rpc/rpc.h>
60109363Smbr#include <rpc/rpc_com.h>
611558Srgrimes#include <rpc/pmap_clnt.h>
6274462Salfred#include <rpc/pmap_prot.h>
6374462Salfred#include <rpcsvc/mount.h>
649336Sdfr#include <nfs/nfsproto.h>
65192934Srmacklem#include <nfs/nfssvc.h>
6683653Speter#include <nfsserver/nfs.h>
671558Srgrimes
68192934Srmacklem#include <fs/nfs/nfsport.h>
69192934Srmacklem
701558Srgrimes#include <arpa/inet.h>
711558Srgrimes
721558Srgrimes#include <ctype.h>
7337663Scharnier#include <err.h>
741558Srgrimes#include <errno.h>
751558Srgrimes#include <grp.h>
76149433Spjd#include <libutil.h>
77103949Smike#include <limits.h>
781558Srgrimes#include <netdb.h>
791558Srgrimes#include <pwd.h>
801558Srgrimes#include <signal.h>
811558Srgrimes#include <stdio.h>
821558Srgrimes#include <stdlib.h>
831558Srgrimes#include <string.h>
841558Srgrimes#include <unistd.h>
851558Srgrimes#include "pathnames.h"
86158857Srodrigc#include "mntopts.h"
871558Srgrimes
881558Srgrimes#ifdef DEBUG
891558Srgrimes#include <stdarg.h>
901558Srgrimes#endif
911558Srgrimes
921558Srgrimes/*
931558Srgrimes * Structures for keeping the mount list and export list
941558Srgrimes */
951558Srgrimesstruct mountlist {
96194880Sdfr	char	ml_host[MNTNAMLEN+1];
97194880Sdfr	char	ml_dirp[MNTPATHLEN+1];
98324955Smanu
99324955Smanu	SLIST_ENTRY(mountlist)	next;
1001558Srgrimes};
1011558Srgrimes
1021558Srgrimesstruct dirlist {
1031558Srgrimes	struct dirlist	*dp_left;
1041558Srgrimes	struct dirlist	*dp_right;
1051558Srgrimes	int		dp_flag;
1061558Srgrimes	struct hostlist	*dp_hosts;	/* List of hosts this dir exported to */
107324234Smanu	char		*dp_dirp;
1081558Srgrimes};
1091558Srgrimes/* dp_flag bits */
1101558Srgrimes#define	DP_DEFSET	0x1
1119336Sdfr#define DP_HOSTSET	0x2
1121558Srgrimes
1131558Srgrimesstruct exportlist {
1141558Srgrimes	struct dirlist	*ex_dirl;
1151558Srgrimes	struct dirlist	*ex_defdir;
116349128Srmacklem	struct grouplist *ex_grphead;
1171558Srgrimes	int		ex_flag;
1181558Srgrimes	fsid_t		ex_fs;
1191558Srgrimes	char		*ex_fsdir;
12027447Sdfr	char		*ex_indexfile;
121184588Sdfr	int		ex_numsecflavors;
122184588Sdfr	int		ex_secflavors[MAXSECFLAVORS];
123240902Srmacklem	int		ex_defnumsecflavors;
124240902Srmacklem	int		ex_defsecflavors[MAXSECFLAVORS];
125324955Smanu
126324955Smanu	SLIST_ENTRY(exportlist) entries;
1271558Srgrimes};
1281558Srgrimes/* ex_flag bits */
1291558Srgrimes#define	EX_LINKED	0x1
1301558Srgrimes
131349124SrmacklemSLIST_HEAD(exportlisthead, exportlist);
132349124Srmacklem
1331558Srgrimesstruct netmsk {
13474462Salfred	struct sockaddr_storage nt_net;
13575801Siedowse	struct sockaddr_storage nt_mask;
13642144Sdfr	char		*nt_name;
1371558Srgrimes};
1381558Srgrimes
1391558Srgrimesunion grouptypes {
14074462Salfred	struct addrinfo *gt_addrinfo;
1411558Srgrimes	struct netmsk	gt_net;
1421558Srgrimes};
1431558Srgrimes
1441558Srgrimesstruct grouplist {
1451558Srgrimes	int gr_type;
1461558Srgrimes	union grouptypes gr_ptr;
1471558Srgrimes	struct grouplist *gr_next;
148240902Srmacklem	int gr_numsecflavors;
149240902Srmacklem	int gr_secflavors[MAXSECFLAVORS];
1501558Srgrimes};
1511558Srgrimes/* Group types */
1521558Srgrimes#define	GT_NULL		0x0
1531558Srgrimes#define	GT_HOST		0x1
1541558Srgrimes#define	GT_NET		0x2
15575641Siedowse#define	GT_DEFAULT	0x3
1567401Swpaul#define GT_IGNORE	0x5
1571558Srgrimes
1581558Srgrimesstruct hostlist {
1599336Sdfr	int		 ht_flag;	/* Uses DP_xx bits */
1601558Srgrimes	struct grouplist *ht_grp;
1611558Srgrimes	struct hostlist	 *ht_next;
1621558Srgrimes};
1631558Srgrimes
1649336Sdfrstruct fhreturn {
1659336Sdfr	int	fhr_flag;
1669336Sdfr	int	fhr_vers;
1679336Sdfr	nfsfh_t	fhr_fh;
168184588Sdfr	int	fhr_numsecflavors;
169184588Sdfr	int	*fhr_secflavors;
1709336Sdfr};
1719336Sdfr
172222623Srmacklem#define	GETPORT_MAXTRY	20	/* Max tries to get a port # */
173222623Srmacklem
1741558Srgrimes/* Global defs */
175285128Straszstatic char	*add_expdir(struct dirlist **, char *, int);
176285128Straszstatic void	add_dlist(struct dirlist **, struct dirlist *,
177285128Strasz		    struct grouplist *, int, struct exportlist *);
178285128Straszstatic void	add_mlist(char *, char *);
179285128Straszstatic int	check_dirpath(char *);
180285128Straszstatic int	check_options(struct dirlist *);
181285128Straszstatic int	checkmask(struct sockaddr *sa);
182285128Straszstatic int	chk_host(struct dirlist *, struct sockaddr *, int *, int *,
183285128Strasz		    int *, int **);
184293305Sjpaetzelstatic char	*strsep_quote(char **stringp, const char *delim);
185222623Srmacklemstatic int	create_service(struct netconfig *nconf);
186222623Srmacklemstatic void	complete_service(struct netconfig *nconf, char *port_str);
187222623Srmacklemstatic void	clearout_service(void);
188285128Straszstatic void	del_mlist(char *hostp, char *dirp);
189285128Straszstatic struct dirlist	*dirp_search(struct dirlist *, char *);
190285128Straszstatic int	do_mount(struct exportlist *, struct grouplist *, int,
191285128Strasz		    struct xucred *, char *, int, struct statfs *);
192285128Straszstatic int	do_opt(char **, char **, struct exportlist *,
193285128Strasz		    struct grouplist *, int *, int *, struct xucred *);
194349124Srmacklemstatic struct exportlist	*ex_search(fsid_t *, struct exportlisthead *);
195285128Straszstatic struct exportlist	*get_exp(void);
196285128Straszstatic void	free_dir(struct dirlist *);
197285128Straszstatic void	free_exp(struct exportlist *);
198285128Straszstatic void	free_grp(struct grouplist *);
199285128Straszstatic void	free_host(struct hostlist *);
200285128Straszstatic void	get_exportlist(void);
201349124Srmacklemstatic void	insert_exports(struct exportlist *, struct exportlisthead *);
202349124Srmacklemstatic void	free_exports(struct exportlisthead *);
203349126Srmacklemstatic void	read_exportfile(void);
204349126Srmacklemstatic void	delete_export(struct iovec *, int, struct statfs *, char *);
205285128Straszstatic int	get_host(char *, struct grouplist *, struct grouplist *);
206285128Straszstatic struct hostlist *get_ht(void);
207285128Straszstatic int	get_line(void);
208285128Straszstatic void	get_mountlist(void);
209285128Straszstatic int	get_net(char *, struct netmsk *, int);
210329392Sbrdstatic void	getexp_err(struct exportlist *, struct grouplist *, const char *);
211285128Straszstatic struct grouplist	*get_grp(void);
212285128Straszstatic void	hang_dirp(struct dirlist *, struct grouplist *,
21392882Simp				struct exportlist *, int);
214285128Straszstatic void	huphandler(int sig);
215285128Straszstatic int	makemask(struct sockaddr_storage *ssp, int bitlen);
216285128Straszstatic void	mntsrv(struct svc_req *, SVCXPRT *);
217285128Straszstatic void	nextfield(char **, char **);
218285128Straszstatic void	out_of_mem(void);
219285128Straszstatic void	parsecred(char *, struct xucred *);
220285128Straszstatic int	parsesec(char *, struct exportlist *);
221285128Straszstatic int	put_exlist(struct dirlist *, XDR *, struct dirlist *,
222285128Strasz		    int *, int);
223285128Straszstatic void	*sa_rawaddr(struct sockaddr *sa, int *nbytes);
224285128Straszstatic int	sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
225285128Strasz		    struct sockaddr *samask);
226285128Straszstatic int	scan_tree(struct dirlist *, struct sockaddr *);
227285128Straszstatic void	usage(void);
228285128Straszstatic int	xdr_dir(XDR *, char *);
229285128Straszstatic int	xdr_explist(XDR *, caddr_t);
230285128Straszstatic int	xdr_explist_brief(XDR *, caddr_t);
231285128Straszstatic int	xdr_explist_common(XDR *, caddr_t, int);
232285128Straszstatic int	xdr_fhs(XDR *, caddr_t);
233285128Straszstatic int	xdr_mlist(XDR *, caddr_t);
234285128Straszstatic void	terminate(int);
2351558Srgrimes
236349756Srmacklem#define	EXPHASH(f)	(fnv_32_buf((f), sizeof(fsid_t), 0) % exphashsize)
237349756Srmacklemstatic struct exportlisthead *exphead = NULL;
238349756Srmacklemstatic int exphashsize = 0;
239349124Srmacklemstatic SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(&mlhead);
240285128Straszstatic char *exnames_default[2] = { _PATH_EXPORTS, NULL };
241285128Straszstatic char **exnames;
242285128Straszstatic char **hosts = NULL;
243285128Straszstatic struct xucred def_anon = {
24491354Sdd	XUCRED_VERSION,
24572650Sgreen	(uid_t)-2,
2461558Srgrimes	1,
24772650Sgreen	{ (gid_t)-2 },
24872650Sgreen	NULL
2491558Srgrimes};
250285128Straszstatic int force_v2 = 0;
251285128Straszstatic int resvport_only = 1;
252285128Straszstatic int nhosts = 0;
253285128Straszstatic int dir_only = 1;
254285128Straszstatic int dolog = 0;
255285128Straszstatic int got_sighup = 0;
256285128Straszstatic int xcreated = 0;
25774462Salfred
258285128Straszstatic char *svcport_str = NULL;
259285128Straszstatic int mallocd_svcport = 0;
260285128Straszstatic int *sock_fd;
261285128Straszstatic int sock_fdcnt;
262285128Straszstatic int sock_fdpos;
263285128Straszstatic int suspend_nfsd = 0;
264172827Smatteo
265285128Straszstatic int opt_flags;
26674462Salfredstatic int have_v6 = 1;
26774462Salfred
268285128Straszstatic int v4root_phase = 0;
269285128Straszstatic char v4root_dirpath[PATH_MAX + 1];
270285128Straszstatic int has_publicfh = 0;
271192934Srmacklem
272285128Straszstatic struct pidfh *pfh = NULL;
27375801Siedowse/* Bits for opt_flags above */
2741558Srgrimes#define	OP_MAPROOT	0x01
2751558Srgrimes#define	OP_MAPALL	0x02
27683653Speter/* 0x4 free */
2771558Srgrimes#define	OP_MASK		0x08
2781558Srgrimes#define	OP_NET		0x10
2791558Srgrimes#define	OP_ALLDIRS	0x40
28075801Siedowse#define	OP_HAVEMASK	0x80	/* A mask was specified or inferred. */
281100336Sjoerg#define	OP_QUIET	0x100
28274462Salfred#define OP_MASKLEN	0x200
283184588Sdfr#define OP_SEC		0x400
2841558Srgrimes
2851558Srgrimes#ifdef DEBUG
286285128Straszstatic int debug = 1;
287285128Straszstatic void	SYSLOG(int, const char *, ...) __printflike(2, 3);
2881558Srgrimes#define syslog SYSLOG
2891558Srgrimes#else
290285128Straszstatic int debug = 0;
2911558Srgrimes#endif
2921558Srgrimes
2931558Srgrimes/*
294293305Sjpaetzel * Similar to strsep(), but it allows for quoted strings
295293305Sjpaetzel * and escaped characters.
296293305Sjpaetzel *
297293305Sjpaetzel * It returns the string (or NULL, if *stringp is NULL),
298293305Sjpaetzel * which is a de-quoted version of the string if necessary.
299293305Sjpaetzel *
300293305Sjpaetzel * It modifies *stringp in place.
301293305Sjpaetzel */
302293305Sjpaetzelstatic char *
303293305Sjpaetzelstrsep_quote(char **stringp, const char *delim)
304293305Sjpaetzel{
305293305Sjpaetzel	char *srcptr, *dstptr, *retval;
306293305Sjpaetzel	char quot = 0;
307293305Sjpaetzel
308293305Sjpaetzel	if (stringp == NULL || *stringp == NULL)
309293305Sjpaetzel		return (NULL);
310293305Sjpaetzel
311293305Sjpaetzel	srcptr = dstptr = retval = *stringp;
312293305Sjpaetzel
313293305Sjpaetzel	while (*srcptr) {
314293305Sjpaetzel		/*
315293305Sjpaetzel		 * We're looking for several edge cases here.
316293305Sjpaetzel		 * First:  if we're in quote state (quot != 0),
317293305Sjpaetzel		 * then we ignore the delim characters, but otherwise
318293305Sjpaetzel		 * process as normal, unless it is the quote character.
319293305Sjpaetzel		 * Second:  if the current character is a backslash,
320293305Sjpaetzel		 * we take the next character as-is, without checking
321293305Sjpaetzel		 * for delim, quote, or backslash.  Exception:  if the
322293305Sjpaetzel		 * next character is a NUL, that's the end of the string.
323293305Sjpaetzel		 * Third:  if the character is a quote character, we toggle
324293305Sjpaetzel		 * quote state.
325293305Sjpaetzel		 * Otherwise:  check the current character for NUL, or
326293305Sjpaetzel		 * being in delim, and end the string if either is true.
327293305Sjpaetzel		 */
328293305Sjpaetzel		if (*srcptr == '\\') {
329293305Sjpaetzel			srcptr++;
330293305Sjpaetzel			/*
331293305Sjpaetzel			 * The edge case here is if the next character
332293305Sjpaetzel			 * is NUL, we want to stop processing.  But if
333293305Sjpaetzel			 * it's not NUL, then we simply want to copy it.
334293305Sjpaetzel			 */
335293305Sjpaetzel			if (*srcptr) {
336293305Sjpaetzel				*dstptr++ = *srcptr++;
337293305Sjpaetzel			}
338293305Sjpaetzel			continue;
339293305Sjpaetzel		}
340293305Sjpaetzel		if (quot == 0 && (*srcptr == '\'' || *srcptr == '"')) {
341293305Sjpaetzel			quot = *srcptr++;
342293305Sjpaetzel			continue;
343293305Sjpaetzel		}
344293305Sjpaetzel		if (quot && *srcptr == quot) {
345293305Sjpaetzel			/* End of the quoted part */
346293305Sjpaetzel			quot = 0;
347293305Sjpaetzel			srcptr++;
348293305Sjpaetzel			continue;
349293305Sjpaetzel		}
350293305Sjpaetzel		if (!quot && strchr(delim, *srcptr))
351293305Sjpaetzel			break;
352293305Sjpaetzel		*dstptr++ = *srcptr++;
353293305Sjpaetzel	}
354293305Sjpaetzel
355349457Smav	*stringp = (*srcptr == '\0') ? NULL : srcptr + 1;
356293305Sjpaetzel	*dstptr = 0; /* Terminate the string */
357293305Sjpaetzel	return (retval);
358293305Sjpaetzel}
359293305Sjpaetzel
360293305Sjpaetzel/*
3611558Srgrimes * Mountd server for NFS mount protocol as described in:
3621558Srgrimes * NFS: Network File System Protocol Specification, RFC1094, Appendix A
3631558Srgrimes * The optional arguments are the exports file name
3641558Srgrimes * default: _PATH_EXPORTS
3651558Srgrimes * and "-n" to allow nonroot mount.
3661558Srgrimes */
3671558Srgrimesint
368216587Scharniermain(int argc, char **argv)
3691558Srgrimes{
37075754Siedowse	fd_set readfds;
371172827Smatteo	struct netconfig *nconf;
372172827Smatteo	char *endptr, **hosts_bak;
373172827Smatteo	void *nc_handle;
374149433Spjd	pid_t otherpid;
375172827Smatteo	in_port_t svcport;
376172827Smatteo	int c, k, s;
377109363Smbr	int maxrec = RPC_MAXDATASIZE;
378222623Srmacklem	int attempt_cnt, port_len, port_pos, ret;
379222623Srmacklem	char **port_list;
3801558Srgrimes
38174462Salfred	/* Check that another mountd isn't already running. */
382150214Spjd	pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid);
383149433Spjd	if (pfh == NULL) {
384149433Spjd		if (errno == EEXIST)
385149433Spjd			errx(1, "mountd already running, pid: %d.", otherpid);
386149433Spjd		warn("cannot open or create pidfile");
387149433Spjd	}
38874462Salfred
38974462Salfred	s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
39074462Salfred	if (s < 0)
39174462Salfred		have_v6 = 0;
39274462Salfred	else
39374462Salfred		close(s);
3942999Swollman
395282214Strasz	while ((c = getopt(argc, argv, "2deh:lnp:rS")) != -1)
3961558Srgrimes		switch (c) {
39725087Sdfr		case '2':
39825087Sdfr			force_v2 = 1;
39925087Sdfr			break;
400192993Srmacklem		case 'e':
401220980Srmacklem			/* now a no-op, since this is the default */
402192934Srmacklem			break;
4039336Sdfr		case 'n':
4049336Sdfr			resvport_only = 0;
4059336Sdfr			break;
4069336Sdfr		case 'r':
4079336Sdfr			dir_only = 0;
4089336Sdfr			break;
4098688Sphk		case 'd':
4108688Sphk			debug = debug ? 0 : 1;
4118688Sphk			break;
41231656Sguido		case 'l':
413121767Speter			dolog = 1;
41431656Sguido			break;
415126572Sbms		case 'p':
416126572Sbms			endptr = NULL;
417126572Sbms			svcport = (in_port_t)strtoul(optarg, &endptr, 10);
418126572Sbms			if (endptr == NULL || *endptr != '\0' ||
419126572Sbms			    svcport == 0 || svcport >= IPPORT_MAX)
420126572Sbms				usage();
421172827Smatteo			svcport_str = strdup(optarg);
422126572Sbms			break;
423172827Smatteo		case 'h':
424172827Smatteo			++nhosts;
425172827Smatteo			hosts_bak = hosts;
426172827Smatteo			hosts_bak = realloc(hosts, nhosts * sizeof(char *));
427172827Smatteo			if (hosts_bak == NULL) {
428172827Smatteo				if (hosts != NULL) {
429172827Smatteo					for (k = 0; k < nhosts; k++)
430172827Smatteo						free(hosts[k]);
431172827Smatteo					free(hosts);
432172827Smatteo					out_of_mem();
433172827Smatteo				}
434172827Smatteo			}
435172827Smatteo			hosts = hosts_bak;
436172827Smatteo			hosts[nhosts - 1] = strdup(optarg);
437172827Smatteo			if (hosts[nhosts - 1] == NULL) {
438172827Smatteo				for (k = 0; k < (nhosts - 1); k++)
439172827Smatteo					free(hosts[k]);
440172827Smatteo				free(hosts);
441172827Smatteo				out_of_mem();
442172827Smatteo			}
443172827Smatteo			break;
444241568Srmacklem		case 'S':
445241568Srmacklem			suspend_nfsd = 1;
446241568Srmacklem			break;
4471558Srgrimes		default:
44837663Scharnier			usage();
449298089Spfg		}
450192934Srmacklem
451282214Strasz	if (modfind("nfsd") < 0) {
452192934Srmacklem		/* Not present in kernel, try loading it */
453282214Strasz		if (kldload("nfsd") < 0 || modfind("nfsd") < 0)
454192934Srmacklem			errx(1, "NFS server is not available");
455192934Srmacklem	}
456192934Srmacklem
4571558Srgrimes	argc -= optind;
4581558Srgrimes	argv += optind;
459166440Spjd	if (argc > 0)
460166440Spjd		exnames = argv;
461166440Spjd	else
462166440Spjd		exnames = exnames_default;
4631558Srgrimes	openlog("mountd", LOG_PID, LOG_DAEMON);
4641558Srgrimes	if (debug)
46537663Scharnier		warnx("getting export list");
4661558Srgrimes	get_exportlist();
4671558Srgrimes	if (debug)
46837663Scharnier		warnx("getting mount list");
4691558Srgrimes	get_mountlist();
4701558Srgrimes	if (debug)
47137663Scharnier		warnx("here we go");
4721558Srgrimes	if (debug == 0) {
4731558Srgrimes		daemon(0, 0);
4741558Srgrimes		signal(SIGINT, SIG_IGN);
4751558Srgrimes		signal(SIGQUIT, SIG_IGN);
4761558Srgrimes	}
47775754Siedowse	signal(SIGHUP, huphandler);
47874462Salfred	signal(SIGTERM, terminate);
479164394Srodrigc	signal(SIGPIPE, SIG_IGN);
480149433Spjd
481149433Spjd	pidfile_write(pfh);
482149433Spjd
483194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
484194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
485109363Smbr	rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
486109363Smbr
48724759Sguido	if (!resvport_only) {
488308449Srmacklem		if (sysctlbyname("vfs.nfsd.nfs_privport", NULL, NULL,
48983687Speter		    &resvport_only, sizeof(resvport_only)) != 0 &&
49083687Speter		    errno != ENOENT) {
49124759Sguido			syslog(LOG_ERR, "sysctl: %m");
49224759Sguido			exit(1);
49324759Sguido		}
49424330Sguido	}
495126572Sbms
496172827Smatteo	/*
497172827Smatteo	 * If no hosts were specified, add a wildcard entry to bind to
498172827Smatteo	 * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
499172827Smatteo	 * list.
500172827Smatteo	 */
501172827Smatteo	if (nhosts == 0) {
502292864Suqs		hosts = malloc(sizeof(char *));
503172827Smatteo		if (hosts == NULL)
504172827Smatteo			out_of_mem();
505172827Smatteo		hosts[0] = "*";
506172827Smatteo		nhosts = 1;
507172827Smatteo	} else {
508172827Smatteo		hosts_bak = hosts;
509172827Smatteo		if (have_v6) {
510172827Smatteo			hosts_bak = realloc(hosts, (nhosts + 2) *
511172827Smatteo			    sizeof(char *));
512172827Smatteo			if (hosts_bak == NULL) {
513172827Smatteo				for (k = 0; k < nhosts; k++)
514172827Smatteo					free(hosts[k]);
515172827Smatteo		    		free(hosts);
516172827Smatteo		    		out_of_mem();
517172827Smatteo			} else
518172827Smatteo				hosts = hosts_bak;
519172827Smatteo			nhosts += 2;
520172827Smatteo			hosts[nhosts - 2] = "::1";
521172827Smatteo		} else {
522172827Smatteo			hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
523172827Smatteo			if (hosts_bak == NULL) {
524172827Smatteo				for (k = 0; k < nhosts; k++)
525172827Smatteo					free(hosts[k]);
526172827Smatteo				free(hosts);
527172827Smatteo				out_of_mem();
528172827Smatteo			} else {
529172827Smatteo				nhosts += 1;
530172827Smatteo				hosts = hosts_bak;
531126572Sbms			}
532172827Smatteo		}
53374462Salfred
534172827Smatteo		hosts[nhosts - 1] = "127.0.0.1";
53574462Salfred	}
53674462Salfred
537222623Srmacklem	attempt_cnt = 1;
538222623Srmacklem	sock_fdcnt = 0;
539222623Srmacklem	sock_fd = NULL;
540222623Srmacklem	port_list = NULL;
541222623Srmacklem	port_len = 0;
542172827Smatteo	nc_handle = setnetconfig();
543172827Smatteo	while ((nconf = getnetconfig(nc_handle))) {
544172827Smatteo		if (nconf->nc_flag & NC_VISIBLE) {
545172827Smatteo			if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
546172827Smatteo			    "inet6") == 0) {
547172827Smatteo				/* DO NOTHING */
548222623Srmacklem			} else {
549222623Srmacklem				ret = create_service(nconf);
550222623Srmacklem				if (ret == 1)
551222623Srmacklem					/* Ignore this call */
552222623Srmacklem					continue;
553222623Srmacklem				if (ret < 0) {
554222623Srmacklem					/*
555222623Srmacklem					 * Failed to bind port, so close off
556222623Srmacklem					 * all sockets created and try again
557222623Srmacklem					 * if the port# was dynamically
558222623Srmacklem					 * assigned via bind(2).
559222623Srmacklem					 */
560222623Srmacklem					clearout_service();
561222623Srmacklem					if (mallocd_svcport != 0 &&
562222623Srmacklem					    attempt_cnt < GETPORT_MAXTRY) {
563222623Srmacklem						free(svcport_str);
564222623Srmacklem						svcport_str = NULL;
565222623Srmacklem						mallocd_svcport = 0;
566222623Srmacklem					} else {
567222623Srmacklem						errno = EADDRINUSE;
568222623Srmacklem						syslog(LOG_ERR,
569222623Srmacklem						    "bindresvport_sa: %m");
570222623Srmacklem						exit(1);
571222623Srmacklem					}
572222623Srmacklem
573222623Srmacklem					/* Start over at the first service. */
574222623Srmacklem					free(sock_fd);
575222623Srmacklem					sock_fdcnt = 0;
576222623Srmacklem					sock_fd = NULL;
577222623Srmacklem					nc_handle = setnetconfig();
578222623Srmacklem					attempt_cnt++;
579222623Srmacklem				} else if (mallocd_svcport != 0 &&
580222623Srmacklem				    attempt_cnt == GETPORT_MAXTRY) {
581222623Srmacklem					/*
582222623Srmacklem					 * For the last attempt, allow
583222623Srmacklem					 * different port #s for each nconf
584222623Srmacklem					 * by saving the svcport_str and
585222623Srmacklem					 * setting it back to NULL.
586222623Srmacklem					 */
587222623Srmacklem					port_list = realloc(port_list,
588222623Srmacklem					    (port_len + 1) * sizeof(char *));
589222623Srmacklem					if (port_list == NULL)
590222623Srmacklem						out_of_mem();
591222623Srmacklem					port_list[port_len++] = svcport_str;
592222623Srmacklem					svcport_str = NULL;
593222623Srmacklem					mallocd_svcport = 0;
594222623Srmacklem				}
595222623Srmacklem			}
596222623Srmacklem		}
597222623Srmacklem	}
598222623Srmacklem
599222623Srmacklem	/*
600222623Srmacklem	 * Successfully bound the ports, so call complete_service() to
601222623Srmacklem	 * do the rest of the setup on the service(s).
602222623Srmacklem	 */
603222623Srmacklem	sock_fdpos = 0;
604222623Srmacklem	port_pos = 0;
605222623Srmacklem	nc_handle = setnetconfig();
606222623Srmacklem	while ((nconf = getnetconfig(nc_handle))) {
607222623Srmacklem		if (nconf->nc_flag & NC_VISIBLE) {
608222623Srmacklem			if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
609222623Srmacklem			    "inet6") == 0) {
610222623Srmacklem				/* DO NOTHING */
611222623Srmacklem			} else if (port_list != NULL) {
612222623Srmacklem				if (port_pos >= port_len) {
613222623Srmacklem					syslog(LOG_ERR, "too many port#s");
614222623Srmacklem					exit(1);
615222623Srmacklem				}
616222623Srmacklem				complete_service(nconf, port_list[port_pos++]);
617172827Smatteo			} else
618222623Srmacklem				complete_service(nconf, svcport_str);
619172827Smatteo		}
62074462Salfred	}
621172827Smatteo	endnetconfig(nc_handle);
622222623Srmacklem	free(sock_fd);
623222623Srmacklem	if (port_list != NULL) {
624222623Srmacklem		for (port_pos = 0; port_pos < port_len; port_pos++)
625222623Srmacklem			free(port_list[port_pos]);
626222623Srmacklem		free(port_list);
627222623Srmacklem	}
62874462Salfred
62974462Salfred	if (xcreated == 0) {
63074462Salfred		syslog(LOG_ERR, "could not create any services");
6311558Srgrimes		exit(1);
6321558Srgrimes	}
63375754Siedowse
63475754Siedowse	/* Expand svc_run() here so that we can call get_exportlist(). */
63575754Siedowse	for (;;) {
63675754Siedowse		if (got_sighup) {
63775754Siedowse			get_exportlist();
63875754Siedowse			got_sighup = 0;
63975754Siedowse		}
64075754Siedowse		readfds = svc_fdset;
64175754Siedowse		switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) {
64275754Siedowse		case -1:
64375754Siedowse			if (errno == EINTR)
64475754Siedowse                                continue;
64575754Siedowse			syslog(LOG_ERR, "mountd died: select: %m");
64675754Siedowse			exit(1);
64775754Siedowse		case 0:
64875754Siedowse			continue;
64975754Siedowse		default:
65075754Siedowse			svc_getreqset(&readfds);
65175754Siedowse		}
65275754Siedowse	}
653172827Smatteo}
654172827Smatteo
655172827Smatteo/*
656172827Smatteo * This routine creates and binds sockets on the appropriate
657222623Srmacklem * addresses. It gets called one time for each transport.
658222623Srmacklem * It returns 0 upon success, 1 for ingore the call and -1 to indicate
659222623Srmacklem * bind failed with EADDRINUSE.
660222623Srmacklem * Any file descriptors that have been created are stored in sock_fd and
661222623Srmacklem * the total count of them is maintained in sock_fdcnt.
662172827Smatteo */
663222623Srmacklemstatic int
664172827Smatteocreate_service(struct netconfig *nconf)
665172827Smatteo{
666172827Smatteo	struct addrinfo hints, *res = NULL;
667172827Smatteo	struct sockaddr_in *sin;
668172827Smatteo	struct sockaddr_in6 *sin6;
669172827Smatteo	struct __rpc_sockinfo si;
670172827Smatteo	int aicode;
671172827Smatteo	int fd;
672172827Smatteo	int nhostsbak;
673172827Smatteo	int one = 1;
674172827Smatteo	int r;
675172827Smatteo	u_int32_t host_addr[4];  /* IPv4 or IPv6 */
676222623Srmacklem	int mallocd_res;
677172827Smatteo
678172827Smatteo	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
679172827Smatteo	    (nconf->nc_semantics != NC_TPI_COTS) &&
680172827Smatteo	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
681222623Srmacklem		return (1);	/* not my type */
682172827Smatteo
683172827Smatteo	/*
684172827Smatteo	 * XXX - using RPC library internal functions.
685172827Smatteo	 */
686172827Smatteo	if (!__rpc_nconf2sockinfo(nconf, &si)) {
687172827Smatteo		syslog(LOG_ERR, "cannot get information for %s",
688172827Smatteo		    nconf->nc_netid);
689222623Srmacklem		return (1);
690172827Smatteo	}
691172827Smatteo
692172827Smatteo	/* Get mountd's address on this transport */
693172827Smatteo	memset(&hints, 0, sizeof hints);
694172827Smatteo	hints.ai_family = si.si_af;
695172827Smatteo	hints.ai_socktype = si.si_socktype;
696172827Smatteo	hints.ai_protocol = si.si_proto;
697172827Smatteo
698172827Smatteo	/*
699172827Smatteo	 * Bind to specific IPs if asked to
700172827Smatteo	 */
701172827Smatteo	nhostsbak = nhosts;
702172827Smatteo	while (nhostsbak > 0) {
703172827Smatteo		--nhostsbak;
704222623Srmacklem		sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int));
705222623Srmacklem		if (sock_fd == NULL)
706222623Srmacklem			out_of_mem();
707222623Srmacklem		sock_fd[sock_fdcnt++] = -1;	/* Set invalid for now. */
708222623Srmacklem		mallocd_res = 0;
709222623Srmacklem
710277352Srstone		hints.ai_flags = AI_PASSIVE;
711277352Srstone
712172827Smatteo		/*
713172827Smatteo		 * XXX - using RPC library internal functions.
714172827Smatteo		 */
715172827Smatteo		if ((fd = __rpc_nconf2fd(nconf)) < 0) {
716172827Smatteo			int non_fatal = 0;
717244538Skevlo	    		if (errno == EAFNOSUPPORT &&
718172827Smatteo			    nconf->nc_semantics != NC_TPI_CLTS)
719172827Smatteo				non_fatal = 1;
720172827Smatteo
721172827Smatteo			syslog(non_fatal ? LOG_DEBUG : LOG_ERR,
722172827Smatteo			    "cannot create socket for %s", nconf->nc_netid);
723222623Srmacklem			if (non_fatal != 0)
724222623Srmacklem				continue;
725222623Srmacklem			exit(1);
726172827Smatteo		}
727172827Smatteo
728172827Smatteo		switch (hints.ai_family) {
729172827Smatteo		case AF_INET:
730172827Smatteo			if (inet_pton(AF_INET, hosts[nhostsbak],
731172827Smatteo			    host_addr) == 1) {
732222623Srmacklem				hints.ai_flags |= AI_NUMERICHOST;
733172827Smatteo			} else {
734172827Smatteo				/*
735172827Smatteo				 * Skip if we have an AF_INET6 address.
736172827Smatteo				 */
737172827Smatteo				if (inet_pton(AF_INET6, hosts[nhostsbak],
738172827Smatteo				    host_addr) == 1) {
739172827Smatteo					close(fd);
740172827Smatteo					continue;
741172827Smatteo				}
742172827Smatteo			}
743172827Smatteo			break;
744172827Smatteo		case AF_INET6:
745172827Smatteo			if (inet_pton(AF_INET6, hosts[nhostsbak],
746172827Smatteo			    host_addr) == 1) {
747222623Srmacklem				hints.ai_flags |= AI_NUMERICHOST;
748172827Smatteo			} else {
749172827Smatteo				/*
750172827Smatteo				 * Skip if we have an AF_INET address.
751172827Smatteo				 */
752172827Smatteo				if (inet_pton(AF_INET, hosts[nhostsbak],
753172827Smatteo				    host_addr) == 1) {
754172827Smatteo					close(fd);
755172827Smatteo					continue;
756172827Smatteo				}
757172827Smatteo			}
758172827Smatteo
759172827Smatteo			/*
760172827Smatteo			 * We're doing host-based access checks here, so don't
761172827Smatteo			 * allow v4-in-v6 to confuse things. The kernel will
762172827Smatteo			 * disable it by default on NFS sockets too.
763172827Smatteo			 */
764172827Smatteo			if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one,
765172827Smatteo			    sizeof one) < 0) {
766172827Smatteo				syslog(LOG_ERR,
767172827Smatteo				    "can't disable v4-in-v6 on IPv6 socket");
768172827Smatteo				exit(1);
769172827Smatteo			}
770172827Smatteo			break;
771172827Smatteo		default:
772172827Smatteo			break;
773172827Smatteo		}
774172827Smatteo
775172827Smatteo		/*
776172827Smatteo		 * If no hosts were specified, just bind to INADDR_ANY
777172827Smatteo		 */
778172827Smatteo		if (strcmp("*", hosts[nhostsbak]) == 0) {
779172827Smatteo			if (svcport_str == NULL) {
780172827Smatteo				res = malloc(sizeof(struct addrinfo));
781172827Smatteo				if (res == NULL)
782172827Smatteo					out_of_mem();
783222623Srmacklem				mallocd_res = 1;
784172827Smatteo				res->ai_flags = hints.ai_flags;
785172827Smatteo				res->ai_family = hints.ai_family;
786172827Smatteo				res->ai_protocol = hints.ai_protocol;
787172827Smatteo				switch (res->ai_family) {
788172827Smatteo				case AF_INET:
789172827Smatteo					sin = malloc(sizeof(struct sockaddr_in));
790172827Smatteo					if (sin == NULL)
791172827Smatteo						out_of_mem();
792172827Smatteo					sin->sin_family = AF_INET;
793172827Smatteo					sin->sin_port = htons(0);
794172827Smatteo					sin->sin_addr.s_addr = htonl(INADDR_ANY);
795172827Smatteo					res->ai_addr = (struct sockaddr*) sin;
796172827Smatteo					res->ai_addrlen = (socklen_t)
797222623Srmacklem					    sizeof(struct sockaddr_in);
798172827Smatteo					break;
799172827Smatteo				case AF_INET6:
800172827Smatteo					sin6 = malloc(sizeof(struct sockaddr_in6));
801173056Ssimon					if (sin6 == NULL)
802172827Smatteo						out_of_mem();
803172827Smatteo					sin6->sin6_family = AF_INET6;
804172827Smatteo					sin6->sin6_port = htons(0);
805172827Smatteo					sin6->sin6_addr = in6addr_any;
806172827Smatteo					res->ai_addr = (struct sockaddr*) sin6;
807172827Smatteo					res->ai_addrlen = (socklen_t)
808222623Srmacklem					    sizeof(struct sockaddr_in6);
809222623Srmacklem					break;
810172827Smatteo				default:
811222623Srmacklem					syslog(LOG_ERR, "bad addr fam %d",
812222623Srmacklem					    res->ai_family);
813222623Srmacklem					exit(1);
814172827Smatteo				}
815172827Smatteo			} else {
816172827Smatteo				if ((aicode = getaddrinfo(NULL, svcport_str,
817172827Smatteo				    &hints, &res)) != 0) {
818172827Smatteo					syslog(LOG_ERR,
819172827Smatteo					    "cannot get local address for %s: %s",
820172827Smatteo					    nconf->nc_netid,
821172827Smatteo					    gai_strerror(aicode));
822222623Srmacklem					close(fd);
823172827Smatteo					continue;
824172827Smatteo				}
825172827Smatteo			}
826172827Smatteo		} else {
827172827Smatteo			if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
828172827Smatteo			    &hints, &res)) != 0) {
829172827Smatteo				syslog(LOG_ERR,
830172827Smatteo				    "cannot get local address for %s: %s",
831172827Smatteo				    nconf->nc_netid, gai_strerror(aicode));
832222623Srmacklem				close(fd);
833172827Smatteo				continue;
834172827Smatteo			}
835172827Smatteo		}
836172827Smatteo
837222623Srmacklem		/* Store the fd. */
838222623Srmacklem		sock_fd[sock_fdcnt - 1] = fd;
839222623Srmacklem
840222623Srmacklem		/* Now, attempt the bind. */
841172827Smatteo		r = bindresvport_sa(fd, res->ai_addr);
842172827Smatteo		if (r != 0) {
843222623Srmacklem			if (errno == EADDRINUSE && mallocd_svcport != 0) {
844222623Srmacklem				if (mallocd_res != 0) {
845222623Srmacklem					free(res->ai_addr);
846222623Srmacklem					free(res);
847222623Srmacklem				} else
848222623Srmacklem					freeaddrinfo(res);
849222623Srmacklem				return (-1);
850222623Srmacklem			}
851172827Smatteo			syslog(LOG_ERR, "bindresvport_sa: %m");
852172827Smatteo			exit(1);
853172827Smatteo		}
854172827Smatteo
855222623Srmacklem		if (svcport_str == NULL) {
856222623Srmacklem			svcport_str = malloc(NI_MAXSERV * sizeof(char));
857222623Srmacklem			if (svcport_str == NULL)
858222623Srmacklem				out_of_mem();
859222623Srmacklem			mallocd_svcport = 1;
860222623Srmacklem
861222623Srmacklem			if (getnameinfo(res->ai_addr,
862222623Srmacklem			    res->ai_addr->sa_len, NULL, NI_MAXHOST,
863222623Srmacklem			    svcport_str, NI_MAXSERV * sizeof(char),
864222623Srmacklem			    NI_NUMERICHOST | NI_NUMERICSERV))
865222623Srmacklem				errx(1, "Cannot get port number");
866222623Srmacklem		}
867222623Srmacklem		if (mallocd_res != 0) {
868222623Srmacklem			free(res->ai_addr);
869222623Srmacklem			free(res);
870222623Srmacklem		} else
871222623Srmacklem			freeaddrinfo(res);
872222623Srmacklem		res = NULL;
873222623Srmacklem	}
874222623Srmacklem	return (0);
875222623Srmacklem}
876222623Srmacklem
877222623Srmacklem/*
878222623Srmacklem * Called after all the create_service() calls have succeeded, to complete
879222623Srmacklem * the setup and registration.
880222623Srmacklem */
881222623Srmacklemstatic void
882222623Srmacklemcomplete_service(struct netconfig *nconf, char *port_str)
883222623Srmacklem{
884222623Srmacklem	struct addrinfo hints, *res = NULL;
885222623Srmacklem	struct __rpc_sockinfo si;
886222623Srmacklem	struct netbuf servaddr;
887222623Srmacklem	SVCXPRT	*transp = NULL;
888222623Srmacklem	int aicode, fd, nhostsbak;
889222623Srmacklem	int registered = 0;
890222623Srmacklem
891222623Srmacklem	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
892222623Srmacklem	    (nconf->nc_semantics != NC_TPI_COTS) &&
893222623Srmacklem	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
894222623Srmacklem		return;	/* not my type */
895222623Srmacklem
896222623Srmacklem	/*
897222623Srmacklem	 * XXX - using RPC library internal functions.
898222623Srmacklem	 */
899222623Srmacklem	if (!__rpc_nconf2sockinfo(nconf, &si)) {
900222623Srmacklem		syslog(LOG_ERR, "cannot get information for %s",
901222623Srmacklem		    nconf->nc_netid);
902222623Srmacklem		return;
903222623Srmacklem	}
904222623Srmacklem
905222623Srmacklem	nhostsbak = nhosts;
906222623Srmacklem	while (nhostsbak > 0) {
907222623Srmacklem		--nhostsbak;
908222623Srmacklem		if (sock_fdpos >= sock_fdcnt) {
909222623Srmacklem			/* Should never happen. */
910222623Srmacklem			syslog(LOG_ERR, "Ran out of socket fd's");
911222623Srmacklem			return;
912222623Srmacklem		}
913222623Srmacklem		fd = sock_fd[sock_fdpos++];
914222623Srmacklem		if (fd < 0)
915222623Srmacklem			continue;
916222623Srmacklem
917341171Ssef		/*
918341171Ssef		 * Using -1 tells listen(2) to use
919341171Ssef		 * kern.ipc.soacceptqueue for the backlog.
920341171Ssef		 */
921172827Smatteo		if (nconf->nc_semantics != NC_TPI_CLTS)
922341171Ssef			listen(fd, -1);
923172827Smatteo
924172827Smatteo		if (nconf->nc_semantics == NC_TPI_CLTS )
925172827Smatteo			transp = svc_dg_create(fd, 0, 0);
926172827Smatteo		else
927172827Smatteo			transp = svc_vc_create(fd, RPC_MAXDATASIZE,
928172827Smatteo			    RPC_MAXDATASIZE);
929172827Smatteo
930172827Smatteo		if (transp != (SVCXPRT *) NULL) {
931194880Sdfr			if (!svc_reg(transp, MOUNTPROG, MOUNTVERS, mntsrv,
932172827Smatteo			    NULL))
933172827Smatteo				syslog(LOG_ERR,
934194880Sdfr				    "can't register %s MOUNTVERS service",
935172827Smatteo				    nconf->nc_netid);
936172827Smatteo			if (!force_v2) {
937194880Sdfr				if (!svc_reg(transp, MOUNTPROG, MOUNTVERS3,
938172827Smatteo				    mntsrv, NULL))
939172827Smatteo					syslog(LOG_ERR,
940194880Sdfr					    "can't register %s MOUNTVERS3 service",
941172827Smatteo					    nconf->nc_netid);
942172827Smatteo			}
943172827Smatteo		} else
944172827Smatteo			syslog(LOG_WARNING, "can't create %s services",
945172827Smatteo			    nconf->nc_netid);
946172827Smatteo
947172827Smatteo		if (registered == 0) {
948172827Smatteo			registered = 1;
949172827Smatteo			memset(&hints, 0, sizeof hints);
950172827Smatteo			hints.ai_flags = AI_PASSIVE;
951172827Smatteo			hints.ai_family = si.si_af;
952172827Smatteo			hints.ai_socktype = si.si_socktype;
953172827Smatteo			hints.ai_protocol = si.si_proto;
954172827Smatteo
955222623Srmacklem			if ((aicode = getaddrinfo(NULL, port_str, &hints,
956172827Smatteo			    &res)) != 0) {
957172827Smatteo				syslog(LOG_ERR, "cannot get local address: %s",
958172827Smatteo				    gai_strerror(aicode));
959172827Smatteo				exit(1);
960172827Smatteo			}
961172827Smatteo
962172827Smatteo			servaddr.buf = malloc(res->ai_addrlen);
963172827Smatteo			memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
964172827Smatteo			servaddr.len = res->ai_addrlen;
965172827Smatteo
966194880Sdfr			rpcb_set(MOUNTPROG, MOUNTVERS, nconf, &servaddr);
967194880Sdfr			rpcb_set(MOUNTPROG, MOUNTVERS3, nconf, &servaddr);
968172827Smatteo
969172827Smatteo			xcreated++;
970172827Smatteo			freeaddrinfo(res);
971172827Smatteo		}
972172827Smatteo	} /* end while */
9731558Srgrimes}
9741558Srgrimes
975222623Srmacklem/*
976222623Srmacklem * Clear out sockets after a failure to bind one of them, so that the
977222623Srmacklem * cycle of socket creation/binding can start anew.
978222623Srmacklem */
97937663Scharnierstatic void
980222623Srmacklemclearout_service(void)
981222623Srmacklem{
982222623Srmacklem	int i;
983222623Srmacklem
984222623Srmacklem	for (i = 0; i < sock_fdcnt; i++) {
985222623Srmacklem		if (sock_fd[i] >= 0) {
986222623Srmacklem			shutdown(sock_fd[i], SHUT_RDWR);
987222623Srmacklem			close(sock_fd[i]);
988222623Srmacklem		}
989222623Srmacklem	}
990222623Srmacklem}
991222623Srmacklem
992222623Srmacklemstatic void
993216587Scharnierusage(void)
99437663Scharnier{
99537663Scharnier	fprintf(stderr,
996192993Srmacklem		"usage: mountd [-2] [-d] [-e] [-l] [-n] [-p <port>] [-r] "
997241568Srmacklem		"[-S] [-h <bindip>] [export_file ...]\n");
99837663Scharnier	exit(1);
99937663Scharnier}
100037663Scharnier
10011558Srgrimes/*
10021558Srgrimes * The mount rpc service
10031558Srgrimes */
10041558Srgrimesvoid
1005216587Scharniermntsrv(struct svc_req *rqstp, SVCXPRT *transp)
10061558Srgrimes{
10071558Srgrimes	struct exportlist *ep;
10081558Srgrimes	struct dirlist *dp;
10099336Sdfr	struct fhreturn fhr;
10101558Srgrimes	struct stat stb;
10111558Srgrimes	struct statfs fsb;
101274462Salfred	char host[NI_MAXHOST], numerichost[NI_MAXHOST];
101374462Salfred	int lookup_failed = 1;
101474462Salfred	struct sockaddr *saddr;
10159336Sdfr	u_short sport;
1016194880Sdfr	char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN];
101728911Sguido	int bad = 0, defset, hostset;
10189336Sdfr	sigset_t sighup_mask;
1019240902Srmacklem	int numsecflavors, *secflavorsp;
10201558Srgrimes
10219336Sdfr	sigemptyset(&sighup_mask);
10229336Sdfr	sigaddset(&sighup_mask, SIGHUP);
102374462Salfred	saddr = svc_getrpccaller(transp)->buf;
102474462Salfred	switch (saddr->sa_family) {
102574462Salfred	case AF_INET6:
102675635Siedowse		sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
102774462Salfred		break;
102874462Salfred	case AF_INET:
102975635Siedowse		sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
103074462Salfred		break;
103174462Salfred	default:
103274462Salfred		syslog(LOG_ERR, "request from unknown address family");
103374462Salfred		return;
103474462Salfred	}
1035346769Smav	switch (rqstp->rq_proc) {
1036346769Smav	case MOUNTPROC_MNT:
1037346769Smav	case MOUNTPROC_UMNT:
1038346769Smav	case MOUNTPROC_UMNTALL:
1039346769Smav		lookup_failed = getnameinfo(saddr, saddr->sa_len, host,
1040346769Smav		    sizeof host, NULL, 0, 0);
1041346769Smav	}
104274462Salfred	getnameinfo(saddr, saddr->sa_len, numerichost,
104374462Salfred	    sizeof numerichost, NULL, 0, NI_NUMERICHOST);
10441558Srgrimes	switch (rqstp->rq_proc) {
10451558Srgrimes	case NULLPROC:
1046121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
104737663Scharnier			syslog(LOG_ERR, "can't send reply");
10481558Srgrimes		return;
1049194880Sdfr	case MOUNTPROC_MNT:
10509336Sdfr		if (sport >= IPPORT_RESERVED && resvport_only) {
105131656Sguido			syslog(LOG_NOTICE,
105231656Sguido			    "mount request from %s from unprivileged port",
105374462Salfred			    numerichost);
10541558Srgrimes			svcerr_weakauth(transp);
10551558Srgrimes			return;
10561558Srgrimes		}
1057121556Speter		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
105831656Sguido			syslog(LOG_NOTICE, "undecodable mount request from %s",
105974462Salfred			    numerichost);
10601558Srgrimes			svcerr_decode(transp);
10611558Srgrimes			return;
10621558Srgrimes		}
10631558Srgrimes
10641558Srgrimes		/*
10651558Srgrimes		 * Get the real pathname and make sure it is a directory
10669336Sdfr		 * or a regular file if the -r option was specified
10679336Sdfr		 * and it exists.
10681558Srgrimes		 */
106951968Salfred		if (realpath(rpcpath, dirpath) == NULL ||
10701558Srgrimes		    stat(dirpath, &stb) < 0 ||
10711558Srgrimes		    statfs(dirpath, &fsb) < 0) {
10721558Srgrimes			chdir("/");	/* Just in case realpath doesn't */
107331656Sguido			syslog(LOG_NOTICE,
107437663Scharnier			    "mount request from %s for non existent path %s",
107574462Salfred			    numerichost, dirpath);
10761558Srgrimes			if (debug)
107737663Scharnier				warnx("stat failed on %s", dirpath);
107828911Sguido			bad = ENOENT;	/* We will send error reply later */
10791558Srgrimes		}
1080330092Srpokala		if (!bad &&
1081330092Srpokala		    !S_ISDIR(stb.st_mode) &&
1082330092Srpokala		    (dir_only || !S_ISREG(stb.st_mode))) {
1083330092Srpokala			syslog(LOG_NOTICE,
1084330092Srpokala			    "mount request from %s for non-directory path %s",
1085330092Srpokala			    numerichost, dirpath);
1086330092Srpokala			if (debug)
1087330092Srpokala				warnx("mounting non-directory %s", dirpath);
1088330092Srpokala			bad = ENOTDIR;	/* We will send error reply later */
1089330092Srpokala		}
10901558Srgrimes
10911558Srgrimes		/* Check in the exports list */
10929336Sdfr		sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1093330092Srpokala		if (bad)
1094330092Srpokala			ep = NULL;
1095330092Srpokala		else
1096349756Srmacklem			ep = ex_search(&fsb.f_fsid, exphead);
10979336Sdfr		hostset = defset = 0;
1098240902Srmacklem		if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset,
1099240902Srmacklem		    &numsecflavors, &secflavorsp) ||
11001558Srgrimes		    ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
1101240902Srmacklem		      chk_host(dp, saddr, &defset, &hostset, &numsecflavors,
1102240902Srmacklem		       &secflavorsp)) ||
110374462Salfred		    (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
110474462Salfred		     scan_tree(ep->ex_dirl, saddr) == 0))) {
110528911Sguido			if (bad) {
1106121556Speter				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
110728911Sguido				    (caddr_t)&bad))
110837663Scharnier					syslog(LOG_ERR, "can't send reply");
110928911Sguido				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
111028911Sguido				return;
111128911Sguido			}
1112240902Srmacklem			if (hostset & DP_HOSTSET) {
11139336Sdfr				fhr.fhr_flag = hostset;
1114240902Srmacklem				fhr.fhr_numsecflavors = numsecflavors;
1115240902Srmacklem				fhr.fhr_secflavors = secflavorsp;
1116240902Srmacklem			} else {
11179336Sdfr				fhr.fhr_flag = defset;
1118240902Srmacklem				fhr.fhr_numsecflavors = ep->ex_defnumsecflavors;
1119240902Srmacklem				fhr.fhr_secflavors = ep->ex_defsecflavors;
1120240902Srmacklem			}
11219336Sdfr			fhr.fhr_vers = rqstp->rq_vers;
11221558Srgrimes			/* Get the file handle */
112323681Speter			memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
11249336Sdfr			if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
11251558Srgrimes				bad = errno;
112637663Scharnier				syslog(LOG_ERR, "can't get fh for %s", dirpath);
1127121556Speter				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
11281558Srgrimes				    (caddr_t)&bad))
112937663Scharnier					syslog(LOG_ERR, "can't send reply");
11309336Sdfr				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
11311558Srgrimes				return;
11321558Srgrimes			}
1133121556Speter			if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs,
1134121556Speter			    (caddr_t)&fhr))
113537663Scharnier				syslog(LOG_ERR, "can't send reply");
113674462Salfred			if (!lookup_failed)
113774462Salfred				add_mlist(host, dirpath);
11381558Srgrimes			else
113974462Salfred				add_mlist(numerichost, dirpath);
11401558Srgrimes			if (debug)
114137663Scharnier				warnx("mount successful");
1142121767Speter			if (dolog)
114331656Sguido				syslog(LOG_NOTICE,
114431656Sguido				    "mount request succeeded from %s for %s",
114574462Salfred				    numerichost, dirpath);
114631656Sguido		} else {
1147330092Srpokala			if (!bad)
1148330092Srpokala				bad = EACCES;
114931656Sguido			syslog(LOG_NOTICE,
115031656Sguido			    "mount request denied from %s for %s",
115174462Salfred			    numerichost, dirpath);
115231656Sguido		}
115328911Sguido
1154121556Speter		if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long,
1155121556Speter		    (caddr_t)&bad))
115637663Scharnier			syslog(LOG_ERR, "can't send reply");
11579336Sdfr		sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
11581558Srgrimes		return;
1159194880Sdfr	case MOUNTPROC_DUMP:
1160121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
116137663Scharnier			syslog(LOG_ERR, "can't send reply");
1162121767Speter		else if (dolog)
116331656Sguido			syslog(LOG_NOTICE,
116431656Sguido			    "dump request succeeded from %s",
116574462Salfred			    numerichost);
11661558Srgrimes		return;
1167194880Sdfr	case MOUNTPROC_UMNT:
11689336Sdfr		if (sport >= IPPORT_RESERVED && resvport_only) {
116931656Sguido			syslog(LOG_NOTICE,
117031656Sguido			    "umount request from %s from unprivileged port",
117174462Salfred			    numerichost);
11721558Srgrimes			svcerr_weakauth(transp);
11731558Srgrimes			return;
11741558Srgrimes		}
1175121556Speter		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
117631656Sguido			syslog(LOG_NOTICE, "undecodable umount request from %s",
117774462Salfred			    numerichost);
11781558Srgrimes			svcerr_decode(transp);
11791558Srgrimes			return;
11801558Srgrimes		}
118151968Salfred		if (realpath(rpcpath, dirpath) == NULL) {
118251968Salfred			syslog(LOG_NOTICE, "umount request from %s "
118351968Salfred			    "for non existent path %s",
118474462Salfred			    numerichost, dirpath);
118551968Salfred		}
1186121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
118737663Scharnier			syslog(LOG_ERR, "can't send reply");
118874462Salfred		if (!lookup_failed)
118975635Siedowse			del_mlist(host, dirpath);
119075635Siedowse		del_mlist(numerichost, dirpath);
1191121767Speter		if (dolog)
119231656Sguido			syslog(LOG_NOTICE,
119331656Sguido			    "umount request succeeded from %s for %s",
119474462Salfred			    numerichost, dirpath);
11951558Srgrimes		return;
1196194880Sdfr	case MOUNTPROC_UMNTALL:
11979336Sdfr		if (sport >= IPPORT_RESERVED && resvport_only) {
119831656Sguido			syslog(LOG_NOTICE,
119931656Sguido			    "umountall request from %s from unprivileged port",
120074462Salfred			    numerichost);
12011558Srgrimes			svcerr_weakauth(transp);
12021558Srgrimes			return;
12031558Srgrimes		}
1204121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
120537663Scharnier			syslog(LOG_ERR, "can't send reply");
120674462Salfred		if (!lookup_failed)
120775635Siedowse			del_mlist(host, NULL);
120875635Siedowse		del_mlist(numerichost, NULL);
1209121767Speter		if (dolog)
121031656Sguido			syslog(LOG_NOTICE,
121131656Sguido			    "umountall request succeeded from %s",
121274462Salfred			    numerichost);
12131558Srgrimes		return;
1214194880Sdfr	case MOUNTPROC_EXPORT:
1215121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
1216121556Speter			if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief,
1217121556Speter			    (caddr_t)NULL))
1218100117Salfred				syslog(LOG_ERR, "can't send reply");
1219121767Speter		if (dolog)
122031656Sguido			syslog(LOG_NOTICE,
122131656Sguido			    "export request succeeded from %s",
122274462Salfred			    numerichost);
12231558Srgrimes		return;
12241558Srgrimes	default:
12251558Srgrimes		svcerr_noproc(transp);
12261558Srgrimes		return;
12271558Srgrimes	}
12281558Srgrimes}
12291558Srgrimes
12301558Srgrimes/*
12311558Srgrimes * Xdr conversion for a dirpath string
12321558Srgrimes */
1233285128Straszstatic int
1234216587Scharnierxdr_dir(XDR *xdrsp, char *dirp)
12351558Srgrimes{
1236194880Sdfr	return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
12371558Srgrimes}
12381558Srgrimes
12391558Srgrimes/*
12409336Sdfr * Xdr routine to generate file handle reply
12411558Srgrimes */
1242285128Straszstatic int
1243216587Scharnierxdr_fhs(XDR *xdrsp, caddr_t cp)
12441558Srgrimes{
124592806Sobrien	struct fhreturn *fhrp = (struct fhreturn *)cp;
12469336Sdfr	u_long ok = 0, len, auth;
1247184588Sdfr	int i;
12481558Srgrimes
12491558Srgrimes	if (!xdr_long(xdrsp, &ok))
12501558Srgrimes		return (0);
12519336Sdfr	switch (fhrp->fhr_vers) {
12529336Sdfr	case 1:
12539336Sdfr		return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
12549336Sdfr	case 3:
12559336Sdfr		len = NFSX_V3FH;
12569336Sdfr		if (!xdr_long(xdrsp, &len))
12579336Sdfr			return (0);
12589336Sdfr		if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
12599336Sdfr			return (0);
1260184588Sdfr		if (fhrp->fhr_numsecflavors) {
1261184588Sdfr			if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors))
1262184588Sdfr				return (0);
1263184588Sdfr			for (i = 0; i < fhrp->fhr_numsecflavors; i++)
1264184588Sdfr				if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i]))
1265184588Sdfr					return (0);
1266184588Sdfr			return (1);
1267184588Sdfr		} else {
1268184588Sdfr			auth = AUTH_SYS;
1269184588Sdfr			len = 1;
1270184588Sdfr			if (!xdr_long(xdrsp, &len))
1271184588Sdfr				return (0);
1272184588Sdfr			return (xdr_long(xdrsp, &auth));
1273184588Sdfr		}
1274298089Spfg	}
12759336Sdfr	return (0);
12761558Srgrimes}
12771558Srgrimes
1278285128Straszstatic int
1279216587Scharnierxdr_mlist(XDR *xdrsp, caddr_t cp __unused)
12801558Srgrimes{
12811558Srgrimes	struct mountlist *mlp;
12821558Srgrimes	int true = 1;
12831558Srgrimes	int false = 0;
12841558Srgrimes	char *strp;
12851558Srgrimes
1286324955Smanu	SLIST_FOREACH(mlp, &mlhead, next) {
12871558Srgrimes		if (!xdr_bool(xdrsp, &true))
12881558Srgrimes			return (0);
12891558Srgrimes		strp = &mlp->ml_host[0];
1290194880Sdfr		if (!xdr_string(xdrsp, &strp, MNTNAMLEN))
12911558Srgrimes			return (0);
12921558Srgrimes		strp = &mlp->ml_dirp[0];
1293194880Sdfr		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
12941558Srgrimes			return (0);
12951558Srgrimes	}
12961558Srgrimes	if (!xdr_bool(xdrsp, &false))
12971558Srgrimes		return (0);
12981558Srgrimes	return (1);
12991558Srgrimes}
13001558Srgrimes
13011558Srgrimes/*
13021558Srgrimes * Xdr conversion for export list
13031558Srgrimes */
1304285128Straszstatic int
1305216587Scharnierxdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief)
13061558Srgrimes{
13071558Srgrimes	struct exportlist *ep;
13081558Srgrimes	int false = 0;
13099336Sdfr	int putdef;
13109336Sdfr	sigset_t sighup_mask;
1311349756Srmacklem	int i;
13121558Srgrimes
13139336Sdfr	sigemptyset(&sighup_mask);
13149336Sdfr	sigaddset(&sighup_mask, SIGHUP);
13159336Sdfr	sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1316324955Smanu
1317349756Srmacklem	for (i = 0; i < exphashsize; i++)
1318349756Srmacklem		SLIST_FOREACH(ep, &exphead[i], entries) {
1319349756Srmacklem			putdef = 0;
1320349756Srmacklem			if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
1321349756Srmacklem				       &putdef, brief))
1322349756Srmacklem				goto errout;
1323349756Srmacklem			if (ep->ex_defdir && putdef == 0 &&
1324349756Srmacklem				put_exlist(ep->ex_defdir, xdrsp, NULL,
1325349756Srmacklem				&putdef, brief))
1326349756Srmacklem				goto errout;
1327349756Srmacklem		}
13289336Sdfr	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
13291558Srgrimes	if (!xdr_bool(xdrsp, &false))
13301558Srgrimes		return (0);
13311558Srgrimes	return (1);
13321558Srgrimeserrout:
13339336Sdfr	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
13341558Srgrimes	return (0);
13351558Srgrimes}
13361558Srgrimes
13371558Srgrimes/*
13381558Srgrimes * Called from xdr_explist() to traverse the tree and export the
13391558Srgrimes * directory paths.
13401558Srgrimes */
1341285128Straszstatic int
1342216587Scharnierput_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, int *putdefp,
1343216587Scharnier	int brief)
13441558Srgrimes{
13451558Srgrimes	struct grouplist *grp;
13461558Srgrimes	struct hostlist *hp;
13471558Srgrimes	int true = 1;
13481558Srgrimes	int false = 0;
13491558Srgrimes	int gotalldir = 0;
13501558Srgrimes	char *strp;
13511558Srgrimes
13521558Srgrimes	if (dp) {
1353100117Salfred		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief))
13541558Srgrimes			return (1);
13551558Srgrimes		if (!xdr_bool(xdrsp, &true))
13561558Srgrimes			return (1);
13571558Srgrimes		strp = dp->dp_dirp;
1358194880Sdfr		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
13591558Srgrimes			return (1);
13601558Srgrimes		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
13611558Srgrimes			gotalldir = 1;
13621558Srgrimes			*putdefp = 1;
13631558Srgrimes		}
1364100117Salfred		if (brief) {
1365100117Salfred			if (!xdr_bool(xdrsp, &true))
1366100117Salfred				return (1);
1367100117Salfred			strp = "(...)";
1368194880Sdfr			if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1369100117Salfred				return (1);
1370100117Salfred		} else if ((dp->dp_flag & DP_DEFSET) == 0 &&
13711558Srgrimes		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
13721558Srgrimes			hp = dp->dp_hosts;
13731558Srgrimes			while (hp) {
13741558Srgrimes				grp = hp->ht_grp;
13751558Srgrimes				if (grp->gr_type == GT_HOST) {
13761558Srgrimes					if (!xdr_bool(xdrsp, &true))
13771558Srgrimes						return (1);
137874462Salfred					strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
13798871Srgrimes					if (!xdr_string(xdrsp, &strp,
1380194880Sdfr					    MNTNAMLEN))
13811558Srgrimes						return (1);
13821558Srgrimes				} else if (grp->gr_type == GT_NET) {
13831558Srgrimes					if (!xdr_bool(xdrsp, &true))
13841558Srgrimes						return (1);
13851558Srgrimes					strp = grp->gr_ptr.gt_net.nt_name;
13868871Srgrimes					if (!xdr_string(xdrsp, &strp,
1387194880Sdfr					    MNTNAMLEN))
13881558Srgrimes						return (1);
13891558Srgrimes				}
13901558Srgrimes				hp = hp->ht_next;
13911558Srgrimes				if (gotalldir && hp == (struct hostlist *)NULL) {
13921558Srgrimes					hp = adp->dp_hosts;
13931558Srgrimes					gotalldir = 0;
13941558Srgrimes				}
13951558Srgrimes			}
13961558Srgrimes		}
13971558Srgrimes		if (!xdr_bool(xdrsp, &false))
13981558Srgrimes			return (1);
1399100117Salfred		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief))
14001558Srgrimes			return (1);
14011558Srgrimes	}
14021558Srgrimes	return (0);
14031558Srgrimes}
14041558Srgrimes
1405285128Straszstatic int
1406216587Scharnierxdr_explist(XDR *xdrsp, caddr_t cp)
1407100117Salfred{
1408100117Salfred
1409100117Salfred	return xdr_explist_common(xdrsp, cp, 0);
1410100117Salfred}
1411100117Salfred
1412285128Straszstatic int
1413216587Scharnierxdr_explist_brief(XDR *xdrsp, caddr_t cp)
1414100117Salfred{
1415100117Salfred
1416100117Salfred	return xdr_explist_common(xdrsp, cp, 1);
1417100117Salfred}
1418100117Salfred
1419285128Straszstatic char *line;
1420285128Straszstatic size_t linesize;
1421285128Straszstatic FILE *exp_file;
14221558Srgrimes
14231558Srgrimes/*
1424166440Spjd * Get the export list from one, currently open file
14251558Srgrimes */
1426166440Spjdstatic void
1427216587Scharnierget_exportlist_one(void)
14281558Srgrimes{
1429324955Smanu	struct exportlist *ep;
14301558Srgrimes	struct grouplist *grp, *tgrp;
14311558Srgrimes	struct dirlist *dirhead;
1432166440Spjd	struct statfs fsb;
143372650Sgreen	struct xucred anon;
14341558Srgrimes	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
1435166440Spjd	int len, has_host, exflags, got_nondir, dirplen, netgrp;
14361558Srgrimes
1437192934Srmacklem	v4root_phase = 0;
14381558Srgrimes	dirhead = (struct dirlist *)NULL;
14391558Srgrimes	while (get_line()) {
14401558Srgrimes		if (debug)
144137663Scharnier			warnx("got line %s", line);
14421558Srgrimes		cp = line;
14431558Srgrimes		nextfield(&cp, &endcp);
14441558Srgrimes		if (*cp == '#')
14451558Srgrimes			goto nextline;
14461558Srgrimes
14471558Srgrimes		/*
14481558Srgrimes		 * Set defaults.
14491558Srgrimes		 */
14501558Srgrimes		has_host = FALSE;
14511558Srgrimes		anon = def_anon;
14521558Srgrimes		exflags = MNT_EXPORTED;
14531558Srgrimes		got_nondir = 0;
14541558Srgrimes		opt_flags = 0;
14551558Srgrimes		ep = (struct exportlist *)NULL;
1456192934Srmacklem		dirp = NULL;
14571558Srgrimes
14581558Srgrimes		/*
1459192934Srmacklem		 * Handle the V4 root dir.
1460192934Srmacklem		 */
1461192934Srmacklem		if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') {
1462192934Srmacklem			/*
1463192934Srmacklem			 * V4: just indicates that it is the v4 root point,
1464192934Srmacklem			 * so skip over that and set v4root_phase.
1465192934Srmacklem			 */
1466192934Srmacklem			if (v4root_phase > 0) {
1467192934Srmacklem				syslog(LOG_ERR, "V4:duplicate line, ignored");
1468192934Srmacklem				goto nextline;
1469192934Srmacklem			}
1470192934Srmacklem			v4root_phase = 1;
1471192934Srmacklem			cp += 3;
1472192934Srmacklem			nextfield(&cp, &endcp);
1473192934Srmacklem		}
1474192934Srmacklem
1475192934Srmacklem		/*
14761558Srgrimes		 * Create new exports list entry
14771558Srgrimes		 */
14781558Srgrimes		len = endcp-cp;
14791558Srgrimes		tgrp = grp = get_grp();
14801558Srgrimes		while (len > 0) {
1481194880Sdfr			if (len > MNTNAMLEN) {
1482329392Sbrd			    getexp_err(ep, tgrp, "mountpoint too long");
14831558Srgrimes			    goto nextline;
14841558Srgrimes			}
14851558Srgrimes			if (*cp == '-') {
14861558Srgrimes			    if (ep == (struct exportlist *)NULL) {
1487329392Sbrd				getexp_err(ep, tgrp,
1488329392Sbrd				    "flag before export path definition");
14891558Srgrimes				goto nextline;
14901558Srgrimes			    }
14911558Srgrimes			    if (debug)
149237663Scharnier				warnx("doing opt %s", cp);
14931558Srgrimes			    got_nondir = 1;
14941558Srgrimes			    if (do_opt(&cp, &endcp, ep, grp, &has_host,
14951558Srgrimes				&exflags, &anon)) {
1496329392Sbrd				getexp_err(ep, tgrp, NULL);
14971558Srgrimes				goto nextline;
14981558Srgrimes			    }
14991558Srgrimes			} else if (*cp == '/') {
15001558Srgrimes			    savedc = *endcp;
15011558Srgrimes			    *endcp = '\0';
1502192934Srmacklem			    if (v4root_phase > 1) {
1503192934Srmacklem				    if (dirp != NULL) {
1504329392Sbrd					getexp_err(ep, tgrp, "Multiple V4 dirs");
1505192934Srmacklem					goto nextline;
1506192934Srmacklem				    }
1507192934Srmacklem			    }
15081558Srgrimes			    if (check_dirpath(cp) &&
15091558Srgrimes				statfs(cp, &fsb) >= 0) {
1510283008Srmacklem				if ((fsb.f_flags & MNT_AUTOMOUNTED) != 0)
1511283008Srmacklem				    syslog(LOG_ERR, "Warning: exporting of "
1512283008Srmacklem					"automounted fs %s not supported", cp);
15131558Srgrimes				if (got_nondir) {
1514329392Sbrd				    getexp_err(ep, tgrp, "dirs must be first");
15151558Srgrimes				    goto nextline;
15161558Srgrimes				}
1517192934Srmacklem				if (v4root_phase == 1) {
1518192934Srmacklem				    if (dirp != NULL) {
1519329392Sbrd					getexp_err(ep, tgrp, "Multiple V4 dirs");
15201558Srgrimes					goto nextline;
15211558Srgrimes				    }
1522192934Srmacklem				    if (strlen(v4root_dirpath) == 0) {
1523192934Srmacklem					strlcpy(v4root_dirpath, cp,
1524192934Srmacklem					    sizeof (v4root_dirpath));
1525192934Srmacklem				    } else if (strcmp(v4root_dirpath, cp)
1526192934Srmacklem					!= 0) {
1527192934Srmacklem					syslog(LOG_ERR,
1528192934Srmacklem					    "different V4 dirpath %s", cp);
1529329392Sbrd					getexp_err(ep, tgrp, NULL);
1530192934Srmacklem					goto nextline;
1531192934Srmacklem				    }
1532192934Srmacklem				    dirp = cp;
1533192934Srmacklem				    v4root_phase = 2;
1534192934Srmacklem				    got_nondir = 1;
1535192934Srmacklem				    ep = get_exp();
15361558Srgrimes				} else {
1537192934Srmacklem				    if (ep) {
1538192934Srmacklem					if (ep->ex_fs.val[0] !=
1539192934Srmacklem					    fsb.f_fsid.val[0] ||
1540192934Srmacklem					    ep->ex_fs.val[1] !=
1541192934Srmacklem					    fsb.f_fsid.val[1]) {
1542329392Sbrd						getexp_err(ep, tgrp,
1543329392Sbrd						    "fsid mismatch");
1544192934Srmacklem						goto nextline;
1545192934Srmacklem					}
1546192934Srmacklem				    } else {
1547192934Srmacklem					/*
1548192934Srmacklem					 * See if this directory is already
1549192934Srmacklem					 * in the list.
1550192934Srmacklem					 */
1551349756Srmacklem					ep = ex_search(&fsb.f_fsid, exphead);
1552192934Srmacklem					if (ep == (struct exportlist *)NULL) {
1553192934Srmacklem					    ep = get_exp();
1554192934Srmacklem					    ep->ex_fs = fsb.f_fsid;
1555324234Smanu					    ep->ex_fsdir = strdup(fsb.f_mntonname);
1556324234Smanu					    if (ep->ex_fsdir == NULL)
1557192934Srmacklem						out_of_mem();
1558192934Srmacklem					    if (debug)
1559192934Srmacklem						warnx(
1560192934Srmacklem						  "making new ep fs=0x%x,0x%x",
1561192934Srmacklem						  fsb.f_fsid.val[0],
1562192934Srmacklem						  fsb.f_fsid.val[1]);
1563192934Srmacklem					} else if (debug)
1564192934Srmacklem					    warnx("found ep fs=0x%x,0x%x",
1565192934Srmacklem						fsb.f_fsid.val[0],
1566192934Srmacklem						fsb.f_fsid.val[1]);
1567192934Srmacklem				    }
1568192934Srmacklem
15691558Srgrimes				    /*
1570192934Srmacklem				     * Add dirpath to export mount point.
15711558Srgrimes				     */
1572192934Srmacklem				    dirp = add_expdir(&dirhead, cp, len);
1573192934Srmacklem				    dirplen = len;
15741558Srgrimes				}
15751558Srgrimes			    } else {
1576329392Sbrd				getexp_err(ep, tgrp,
1577329392Sbrd				    "symbolic link in export path or statfs failed");
15781558Srgrimes				goto nextline;
15791558Srgrimes			    }
15801558Srgrimes			    *endcp = savedc;
15811558Srgrimes			} else {
15821558Srgrimes			    savedc = *endcp;
15831558Srgrimes			    *endcp = '\0';
15841558Srgrimes			    got_nondir = 1;
15851558Srgrimes			    if (ep == (struct exportlist *)NULL) {
1586329392Sbrd				getexp_err(ep, tgrp,
1587329392Sbrd				    "host(s) before export path definition");
15881558Srgrimes				goto nextline;
15891558Srgrimes			    }
15901558Srgrimes
15911558Srgrimes			    /*
15921558Srgrimes			     * Get the host or netgroup.
15931558Srgrimes			     */
15941558Srgrimes			    setnetgrent(cp);
15951558Srgrimes			    netgrp = getnetgrent(&hst, &usr, &dom);
15961558Srgrimes			    do {
15971558Srgrimes				if (has_host) {
15981558Srgrimes				    grp->gr_next = get_grp();
15991558Srgrimes				    grp = grp->gr_next;
16001558Srgrimes				}
16011558Srgrimes				if (netgrp) {
160237003Sjoerg				    if (hst == 0) {
160337663Scharnier					syslog(LOG_ERR,
160437663Scharnier				"null hostname in netgroup %s, skipping", cp);
160537004Sjoerg					grp->gr_type = GT_IGNORE;
160637003Sjoerg				    } else if (get_host(hst, grp, tgrp)) {
160737663Scharnier					syslog(LOG_ERR,
160837663Scharnier			"bad host %s in netgroup %s, skipping", hst, cp);
160929317Sjlemon					grp->gr_type = GT_IGNORE;
16101558Srgrimes				    }
16117401Swpaul				} else if (get_host(cp, grp, tgrp)) {
161237663Scharnier				    syslog(LOG_ERR, "bad host %s, skipping", cp);
161329317Sjlemon				    grp->gr_type = GT_IGNORE;
16141558Srgrimes				}
16151558Srgrimes				has_host = TRUE;
16161558Srgrimes			    } while (netgrp && getnetgrent(&hst, &usr, &dom));
16171558Srgrimes			    endnetgrent();
16181558Srgrimes			    *endcp = savedc;
16191558Srgrimes			}
16201558Srgrimes			cp = endcp;
16211558Srgrimes			nextfield(&cp, &endcp);
16221558Srgrimes			len = endcp - cp;
16231558Srgrimes		}
16241558Srgrimes		if (check_options(dirhead)) {
1625329392Sbrd			getexp_err(ep, tgrp, NULL);
16261558Srgrimes			goto nextline;
16271558Srgrimes		}
16281558Srgrimes		if (!has_host) {
162975641Siedowse			grp->gr_type = GT_DEFAULT;
16301558Srgrimes			if (debug)
163137663Scharnier				warnx("adding a default entry");
16321558Srgrimes
16331558Srgrimes		/*
16341558Srgrimes		 * Don't allow a network export coincide with a list of
16351558Srgrimes		 * host(s) on the same line.
16361558Srgrimes		 */
16371558Srgrimes		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1638329392Sbrd			getexp_err(ep, tgrp, "network/host conflict");
16391558Srgrimes			goto nextline;
164029317Sjlemon
164174462Salfred		/*
164274462Salfred		 * If an export list was specified on this line, make sure
164329317Sjlemon		 * that we have at least one valid entry, otherwise skip it.
164429317Sjlemon		 */
164529317Sjlemon		} else {
164629317Sjlemon			grp = tgrp;
164774462Salfred			while (grp && grp->gr_type == GT_IGNORE)
164829317Sjlemon				grp = grp->gr_next;
164929317Sjlemon			if (! grp) {
1650329392Sbrd			    getexp_err(ep, tgrp, "no valid entries");
165129317Sjlemon			    goto nextline;
165229317Sjlemon			}
16531558Srgrimes		}
16541558Srgrimes
1655192934Srmacklem		if (v4root_phase == 1) {
1656329392Sbrd			getexp_err(ep, tgrp, "V4:root, no dirp, ignored");
1657192934Srmacklem			goto nextline;
1658192934Srmacklem		}
1659192934Srmacklem
16601558Srgrimes		/*
16611558Srgrimes		 * Loop through hosts, pushing the exports into the kernel.
16621558Srgrimes		 * After loop, tgrp points to the start of the list and
16631558Srgrimes		 * grp points to the last entry in the list.
16641558Srgrimes		 */
16651558Srgrimes		grp = tgrp;
16661558Srgrimes		do {
166775635Siedowse			if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
166875635Siedowse			    &fsb)) {
1669329392Sbrd				getexp_err(ep, tgrp, NULL);
167075635Siedowse				goto nextline;
167175635Siedowse			}
16721558Srgrimes		} while (grp->gr_next && (grp = grp->gr_next));
16731558Srgrimes
16741558Srgrimes		/*
1675192934Srmacklem		 * For V4: don't enter in mount lists.
1676192934Srmacklem		 */
1677194773Srmacklem		if (v4root_phase > 0 && v4root_phase <= 2) {
1678194773Srmacklem			/*
1679194773Srmacklem			 * Since these structures aren't used by mountd,
1680194773Srmacklem			 * free them up now.
1681194773Srmacklem			 */
1682194773Srmacklem			if (ep != NULL)
1683194773Srmacklem				free_exp(ep);
1684194773Srmacklem			while (tgrp != NULL) {
1685194773Srmacklem				grp = tgrp;
1686194773Srmacklem				tgrp = tgrp->gr_next;
1687194773Srmacklem				free_grp(grp);
1688194773Srmacklem			}
1689192934Srmacklem			goto nextline;
1690194773Srmacklem		}
1691192934Srmacklem
1692192934Srmacklem		/*
16931558Srgrimes		 * Success. Update the data structures.
16941558Srgrimes		 */
16951558Srgrimes		if (has_host) {
16969336Sdfr			hang_dirp(dirhead, tgrp, ep, opt_flags);
1697349128Srmacklem			grp->gr_next = ep->ex_grphead;
1698349128Srmacklem			ep->ex_grphead = tgrp;
16991558Srgrimes		} else {
17001558Srgrimes			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
17019336Sdfr				opt_flags);
17021558Srgrimes			free_grp(grp);
17031558Srgrimes		}
17041558Srgrimes		dirhead = (struct dirlist *)NULL;
17051558Srgrimes		if ((ep->ex_flag & EX_LINKED) == 0) {
1706349756Srmacklem			insert_exports(ep, exphead);
17071558Srgrimes
17081558Srgrimes			ep->ex_flag |= EX_LINKED;
17091558Srgrimes		}
17101558Srgrimesnextline:
1711192934Srmacklem		v4root_phase = 0;
17121558Srgrimes		if (dirhead) {
17131558Srgrimes			free_dir(dirhead);
17141558Srgrimes			dirhead = (struct dirlist *)NULL;
17151558Srgrimes		}
17161558Srgrimes	}
17171558Srgrimes}
17181558Srgrimes
17191558Srgrimes/*
1720166440Spjd * Get the export list from all specified files
1721166440Spjd */
1722285128Straszstatic void
1723216587Scharnierget_exportlist(void)
1724166440Spjd{
1725166440Spjd	struct export_args export;
1726166440Spjd	struct iovec *iov;
1727349126Srmacklem	struct statfs *mntbufp;
1728166440Spjd	char errmsg[255];
1729230352Seadler	int num, i;
1730166440Spjd	int iovlen;
1731192934Srmacklem	struct nfsex_args eargs;
1732166440Spjd
1733241568Srmacklem	if (suspend_nfsd != 0)
1734241568Srmacklem		(void)nfssvc(NFSSVC_SUSPENDNFSD, NULL);
1735192934Srmacklem	v4root_dirpath[0] = '\0';
1736166440Spjd	bzero(&export, sizeof(export));
1737166440Spjd	export.ex_flags = MNT_DELEXPORT;
1738166440Spjd	iov = NULL;
1739166440Spjd	iovlen = 0;
1740166440Spjd	bzero(errmsg, sizeof(errmsg));
1741166440Spjd
1742166440Spjd	/*
1743166440Spjd	 * First, get rid of the old list
1744166440Spjd	 */
1745349756Srmacklem	if (exphead != NULL)
1746349756Srmacklem		free_exports(exphead);
1747166440Spjd
1748166440Spjd	/*
1749192934Srmacklem	 * and the old V4 root dir.
1750192934Srmacklem	 */
1751192934Srmacklem	bzero(&eargs, sizeof (eargs));
1752192934Srmacklem	eargs.export.ex_flags = MNT_DELEXPORT;
1753282214Strasz	if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&eargs) < 0 &&
1754192934Srmacklem	    errno != ENOENT)
1755192934Srmacklem		syslog(LOG_ERR, "Can't delete exports for V4:");
1756192934Srmacklem
1757192934Srmacklem	/*
1758192934Srmacklem	 * and clear flag that notes if a public fh has been exported.
1759192934Srmacklem	 */
1760192934Srmacklem	has_publicfh = 0;
1761192934Srmacklem
1762192934Srmacklem	/*
1763166440Spjd	 * And delete exports that are in the kernel for all local
1764166440Spjd	 * filesystems.
1765166440Spjd	 * XXX: Should know how to handle all local exportable filesystems.
1766166440Spjd	 */
1767166440Spjd	num = getmntinfo(&mntbufp, MNT_NOWAIT);
1768166440Spjd
1769349756Srmacklem	/* Allocate hash tables, for first call. */
1770349756Srmacklem	if (exphead == NULL) {
1771349756Srmacklem		/* Target an average linked list length of 10. */
1772349756Srmacklem		exphashsize = num / 10;
1773349756Srmacklem		if (exphashsize < 1)
1774349756Srmacklem			exphashsize = 1;
1775349756Srmacklem		else if (exphashsize > 100000)
1776349756Srmacklem			exphashsize = 100000;
1777349756Srmacklem		exphead = malloc(exphashsize * sizeof(*exphead));
1778349756Srmacklem		if (exphead == NULL)
1779349756Srmacklem			errx(1, "Can't malloc hash table");
1780349756Srmacklem
1781349756Srmacklem		for (i = 0; i < exphashsize; i++)
1782349756Srmacklem			SLIST_INIT(&exphead[i]);
1783349756Srmacklem	}
1784166440Spjd	if (num > 0) {
1785166440Spjd		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
1786166440Spjd		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
1787166440Spjd		build_iovec(&iov, &iovlen, "from", NULL, 0);
1788166440Spjd		build_iovec(&iov, &iovlen, "update", NULL, 0);
1789166440Spjd		build_iovec(&iov, &iovlen, "export", &export, sizeof(export));
1790166440Spjd		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
1791166440Spjd	}
1792166440Spjd
1793349126Srmacklem	for (i = 0; i < num; i++)
1794349126Srmacklem		delete_export(iov, iovlen, &mntbufp[i], errmsg);
1795166440Spjd
1796166440Spjd	if (iov != NULL) {
1797166440Spjd		/* Free strings allocated by strdup() in getmntopts.c */
1798166440Spjd		free(iov[0].iov_base); /* fstype */
1799166440Spjd		free(iov[2].iov_base); /* fspath */
1800166440Spjd		free(iov[4].iov_base); /* from */
1801166440Spjd		free(iov[6].iov_base); /* update */
1802166440Spjd		free(iov[8].iov_base); /* export */
1803166440Spjd		free(iov[10].iov_base); /* errmsg */
1804166440Spjd
1805166440Spjd		/* free iov, allocated by realloc() */
1806166440Spjd		free(iov);
1807166440Spjd		iovlen = 0;
1808166440Spjd	}
1809166440Spjd
1810349126Srmacklem	read_exportfile();
1811192934Srmacklem
1812192934Srmacklem	/*
1813192934Srmacklem	 * If there was no public fh, clear any previous one set.
1814192934Srmacklem	 */
1815282214Strasz	if (has_publicfh == 0)
1816192934Srmacklem		(void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
1817241568Srmacklem
1818241568Srmacklem	/* Resume the nfsd. If they weren't suspended, this is harmless. */
1819241568Srmacklem	(void)nfssvc(NFSSVC_RESUMENFSD, NULL);
1820166440Spjd}
1821166440Spjd
1822166440Spjd/*
1823349124Srmacklem * Insert an export entry in the appropriate list.
1824349124Srmacklem */
1825349124Srmacklemstatic void
1826349124Srmackleminsert_exports(struct exportlist *ep, struct exportlisthead *exhp)
1827349124Srmacklem{
1828349756Srmacklem	uint32_t i;
1829349124Srmacklem
1830349756Srmacklem	i = EXPHASH(&ep->ex_fs);
1831349756Srmacklem	SLIST_INSERT_HEAD(&exhp[i], ep, entries);
1832349124Srmacklem}
1833349124Srmacklem
1834349124Srmacklem/*
1835349124Srmacklem * Free up the exports lists passed in as arguments.
1836349124Srmacklem */
1837349124Srmacklemstatic void
1838349124Srmacklemfree_exports(struct exportlisthead *exhp)
1839349124Srmacklem{
1840349124Srmacklem	struct exportlist *ep, *ep2;
1841349756Srmacklem	int i;
1842349124Srmacklem
1843349756Srmacklem	for (i = 0; i < exphashsize; i++) {
1844349756Srmacklem		SLIST_FOREACH_SAFE(ep, &exhp[i], entries, ep2) {
1845349756Srmacklem			SLIST_REMOVE(&exhp[i], ep, exportlist, entries);
1846349756Srmacklem			free_exp(ep);
1847349756Srmacklem		}
1848349756Srmacklem		SLIST_INIT(&exhp[i]);
1849349124Srmacklem	}
1850349124Srmacklem}
1851349124Srmacklem
1852349124Srmacklem/*
1853349126Srmacklem * Read the exports file(s) and call get_exportlist_one() for each line.
1854349126Srmacklem */
1855349126Srmacklemstatic void
1856349126Srmacklemread_exportfile(void)
1857349126Srmacklem{
1858349126Srmacklem	int done, i;
1859349126Srmacklem
1860349126Srmacklem	/*
1861349126Srmacklem	 * Read in the exports file and build the list, calling
1862349126Srmacklem	 * nmount() as we go along to push the export rules into the kernel.
1863349126Srmacklem	 */
1864349126Srmacklem	done = 0;
1865349126Srmacklem	for (i = 0; exnames[i] != NULL; i++) {
1866349126Srmacklem		if (debug)
1867349126Srmacklem			warnx("reading exports from %s", exnames[i]);
1868349126Srmacklem		if ((exp_file = fopen(exnames[i], "r")) == NULL) {
1869349126Srmacklem			syslog(LOG_WARNING, "can't open %s", exnames[i]);
1870349126Srmacklem			continue;
1871349126Srmacklem		}
1872349126Srmacklem		get_exportlist_one();
1873349126Srmacklem		fclose(exp_file);
1874349126Srmacklem		done++;
1875349126Srmacklem	}
1876349126Srmacklem	if (done == 0) {
1877349126Srmacklem		syslog(LOG_ERR, "can't open any exports file");
1878349126Srmacklem		exit(2);
1879349126Srmacklem	}
1880349126Srmacklem}
1881349126Srmacklem
1882349126Srmacklem/*
1883349126Srmacklem * Delete an exports entry.
1884349126Srmacklem */
1885349126Srmacklemstatic void
1886349126Srmacklemdelete_export(struct iovec *iov, int iovlen, struct statfs *fsp, char *errmsg)
1887349126Srmacklem{
1888349126Srmacklem	struct xvfsconf vfc;
1889349126Srmacklem
1890349126Srmacklem	if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
1891349126Srmacklem		syslog(LOG_ERR, "getvfsbyname() failed for %s",
1892349126Srmacklem		    fsp->f_fstypename);
1893349126Srmacklem		return;
1894349126Srmacklem	}
1895349126Srmacklem
1896349126Srmacklem	/*
1897349126Srmacklem	 * We do not need to delete "export" flag from
1898349126Srmacklem	 * filesystems that do not have it set.
1899349126Srmacklem	 */
1900349126Srmacklem	if (!(fsp->f_flags & MNT_EXPORTED))
1901349126Srmacklem		return;
1902349126Srmacklem	/*
1903349126Srmacklem	 * Do not delete export for network filesystem by
1904349126Srmacklem	 * passing "export" arg to nmount().
1905349126Srmacklem	 * It only makes sense to do this for local filesystems.
1906349126Srmacklem	 */
1907349126Srmacklem	if (vfc.vfc_flags & VFCF_NETWORK)
1908349126Srmacklem		return;
1909349126Srmacklem
1910349126Srmacklem	iov[1].iov_base = fsp->f_fstypename;
1911349126Srmacklem	iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
1912349126Srmacklem	iov[3].iov_base = fsp->f_mntonname;
1913349126Srmacklem	iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
1914349126Srmacklem	iov[5].iov_base = fsp->f_mntfromname;
1915349126Srmacklem	iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
1916349126Srmacklem	errmsg[0] = '\0';
1917349126Srmacklem
1918349126Srmacklem	/*
1919349126Srmacklem	 * EXDEV is returned when path exists but is not a
1920349126Srmacklem	 * mount point.  May happens if raced with unmount.
1921349126Srmacklem	 */
1922349126Srmacklem	if (nmount(iov, iovlen, fsp->f_flags) < 0 && errno != ENOENT &&
1923349126Srmacklem	    errno != ENOTSUP && errno != EXDEV) {
1924349126Srmacklem		syslog(LOG_ERR,
1925349126Srmacklem		    "can't delete exports for %s: %m %s",
1926349126Srmacklem		    fsp->f_mntonname, errmsg);
1927349126Srmacklem	}
1928349126Srmacklem}
1929349126Srmacklem
1930349126Srmacklem/*
19311558Srgrimes * Allocate an export list element
19321558Srgrimes */
1933285128Straszstatic struct exportlist *
1934216587Scharnierget_exp(void)
19351558Srgrimes{
19361558Srgrimes	struct exportlist *ep;
19371558Srgrimes
1938224003Sdelphij	ep = (struct exportlist *)calloc(1, sizeof (struct exportlist));
19391558Srgrimes	if (ep == (struct exportlist *)NULL)
19401558Srgrimes		out_of_mem();
19411558Srgrimes	return (ep);
19421558Srgrimes}
19431558Srgrimes
19441558Srgrimes/*
19451558Srgrimes * Allocate a group list element
19461558Srgrimes */
1947285128Straszstatic struct grouplist *
1948216587Scharnierget_grp(void)
19491558Srgrimes{
19501558Srgrimes	struct grouplist *gp;
19511558Srgrimes
1952224003Sdelphij	gp = (struct grouplist *)calloc(1, sizeof (struct grouplist));
19531558Srgrimes	if (gp == (struct grouplist *)NULL)
19541558Srgrimes		out_of_mem();
19551558Srgrimes	return (gp);
19561558Srgrimes}
19571558Srgrimes
19581558Srgrimes/*
19591558Srgrimes * Clean up upon an error in get_exportlist().
19601558Srgrimes */
1961285128Straszstatic void
1962329392Sbrdgetexp_err(struct exportlist *ep, struct grouplist *grp, const char *reason)
19631558Srgrimes{
19641558Srgrimes	struct grouplist *tgrp;
19651558Srgrimes
1966329392Sbrd	if (!(opt_flags & OP_QUIET)) {
1967329392Sbrd		if (reason != NULL)
1968329392Sbrd			syslog(LOG_ERR, "bad exports list line '%s': %s", line,
1969329392Sbrd			    reason);
1970329392Sbrd		else
1971329392Sbrd			syslog(LOG_ERR, "bad exports list line '%s'", line);
1972329392Sbrd	}
19731558Srgrimes	if (ep && (ep->ex_flag & EX_LINKED) == 0)
19741558Srgrimes		free_exp(ep);
19751558Srgrimes	while (grp) {
19761558Srgrimes		tgrp = grp;
19771558Srgrimes		grp = grp->gr_next;
19781558Srgrimes		free_grp(tgrp);
19791558Srgrimes	}
19801558Srgrimes}
19811558Srgrimes
19821558Srgrimes/*
19831558Srgrimes * Search the export list for a matching fs.
19841558Srgrimes */
1985285128Straszstatic struct exportlist *
1986349124Srmacklemex_search(fsid_t *fsid, struct exportlisthead *exhp)
19871558Srgrimes{
19881558Srgrimes	struct exportlist *ep;
1989349756Srmacklem	uint32_t i;
19901558Srgrimes
1991349756Srmacklem	i = EXPHASH(fsid);
1992349756Srmacklem	SLIST_FOREACH(ep, &exhp[i], entries) {
19931558Srgrimes		if (ep->ex_fs.val[0] == fsid->val[0] &&
19941558Srgrimes		    ep->ex_fs.val[1] == fsid->val[1])
19951558Srgrimes			return (ep);
19961558Srgrimes	}
1997324955Smanu
19981558Srgrimes	return (ep);
19991558Srgrimes}
20001558Srgrimes
20011558Srgrimes/*
20021558Srgrimes * Add a directory path to the list.
20031558Srgrimes */
2004285128Straszstatic char *
2005216587Scharnieradd_expdir(struct dirlist **dpp, char *cp, int len)
20061558Srgrimes{
20071558Srgrimes	struct dirlist *dp;
20081558Srgrimes
2009324234Smanu	dp = malloc(sizeof (struct dirlist));
201037663Scharnier	if (dp == (struct dirlist *)NULL)
201137663Scharnier		out_of_mem();
20121558Srgrimes	dp->dp_left = *dpp;
20131558Srgrimes	dp->dp_right = (struct dirlist *)NULL;
20141558Srgrimes	dp->dp_flag = 0;
20151558Srgrimes	dp->dp_hosts = (struct hostlist *)NULL;
2016324234Smanu	dp->dp_dirp = strndup(cp, len);
2017324234Smanu	if (dp->dp_dirp == NULL)
2018324234Smanu		out_of_mem();
20191558Srgrimes	*dpp = dp;
20201558Srgrimes	return (dp->dp_dirp);
20211558Srgrimes}
20221558Srgrimes
20231558Srgrimes/*
20241558Srgrimes * Hang the dir list element off the dirpath binary tree as required
20251558Srgrimes * and update the entry for host.
20261558Srgrimes */
2027285128Straszstatic void
2028216587Scharnierhang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep,
2029216587Scharnier	int flags)
20301558Srgrimes{
20311558Srgrimes	struct hostlist *hp;
20321558Srgrimes	struct dirlist *dp2;
20331558Srgrimes
20349336Sdfr	if (flags & OP_ALLDIRS) {
20351558Srgrimes		if (ep->ex_defdir)
20361558Srgrimes			free((caddr_t)dp);
20371558Srgrimes		else
20381558Srgrimes			ep->ex_defdir = dp;
20399336Sdfr		if (grp == (struct grouplist *)NULL) {
20401558Srgrimes			ep->ex_defdir->dp_flag |= DP_DEFSET;
2041240902Srmacklem			/* Save the default security flavors list. */
2042240902Srmacklem			ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2043240902Srmacklem			if (ep->ex_numsecflavors > 0)
2044240902Srmacklem				memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2045240902Srmacklem				    sizeof(ep->ex_secflavors));
20469336Sdfr		} else while (grp) {
20471558Srgrimes			hp = get_ht();
20481558Srgrimes			hp->ht_grp = grp;
20491558Srgrimes			hp->ht_next = ep->ex_defdir->dp_hosts;
20501558Srgrimes			ep->ex_defdir->dp_hosts = hp;
2051240902Srmacklem			/* Save the security flavors list for this host set. */
2052240902Srmacklem			grp->gr_numsecflavors = ep->ex_numsecflavors;
2053240902Srmacklem			if (ep->ex_numsecflavors > 0)
2054240902Srmacklem				memcpy(grp->gr_secflavors, ep->ex_secflavors,
2055240902Srmacklem				    sizeof(ep->ex_secflavors));
20561558Srgrimes			grp = grp->gr_next;
20571558Srgrimes		}
20581558Srgrimes	} else {
20591558Srgrimes
20601558Srgrimes		/*
206137663Scharnier		 * Loop through the directories adding them to the tree.
20621558Srgrimes		 */
20631558Srgrimes		while (dp) {
20641558Srgrimes			dp2 = dp->dp_left;
2065240902Srmacklem			add_dlist(&ep->ex_dirl, dp, grp, flags, ep);
20661558Srgrimes			dp = dp2;
20671558Srgrimes		}
20681558Srgrimes	}
20691558Srgrimes}
20701558Srgrimes
20711558Srgrimes/*
20721558Srgrimes * Traverse the binary tree either updating a node that is already there
20731558Srgrimes * for the new directory or adding the new node.
20741558Srgrimes */
2075285128Straszstatic void
2076216587Scharnieradd_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp,
2077240902Srmacklem	int flags, struct exportlist *ep)
20781558Srgrimes{
20791558Srgrimes	struct dirlist *dp;
20801558Srgrimes	struct hostlist *hp;
20811558Srgrimes	int cmp;
20821558Srgrimes
20831558Srgrimes	dp = *dpp;
20841558Srgrimes	if (dp) {
20851558Srgrimes		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
20861558Srgrimes		if (cmp > 0) {
2087240902Srmacklem			add_dlist(&dp->dp_left, newdp, grp, flags, ep);
20881558Srgrimes			return;
20891558Srgrimes		} else if (cmp < 0) {
2090240902Srmacklem			add_dlist(&dp->dp_right, newdp, grp, flags, ep);
20911558Srgrimes			return;
20921558Srgrimes		} else
20931558Srgrimes			free((caddr_t)newdp);
20941558Srgrimes	} else {
20951558Srgrimes		dp = newdp;
20961558Srgrimes		dp->dp_left = (struct dirlist *)NULL;
20971558Srgrimes		*dpp = dp;
20981558Srgrimes	}
20991558Srgrimes	if (grp) {
21001558Srgrimes
21011558Srgrimes		/*
21021558Srgrimes		 * Hang all of the host(s) off of the directory point.
21031558Srgrimes		 */
21041558Srgrimes		do {
21051558Srgrimes			hp = get_ht();
21061558Srgrimes			hp->ht_grp = grp;
21071558Srgrimes			hp->ht_next = dp->dp_hosts;
21081558Srgrimes			dp->dp_hosts = hp;
2109240902Srmacklem			/* Save the security flavors list for this host set. */
2110240902Srmacklem			grp->gr_numsecflavors = ep->ex_numsecflavors;
2111240902Srmacklem			if (ep->ex_numsecflavors > 0)
2112240902Srmacklem				memcpy(grp->gr_secflavors, ep->ex_secflavors,
2113240902Srmacklem				    sizeof(ep->ex_secflavors));
21141558Srgrimes			grp = grp->gr_next;
21151558Srgrimes		} while (grp);
21169336Sdfr	} else {
21171558Srgrimes		dp->dp_flag |= DP_DEFSET;
2118240902Srmacklem		/* Save the default security flavors list. */
2119240902Srmacklem		ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2120240902Srmacklem		if (ep->ex_numsecflavors > 0)
2121240902Srmacklem			memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2122240902Srmacklem			    sizeof(ep->ex_secflavors));
21239336Sdfr	}
21241558Srgrimes}
21251558Srgrimes
21261558Srgrimes/*
21271558Srgrimes * Search for a dirpath on the export point.
21281558Srgrimes */
2129285128Straszstatic struct dirlist *
2130216587Scharnierdirp_search(struct dirlist *dp, char *dirp)
21311558Srgrimes{
21321558Srgrimes	int cmp;
21331558Srgrimes
21341558Srgrimes	if (dp) {
213574462Salfred		cmp = strcmp(dp->dp_dirp, dirp);
21361558Srgrimes		if (cmp > 0)
213774462Salfred			return (dirp_search(dp->dp_left, dirp));
21381558Srgrimes		else if (cmp < 0)
213974462Salfred			return (dirp_search(dp->dp_right, dirp));
21401558Srgrimes		else
21411558Srgrimes			return (dp);
21421558Srgrimes	}
21431558Srgrimes	return (dp);
21441558Srgrimes}
21451558Srgrimes
21461558Srgrimes/*
21471558Srgrimes * Scan for a host match in a directory tree.
21481558Srgrimes */
2149285128Straszstatic int
2150216587Scharnierchk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp,
2151240902Srmacklem	int *hostsetp, int *numsecflavors, int **secflavorsp)
21521558Srgrimes{
21531558Srgrimes	struct hostlist *hp;
21541558Srgrimes	struct grouplist *grp;
215574462Salfred	struct addrinfo *ai;
21561558Srgrimes
21571558Srgrimes	if (dp) {
21581558Srgrimes		if (dp->dp_flag & DP_DEFSET)
21599336Sdfr			*defsetp = dp->dp_flag;
21601558Srgrimes		hp = dp->dp_hosts;
21611558Srgrimes		while (hp) {
21621558Srgrimes			grp = hp->ht_grp;
21631558Srgrimes			switch (grp->gr_type) {
21641558Srgrimes			case GT_HOST:
216574462Salfred				ai = grp->gr_ptr.gt_addrinfo;
216674462Salfred				for (; ai; ai = ai->ai_next) {
216775801Siedowse					if (!sacmp(ai->ai_addr, saddr, NULL)) {
216874462Salfred						*hostsetp =
216974462Salfred						    (hp->ht_flag | DP_HOSTSET);
2170240902Srmacklem						if (numsecflavors != NULL) {
2171240902Srmacklem							*numsecflavors =
2172240902Srmacklem							    grp->gr_numsecflavors;
2173240902Srmacklem							*secflavorsp =
2174240902Srmacklem							    grp->gr_secflavors;
2175240902Srmacklem						}
217674462Salfred						return (1);
217774462Salfred					}
21789336Sdfr				}
217975801Siedowse				break;
21801558Srgrimes			case GT_NET:
218175801Siedowse				if (!sacmp(saddr, (struct sockaddr *)
218275801Siedowse				    &grp->gr_ptr.gt_net.nt_net,
218375801Siedowse				    (struct sockaddr *)
218475801Siedowse				    &grp->gr_ptr.gt_net.nt_mask)) {
218574462Salfred					*hostsetp = (hp->ht_flag | DP_HOSTSET);
2186240902Srmacklem					if (numsecflavors != NULL) {
2187240902Srmacklem						*numsecflavors =
2188240902Srmacklem						    grp->gr_numsecflavors;
2189240902Srmacklem						*secflavorsp =
2190240902Srmacklem						    grp->gr_secflavors;
2191240902Srmacklem					}
219274462Salfred					return (1);
219374462Salfred				}
219475801Siedowse				break;
219575801Siedowse			}
21961558Srgrimes			hp = hp->ht_next;
21971558Srgrimes		}
21981558Srgrimes	}
21991558Srgrimes	return (0);
22001558Srgrimes}
22011558Srgrimes
22021558Srgrimes/*
22031558Srgrimes * Scan tree for a host that matches the address.
22041558Srgrimes */
2205285128Straszstatic int
2206216587Scharnierscan_tree(struct dirlist *dp, struct sockaddr *saddr)
22071558Srgrimes{
22089336Sdfr	int defset, hostset;
22091558Srgrimes
22101558Srgrimes	if (dp) {
22111558Srgrimes		if (scan_tree(dp->dp_left, saddr))
22121558Srgrimes			return (1);
2213240902Srmacklem		if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL))
22141558Srgrimes			return (1);
22151558Srgrimes		if (scan_tree(dp->dp_right, saddr))
22161558Srgrimes			return (1);
22171558Srgrimes	}
22181558Srgrimes	return (0);
22191558Srgrimes}
22201558Srgrimes
22211558Srgrimes/*
22221558Srgrimes * Traverse the dirlist tree and free it up.
22231558Srgrimes */
2224285128Straszstatic void
2225216587Scharnierfree_dir(struct dirlist *dp)
22261558Srgrimes{
22271558Srgrimes
22281558Srgrimes	if (dp) {
22291558Srgrimes		free_dir(dp->dp_left);
22301558Srgrimes		free_dir(dp->dp_right);
22311558Srgrimes		free_host(dp->dp_hosts);
2232324234Smanu		free(dp->dp_dirp);
2233324234Smanu		free(dp);
22341558Srgrimes	}
22351558Srgrimes}
22361558Srgrimes
22371558Srgrimes/*
2238184588Sdfr * Parse a colon separated list of security flavors
2239184588Sdfr */
2240285128Straszstatic int
2241216587Scharnierparsesec(char *seclist, struct exportlist *ep)
2242184588Sdfr{
2243184588Sdfr	char *cp, savedc;
2244184588Sdfr	int flavor;
2245184588Sdfr
2246184588Sdfr	ep->ex_numsecflavors = 0;
2247184588Sdfr	for (;;) {
2248184588Sdfr		cp = strchr(seclist, ':');
2249184588Sdfr		if (cp) {
2250184588Sdfr			savedc = *cp;
2251184588Sdfr			*cp = '\0';
2252184588Sdfr		}
2253184588Sdfr
2254184588Sdfr		if (!strcmp(seclist, "sys"))
2255184588Sdfr			flavor = AUTH_SYS;
2256184588Sdfr		else if (!strcmp(seclist, "krb5"))
2257184588Sdfr			flavor = RPCSEC_GSS_KRB5;
2258184588Sdfr		else if (!strcmp(seclist, "krb5i"))
2259184588Sdfr			flavor = RPCSEC_GSS_KRB5I;
2260184588Sdfr		else if (!strcmp(seclist, "krb5p"))
2261184588Sdfr			flavor = RPCSEC_GSS_KRB5P;
2262184588Sdfr		else {
2263184588Sdfr			if (cp)
2264184588Sdfr				*cp = savedc;
2265184588Sdfr			syslog(LOG_ERR, "bad sec flavor: %s", seclist);
2266184588Sdfr			return (1);
2267184588Sdfr		}
2268184588Sdfr		if (ep->ex_numsecflavors == MAXSECFLAVORS) {
2269184588Sdfr			if (cp)
2270184588Sdfr				*cp = savedc;
2271184588Sdfr			syslog(LOG_ERR, "too many sec flavors: %s", seclist);
2272184588Sdfr			return (1);
2273184588Sdfr		}
2274184588Sdfr		ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
2275184588Sdfr		ep->ex_numsecflavors++;
2276184588Sdfr		if (cp) {
2277184588Sdfr			*cp = savedc;
2278184588Sdfr			seclist = cp + 1;
2279184588Sdfr		} else {
2280184588Sdfr			break;
2281184588Sdfr		}
2282184588Sdfr	}
2283184588Sdfr	return (0);
2284184588Sdfr}
2285184588Sdfr
2286184588Sdfr/*
22871558Srgrimes * Parse the option string and update fields.
22881558Srgrimes * Option arguments may either be -<option>=<value> or
22891558Srgrimes * -<option> <value>
22901558Srgrimes */
2291285128Straszstatic int
2292216587Scharnierdo_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
2293216587Scharnier	int *has_hostp, int *exflagsp, struct xucred *cr)
22941558Srgrimes{
22951558Srgrimes	char *cpoptarg, *cpoptend;
22961558Srgrimes	char *cp, *endcp, *cpopt, savedc, savedc2;
22971558Srgrimes	int allflag, usedarg;
22981558Srgrimes
229951968Salfred	savedc2 = '\0';
23001558Srgrimes	cpopt = *cpp;
23011558Srgrimes	cpopt++;
23021558Srgrimes	cp = *endcpp;
23031558Srgrimes	savedc = *cp;
23041558Srgrimes	*cp = '\0';
23051558Srgrimes	while (cpopt && *cpopt) {
23061558Srgrimes		allflag = 1;
23071558Srgrimes		usedarg = -2;
230837663Scharnier		if ((cpoptend = strchr(cpopt, ','))) {
23091558Srgrimes			*cpoptend++ = '\0';
231037663Scharnier			if ((cpoptarg = strchr(cpopt, '=')))
23111558Srgrimes				*cpoptarg++ = '\0';
23121558Srgrimes		} else {
231337663Scharnier			if ((cpoptarg = strchr(cpopt, '=')))
23141558Srgrimes				*cpoptarg++ = '\0';
23151558Srgrimes			else {
23161558Srgrimes				*cp = savedc;
23171558Srgrimes				nextfield(&cp, &endcp);
23181558Srgrimes				**endcpp = '\0';
23191558Srgrimes				if (endcp > cp && *cp != '-') {
23201558Srgrimes					cpoptarg = cp;
23211558Srgrimes					savedc2 = *endcp;
23221558Srgrimes					*endcp = '\0';
23231558Srgrimes					usedarg = 0;
23241558Srgrimes				}
23251558Srgrimes			}
23261558Srgrimes		}
23271558Srgrimes		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
23281558Srgrimes			*exflagsp |= MNT_EXRDONLY;
23291558Srgrimes		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
23301558Srgrimes		    !(allflag = strcmp(cpopt, "mapall")) ||
23311558Srgrimes		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
23321558Srgrimes			usedarg++;
23331558Srgrimes			parsecred(cpoptarg, cr);
23341558Srgrimes			if (allflag == 0) {
23351558Srgrimes				*exflagsp |= MNT_EXPORTANON;
23361558Srgrimes				opt_flags |= OP_MAPALL;
23371558Srgrimes			} else
23381558Srgrimes				opt_flags |= OP_MAPROOT;
23391558Srgrimes		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
234075801Siedowse		    !strcmp(cpopt, "m"))) {
23411558Srgrimes			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
234237663Scharnier				syslog(LOG_ERR, "bad mask: %s", cpoptarg);
23431558Srgrimes				return (1);
23441558Srgrimes			}
23451558Srgrimes			usedarg++;
23461558Srgrimes			opt_flags |= OP_MASK;
23471558Srgrimes		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
23481558Srgrimes			!strcmp(cpopt, "n"))) {
234974462Salfred			if (strchr(cpoptarg, '/') != NULL) {
235074462Salfred				if (debug)
235174462Salfred					fprintf(stderr, "setting OP_MASKLEN\n");
235274462Salfred				opt_flags |= OP_MASKLEN;
235374462Salfred			}
23541558Srgrimes			if (grp->gr_type != GT_NULL) {
235537663Scharnier				syslog(LOG_ERR, "network/host conflict");
23561558Srgrimes				return (1);
23571558Srgrimes			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
235837663Scharnier				syslog(LOG_ERR, "bad net: %s", cpoptarg);
23591558Srgrimes				return (1);
23601558Srgrimes			}
23611558Srgrimes			grp->gr_type = GT_NET;
23621558Srgrimes			*has_hostp = 1;
23631558Srgrimes			usedarg++;
23641558Srgrimes			opt_flags |= OP_NET;
23651558Srgrimes		} else if (!strcmp(cpopt, "alldirs")) {
23661558Srgrimes			opt_flags |= OP_ALLDIRS;
236727447Sdfr		} else if (!strcmp(cpopt, "public")) {
236827447Sdfr			*exflagsp |= MNT_EXPUBLIC;
236927447Sdfr		} else if (!strcmp(cpopt, "webnfs")) {
237027447Sdfr			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
237127447Sdfr			opt_flags |= OP_MAPALL;
237227447Sdfr		} else if (cpoptarg && !strcmp(cpopt, "index")) {
237327447Sdfr			ep->ex_indexfile = strdup(cpoptarg);
2374100336Sjoerg		} else if (!strcmp(cpopt, "quiet")) {
2375100336Sjoerg			opt_flags |= OP_QUIET;
2376247034Spluknet		} else if (cpoptarg && !strcmp(cpopt, "sec")) {
2377184588Sdfr			if (parsesec(cpoptarg, ep))
2378184588Sdfr				return (1);
2379184588Sdfr			opt_flags |= OP_SEC;
2380184588Sdfr			usedarg++;
23811558Srgrimes		} else {
238237663Scharnier			syslog(LOG_ERR, "bad opt %s", cpopt);
23831558Srgrimes			return (1);
23841558Srgrimes		}
23851558Srgrimes		if (usedarg >= 0) {
23861558Srgrimes			*endcp = savedc2;
23871558Srgrimes			**endcpp = savedc;
23881558Srgrimes			if (usedarg > 0) {
23891558Srgrimes				*cpp = cp;
23901558Srgrimes				*endcpp = endcp;
23911558Srgrimes			}
23921558Srgrimes			return (0);
23931558Srgrimes		}
23941558Srgrimes		cpopt = cpoptend;
23951558Srgrimes	}
23961558Srgrimes	**endcpp = savedc;
23971558Srgrimes	return (0);
23981558Srgrimes}
23991558Srgrimes
24001558Srgrimes/*
24011558Srgrimes * Translate a character string to the corresponding list of network
24021558Srgrimes * addresses for a hostname.
24031558Srgrimes */
2404285128Straszstatic int
2405216587Scharnierget_host(char *cp, struct grouplist *grp, struct grouplist *tgrp)
24061558Srgrimes{
24077401Swpaul	struct grouplist *checkgrp;
240875635Siedowse	struct addrinfo *ai, *tai, hints;
240974462Salfred	int ecode;
241074462Salfred	char host[NI_MAXHOST];
24111558Srgrimes
241274462Salfred	if (grp->gr_type != GT_NULL) {
241374462Salfred		syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
24141558Srgrimes		return (1);
24151558Srgrimes	}
241674462Salfred	memset(&hints, 0, sizeof hints);
241774462Salfred	hints.ai_flags = AI_CANONNAME;
241874462Salfred	hints.ai_protocol = IPPROTO_UDP;
241974462Salfred	ecode = getaddrinfo(cp, NULL, &hints, &ai);
242074462Salfred	if (ecode != 0) {
242175635Siedowse		syslog(LOG_ERR,"can't get address info for host %s", cp);
242274462Salfred		return 1;
242374462Salfred	}
242474462Salfred	grp->gr_ptr.gt_addrinfo = ai;
242574462Salfred	while (ai != NULL) {
242674462Salfred		if (ai->ai_canonname == NULL) {
242774462Salfred			if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
2428146187Sume			    sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
242974462Salfred				strlcpy(host, "?", sizeof(host));
243074462Salfred			ai->ai_canonname = strdup(host);
243174462Salfred			ai->ai_flags |= AI_CANONNAME;
243275641Siedowse		}
243374462Salfred		if (debug)
243475635Siedowse			fprintf(stderr, "got host %s\n", ai->ai_canonname);
243575635Siedowse		/*
243675635Siedowse		 * Sanity check: make sure we don't already have an entry
243775635Siedowse		 * for this host in the grouplist.
243875635Siedowse		 */
243975635Siedowse		for (checkgrp = tgrp; checkgrp != NULL;
244075635Siedowse		    checkgrp = checkgrp->gr_next) {
244175635Siedowse			if (checkgrp->gr_type != GT_HOST)
244275635Siedowse				continue;
244375635Siedowse			for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
244475635Siedowse			    tai = tai->ai_next) {
244575801Siedowse				if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
244675635Siedowse					continue;
244775635Siedowse				if (debug)
244875635Siedowse					fprintf(stderr,
244975635Siedowse					    "ignoring duplicate host %s\n",
245075635Siedowse					    ai->ai_canonname);
245175635Siedowse				grp->gr_type = GT_IGNORE;
245275635Siedowse				return (0);
245375635Siedowse			}
245475635Siedowse		}
245574462Salfred		ai = ai->ai_next;
24561558Srgrimes	}
245775635Siedowse	grp->gr_type = GT_HOST;
24581558Srgrimes	return (0);
24591558Srgrimes}
24601558Srgrimes
24611558Srgrimes/*
24621558Srgrimes * Free up an exports list component
24631558Srgrimes */
2464285128Straszstatic void
2465216587Scharnierfree_exp(struct exportlist *ep)
24661558Srgrimes{
2467349128Srmacklem	struct grouplist *grp, *tgrp;
24681558Srgrimes
24691558Srgrimes	if (ep->ex_defdir) {
24701558Srgrimes		free_host(ep->ex_defdir->dp_hosts);
24711558Srgrimes		free((caddr_t)ep->ex_defdir);
24721558Srgrimes	}
24731558Srgrimes	if (ep->ex_fsdir)
24741558Srgrimes		free(ep->ex_fsdir);
247527447Sdfr	if (ep->ex_indexfile)
247627447Sdfr		free(ep->ex_indexfile);
24771558Srgrimes	free_dir(ep->ex_dirl);
2478349128Srmacklem	grp = ep->ex_grphead;
2479349128Srmacklem	while (grp) {
2480349128Srmacklem		tgrp = grp;
2481349128Srmacklem		grp = grp->gr_next;
2482349128Srmacklem		free_grp(tgrp);
2483349128Srmacklem	}
24841558Srgrimes	free((caddr_t)ep);
24851558Srgrimes}
24861558Srgrimes
24871558Srgrimes/*
24881558Srgrimes * Free hosts.
24891558Srgrimes */
2490285128Straszstatic void
2491216587Scharnierfree_host(struct hostlist *hp)
24921558Srgrimes{
24931558Srgrimes	struct hostlist *hp2;
24941558Srgrimes
24951558Srgrimes	while (hp) {
24961558Srgrimes		hp2 = hp;
24971558Srgrimes		hp = hp->ht_next;
24981558Srgrimes		free((caddr_t)hp2);
24991558Srgrimes	}
25001558Srgrimes}
25011558Srgrimes
2502285128Straszstatic struct hostlist *
2503216587Scharnierget_ht(void)
25041558Srgrimes{
25051558Srgrimes	struct hostlist *hp;
25061558Srgrimes
25071558Srgrimes	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
25081558Srgrimes	if (hp == (struct hostlist *)NULL)
25091558Srgrimes		out_of_mem();
25101558Srgrimes	hp->ht_next = (struct hostlist *)NULL;
25119336Sdfr	hp->ht_flag = 0;
25121558Srgrimes	return (hp);
25131558Srgrimes}
25141558Srgrimes
25151558Srgrimes/*
25161558Srgrimes * Out of memory, fatal
25171558Srgrimes */
2518285128Straszstatic void
2519216587Scharnierout_of_mem(void)
25201558Srgrimes{
25211558Srgrimes
252237663Scharnier	syslog(LOG_ERR, "out of memory");
25231558Srgrimes	exit(2);
25241558Srgrimes}
25251558Srgrimes
25261558Srgrimes/*
2527158857Srodrigc * Do the nmount() syscall with the update flag to push the export info into
25281558Srgrimes * the kernel.
25291558Srgrimes */
2530285128Straszstatic int
2531158857Srodrigcdo_mount(struct exportlist *ep, struct grouplist *grp, int exflags,
2532158857Srodrigc    struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb)
25331558Srgrimes{
253475841Siedowse	struct statfs fsb1;
253574462Salfred	struct addrinfo *ai;
2536282214Strasz	struct export_args *eap;
2537158857Srodrigc	char errmsg[255];
2538158857Srodrigc	char *cp;
25391558Srgrimes	int done;
2540158857Srodrigc	char savedc;
2541158857Srodrigc	struct iovec *iov;
2542184588Sdfr	int i, iovlen;
2543158857Srodrigc	int ret;
2544192934Srmacklem	struct nfsex_args nfsea;
25451558Srgrimes
2546282214Strasz	eap = &nfsea.export;
2547192934Srmacklem
2548158857Srodrigc	cp = NULL;
2549158857Srodrigc	savedc = '\0';
2550158857Srodrigc	iov = NULL;
2551158857Srodrigc	iovlen = 0;
2552158857Srodrigc	ret = 0;
255375801Siedowse
2554192934Srmacklem	bzero(eap, sizeof (struct export_args));
2555158857Srodrigc	bzero(errmsg, sizeof(errmsg));
2556192934Srmacklem	eap->ex_flags = exflags;
2557192934Srmacklem	eap->ex_anon = *anoncrp;
2558192934Srmacklem	eap->ex_indexfile = ep->ex_indexfile;
255975641Siedowse	if (grp->gr_type == GT_HOST)
256074462Salfred		ai = grp->gr_ptr.gt_addrinfo;
256175641Siedowse	else
256275641Siedowse		ai = NULL;
2563192934Srmacklem	eap->ex_numsecflavors = ep->ex_numsecflavors;
2564192934Srmacklem	for (i = 0; i < eap->ex_numsecflavors; i++)
2565192934Srmacklem		eap->ex_secflavors[i] = ep->ex_secflavors[i];
2566192934Srmacklem	if (eap->ex_numsecflavors == 0) {
2567192934Srmacklem		eap->ex_numsecflavors = 1;
2568192934Srmacklem		eap->ex_secflavors[0] = AUTH_SYS;
2569184588Sdfr	}
25701558Srgrimes	done = FALSE;
2571158857Srodrigc
2572192934Srmacklem	if (v4root_phase == 0) {
2573192934Srmacklem		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
2574192934Srmacklem		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
2575192934Srmacklem		build_iovec(&iov, &iovlen, "from", NULL, 0);
2576192934Srmacklem		build_iovec(&iov, &iovlen, "update", NULL, 0);
2577192934Srmacklem		build_iovec(&iov, &iovlen, "export", eap,
2578192934Srmacklem		    sizeof (struct export_args));
2579192934Srmacklem		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
2580192934Srmacklem	}
2581158857Srodrigc
25821558Srgrimes	while (!done) {
25831558Srgrimes		switch (grp->gr_type) {
25841558Srgrimes		case GT_HOST:
258575641Siedowse			if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
258674462Salfred				goto skip;
2587192934Srmacklem			eap->ex_addr = ai->ai_addr;
2588192934Srmacklem			eap->ex_addrlen = ai->ai_addrlen;
2589192934Srmacklem			eap->ex_masklen = 0;
25901558Srgrimes			break;
25911558Srgrimes		case GT_NET:
259275801Siedowse			if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
259374462Salfred			    have_v6 == 0)
259474462Salfred				goto skip;
2595192934Srmacklem			eap->ex_addr =
259675801Siedowse			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
2597192934Srmacklem			eap->ex_addrlen =
2598158857Srodrigc			    ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
2599192934Srmacklem			eap->ex_mask =
260075801Siedowse			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
2601192934Srmacklem			eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
26021558Srgrimes			break;
260375641Siedowse		case GT_DEFAULT:
2604192934Srmacklem			eap->ex_addr = NULL;
2605192934Srmacklem			eap->ex_addrlen = 0;
2606192934Srmacklem			eap->ex_mask = NULL;
2607192934Srmacklem			eap->ex_masklen = 0;
260875641Siedowse			break;
26097401Swpaul		case GT_IGNORE:
2610158857Srodrigc			ret = 0;
2611158857Srodrigc			goto error_exit;
26127401Swpaul			break;
26131558Srgrimes		default:
261437663Scharnier			syslog(LOG_ERR, "bad grouptype");
26151558Srgrimes			if (cp)
26161558Srgrimes				*cp = savedc;
2617158857Srodrigc			ret = 1;
2618158857Srodrigc			goto error_exit;
2619298089Spfg		}
26201558Srgrimes
26211558Srgrimes		/*
2622192934Srmacklem		 * For V4:, use the nfssvc() syscall, instead of mount().
26231558Srgrimes		 */
2624192934Srmacklem		if (v4root_phase == 2) {
2625192934Srmacklem			nfsea.fspec = v4root_dirpath;
2626282214Strasz			if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&nfsea) < 0) {
2627192934Srmacklem				syslog(LOG_ERR, "Exporting V4: failed");
2628192934Srmacklem				return (2);
2629158857Srodrigc			}
2630192934Srmacklem		} else {
2631192934Srmacklem			/*
2632192934Srmacklem			 * XXX:
2633192934Srmacklem			 * Maybe I should just use the fsb->f_mntonname path
2634192934Srmacklem			 * instead of looping back up the dirp to the mount
2635192934Srmacklem			 * point??
2636192934Srmacklem			 * Also, needs to know how to export all types of local
2637192934Srmacklem			 * exportable filesystems and not just "ufs".
2638192934Srmacklem			 */
2639192934Srmacklem			iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
2640192934Srmacklem			iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
2641192934Srmacklem			iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
2642192934Srmacklem			iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
2643192934Srmacklem			iov[5].iov_base = fsb->f_mntfromname; /* "from" */
2644192934Srmacklem			iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
2645270183Sbdrewery			errmsg[0] = '\0';
2646192934Srmacklem
2647192934Srmacklem			while (nmount(iov, iovlen, fsb->f_flags) < 0) {
2648192934Srmacklem				if (cp)
2649192934Srmacklem					*cp-- = savedc;
2650192934Srmacklem				else
2651192934Srmacklem					cp = dirp + dirplen - 1;
2652192934Srmacklem				if (opt_flags & OP_QUIET) {
2653192934Srmacklem					ret = 1;
2654192934Srmacklem					goto error_exit;
2655192934Srmacklem				}
2656192934Srmacklem				if (errno == EPERM) {
2657192934Srmacklem					if (debug)
2658239744Sdelphij						warnx("can't change attributes for %s: %s",
2659239744Sdelphij						    dirp, errmsg);
2660192934Srmacklem					syslog(LOG_ERR,
2661239744Sdelphij					   "can't change attributes for %s: %s",
2662239744Sdelphij					    dirp, errmsg);
2663192934Srmacklem					ret = 1;
2664192934Srmacklem					goto error_exit;
2665192934Srmacklem				}
2666192934Srmacklem				if (opt_flags & OP_ALLDIRS) {
2667192934Srmacklem					if (errno == EINVAL)
2668192934Srmacklem						syslog(LOG_ERR,
2669100336Sjoerg		"-alldirs requested but %s is not a filesystem mountpoint",
2670192934Srmacklem						    dirp);
2671192934Srmacklem					else
2672192934Srmacklem						syslog(LOG_ERR,
2673192934Srmacklem						    "could not remount %s: %m",
2674192934Srmacklem						    dirp);
2675192934Srmacklem					ret = 1;
2676192934Srmacklem					goto error_exit;
2677192934Srmacklem				}
2678192934Srmacklem				/* back up over the last component */
2679192934Srmacklem				while (*cp == '/' && cp > dirp)
2680192934Srmacklem					cp--;
2681192934Srmacklem				while (*(cp - 1) != '/' && cp > dirp)
2682192934Srmacklem					cp--;
2683192934Srmacklem				if (cp == dirp) {
2684192934Srmacklem					if (debug)
2685192934Srmacklem						warnx("mnt unsucc");
2686192934Srmacklem					syslog(LOG_ERR, "can't export %s %s",
2687192934Srmacklem					    dirp, errmsg);
2688192934Srmacklem					ret = 1;
2689192934Srmacklem					goto error_exit;
2690192934Srmacklem				}
2691192934Srmacklem				savedc = *cp;
2692192934Srmacklem				*cp = '\0';
2693192934Srmacklem				/*
2694192934Srmacklem				 * Check that we're still on the same
2695192934Srmacklem				 * filesystem.
2696192934Srmacklem				 */
2697192934Srmacklem				if (statfs(dirp, &fsb1) != 0 ||
2698192934Srmacklem				    bcmp(&fsb1.f_fsid, &fsb->f_fsid,
2699192934Srmacklem				    sizeof (fsb1.f_fsid)) != 0) {
2700192934Srmacklem					*cp = savedc;
2701100336Sjoerg					syslog(LOG_ERR,
2702192934Srmacklem					    "can't export %s %s", dirp,
2703192934Srmacklem					    errmsg);
2704192934Srmacklem					ret = 1;
2705192934Srmacklem					goto error_exit;
2706192934Srmacklem				}
27071558Srgrimes			}
27081558Srgrimes		}
2709192934Srmacklem
2710192934Srmacklem		/*
2711192934Srmacklem		 * For the experimental server:
2712192934Srmacklem		 * If this is the public directory, get the file handle
2713192934Srmacklem		 * and load it into the kernel via the nfssvc() syscall.
2714192934Srmacklem		 */
2715282214Strasz		if ((exflags & MNT_EXPUBLIC) != 0) {
2716192934Srmacklem			fhandle_t fh;
2717192934Srmacklem			char *public_name;
2718192934Srmacklem
2719192934Srmacklem			if (eap->ex_indexfile != NULL)
2720192934Srmacklem				public_name = eap->ex_indexfile;
2721192934Srmacklem			else
2722192934Srmacklem				public_name = dirp;
2723192934Srmacklem			if (getfh(public_name, &fh) < 0)
2724192934Srmacklem				syslog(LOG_ERR,
2725192934Srmacklem				    "Can't get public fh for %s", public_name);
2726192934Srmacklem			else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0)
2727192934Srmacklem				syslog(LOG_ERR,
2728192934Srmacklem				    "Can't set public fh for %s", public_name);
2729192934Srmacklem			else
2730192934Srmacklem				has_publicfh = 1;
2731192934Srmacklem		}
273274462Salfredskip:
273375641Siedowse		if (ai != NULL)
273474462Salfred			ai = ai->ai_next;
273575641Siedowse		if (ai == NULL)
27361558Srgrimes			done = TRUE;
27371558Srgrimes	}
27381558Srgrimes	if (cp)
27391558Srgrimes		*cp = savedc;
2740158857Srodrigcerror_exit:
2741158857Srodrigc	/* free strings allocated by strdup() in getmntopts.c */
2742158857Srodrigc	if (iov != NULL) {
2743158857Srodrigc		free(iov[0].iov_base); /* fstype */
2744158857Srodrigc		free(iov[2].iov_base); /* fspath */
2745158857Srodrigc		free(iov[4].iov_base); /* from */
2746158857Srodrigc		free(iov[6].iov_base); /* update */
2747158857Srodrigc		free(iov[8].iov_base); /* export */
2748158857Srodrigc		free(iov[10].iov_base); /* errmsg */
2749158857Srodrigc
2750158857Srodrigc		/* free iov, allocated by realloc() */
2751158857Srodrigc		free(iov);
2752158857Srodrigc	}
2753158857Srodrigc	return (ret);
27541558Srgrimes}
27551558Srgrimes
27561558Srgrimes/*
27571558Srgrimes * Translate a net address.
275875801Siedowse *
275975801Siedowse * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
27601558Srgrimes */
2761285128Straszstatic int
2762216587Scharnierget_net(char *cp, struct netmsk *net, int maskflg)
27631558Srgrimes{
276475861Siedowse	struct netent *np = NULL;
276574462Salfred	char *name, *p, *prefp;
276675801Siedowse	struct sockaddr_in sin;
276775861Siedowse	struct sockaddr *sa = NULL;
276874462Salfred	struct addrinfo hints, *ai = NULL;
276974462Salfred	char netname[NI_MAXHOST];
277074462Salfred	long preflen;
27711558Srgrimes
277275635Siedowse	p = prefp = NULL;
277374462Salfred	if ((opt_flags & OP_MASKLEN) && !maskflg) {
277474462Salfred		p = strchr(cp, '/');
277574462Salfred		*p = '\0';
277674462Salfred		prefp = p + 1;
277774462Salfred	}
277874462Salfred
277975861Siedowse	/*
278075861Siedowse	 * Check for a numeric address first. We wish to avoid
278175861Siedowse	 * possible DNS lookups in getnetbyname().
278275861Siedowse	 */
278375861Siedowse	if (isxdigit(*cp) || *cp == ':') {
278474462Salfred		memset(&hints, 0, sizeof hints);
278575801Siedowse		/* Ensure the mask and the network have the same family. */
278675801Siedowse		if (maskflg && (opt_flags & OP_NET))
278775801Siedowse			hints.ai_family = net->nt_net.ss_family;
278875801Siedowse		else if (!maskflg && (opt_flags & OP_HAVEMASK))
278975801Siedowse			hints.ai_family = net->nt_mask.ss_family;
279075801Siedowse		else
279175801Siedowse			hints.ai_family = AF_UNSPEC;
279274462Salfred		hints.ai_flags = AI_NUMERICHOST;
279375861Siedowse		if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
279475861Siedowse			sa = ai->ai_addr;
279575861Siedowse		if (sa != NULL && ai->ai_family == AF_INET) {
279674462Salfred			/*
279775801Siedowse			 * The address in `cp' is really a network address, so
279875801Siedowse			 * use inet_network() to re-interpret this correctly.
279975801Siedowse			 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
280074462Salfred			 */
280175801Siedowse			bzero(&sin, sizeof sin);
280274462Salfred			sin.sin_family = AF_INET;
280374462Salfred			sin.sin_len = sizeof sin;
280475801Siedowse			sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
280574462Salfred			if (debug)
280675801Siedowse				fprintf(stderr, "get_net: v4 addr %s\n",
280775801Siedowse				    inet_ntoa(sin.sin_addr));
280874462Salfred			sa = (struct sockaddr *)&sin;
280975861Siedowse		}
281075861Siedowse	}
281175861Siedowse	if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
281275861Siedowse		bzero(&sin, sizeof sin);
281375861Siedowse		sin.sin_family = AF_INET;
281475861Siedowse		sin.sin_len = sizeof sin;
281575861Siedowse		sin.sin_addr = inet_makeaddr(np->n_net, 0);
281675861Siedowse		sa = (struct sockaddr *)&sin;
281775861Siedowse	}
281875861Siedowse	if (sa == NULL)
281974462Salfred		goto fail;
282025318Spst
282175801Siedowse	if (maskflg) {
282275801Siedowse		/* The specified sockaddr is a mask. */
282375801Siedowse		if (checkmask(sa) != 0)
282475801Siedowse			goto fail;
282575801Siedowse		bcopy(sa, &net->nt_mask, sa->sa_len);
282675801Siedowse		opt_flags |= OP_HAVEMASK;
282775801Siedowse	} else {
282875801Siedowse		/* The specified sockaddr is a network address. */
282975801Siedowse		bcopy(sa, &net->nt_net, sa->sa_len);
283074462Salfred
283175801Siedowse		/* Get a network name for the export list. */
283275801Siedowse		if (np) {
283375801Siedowse			name = np->n_name;
283475801Siedowse		} else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2835146187Sume		   NULL, 0, NI_NUMERICHOST) == 0) {
283675801Siedowse			name = netname;
283775801Siedowse		} else {
283875801Siedowse			goto fail;
283975801Siedowse		}
284075801Siedowse		if ((net->nt_name = strdup(name)) == NULL)
284175801Siedowse			out_of_mem();
284275801Siedowse
284375801Siedowse		/*
284475801Siedowse		 * Extract a mask from either a "/<masklen>" suffix, or
284575801Siedowse		 * from the class of an IPv4 address.
284675801Siedowse		 */
284774462Salfred		if (opt_flags & OP_MASKLEN) {
284874462Salfred			preflen = strtol(prefp, NULL, 10);
284975801Siedowse			if (preflen < 0L || preflen == LONG_MAX)
285074462Salfred				goto fail;
285175801Siedowse			bcopy(sa, &net->nt_mask, sa->sa_len);
285275801Siedowse			if (makemask(&net->nt_mask, (int)preflen) != 0)
285375801Siedowse				goto fail;
285475801Siedowse			opt_flags |= OP_HAVEMASK;
285574462Salfred			*p = '/';
285675801Siedowse		} else if (sa->sa_family == AF_INET &&
285775801Siedowse		    (opt_flags & OP_MASK) == 0) {
285875801Siedowse			in_addr_t addr;
285974462Salfred
286075801Siedowse			addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
286175801Siedowse			if (IN_CLASSA(addr))
286275801Siedowse				preflen = 8;
286375801Siedowse			else if (IN_CLASSB(addr))
286475801Siedowse				preflen = 16;
286575801Siedowse			else if (IN_CLASSC(addr))
286675801Siedowse				preflen = 24;
286775801Siedowse			else if (IN_CLASSD(addr))
286875801Siedowse				preflen = 28;
286975801Siedowse			else
287075801Siedowse				preflen = 32;	/* XXX */
287175801Siedowse
287275801Siedowse			bcopy(sa, &net->nt_mask, sa->sa_len);
287375801Siedowse			makemask(&net->nt_mask, (int)preflen);
287475801Siedowse			opt_flags |= OP_HAVEMASK;
287574462Salfred		}
287674462Salfred	}
287774462Salfred
287874462Salfred	if (ai)
287974462Salfred		freeaddrinfo(ai);
288074462Salfred	return 0;
288174462Salfred
288274462Salfredfail:
288374462Salfred	if (ai)
288474462Salfred		freeaddrinfo(ai);
288574462Salfred	return 1;
28861558Srgrimes}
28871558Srgrimes
28881558Srgrimes/*
28891558Srgrimes * Parse out the next white space separated field
28901558Srgrimes */
2891285128Straszstatic void
2892216587Scharniernextfield(char **cp, char **endcp)
28931558Srgrimes{
28941558Srgrimes	char *p;
2895347341Smav	char quot = 0;
28961558Srgrimes
28971558Srgrimes	p = *cp;
28981558Srgrimes	while (*p == ' ' || *p == '\t')
28991558Srgrimes		p++;
2900347341Smav	*cp = p;
2901347341Smav	while (*p != '\0') {
2902347341Smav		if (quot) {
2903347341Smav			if (*p == quot)
2904347341Smav				quot = 0;
2905347341Smav		} else {
2906347341Smav			if (*p == '\\' && *(p + 1) != '\0')
2907347341Smav				p++;
2908347341Smav			else if (*p == '\'' || *p == '"')
2909347341Smav				quot = *p;
2910347341Smav			else if (*p == ' ' || *p == '\t')
2911347341Smav				break;
2912347341Smav		}
2913347341Smav		p++;
2914347341Smav	};
2915347341Smav	*endcp = p;
29161558Srgrimes}
29171558Srgrimes
29181558Srgrimes/*
29191558Srgrimes * Get an exports file line. Skip over blank lines and handle line
29201558Srgrimes * continuations.
29211558Srgrimes */
2922285128Straszstatic int
2923216587Scharnierget_line(void)
29241558Srgrimes{
29251558Srgrimes	char *p, *cp;
292696622Siedowse	size_t len;
29271558Srgrimes	int totlen, cont_line;
29281558Srgrimes
29291558Srgrimes	/*
29301558Srgrimes	 * Loop around ignoring blank lines and getting all continuation lines.
29311558Srgrimes	 */
29321558Srgrimes	p = line;
29331558Srgrimes	totlen = 0;
29341558Srgrimes	do {
293596622Siedowse		if ((p = fgetln(exp_file, &len)) == NULL)
29361558Srgrimes			return (0);
29371558Srgrimes		cp = p + len - 1;
29381558Srgrimes		cont_line = 0;
29391558Srgrimes		while (cp >= p &&
29401558Srgrimes		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
29411558Srgrimes			if (*cp == '\\')
29421558Srgrimes				cont_line = 1;
29431558Srgrimes			cp--;
29441558Srgrimes			len--;
29451558Srgrimes		}
294679117Sdd		if (cont_line) {
294779117Sdd			*++cp = ' ';
294879117Sdd			len++;
294979117Sdd		}
295096622Siedowse		if (linesize < len + totlen + 1) {
295196622Siedowse			linesize = len + totlen + 1;
295296622Siedowse			line = realloc(line, linesize);
295396622Siedowse			if (line == NULL)
295496622Siedowse				out_of_mem();
29551558Srgrimes		}
295696622Siedowse		memcpy(line + totlen, p, len);
295796622Siedowse		totlen += len;
295896622Siedowse		line[totlen] = '\0';
29591558Srgrimes	} while (totlen == 0 || cont_line);
29601558Srgrimes	return (1);
29611558Srgrimes}
29621558Srgrimes
29631558Srgrimes/*
29641558Srgrimes * Parse a description of a credential.
29651558Srgrimes */
2966285128Straszstatic void
2967216587Scharnierparsecred(char *namelist, struct xucred *cr)
29681558Srgrimes{
29691558Srgrimes	char *name;
29701558Srgrimes	int cnt;
29711558Srgrimes	char *names;
29721558Srgrimes	struct passwd *pw;
29731558Srgrimes	struct group *gr;
2974194498Sbrooks	gid_t groups[XU_NGROUPS + 1];
2975136051Sstefanf	int ngroups;
29761558Srgrimes
297791354Sdd	cr->cr_version = XUCRED_VERSION;
29781558Srgrimes	/*
297937663Scharnier	 * Set up the unprivileged user.
29801558Srgrimes	 */
29811558Srgrimes	cr->cr_uid = -2;
29821558Srgrimes	cr->cr_groups[0] = -2;
29831558Srgrimes	cr->cr_ngroups = 1;
29841558Srgrimes	/*
29851558Srgrimes	 * Get the user's password table entry.
29861558Srgrimes	 */
2987347341Smav	names = namelist;
2988347341Smav	name = strsep_quote(&names, ":");
2989293305Sjpaetzel	/* Bug?  name could be NULL here */
29901558Srgrimes	if (isdigit(*name) || *name == '-')
29911558Srgrimes		pw = getpwuid(atoi(name));
29921558Srgrimes	else
29931558Srgrimes		pw = getpwnam(name);
29941558Srgrimes	/*
29951558Srgrimes	 * Credentials specified as those of a user.
29961558Srgrimes	 */
29971558Srgrimes	if (names == NULL) {
29981558Srgrimes		if (pw == NULL) {
299937663Scharnier			syslog(LOG_ERR, "unknown user: %s", name);
30001558Srgrimes			return;
30011558Srgrimes		}
30021558Srgrimes		cr->cr_uid = pw->pw_uid;
3003194498Sbrooks		ngroups = XU_NGROUPS + 1;
3004333197Savg		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) {
300537663Scharnier			syslog(LOG_ERR, "too many groups");
3006333197Savg			ngroups = XU_NGROUPS + 1;
3007333197Savg		}
3008333197Savg
30091558Srgrimes		/*
3010136051Sstefanf		 * Compress out duplicate.
30111558Srgrimes		 */
30121558Srgrimes		cr->cr_ngroups = ngroups - 1;
30131558Srgrimes		cr->cr_groups[0] = groups[0];
30141558Srgrimes		for (cnt = 2; cnt < ngroups; cnt++)
30151558Srgrimes			cr->cr_groups[cnt - 1] = groups[cnt];
30161558Srgrimes		return;
30171558Srgrimes	}
30181558Srgrimes	/*
30191558Srgrimes	 * Explicit credential specified as a colon separated list:
30201558Srgrimes	 *	uid:gid:gid:...
30211558Srgrimes	 */
30221558Srgrimes	if (pw != NULL)
30231558Srgrimes		cr->cr_uid = pw->pw_uid;
30241558Srgrimes	else if (isdigit(*name) || *name == '-')
30251558Srgrimes		cr->cr_uid = atoi(name);
30261558Srgrimes	else {
302737663Scharnier		syslog(LOG_ERR, "unknown user: %s", name);
30281558Srgrimes		return;
30291558Srgrimes	}
30301558Srgrimes	cr->cr_ngroups = 0;
3031194498Sbrooks	while (names != NULL && *names != '\0' && cr->cr_ngroups < XU_NGROUPS) {
3032347341Smav		name = strsep_quote(&names, ":");
30331558Srgrimes		if (isdigit(*name) || *name == '-') {
30341558Srgrimes			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
30351558Srgrimes		} else {
30361558Srgrimes			if ((gr = getgrnam(name)) == NULL) {
303737663Scharnier				syslog(LOG_ERR, "unknown group: %s", name);
30381558Srgrimes				continue;
30391558Srgrimes			}
30401558Srgrimes			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
30411558Srgrimes		}
30421558Srgrimes	}
3043194498Sbrooks	if (names != NULL && *names != '\0' && cr->cr_ngroups == XU_NGROUPS)
304437663Scharnier		syslog(LOG_ERR, "too many groups");
30451558Srgrimes}
30461558Srgrimes
3047194880Sdfr#define	STRSIZ	(MNTNAMLEN+MNTPATHLEN+50)
30481558Srgrimes/*
30491558Srgrimes * Routines that maintain the remote mounttab
30501558Srgrimes */
3051285128Straszstatic void
3052216587Scharnierget_mountlist(void)
30531558Srgrimes{
3054324955Smanu	struct mountlist *mlp;
305523681Speter	char *host, *dirp, *cp;
30561558Srgrimes	char str[STRSIZ];
30571558Srgrimes	FILE *mlfile;
30581558Srgrimes
30591558Srgrimes	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
306053117Sbillf		if (errno == ENOENT)
306153117Sbillf			return;
306253117Sbillf		else {
306353117Sbillf			syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
306453117Sbillf			return;
306553117Sbillf		}
30661558Srgrimes	}
30671558Srgrimes	while (fgets(str, STRSIZ, mlfile) != NULL) {
306823681Speter		cp = str;
306923681Speter		host = strsep(&cp, " \t\n");
307023681Speter		dirp = strsep(&cp, " \t\n");
307123681Speter		if (host == NULL || dirp == NULL)
30721558Srgrimes			continue;
30731558Srgrimes		mlp = (struct mountlist *)malloc(sizeof (*mlp));
307437663Scharnier		if (mlp == (struct mountlist *)NULL)
307537663Scharnier			out_of_mem();
3076194880Sdfr		strncpy(mlp->ml_host, host, MNTNAMLEN);
3077194880Sdfr		mlp->ml_host[MNTNAMLEN] = '\0';
3078194880Sdfr		strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3079194880Sdfr		mlp->ml_dirp[MNTPATHLEN] = '\0';
3080324955Smanu
3081324955Smanu		SLIST_INSERT_HEAD(&mlhead, mlp, next);
30821558Srgrimes	}
30831558Srgrimes	fclose(mlfile);
30841558Srgrimes}
30851558Srgrimes
3086285128Straszstatic void
308775635Siedowsedel_mlist(char *hostp, char *dirp)
30881558Srgrimes{
3089324955Smanu	struct mountlist *mlp, *mlp2;
30901558Srgrimes	FILE *mlfile;
30911558Srgrimes	int fnd = 0;
30921558Srgrimes
3093324955Smanu	SLIST_FOREACH_SAFE(mlp, &mlhead, next, mlp2) {
30941558Srgrimes		if (!strcmp(mlp->ml_host, hostp) &&
30951558Srgrimes		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
30961558Srgrimes			fnd = 1;
3097324955Smanu			SLIST_REMOVE(&mlhead, mlp, mountlist, next);
3098324955Smanu			free((caddr_t)mlp);
30991558Srgrimes		}
31001558Srgrimes	}
31011558Srgrimes	if (fnd) {
31021558Srgrimes		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
310337663Scharnier			syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
31041558Srgrimes			return;
31051558Srgrimes		}
3106324955Smanu		SLIST_FOREACH(mlp, &mlhead, next) {
31071558Srgrimes			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
31081558Srgrimes		}
31091558Srgrimes		fclose(mlfile);
31101558Srgrimes	}
31111558Srgrimes}
31121558Srgrimes
3113285128Straszstatic void
3114216587Scharnieradd_mlist(char *hostp, char *dirp)
31151558Srgrimes{
3116324955Smanu	struct mountlist *mlp;
31171558Srgrimes	FILE *mlfile;
31181558Srgrimes
3119324955Smanu	SLIST_FOREACH(mlp, &mlhead, next) {
31201558Srgrimes		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
31211558Srgrimes			return;
31221558Srgrimes	}
3123324955Smanu
31241558Srgrimes	mlp = (struct mountlist *)malloc(sizeof (*mlp));
312537663Scharnier	if (mlp == (struct mountlist *)NULL)
312637663Scharnier		out_of_mem();
3127194880Sdfr	strncpy(mlp->ml_host, hostp, MNTNAMLEN);
3128194880Sdfr	mlp->ml_host[MNTNAMLEN] = '\0';
3129194880Sdfr	strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3130194880Sdfr	mlp->ml_dirp[MNTPATHLEN] = '\0';
3131324955Smanu	SLIST_INSERT_HEAD(&mlhead, mlp, next);
31321558Srgrimes	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
313337663Scharnier		syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
31341558Srgrimes		return;
31351558Srgrimes	}
31361558Srgrimes	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
31371558Srgrimes	fclose(mlfile);
31381558Srgrimes}
31391558Srgrimes
31401558Srgrimes/*
31411558Srgrimes * Free up a group list.
31421558Srgrimes */
3143285128Straszstatic void
3144216587Scharnierfree_grp(struct grouplist *grp)
31451558Srgrimes{
31461558Srgrimes	if (grp->gr_type == GT_HOST) {
314774462Salfred		if (grp->gr_ptr.gt_addrinfo != NULL)
314874462Salfred			freeaddrinfo(grp->gr_ptr.gt_addrinfo);
31491558Srgrimes	} else if (grp->gr_type == GT_NET) {
31501558Srgrimes		if (grp->gr_ptr.gt_net.nt_name)
31511558Srgrimes			free(grp->gr_ptr.gt_net.nt_name);
31521558Srgrimes	}
31531558Srgrimes	free((caddr_t)grp);
31541558Srgrimes}
31551558Srgrimes
31561558Srgrimes#ifdef DEBUG
3157285128Straszstatic void
31581558SrgrimesSYSLOG(int pri, const char *fmt, ...)
31591558Srgrimes{
31601558Srgrimes	va_list ap;
31611558Srgrimes
31621558Srgrimes	va_start(ap, fmt);
31631558Srgrimes	vfprintf(stderr, fmt, ap);
31641558Srgrimes	va_end(ap);
31651558Srgrimes}
31661558Srgrimes#endif /* DEBUG */
31671558Srgrimes
31681558Srgrimes/*
31691558Srgrimes * Check options for consistency.
31701558Srgrimes */
3171285128Straszstatic int
3172216587Scharniercheck_options(struct dirlist *dp)
31731558Srgrimes{
31741558Srgrimes
3175192934Srmacklem	if (v4root_phase == 0 && dp == NULL)
31761558Srgrimes	    return (1);
317783653Speter	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
317883653Speter	    syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
31791558Srgrimes	    return (1);
31801558Srgrimes	}
31811558Srgrimes	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
318275801Siedowse		syslog(LOG_ERR, "-mask requires -network");
318375801Siedowse		return (1);
31841558Srgrimes	}
318575801Siedowse	if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
318675801Siedowse		syslog(LOG_ERR, "-network requires mask specification");
318775801Siedowse		return (1);
318875801Siedowse	}
318975801Siedowse	if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
319075801Siedowse		syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
319175801Siedowse		return (1);
319275801Siedowse	}
3193192934Srmacklem	if (v4root_phase > 0 &&
3194192934Srmacklem	    (opt_flags &
3195192934Srmacklem	     ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) {
3196192934Srmacklem	    syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:");
3197192934Srmacklem	    return (1);
3198192934Srmacklem	}
3199207689Srmacklem	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
3200207689Srmacklem	    syslog(LOG_ERR, "-alldirs has multiple directories");
3201207689Srmacklem	    return (1);
3202207689Srmacklem	}
32031558Srgrimes	return (0);
32041558Srgrimes}
32051558Srgrimes
32061558Srgrimes/*
32071558Srgrimes * Check an absolute directory path for any symbolic links. Return true
32081558Srgrimes */
3209285128Straszstatic int
3210216587Scharniercheck_dirpath(char *dirp)
32111558Srgrimes{
32121558Srgrimes	char *cp;
32131558Srgrimes	int ret = 1;
32141558Srgrimes	struct stat sb;
32151558Srgrimes
32161558Srgrimes	cp = dirp + 1;
32171558Srgrimes	while (*cp && ret) {
32181558Srgrimes		if (*cp == '/') {
32191558Srgrimes			*cp = '\0';
32209336Sdfr			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
32211558Srgrimes				ret = 0;
32221558Srgrimes			*cp = '/';
32231558Srgrimes		}
32241558Srgrimes		cp++;
32251558Srgrimes	}
32269336Sdfr	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
32271558Srgrimes		ret = 0;
32281558Srgrimes	return (ret);
32291558Srgrimes}
32309336Sdfr
323175801Siedowse/*
323275801Siedowse * Make a netmask according to the specified prefix length. The ss_family
323375801Siedowse * and other non-address fields must be initialised before calling this.
323475801Siedowse */
3235285128Straszstatic int
323675801Siedowsemakemask(struct sockaddr_storage *ssp, int bitlen)
323774462Salfred{
323875801Siedowse	u_char *p;
323975801Siedowse	int bits, i, len;
324074462Salfred
324175801Siedowse	if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
324275801Siedowse		return (-1);
3243103949Smike	if (bitlen > len * CHAR_BIT)
324475801Siedowse		return (-1);
324574462Salfred
324675801Siedowse	for (i = 0; i < len; i++) {
3247298912Saraujo		bits = MIN(CHAR_BIT, bitlen);
3248219125Sru		*p++ = (u_char)~0 << (CHAR_BIT - bits);
324975801Siedowse		bitlen -= bits;
325074462Salfred	}
325175801Siedowse	return 0;
325274462Salfred}
325374462Salfred
325475801Siedowse/*
325575801Siedowse * Check that the sockaddr is a valid netmask. Returns 0 if the mask
325675801Siedowse * is acceptable (i.e. of the form 1...10....0).
325775801Siedowse */
3258285128Straszstatic int
325975801Siedowsecheckmask(struct sockaddr *sa)
326074462Salfred{
326175801Siedowse	u_char *mask;
326275801Siedowse	int i, len;
326374462Salfred
326475801Siedowse	if ((mask = sa_rawaddr(sa, &len)) == NULL)
326575801Siedowse		return (-1);
326675801Siedowse
326775801Siedowse	for (i = 0; i < len; i++)
326875801Siedowse		if (mask[i] != 0xff)
326975801Siedowse			break;
327075801Siedowse	if (i < len) {
327175801Siedowse		if (~mask[i] & (u_char)(~mask[i] + 1))
327275801Siedowse			return (-1);
327375801Siedowse		i++;
327474462Salfred	}
327575801Siedowse	for (; i < len; i++)
327675801Siedowse		if (mask[i] != 0)
327775801Siedowse			return (-1);
327875801Siedowse	return (0);
327974462Salfred}
328074462Salfred
328175801Siedowse/*
328275801Siedowse * Compare two sockaddrs according to a specified mask. Return zero if
328375801Siedowse * `sa1' matches `sa2' when filtered by the netmask in `samask'.
3284228990Suqs * If samask is NULL, perform a full comparison.
328575801Siedowse */
3286285128Straszstatic int
328775801Siedowsesacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
328874462Salfred{
328975801Siedowse	unsigned char *p1, *p2, *mask;
329075801Siedowse	int len, i;
329174462Salfred
329275801Siedowse	if (sa1->sa_family != sa2->sa_family ||
329375801Siedowse	    (p1 = sa_rawaddr(sa1, &len)) == NULL ||
329475801Siedowse	    (p2 = sa_rawaddr(sa2, NULL)) == NULL)
329575801Siedowse		return (1);
329675801Siedowse
329775801Siedowse	switch (sa1->sa_family) {
329874462Salfred	case AF_INET6:
329975801Siedowse		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
330075801Siedowse		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
330175801Siedowse			return (1);
330274462Salfred		break;
330374462Salfred	}
330474462Salfred
330575801Siedowse	/* Simple binary comparison if no mask specified. */
330675801Siedowse	if (samask == NULL)
330775801Siedowse		return (memcmp(p1, p2, len));
330874462Salfred
330975801Siedowse	/* Set up the mask, and do a mask-based comparison. */
331075801Siedowse	if (sa1->sa_family != samask->sa_family ||
331175801Siedowse	    (mask = sa_rawaddr(samask, NULL)) == NULL)
331275801Siedowse		return (1);
331374462Salfred
331475801Siedowse	for (i = 0; i < len; i++)
331575801Siedowse		if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
331675801Siedowse			return (1);
331775801Siedowse	return (0);
331874462Salfred}
331974462Salfred
332075801Siedowse/*
332175801Siedowse * Return a pointer to the part of the sockaddr that contains the
332275801Siedowse * raw address, and set *nbytes to its length in bytes. Returns
332375801Siedowse * NULL if the address family is unknown.
332475801Siedowse */
3325285128Straszstatic void *
332675801Siedowsesa_rawaddr(struct sockaddr *sa, int *nbytes) {
332775801Siedowse	void *p;
332874462Salfred	int len;
332974462Salfred
333075801Siedowse	switch (sa->sa_family) {
333174462Salfred	case AF_INET:
333275801Siedowse		len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
333375801Siedowse		p = &((struct sockaddr_in *)sa)->sin_addr;
333474462Salfred		break;
333574462Salfred	case AF_INET6:
333675801Siedowse		len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
333775801Siedowse		p = &((struct sockaddr_in6 *)sa)->sin6_addr;
333874462Salfred		break;
333974462Salfred	default:
334075801Siedowse		p = NULL;
334175801Siedowse		len = 0;
334274462Salfred	}
334374462Salfred
334475801Siedowse	if (nbytes != NULL)
334575801Siedowse		*nbytes = len;
334675801Siedowse	return (p);
334774462Salfred}
334874462Salfred
3349285128Straszstatic void
3350216587Scharnierhuphandler(int sig __unused)
335175754Siedowse{
3352285128Strasz
335375754Siedowse	got_sighup = 1;
335475754Siedowse}
335575754Siedowse
3356285128Straszstatic void
3357285128Straszterminate(int sig __unused)
335874462Salfred{
3359149433Spjd	pidfile_remove(pfh);
3360194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
3361194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
336274462Salfred	exit (0);
336374462Salfred}
3364