mountd.c revision 4acc83b445467fe46f26270857b325af74ce72da
1/*
2 * Copyright (c) 1989, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Herb Hasler and Rick Macklem at The University of Guelph.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38static char copyright[] =
39"@(#) Copyright (c) 1989, 1993\n\
40	The Regents of the University of California.  All rights reserved.\n";
41#endif /*not lint*/
42
43#ifndef lint
44/*static char sccsid[] = "@(#)mountd.c	8.15 (Berkeley) 5/1/95"; */
45static const char rcsid[] =
46	"$Id: mountd.c,v 1.21 1997/04/30 18:40:12 pst Exp $";
47#endif /*not lint*/
48
49#include <sys/param.h>
50#include <sys/file.h>
51#include <sys/ioctl.h>
52#include <sys/mount.h>
53#include <sys/socket.h>
54#include <sys/stat.h>
55#include <sys/syslog.h>
56#include <sys/ucred.h>
57#include <sys/sysctl.h>
58
59#include <rpc/rpc.h>
60#include <rpc/pmap_clnt.h>
61#include <rpc/pmap_prot.h>
62#ifdef ISO
63#include <netiso/iso.h>
64#endif
65#include <nfs/rpcv2.h>
66#include <nfs/nfsproto.h>
67#include <nfs/nfs.h>
68#include <ufs/ufs/ufsmount.h>
69#include <msdosfs/msdosfsmount.h>
70#include <isofs/cd9660/cd9660_mount.h>	/* XXX need isofs in include */
71
72#include <arpa/inet.h>
73
74#include <ctype.h>
75#include <errno.h>
76#include <grp.h>
77#include <netdb.h>
78#include <pwd.h>
79#include <signal.h>
80#include <stdio.h>
81#include <stdlib.h>
82#include <string.h>
83#include <unistd.h>
84#include "pathnames.h"
85
86#ifdef DEBUG
87#include <stdarg.h>
88#endif
89
90/*
91 * Structures for keeping the mount list and export list
92 */
93struct mountlist {
94	struct mountlist *ml_next;
95	char	ml_host[RPCMNT_NAMELEN+1];
96	char	ml_dirp[RPCMNT_PATHLEN+1];
97};
98
99struct dirlist {
100	struct dirlist	*dp_left;
101	struct dirlist	*dp_right;
102	int		dp_flag;
103	struct hostlist	*dp_hosts;	/* List of hosts this dir exported to */
104	char		dp_dirp[1];	/* Actually malloc'd to size of dir */
105};
106/* dp_flag bits */
107#define	DP_DEFSET	0x1
108#define DP_HOSTSET	0x2
109#define DP_KERB		0x4
110
111struct exportlist {
112	struct exportlist *ex_next;
113	struct dirlist	*ex_dirl;
114	struct dirlist	*ex_defdir;
115	int		ex_flag;
116	fsid_t		ex_fs;
117	char		*ex_fsdir;
118	char		*ex_indexfile;
119};
120/* ex_flag bits */
121#define	EX_LINKED	0x1
122
123struct netmsk {
124	u_long	nt_net;
125	u_long	nt_mask;
126	char *nt_name;
127};
128
129union grouptypes {
130	struct hostent *gt_hostent;
131	struct netmsk	gt_net;
132#ifdef ISO
133	struct sockaddr_iso *gt_isoaddr;
134#endif
135};
136
137struct grouplist {
138	int gr_type;
139	union grouptypes gr_ptr;
140	struct grouplist *gr_next;
141};
142/* Group types */
143#define	GT_NULL		0x0
144#define	GT_HOST		0x1
145#define	GT_NET		0x2
146#define	GT_ISO		0x4
147#define GT_IGNORE	0x5
148
149struct hostlist {
150	int		 ht_flag;	/* Uses DP_xx bits */
151	struct grouplist *ht_grp;
152	struct hostlist	 *ht_next;
153};
154
155struct fhreturn {
156	int	fhr_flag;
157	int	fhr_vers;
158	nfsfh_t	fhr_fh;
159};
160
161/* Global defs */
162char	*add_expdir __P((struct dirlist **, char *, int));
163void	add_dlist __P((struct dirlist **, struct dirlist *,
164				struct grouplist *, int));
165void	add_mlist __P((char *, char *));
166int	check_dirpath __P((char *));
167int	check_options __P((struct dirlist *));
168int	chk_host __P((struct dirlist *, u_long, int *, int *));
169void	del_mlist __P((char *, char *));
170struct dirlist *dirp_search __P((struct dirlist *, char *));
171int	do_mount __P((struct exportlist *, struct grouplist *, int,
172		struct ucred *, char *, int, struct statfs *));
173int	do_opt __P((char **, char **, struct exportlist *, struct grouplist *,
174				int *, int *, struct ucred *));
175struct	exportlist *ex_search __P((fsid_t *));
176struct	exportlist *get_exp __P((void));
177void	free_dir __P((struct dirlist *));
178void	free_exp __P((struct exportlist *));
179void	free_grp __P((struct grouplist *));
180void	free_host __P((struct hostlist *));
181void	get_exportlist __P((void));
182int	get_host __P((char *, struct grouplist *, struct grouplist *));
183int	get_num __P((char *));
184struct hostlist *get_ht __P((void));
185int	get_line __P((void));
186void	get_mountlist __P((void));
187int	get_net __P((char *, struct netmsk *, int));
188void	getexp_err __P((struct exportlist *, struct grouplist *));
189struct grouplist *get_grp __P((void));
190void	hang_dirp __P((struct dirlist *, struct grouplist *,
191				struct exportlist *, int));
192void	mntsrv __P((struct svc_req *, SVCXPRT *));
193void	nextfield __P((char **, char **));
194void	out_of_mem __P((void));
195void	parsecred __P((char *, struct ucred *));
196int	put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *));
197int	scan_tree __P((struct dirlist *, u_long));
198void	send_umntall __P((void));
199int	umntall_each __P((caddr_t, struct sockaddr_in *));
200int	xdr_dir __P((XDR *, char *));
201int	xdr_explist __P((XDR *, caddr_t));
202int	xdr_fhs __P((XDR *, caddr_t));
203int	xdr_mlist __P((XDR *, caddr_t));
204
205/* C library */
206int	getnetgrent();
207void	endnetgrent();
208void	setnetgrent();
209
210#ifdef ISO
211struct iso_addr *iso_addr();
212#endif
213
214struct exportlist *exphead;
215struct mountlist *mlhead;
216struct grouplist *grphead;
217char exname[MAXPATHLEN];
218struct ucred def_anon = {
219	1,
220	(uid_t) -2,
221	1,
222	{ (gid_t) -2 }
223};
224int force_v2 = 0;
225int resvport_only = 1;
226int dir_only = 1;
227int opt_flags;
228/* Bits for above */
229#define	OP_MAPROOT	0x01
230#define	OP_MAPALL	0x02
231#define	OP_KERB		0x04
232#define	OP_MASK		0x08
233#define	OP_NET		0x10
234#define	OP_ISO		0x20
235#define	OP_ALLDIRS	0x40
236
237#ifdef DEBUG
238int debug = 1;
239void	SYSLOG __P((int, const char *, ...));
240#define syslog SYSLOG
241#else
242int debug = 0;
243#endif
244
245/*
246 * Mountd server for NFS mount protocol as described in:
247 * NFS: Network File System Protocol Specification, RFC1094, Appendix A
248 * The optional arguments are the exports file name
249 * default: _PATH_EXPORTS
250 * and "-n" to allow nonroot mount.
251 */
252int
253main(argc, argv)
254	int argc;
255	char **argv;
256{
257	SVCXPRT *udptransp, *tcptransp;
258	int c;
259	int mib[3];
260#ifdef __FreeBSD__
261	struct vfsconf vfc;
262	int error;
263
264	error = getvfsbyname("nfs", &vfc);
265	if (error && vfsisloadable("nfs")) {
266		if(vfsload("nfs"))
267			err(1, "vfsload(nfs)");
268		endvfsent();	/* flush cache */
269		error = getvfsbyname("nfs", &vfc);
270	}
271	if (error)
272		errx(1, "NFS support is not available in the running kernel");
273#endif	/* __FreeBSD__ */
274
275	while ((c = getopt(argc, argv, "2dnr")) != -1)
276		switch (c) {
277		case '2':
278			force_v2 = 1;
279			break;
280		case 'n':
281			resvport_only = 0;
282			break;
283		case 'r':
284			dir_only = 0;
285			break;
286		case 'd':
287			debug = debug ? 0 : 1;
288			break;
289		default:
290			fprintf(stderr, "Usage: mountd [-d] [-r] [-n] [export_file]\n");
291			exit(1);
292		};
293	argc -= optind;
294	argv += optind;
295	grphead = (struct grouplist *)NULL;
296	exphead = (struct exportlist *)NULL;
297	mlhead = (struct mountlist *)NULL;
298	if (argc == 1) {
299		strncpy(exname, *argv, MAXPATHLEN-1);
300		exname[MAXPATHLEN-1] = '\0';
301	} else
302		strcpy(exname, _PATH_EXPORTS);
303	openlog("mountd", LOG_PID, LOG_DAEMON);
304	if (debug)
305		fprintf(stderr,"Getting export list.\n");
306	get_exportlist();
307	if (debug)
308		fprintf(stderr,"Getting mount list.\n");
309	get_mountlist();
310	if (debug)
311		fprintf(stderr,"Here we go.\n");
312	if (debug == 0) {
313		daemon(0, 0);
314		signal(SIGINT, SIG_IGN);
315		signal(SIGQUIT, SIG_IGN);
316	}
317	signal(SIGHUP, (void (*) __P((int))) get_exportlist);
318	signal(SIGTERM, (void (*) __P((int))) send_umntall);
319	{ FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
320	  if (pidfile != NULL) {
321		fprintf(pidfile, "%d\n", getpid());
322		fclose(pidfile);
323	  }
324	}
325
326	if (!resvport_only) {
327		mib[0] = CTL_VFS;
328		mib[1] = MOUNT_NFS;
329		mib[2] = NFS_NFSPRIVPORT;
330		if (sysctl(mib, 3, NULL, NULL, &resvport_only,
331		    sizeof(resvport_only)) != 0 && errno != ENOENT) {
332			syslog(LOG_ERR, "sysctl: %m");
333			exit(1);
334		}
335	}
336
337	if ((udptransp = svcudp_create(RPC_ANYSOCK)) == NULL ||
338	    (tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL) {
339		syslog(LOG_ERR, "Can't create socket");
340		exit(1);
341	}
342	pmap_unset(RPCPROG_MNT, 1);
343	pmap_unset(RPCPROG_MNT, 3);
344	if (!force_v2)
345		if (!svc_register(udptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_UDP) ||
346		    !svc_register(tcptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_TCP)) {
347			syslog(LOG_ERR, "Can't register mount");
348			exit(1);
349		}
350	if (!svc_register(udptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_UDP) ||
351	    !svc_register(tcptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_TCP)) {
352		syslog(LOG_ERR, "Can't register mount");
353		exit(1);
354	}
355	svc_run();
356	syslog(LOG_ERR, "Mountd died");
357	exit(1);
358}
359
360/*
361 * The mount rpc service
362 */
363void
364mntsrv(rqstp, transp)
365	struct svc_req *rqstp;
366	SVCXPRT *transp;
367{
368	struct exportlist *ep;
369	struct dirlist *dp;
370	struct fhreturn fhr;
371	struct stat stb;
372	struct statfs fsb;
373	struct hostent *hp;
374	u_long saddr;
375	u_short sport;
376	char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN];
377	int bad = ENOENT, defset, hostset;
378	sigset_t sighup_mask;
379
380	sigemptyset(&sighup_mask);
381	sigaddset(&sighup_mask, SIGHUP);
382	saddr = transp->xp_raddr.sin_addr.s_addr;
383	sport = ntohs(transp->xp_raddr.sin_port);
384	hp = (struct hostent *)NULL;
385	switch (rqstp->rq_proc) {
386	case NULLPROC:
387		if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
388			syslog(LOG_ERR, "Can't send reply");
389		return;
390	case RPCMNT_MOUNT:
391		if (sport >= IPPORT_RESERVED && resvport_only) {
392			svcerr_weakauth(transp);
393			return;
394		}
395		if (!svc_getargs(transp, xdr_dir, rpcpath)) {
396			svcerr_decode(transp);
397			return;
398		}
399
400		/*
401		 * Get the real pathname and make sure it is a directory
402		 * or a regular file if the -r option was specified
403		 * and it exists.
404		 */
405		if (realpath(rpcpath, dirpath) == 0 ||
406		    stat(dirpath, &stb) < 0 ||
407		    (!S_ISDIR(stb.st_mode) &&
408		     (dir_only || !S_ISREG(stb.st_mode))) ||
409		    statfs(dirpath, &fsb) < 0) {
410			chdir("/");	/* Just in case realpath doesn't */
411			if (debug)
412				fprintf(stderr, "stat failed on %s\n", dirpath);
413			if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
414				syslog(LOG_ERR, "Can't send reply");
415			return;
416		}
417
418		/* Check in the exports list */
419		sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
420		ep = ex_search(&fsb.f_fsid);
421		hostset = defset = 0;
422		if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) ||
423		    ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
424		     chk_host(dp, saddr, &defset, &hostset)) ||
425		     (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
426		      scan_tree(ep->ex_dirl, saddr) == 0))) {
427			if (hostset & DP_HOSTSET)
428				fhr.fhr_flag = hostset;
429			else
430				fhr.fhr_flag = defset;
431			fhr.fhr_vers = rqstp->rq_vers;
432			/* Get the file handle */
433			memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
434			if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
435				bad = errno;
436				syslog(LOG_ERR, "Can't get fh for %s", dirpath);
437				if (!svc_sendreply(transp, xdr_long,
438				    (caddr_t)&bad))
439					syslog(LOG_ERR, "Can't send reply");
440				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
441				return;
442			}
443			if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&fhr))
444				syslog(LOG_ERR, "Can't send reply");
445			if (hp == NULL)
446				hp = gethostbyaddr((caddr_t)&saddr,
447				    sizeof(saddr), AF_INET);
448			if (hp)
449				add_mlist(hp->h_name, dirpath);
450			else
451				add_mlist(inet_ntoa(transp->xp_raddr.sin_addr),
452					dirpath);
453			if (debug)
454				fprintf(stderr,"Mount successfull.\n");
455		} else {
456			bad = EACCES;
457			if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
458				syslog(LOG_ERR, "Can't send reply");
459		}
460		sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
461		return;
462	case RPCMNT_DUMP:
463		if (!svc_sendreply(transp, xdr_mlist, (caddr_t)NULL))
464			syslog(LOG_ERR, "Can't send reply");
465		return;
466	case RPCMNT_UMOUNT:
467		if (sport >= IPPORT_RESERVED && resvport_only) {
468			svcerr_weakauth(transp);
469			return;
470		}
471		if (!svc_getargs(transp, xdr_dir, dirpath)) {
472			svcerr_decode(transp);
473			return;
474		}
475		if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
476			syslog(LOG_ERR, "Can't send reply");
477		hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
478		if (hp)
479			del_mlist(hp->h_name, dirpath);
480		del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), dirpath);
481		return;
482	case RPCMNT_UMNTALL:
483		if (sport >= IPPORT_RESERVED && resvport_only) {
484			svcerr_weakauth(transp);
485			return;
486		}
487		if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
488			syslog(LOG_ERR, "Can't send reply");
489		hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
490		if (hp)
491			del_mlist(hp->h_name, (char *)NULL);
492		del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), (char *)NULL);
493		return;
494	case RPCMNT_EXPORT:
495		if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL))
496			syslog(LOG_ERR, "Can't send reply");
497		return;
498	default:
499		svcerr_noproc(transp);
500		return;
501	}
502}
503
504/*
505 * Xdr conversion for a dirpath string
506 */
507int
508xdr_dir(xdrsp, dirp)
509	XDR *xdrsp;
510	char *dirp;
511{
512	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
513}
514
515/*
516 * Xdr routine to generate file handle reply
517 */
518int
519xdr_fhs(xdrsp, cp)
520	XDR *xdrsp;
521	caddr_t cp;
522{
523	register struct fhreturn *fhrp = (struct fhreturn *)cp;
524	u_long ok = 0, len, auth;
525
526	if (!xdr_long(xdrsp, &ok))
527		return (0);
528	switch (fhrp->fhr_vers) {
529	case 1:
530		return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
531	case 3:
532		len = NFSX_V3FH;
533		if (!xdr_long(xdrsp, &len))
534			return (0);
535		if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
536			return (0);
537		if (fhrp->fhr_flag & DP_KERB)
538			auth = RPCAUTH_KERB4;
539		else
540			auth = RPCAUTH_UNIX;
541		len = 1;
542		if (!xdr_long(xdrsp, &len))
543			return (0);
544		return (xdr_long(xdrsp, &auth));
545	};
546	return (0);
547}
548
549int
550xdr_mlist(xdrsp, cp)
551	XDR *xdrsp;
552	caddr_t cp;
553{
554	struct mountlist *mlp;
555	int true = 1;
556	int false = 0;
557	char *strp;
558
559	mlp = mlhead;
560	while (mlp) {
561		if (!xdr_bool(xdrsp, &true))
562			return (0);
563		strp = &mlp->ml_host[0];
564		if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
565			return (0);
566		strp = &mlp->ml_dirp[0];
567		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
568			return (0);
569		mlp = mlp->ml_next;
570	}
571	if (!xdr_bool(xdrsp, &false))
572		return (0);
573	return (1);
574}
575
576/*
577 * Xdr conversion for export list
578 */
579int
580xdr_explist(xdrsp, cp)
581	XDR *xdrsp;
582	caddr_t cp;
583{
584	struct exportlist *ep;
585	int false = 0;
586	int putdef;
587	sigset_t sighup_mask;
588
589	sigemptyset(&sighup_mask);
590	sigaddset(&sighup_mask, SIGHUP);
591	sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
592	ep = exphead;
593	while (ep) {
594		putdef = 0;
595		if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef))
596			goto errout;
597		if (ep->ex_defdir && putdef == 0 &&
598			put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
599			&putdef))
600			goto errout;
601		ep = ep->ex_next;
602	}
603	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
604	if (!xdr_bool(xdrsp, &false))
605		return (0);
606	return (1);
607errout:
608	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
609	return (0);
610}
611
612/*
613 * Called from xdr_explist() to traverse the tree and export the
614 * directory paths.
615 */
616int
617put_exlist(dp, xdrsp, adp, putdefp)
618	struct dirlist *dp;
619	XDR *xdrsp;
620	struct dirlist *adp;
621	int *putdefp;
622{
623	struct grouplist *grp;
624	struct hostlist *hp;
625	int true = 1;
626	int false = 0;
627	int gotalldir = 0;
628	char *strp;
629
630	if (dp) {
631		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp))
632			return (1);
633		if (!xdr_bool(xdrsp, &true))
634			return (1);
635		strp = dp->dp_dirp;
636		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
637			return (1);
638		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
639			gotalldir = 1;
640			*putdefp = 1;
641		}
642		if ((dp->dp_flag & DP_DEFSET) == 0 &&
643		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
644			hp = dp->dp_hosts;
645			while (hp) {
646				grp = hp->ht_grp;
647				if (grp->gr_type == GT_HOST) {
648					if (!xdr_bool(xdrsp, &true))
649						return (1);
650					strp = grp->gr_ptr.gt_hostent->h_name;
651					if (!xdr_string(xdrsp, &strp,
652					    RPCMNT_NAMELEN))
653						return (1);
654				} else if (grp->gr_type == GT_NET) {
655					if (!xdr_bool(xdrsp, &true))
656						return (1);
657					strp = grp->gr_ptr.gt_net.nt_name;
658					if (!xdr_string(xdrsp, &strp,
659					    RPCMNT_NAMELEN))
660						return (1);
661				}
662				hp = hp->ht_next;
663				if (gotalldir && hp == (struct hostlist *)NULL) {
664					hp = adp->dp_hosts;
665					gotalldir = 0;
666				}
667			}
668		}
669		if (!xdr_bool(xdrsp, &false))
670			return (1);
671		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))
672			return (1);
673	}
674	return (0);
675}
676
677#define LINESIZ	10240
678char line[LINESIZ];
679FILE *exp_file;
680
681/*
682 * Get the export list
683 */
684void
685get_exportlist()
686{
687	struct exportlist *ep, *ep2;
688	struct grouplist *grp, *tgrp;
689	struct exportlist **epp;
690	struct dirlist *dirhead;
691	struct statfs fsb, *fsp;
692	struct hostent *hpe;
693	struct ucred anon;
694	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
695	int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp;
696
697	/*
698	 * First, get rid of the old list
699	 */
700	ep = exphead;
701	while (ep) {
702		ep2 = ep;
703		ep = ep->ex_next;
704		free_exp(ep2);
705	}
706	exphead = (struct exportlist *)NULL;
707
708	grp = grphead;
709	while (grp) {
710		tgrp = grp;
711		grp = grp->gr_next;
712		free_grp(tgrp);
713	}
714	grphead = (struct grouplist *)NULL;
715
716	/*
717	 * And delete exports that are in the kernel for all local
718	 * file systems.
719	 * XXX: Should know how to handle all local exportable file systems
720	 *      instead of just "ufs".
721	 */
722	num = getmntinfo(&fsp, MNT_NOWAIT);
723	for (i = 0; i < num; i++) {
724		union {
725			struct ufs_args ua;
726			struct iso_args ia;
727			struct mfs_args ma;
728			struct msdosfs_args da;
729		} targs;
730
731		if (!strcmp(fsp->f_fstypename, "mfs") ||
732		    !strcmp(fsp->f_fstypename, "ufs") ||
733		    !strcmp(fsp->f_fstypename, "msdos") ||
734		    !strcmp(fsp->f_fstypename, "cd9660")) {
735			targs.ua.fspec = NULL;
736			targs.ua.export.ex_flags = MNT_DELEXPORT;
737			if (mount(fsp->f_fstypename, fsp->f_mntonname,
738				  fsp->f_flags | MNT_UPDATE,
739				  (caddr_t)&targs) < 0)
740				syslog(LOG_ERR, "Can't delete exports for %s",
741				       fsp->f_mntonname);
742		}
743		fsp++;
744	}
745
746	/*
747	 * Read in the exports file and build the list, calling
748	 * mount() as we go along to push the export rules into the kernel.
749	 */
750	if ((exp_file = fopen(exname, "r")) == NULL) {
751		syslog(LOG_ERR, "Can't open %s", exname);
752		exit(2);
753	}
754	dirhead = (struct dirlist *)NULL;
755	while (get_line()) {
756		if (debug)
757			fprintf(stderr,"Got line %s\n",line);
758		cp = line;
759		nextfield(&cp, &endcp);
760		if (*cp == '#')
761			goto nextline;
762
763		/*
764		 * Set defaults.
765		 */
766		has_host = FALSE;
767		anon = def_anon;
768		exflags = MNT_EXPORTED;
769		got_nondir = 0;
770		opt_flags = 0;
771		ep = (struct exportlist *)NULL;
772
773		/*
774		 * Create new exports list entry
775		 */
776		len = endcp-cp;
777		tgrp = grp = get_grp();
778		while (len > 0) {
779			if (len > RPCMNT_NAMELEN) {
780			    getexp_err(ep, tgrp);
781			    goto nextline;
782			}
783			if (*cp == '-') {
784			    if (ep == (struct exportlist *)NULL) {
785				getexp_err(ep, tgrp);
786				goto nextline;
787			    }
788			    if (debug)
789				fprintf(stderr, "doing opt %s\n", cp);
790			    got_nondir = 1;
791			    if (do_opt(&cp, &endcp, ep, grp, &has_host,
792				&exflags, &anon)) {
793				getexp_err(ep, tgrp);
794				goto nextline;
795			    }
796			} else if (*cp == '/') {
797			    savedc = *endcp;
798			    *endcp = '\0';
799			    if (check_dirpath(cp) &&
800				statfs(cp, &fsb) >= 0) {
801				if (got_nondir) {
802				    syslog(LOG_ERR, "Dirs must be first");
803				    getexp_err(ep, tgrp);
804				    goto nextline;
805				}
806				if (ep) {
807				    if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] ||
808					ep->ex_fs.val[1] != fsb.f_fsid.val[1]) {
809					getexp_err(ep, tgrp);
810					goto nextline;
811				    }
812				} else {
813				    /*
814				     * See if this directory is already
815				     * in the list.
816				     */
817				    ep = ex_search(&fsb.f_fsid);
818				    if (ep == (struct exportlist *)NULL) {
819					ep = get_exp();
820					ep->ex_fs = fsb.f_fsid;
821					ep->ex_fsdir = (char *)
822					    malloc(strlen(fsb.f_mntonname) + 1);
823					if (ep->ex_fsdir)
824					    strcpy(ep->ex_fsdir,
825						fsb.f_mntonname);
826					else
827					    out_of_mem();
828					if (debug)
829					  fprintf(stderr,
830					      "Making new ep fs=0x%x,0x%x\n",
831					      fsb.f_fsid.val[0],
832					      fsb.f_fsid.val[1]);
833				    } else if (debug)
834					fprintf(stderr,
835					    "Found ep fs=0x%x,0x%x\n",
836					    fsb.f_fsid.val[0],
837					    fsb.f_fsid.val[1]);
838				}
839
840				/*
841				 * Add dirpath to export mount point.
842				 */
843				dirp = add_expdir(&dirhead, cp, len);
844				dirplen = len;
845			    } else {
846				getexp_err(ep, tgrp);
847				goto nextline;
848			    }
849			    *endcp = savedc;
850			} else {
851			    savedc = *endcp;
852			    *endcp = '\0';
853			    got_nondir = 1;
854			    if (ep == (struct exportlist *)NULL) {
855				getexp_err(ep, tgrp);
856				goto nextline;
857			    }
858
859			    /*
860			     * Get the host or netgroup.
861			     */
862			    setnetgrent(cp);
863			    netgrp = getnetgrent(&hst, &usr, &dom);
864			    do {
865				if (has_host) {
866				    grp->gr_next = get_grp();
867				    grp = grp->gr_next;
868				}
869				if (netgrp) {
870				    if (get_host(hst, grp, tgrp)) {
871					syslog(LOG_ERR, "Bad netgroup %s", cp);
872					getexp_err(ep, tgrp);
873					endnetgrent();
874					goto nextline;
875				    }
876				} else if (get_host(cp, grp, tgrp)) {
877				    getexp_err(ep, tgrp);
878				    goto nextline;
879				}
880				has_host = TRUE;
881			    } while (netgrp && getnetgrent(&hst, &usr, &dom));
882			    endnetgrent();
883			    *endcp = savedc;
884			}
885			cp = endcp;
886			nextfield(&cp, &endcp);
887			len = endcp - cp;
888		}
889		if (check_options(dirhead)) {
890			getexp_err(ep, tgrp);
891			goto nextline;
892		}
893		if (!has_host) {
894			grp->gr_type = GT_HOST;
895			if (debug)
896				fprintf(stderr,"Adding a default entry\n");
897			/* add a default group and make the grp list NULL */
898			hpe = (struct hostent *)malloc(sizeof(struct hostent));
899			if (hpe == (struct hostent *)NULL)
900				out_of_mem();
901			hpe->h_name = strdup("Default");
902			hpe->h_addrtype = AF_INET;
903			hpe->h_length = sizeof (u_long);
904			hpe->h_addr_list = (char **)NULL;
905			grp->gr_ptr.gt_hostent = hpe;
906
907		/*
908		 * Don't allow a network export coincide with a list of
909		 * host(s) on the same line.
910		 */
911		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
912			getexp_err(ep, tgrp);
913			goto nextline;
914		}
915
916		/*
917		 * Loop through hosts, pushing the exports into the kernel.
918		 * After loop, tgrp points to the start of the list and
919		 * grp points to the last entry in the list.
920		 */
921		grp = tgrp;
922		do {
923		    if (do_mount(ep, grp, exflags, &anon, dirp,
924			dirplen, &fsb)) {
925			getexp_err(ep, tgrp);
926			goto nextline;
927		    }
928		} while (grp->gr_next && (grp = grp->gr_next));
929
930		/*
931		 * Success. Update the data structures.
932		 */
933		if (has_host) {
934			hang_dirp(dirhead, tgrp, ep, opt_flags);
935			grp->gr_next = grphead;
936			grphead = tgrp;
937		} else {
938			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
939				opt_flags);
940			free_grp(grp);
941		}
942		dirhead = (struct dirlist *)NULL;
943		if ((ep->ex_flag & EX_LINKED) == 0) {
944			ep2 = exphead;
945			epp = &exphead;
946
947			/*
948			 * Insert in the list in alphabetical order.
949			 */
950			while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
951				epp = &ep2->ex_next;
952				ep2 = ep2->ex_next;
953			}
954			if (ep2)
955				ep->ex_next = ep2;
956			*epp = ep;
957			ep->ex_flag |= EX_LINKED;
958		}
959nextline:
960		if (dirhead) {
961			free_dir(dirhead);
962			dirhead = (struct dirlist *)NULL;
963		}
964	}
965	fclose(exp_file);
966}
967
968/*
969 * Allocate an export list element
970 */
971struct exportlist *
972get_exp()
973{
974	struct exportlist *ep;
975
976	ep = (struct exportlist *)malloc(sizeof (struct exportlist));
977	if (ep == (struct exportlist *)NULL)
978		out_of_mem();
979	memset(ep, 0, sizeof(struct exportlist));
980	return (ep);
981}
982
983/*
984 * Allocate a group list element
985 */
986struct grouplist *
987get_grp()
988{
989	struct grouplist *gp;
990
991	gp = (struct grouplist *)malloc(sizeof (struct grouplist));
992	if (gp == (struct grouplist *)NULL)
993		out_of_mem();
994	memset(gp, 0, sizeof(struct grouplist));
995	return (gp);
996}
997
998/*
999 * Clean up upon an error in get_exportlist().
1000 */
1001void
1002getexp_err(ep, grp)
1003	struct exportlist *ep;
1004	struct grouplist *grp;
1005{
1006	struct grouplist *tgrp;
1007
1008	syslog(LOG_ERR, "Bad exports list line %s", line);
1009	if (ep && (ep->ex_flag & EX_LINKED) == 0)
1010		free_exp(ep);
1011	while (grp) {
1012		tgrp = grp;
1013		grp = grp->gr_next;
1014		free_grp(tgrp);
1015	}
1016}
1017
1018/*
1019 * Search the export list for a matching fs.
1020 */
1021struct exportlist *
1022ex_search(fsid)
1023	fsid_t *fsid;
1024{
1025	struct exportlist *ep;
1026
1027	ep = exphead;
1028	while (ep) {
1029		if (ep->ex_fs.val[0] == fsid->val[0] &&
1030		    ep->ex_fs.val[1] == fsid->val[1])
1031			return (ep);
1032		ep = ep->ex_next;
1033	}
1034	return (ep);
1035}
1036
1037/*
1038 * Add a directory path to the list.
1039 */
1040char *
1041add_expdir(dpp, cp, len)
1042	struct dirlist **dpp;
1043	char *cp;
1044	int len;
1045{
1046	struct dirlist *dp;
1047
1048	dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
1049	dp->dp_left = *dpp;
1050	dp->dp_right = (struct dirlist *)NULL;
1051	dp->dp_flag = 0;
1052	dp->dp_hosts = (struct hostlist *)NULL;
1053	strcpy(dp->dp_dirp, cp);
1054	*dpp = dp;
1055	return (dp->dp_dirp);
1056}
1057
1058/*
1059 * Hang the dir list element off the dirpath binary tree as required
1060 * and update the entry for host.
1061 */
1062void
1063hang_dirp(dp, grp, ep, flags)
1064	struct dirlist *dp;
1065	struct grouplist *grp;
1066	struct exportlist *ep;
1067	int flags;
1068{
1069	struct hostlist *hp;
1070	struct dirlist *dp2;
1071
1072	if (flags & OP_ALLDIRS) {
1073		if (ep->ex_defdir)
1074			free((caddr_t)dp);
1075		else
1076			ep->ex_defdir = dp;
1077		if (grp == (struct grouplist *)NULL) {
1078			ep->ex_defdir->dp_flag |= DP_DEFSET;
1079			if (flags & OP_KERB)
1080				ep->ex_defdir->dp_flag |= DP_KERB;
1081		} else while (grp) {
1082			hp = get_ht();
1083			if (flags & OP_KERB)
1084				hp->ht_flag |= DP_KERB;
1085			hp->ht_grp = grp;
1086			hp->ht_next = ep->ex_defdir->dp_hosts;
1087			ep->ex_defdir->dp_hosts = hp;
1088			grp = grp->gr_next;
1089		}
1090	} else {
1091
1092		/*
1093		 * Loop throught the directories adding them to the tree.
1094		 */
1095		while (dp) {
1096			dp2 = dp->dp_left;
1097			add_dlist(&ep->ex_dirl, dp, grp, flags);
1098			dp = dp2;
1099		}
1100	}
1101}
1102
1103/*
1104 * Traverse the binary tree either updating a node that is already there
1105 * for the new directory or adding the new node.
1106 */
1107void
1108add_dlist(dpp, newdp, grp, flags)
1109	struct dirlist **dpp;
1110	struct dirlist *newdp;
1111	struct grouplist *grp;
1112	int flags;
1113{
1114	struct dirlist *dp;
1115	struct hostlist *hp;
1116	int cmp;
1117
1118	dp = *dpp;
1119	if (dp) {
1120		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
1121		if (cmp > 0) {
1122			add_dlist(&dp->dp_left, newdp, grp, flags);
1123			return;
1124		} else if (cmp < 0) {
1125			add_dlist(&dp->dp_right, newdp, grp, flags);
1126			return;
1127		} else
1128			free((caddr_t)newdp);
1129	} else {
1130		dp = newdp;
1131		dp->dp_left = (struct dirlist *)NULL;
1132		*dpp = dp;
1133	}
1134	if (grp) {
1135
1136		/*
1137		 * Hang all of the host(s) off of the directory point.
1138		 */
1139		do {
1140			hp = get_ht();
1141			if (flags & OP_KERB)
1142				hp->ht_flag |= DP_KERB;
1143			hp->ht_grp = grp;
1144			hp->ht_next = dp->dp_hosts;
1145			dp->dp_hosts = hp;
1146			grp = grp->gr_next;
1147		} while (grp);
1148	} else {
1149		dp->dp_flag |= DP_DEFSET;
1150		if (flags & OP_KERB)
1151			dp->dp_flag |= DP_KERB;
1152	}
1153}
1154
1155/*
1156 * Search for a dirpath on the export point.
1157 */
1158struct dirlist *
1159dirp_search(dp, dirpath)
1160	struct dirlist *dp;
1161	char *dirpath;
1162{
1163	int cmp;
1164
1165	if (dp) {
1166		cmp = strcmp(dp->dp_dirp, dirpath);
1167		if (cmp > 0)
1168			return (dirp_search(dp->dp_left, dirpath));
1169		else if (cmp < 0)
1170			return (dirp_search(dp->dp_right, dirpath));
1171		else
1172			return (dp);
1173	}
1174	return (dp);
1175}
1176
1177/*
1178 * Scan for a host match in a directory tree.
1179 */
1180int
1181chk_host(dp, saddr, defsetp, hostsetp)
1182	struct dirlist *dp;
1183	u_long saddr;
1184	int *defsetp;
1185	int *hostsetp;
1186{
1187	struct hostlist *hp;
1188	struct grouplist *grp;
1189	u_long **addrp;
1190
1191	if (dp) {
1192		if (dp->dp_flag & DP_DEFSET)
1193			*defsetp = dp->dp_flag;
1194		hp = dp->dp_hosts;
1195		while (hp) {
1196			grp = hp->ht_grp;
1197			switch (grp->gr_type) {
1198			case GT_HOST:
1199			    addrp = (u_long **)
1200				grp->gr_ptr.gt_hostent->h_addr_list;
1201			    while (*addrp) {
1202				if (**addrp == saddr) {
1203				    *hostsetp = (hp->ht_flag | DP_HOSTSET);
1204				    return (1);
1205				}
1206				addrp++;
1207			    }
1208			    break;
1209			case GT_NET:
1210			    if ((saddr & grp->gr_ptr.gt_net.nt_mask) ==
1211				grp->gr_ptr.gt_net.nt_net) {
1212				*hostsetp = (hp->ht_flag | DP_HOSTSET);
1213				return (1);
1214			    }
1215			    break;
1216			};
1217			hp = hp->ht_next;
1218		}
1219	}
1220	return (0);
1221}
1222
1223/*
1224 * Scan tree for a host that matches the address.
1225 */
1226int
1227scan_tree(dp, saddr)
1228	struct dirlist *dp;
1229	u_long saddr;
1230{
1231	int defset, hostset;
1232
1233	if (dp) {
1234		if (scan_tree(dp->dp_left, saddr))
1235			return (1);
1236		if (chk_host(dp, saddr, &defset, &hostset))
1237			return (1);
1238		if (scan_tree(dp->dp_right, saddr))
1239			return (1);
1240	}
1241	return (0);
1242}
1243
1244/*
1245 * Traverse the dirlist tree and free it up.
1246 */
1247void
1248free_dir(dp)
1249	struct dirlist *dp;
1250{
1251
1252	if (dp) {
1253		free_dir(dp->dp_left);
1254		free_dir(dp->dp_right);
1255		free_host(dp->dp_hosts);
1256		free((caddr_t)dp);
1257	}
1258}
1259
1260/*
1261 * Parse the option string and update fields.
1262 * Option arguments may either be -<option>=<value> or
1263 * -<option> <value>
1264 */
1265int
1266do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
1267	char **cpp, **endcpp;
1268	struct exportlist *ep;
1269	struct grouplist *grp;
1270	int *has_hostp;
1271	int *exflagsp;
1272	struct ucred *cr;
1273{
1274	char *cpoptarg, *cpoptend;
1275	char *cp, *endcp, *cpopt, savedc, savedc2;
1276	int allflag, usedarg;
1277
1278	cpopt = *cpp;
1279	cpopt++;
1280	cp = *endcpp;
1281	savedc = *cp;
1282	*cp = '\0';
1283	while (cpopt && *cpopt) {
1284		allflag = 1;
1285		usedarg = -2;
1286		if (cpoptend = strchr(cpopt, ',')) {
1287			*cpoptend++ = '\0';
1288			if (cpoptarg = strchr(cpopt, '='))
1289				*cpoptarg++ = '\0';
1290		} else {
1291			if (cpoptarg = strchr(cpopt, '='))
1292				*cpoptarg++ = '\0';
1293			else {
1294				*cp = savedc;
1295				nextfield(&cp, &endcp);
1296				**endcpp = '\0';
1297				if (endcp > cp && *cp != '-') {
1298					cpoptarg = cp;
1299					savedc2 = *endcp;
1300					*endcp = '\0';
1301					usedarg = 0;
1302				}
1303			}
1304		}
1305		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
1306			*exflagsp |= MNT_EXRDONLY;
1307		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
1308		    !(allflag = strcmp(cpopt, "mapall")) ||
1309		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
1310			usedarg++;
1311			parsecred(cpoptarg, cr);
1312			if (allflag == 0) {
1313				*exflagsp |= MNT_EXPORTANON;
1314				opt_flags |= OP_MAPALL;
1315			} else
1316				opt_flags |= OP_MAPROOT;
1317		} else if (!strcmp(cpopt, "kerb") || !strcmp(cpopt, "k")) {
1318			*exflagsp |= MNT_EXKERB;
1319			opt_flags |= OP_KERB;
1320		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
1321			!strcmp(cpopt, "m"))) {
1322			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
1323				syslog(LOG_ERR, "Bad mask: %s", cpoptarg);
1324				return (1);
1325			}
1326			usedarg++;
1327			opt_flags |= OP_MASK;
1328		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
1329			!strcmp(cpopt, "n"))) {
1330			if (grp->gr_type != GT_NULL) {
1331				syslog(LOG_ERR, "Network/host conflict");
1332				return (1);
1333			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
1334				syslog(LOG_ERR, "Bad net: %s", cpoptarg);
1335				return (1);
1336			}
1337			grp->gr_type = GT_NET;
1338			*has_hostp = 1;
1339			usedarg++;
1340			opt_flags |= OP_NET;
1341		} else if (!strcmp(cpopt, "alldirs")) {
1342			opt_flags |= OP_ALLDIRS;
1343		} else if (!strcmp(cpopt, "public")) {
1344			*exflagsp |= MNT_EXPUBLIC;
1345		} else if (!strcmp(cpopt, "webnfs")) {
1346			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
1347			opt_flags |= OP_MAPALL;
1348		} else if (cpoptarg && !strcmp(cpopt, "index")) {
1349			ep->ex_indexfile = strdup(cpoptarg);
1350#ifdef ISO
1351		} else if (cpoptarg && !strcmp(cpopt, "iso")) {
1352			if (get_isoaddr(cpoptarg, grp)) {
1353				syslog(LOG_ERR, "Bad iso addr: %s", cpoptarg);
1354				return (1);
1355			}
1356			*has_hostp = 1;
1357			usedarg++;
1358			opt_flags |= OP_ISO;
1359#endif /* ISO */
1360		} else {
1361			syslog(LOG_ERR, "Bad opt %s", cpopt);
1362			return (1);
1363		}
1364		if (usedarg >= 0) {
1365			*endcp = savedc2;
1366			**endcpp = savedc;
1367			if (usedarg > 0) {
1368				*cpp = cp;
1369				*endcpp = endcp;
1370			}
1371			return (0);
1372		}
1373		cpopt = cpoptend;
1374	}
1375	**endcpp = savedc;
1376	return (0);
1377}
1378
1379/*
1380 * Translate a character string to the corresponding list of network
1381 * addresses for a hostname.
1382 */
1383int
1384get_host(cp, grp, tgrp)
1385	char *cp;
1386	struct grouplist *grp;
1387	struct grouplist *tgrp;
1388{
1389	struct grouplist *checkgrp;
1390	struct hostent *hp, *nhp;
1391	char **addrp, **naddrp;
1392	struct hostent t_host;
1393	int i;
1394	u_long saddr;
1395	char *aptr[2];
1396
1397	if (grp->gr_type != GT_NULL)
1398		return (1);
1399	if ((hp = gethostbyname(cp)) == NULL) {
1400		if (isdigit(*cp)) {
1401			saddr = inet_addr(cp);
1402			if (saddr == -1) {
1403 				syslog(LOG_ERR, "Inet_addr failed for %s", cp);
1404				return (1);
1405			}
1406			if ((hp = gethostbyaddr((caddr_t)&saddr, sizeof (saddr),
1407				AF_INET)) == NULL) {
1408				hp = &t_host;
1409				hp->h_name = cp;
1410				hp->h_addrtype = AF_INET;
1411				hp->h_length = sizeof (u_long);
1412				hp->h_addr_list = aptr;
1413				aptr[0] = (char *)&saddr;
1414				aptr[1] = (char *)NULL;
1415			}
1416		} else {
1417 			syslog(LOG_ERR, "Gethostbyname failed for %s", cp);
1418			return (1);
1419		}
1420	}
1421        /*
1422         * Sanity check: make sure we don't already have an entry
1423         * for this host in the grouplist.
1424         */
1425        checkgrp = tgrp;
1426        while (checkgrp) {
1427		if (checkgrp->gr_type == GT_HOST &&
1428                    checkgrp->gr_ptr.gt_hostent != NULL &&
1429                    !strcmp(checkgrp->gr_ptr.gt_hostent->h_name, hp->h_name)) {
1430                        grp->gr_type = GT_IGNORE;
1431			return(0);
1432		}
1433                checkgrp = checkgrp->gr_next;
1434        }
1435
1436	grp->gr_type = GT_HOST;
1437	nhp = grp->gr_ptr.gt_hostent = (struct hostent *)
1438		malloc(sizeof(struct hostent));
1439	if (nhp == (struct hostent *)NULL)
1440		out_of_mem();
1441	memmove(nhp, hp, sizeof(struct hostent));
1442	i = strlen(hp->h_name)+1;
1443	nhp->h_name = (char *)malloc(i);
1444	if (nhp->h_name == (char *)NULL)
1445		out_of_mem();
1446	memmove(nhp->h_name, hp->h_name, i);
1447	addrp = hp->h_addr_list;
1448	i = 1;
1449	while (*addrp++)
1450		i++;
1451	naddrp = nhp->h_addr_list = (char **)
1452		malloc(i*sizeof(char *));
1453	if (naddrp == (char **)NULL)
1454		out_of_mem();
1455	addrp = hp->h_addr_list;
1456	while (*addrp) {
1457		*naddrp = (char *)
1458		    malloc(hp->h_length);
1459		if (*naddrp == (char *)NULL)
1460		    out_of_mem();
1461		memmove(*naddrp, *addrp, hp->h_length);
1462		addrp++;
1463		naddrp++;
1464	}
1465	*naddrp = (char *)NULL;
1466	if (debug)
1467		fprintf(stderr, "got host %s\n", hp->h_name);
1468	return (0);
1469}
1470
1471/*
1472 * Free up an exports list component
1473 */
1474void
1475free_exp(ep)
1476	struct exportlist *ep;
1477{
1478
1479	if (ep->ex_defdir) {
1480		free_host(ep->ex_defdir->dp_hosts);
1481		free((caddr_t)ep->ex_defdir);
1482	}
1483	if (ep->ex_fsdir)
1484		free(ep->ex_fsdir);
1485	if (ep->ex_indexfile)
1486		free(ep->ex_indexfile);
1487	free_dir(ep->ex_dirl);
1488	free((caddr_t)ep);
1489}
1490
1491/*
1492 * Free hosts.
1493 */
1494void
1495free_host(hp)
1496	struct hostlist *hp;
1497{
1498	struct hostlist *hp2;
1499
1500	while (hp) {
1501		hp2 = hp;
1502		hp = hp->ht_next;
1503		free((caddr_t)hp2);
1504	}
1505}
1506
1507struct hostlist *
1508get_ht()
1509{
1510	struct hostlist *hp;
1511
1512	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
1513	if (hp == (struct hostlist *)NULL)
1514		out_of_mem();
1515	hp->ht_next = (struct hostlist *)NULL;
1516	hp->ht_flag = 0;
1517	return (hp);
1518}
1519
1520#ifdef ISO
1521/*
1522 * Translate an iso address.
1523 */
1524get_isoaddr(cp, grp)
1525	char *cp;
1526	struct grouplist *grp;
1527{
1528	struct iso_addr *isop;
1529	struct sockaddr_iso *isoaddr;
1530
1531	if (grp->gr_type != GT_NULL)
1532		return (1);
1533	if ((isop = iso_addr(cp)) == NULL) {
1534		syslog(LOG_ERR,
1535		    "iso_addr failed, ignored");
1536		return (1);
1537	}
1538	isoaddr = (struct sockaddr_iso *)
1539	    malloc(sizeof (struct sockaddr_iso));
1540	if (isoaddr == (struct sockaddr_iso *)NULL)
1541		out_of_mem();
1542	memset(isoaddr, 0, sizeof(struct sockaddr_iso));
1543	memmove(&isoaddr->siso_addr, isop, sizeof(struct iso_addr));
1544	isoaddr->siso_len = sizeof(struct sockaddr_iso);
1545	isoaddr->siso_family = AF_ISO;
1546	grp->gr_type = GT_ISO;
1547	grp->gr_ptr.gt_isoaddr = isoaddr;
1548	return (0);
1549}
1550#endif	/* ISO */
1551
1552/*
1553 * Out of memory, fatal
1554 */
1555void
1556out_of_mem()
1557{
1558
1559	syslog(LOG_ERR, "Out of memory");
1560	exit(2);
1561}
1562
1563/*
1564 * Do the mount syscall with the update flag to push the export info into
1565 * the kernel.
1566 */
1567int
1568do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
1569	struct exportlist *ep;
1570	struct grouplist *grp;
1571	int exflags;
1572	struct ucred *anoncrp;
1573	char *dirp;
1574	int dirplen;
1575	struct statfs *fsb;
1576{
1577	char *cp = (char *)NULL;
1578	u_long **addrp;
1579	int done;
1580	char savedc = '\0';
1581	struct sockaddr_in sin, imask;
1582	union {
1583		struct ufs_args ua;
1584		struct iso_args ia;
1585		struct mfs_args ma;
1586#ifdef __NetBSD__
1587		struct msdosfs_args da;
1588#endif
1589	} args;
1590	u_long net;
1591
1592	args.ua.fspec = 0;
1593	args.ua.export.ex_flags = exflags;
1594	args.ua.export.ex_anon = *anoncrp;
1595	args.ua.export.ex_indexfile = ep->ex_indexfile;
1596	memset(&sin, 0, sizeof(sin));
1597	memset(&imask, 0, sizeof(imask));
1598	sin.sin_family = AF_INET;
1599	sin.sin_len = sizeof(sin);
1600	imask.sin_family = AF_INET;
1601	imask.sin_len = sizeof(sin);
1602	if (grp->gr_type == GT_HOST)
1603		addrp = (u_long **)grp->gr_ptr.gt_hostent->h_addr_list;
1604	else
1605		addrp = (u_long **)NULL;
1606	done = FALSE;
1607	while (!done) {
1608		switch (grp->gr_type) {
1609		case GT_HOST:
1610			if (addrp) {
1611				sin.sin_addr.s_addr = **addrp;
1612				args.ua.export.ex_addrlen = sizeof(sin);
1613			} else
1614				args.ua.export.ex_addrlen = 0;
1615			args.ua.export.ex_addr = (struct sockaddr *)&sin;
1616			args.ua.export.ex_masklen = 0;
1617			break;
1618		case GT_NET:
1619			if (grp->gr_ptr.gt_net.nt_mask)
1620			    imask.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_mask;
1621			else {
1622			    net = ntohl(grp->gr_ptr.gt_net.nt_net);
1623			    if (IN_CLASSA(net))
1624				imask.sin_addr.s_addr = inet_addr("255.0.0.0");
1625			    else if (IN_CLASSB(net))
1626				imask.sin_addr.s_addr =
1627				    inet_addr("255.255.0.0");
1628			    else
1629				imask.sin_addr.s_addr =
1630				    inet_addr("255.255.255.0");
1631			    grp->gr_ptr.gt_net.nt_mask = imask.sin_addr.s_addr;
1632			}
1633			sin.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_net;
1634			args.ua.export.ex_addr = (struct sockaddr *)&sin;
1635			args.ua.export.ex_addrlen = sizeof (sin);
1636			args.ua.export.ex_mask = (struct sockaddr *)&imask;
1637			args.ua.export.ex_masklen = sizeof (imask);
1638			break;
1639#ifdef ISO
1640		case GT_ISO:
1641			args.ua.export.ex_addr =
1642				(struct sockaddr *)grp->gr_ptr.gt_isoaddr;
1643			args.ua.export.ex_addrlen =
1644				sizeof(struct sockaddr_iso);
1645			args.ua.export.ex_masklen = 0;
1646			break;
1647#endif	/* ISO */
1648		case GT_IGNORE:
1649			return(0);
1650			break;
1651		default:
1652			syslog(LOG_ERR, "Bad grouptype");
1653			if (cp)
1654				*cp = savedc;
1655			return (1);
1656		};
1657
1658		/*
1659		 * XXX:
1660		 * Maybe I should just use the fsb->f_mntonname path instead
1661		 * of looping back up the dirp to the mount point??
1662		 * Also, needs to know how to export all types of local
1663		 * exportable file systems and not just "ufs".
1664		 */
1665		while (mount(fsb->f_fstypename, dirp,
1666		       fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) {
1667			if (cp)
1668				*cp-- = savedc;
1669			else
1670				cp = dirp + dirplen - 1;
1671			if (errno == EPERM) {
1672				syslog(LOG_ERR,
1673				   "Can't change attributes for %s.\n", dirp);
1674				return (1);
1675			}
1676			if (opt_flags & OP_ALLDIRS) {
1677				syslog(LOG_ERR, "Could not remount %s: %m",
1678					dirp);
1679				return (1);
1680			}
1681			/* back up over the last component */
1682			while (*cp == '/' && cp > dirp)
1683				cp--;
1684			while (*(cp - 1) != '/' && cp > dirp)
1685				cp--;
1686			if (cp == dirp) {
1687				if (debug)
1688					fprintf(stderr,"mnt unsucc\n");
1689				syslog(LOG_ERR, "Can't export %s", dirp);
1690				return (1);
1691			}
1692			savedc = *cp;
1693			*cp = '\0';
1694		}
1695		if (addrp) {
1696			++addrp;
1697			if (*addrp == (u_long *)NULL)
1698				done = TRUE;
1699		} else
1700			done = TRUE;
1701	}
1702	if (cp)
1703		*cp = savedc;
1704	return (0);
1705}
1706
1707/*
1708 * Translate a net address.
1709 */
1710int
1711get_net(cp, net, maskflg)
1712	char *cp;
1713	struct netmsk *net;
1714	int maskflg;
1715{
1716	struct netent *np;
1717	long netaddr;
1718	struct in_addr inetaddr, inetaddr2;
1719	char *name;
1720
1721	if (isdigit(*cp) && ((netaddr = inet_network(cp)) != -1)) {
1722		inetaddr = inet_makeaddr(netaddr, 0);
1723		/*
1724		 * Due to arbritrary subnet masks, you don't know how many
1725		 * bits to shift the address to make it into a network,
1726		 * however you do know how to make a network address into
1727		 * a host with host == 0 and then compare them.
1728		 * (What a pest)
1729		 */
1730		if (!maskflg) {
1731			setnetent(0);
1732			while (np = getnetent()) {
1733				inetaddr2 = inet_makeaddr(np->n_net, 0);
1734				if (inetaddr2.s_addr == inetaddr.s_addr)
1735					break;
1736			}
1737			endnetent();
1738		}
1739	} else if ((np = getnetbyname(cp)) != NULL) {
1740		inetaddr = inet_makeaddr(np->n_net, 0);
1741	} else
1742		return (1);
1743
1744	if (maskflg)
1745		net->nt_mask = inetaddr.s_addr;
1746	else {
1747		if (np)
1748			name = np->n_name;
1749		else
1750			name = inet_ntoa(inetaddr);
1751		net->nt_name = (char *)malloc(strlen(name) + 1);
1752		if (net->nt_name == (char *)NULL)
1753			out_of_mem();
1754		strcpy(net->nt_name, name);
1755		net->nt_net = inetaddr.s_addr;
1756	}
1757	return (0);
1758}
1759
1760/*
1761 * Parse out the next white space separated field
1762 */
1763void
1764nextfield(cp, endcp)
1765	char **cp;
1766	char **endcp;
1767{
1768	char *p;
1769
1770	p = *cp;
1771	while (*p == ' ' || *p == '\t')
1772		p++;
1773	if (*p == '\n' || *p == '\0')
1774		*cp = *endcp = p;
1775	else {
1776		*cp = p++;
1777		while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
1778			p++;
1779		*endcp = p;
1780	}
1781}
1782
1783/*
1784 * Get an exports file line. Skip over blank lines and handle line
1785 * continuations.
1786 */
1787int
1788get_line()
1789{
1790	char *p, *cp;
1791	int len;
1792	int totlen, cont_line;
1793
1794	/*
1795	 * Loop around ignoring blank lines and getting all continuation lines.
1796	 */
1797	p = line;
1798	totlen = 0;
1799	do {
1800		if (fgets(p, LINESIZ - totlen, exp_file) == NULL)
1801			return (0);
1802		len = strlen(p);
1803		cp = p + len - 1;
1804		cont_line = 0;
1805		while (cp >= p &&
1806		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
1807			if (*cp == '\\')
1808				cont_line = 1;
1809			cp--;
1810			len--;
1811		}
1812		*++cp = '\0';
1813		if (len > 0) {
1814			totlen += len;
1815			if (totlen >= LINESIZ) {
1816				syslog(LOG_ERR, "Exports line too long");
1817				exit(2);
1818			}
1819			p = cp;
1820		}
1821	} while (totlen == 0 || cont_line);
1822	return (1);
1823}
1824
1825/*
1826 * Parse a description of a credential.
1827 */
1828void
1829parsecred(namelist, cr)
1830	char *namelist;
1831	struct ucred *cr;
1832{
1833	char *name;
1834	int cnt;
1835	char *names;
1836	struct passwd *pw;
1837	struct group *gr;
1838	int ngroups, groups[NGROUPS + 1];
1839
1840	/*
1841	 * Set up the unpriviledged user.
1842	 */
1843	cr->cr_ref = 1;
1844	cr->cr_uid = -2;
1845	cr->cr_groups[0] = -2;
1846	cr->cr_ngroups = 1;
1847	/*
1848	 * Get the user's password table entry.
1849	 */
1850	names = strsep(&namelist, " \t\n");
1851	name = strsep(&names, ":");
1852	if (isdigit(*name) || *name == '-')
1853		pw = getpwuid(atoi(name));
1854	else
1855		pw = getpwnam(name);
1856	/*
1857	 * Credentials specified as those of a user.
1858	 */
1859	if (names == NULL) {
1860		if (pw == NULL) {
1861			syslog(LOG_ERR, "Unknown user: %s", name);
1862			return;
1863		}
1864		cr->cr_uid = pw->pw_uid;
1865		ngroups = NGROUPS + 1;
1866		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
1867			syslog(LOG_ERR, "Too many groups");
1868		/*
1869		 * Convert from int's to gid_t's and compress out duplicate
1870		 */
1871		cr->cr_ngroups = ngroups - 1;
1872		cr->cr_groups[0] = groups[0];
1873		for (cnt = 2; cnt < ngroups; cnt++)
1874			cr->cr_groups[cnt - 1] = groups[cnt];
1875		return;
1876	}
1877	/*
1878	 * Explicit credential specified as a colon separated list:
1879	 *	uid:gid:gid:...
1880	 */
1881	if (pw != NULL)
1882		cr->cr_uid = pw->pw_uid;
1883	else if (isdigit(*name) || *name == '-')
1884		cr->cr_uid = atoi(name);
1885	else {
1886		syslog(LOG_ERR, "Unknown user: %s", name);
1887		return;
1888	}
1889	cr->cr_ngroups = 0;
1890	while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
1891		name = strsep(&names, ":");
1892		if (isdigit(*name) || *name == '-') {
1893			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
1894		} else {
1895			if ((gr = getgrnam(name)) == NULL) {
1896				syslog(LOG_ERR, "Unknown group: %s", name);
1897				continue;
1898			}
1899			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
1900		}
1901	}
1902	if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
1903		syslog(LOG_ERR, "Too many groups");
1904}
1905
1906#define	STRSIZ	(RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
1907/*
1908 * Routines that maintain the remote mounttab
1909 */
1910void
1911get_mountlist()
1912{
1913	struct mountlist *mlp, **mlpp;
1914	char *host, *dirp, *cp;
1915	int len;
1916	char str[STRSIZ];
1917	FILE *mlfile;
1918
1919	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
1920		syslog(LOG_ERR, "Can't open %s", _PATH_RMOUNTLIST);
1921		return;
1922	}
1923	mlpp = &mlhead;
1924	while (fgets(str, STRSIZ, mlfile) != NULL) {
1925		cp = str;
1926		host = strsep(&cp, " \t\n");
1927		dirp = strsep(&cp, " \t\n");
1928		if (host == NULL || dirp == NULL)
1929			continue;
1930		mlp = (struct mountlist *)malloc(sizeof (*mlp));
1931		strncpy(mlp->ml_host, host, RPCMNT_NAMELEN);
1932		mlp->ml_host[RPCMNT_NAMELEN] = '\0';
1933		strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
1934		mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
1935		mlp->ml_next = (struct mountlist *)NULL;
1936		*mlpp = mlp;
1937		mlpp = &mlp->ml_next;
1938	}
1939	fclose(mlfile);
1940}
1941
1942void
1943del_mlist(hostp, dirp)
1944	char *hostp, *dirp;
1945{
1946	struct mountlist *mlp, **mlpp;
1947	struct mountlist *mlp2;
1948	FILE *mlfile;
1949	int fnd = 0;
1950
1951	mlpp = &mlhead;
1952	mlp = mlhead;
1953	while (mlp) {
1954		if (!strcmp(mlp->ml_host, hostp) &&
1955		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
1956			fnd = 1;
1957			mlp2 = mlp;
1958			*mlpp = mlp = mlp->ml_next;
1959			free((caddr_t)mlp2);
1960		} else {
1961			mlpp = &mlp->ml_next;
1962			mlp = mlp->ml_next;
1963		}
1964	}
1965	if (fnd) {
1966		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
1967			syslog(LOG_ERR,"Can't update %s", _PATH_RMOUNTLIST);
1968			return;
1969		}
1970		mlp = mlhead;
1971		while (mlp) {
1972			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
1973			mlp = mlp->ml_next;
1974		}
1975		fclose(mlfile);
1976	}
1977}
1978
1979void
1980add_mlist(hostp, dirp)
1981	char *hostp, *dirp;
1982{
1983	struct mountlist *mlp, **mlpp;
1984	FILE *mlfile;
1985
1986	mlpp = &mlhead;
1987	mlp = mlhead;
1988	while (mlp) {
1989		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
1990			return;
1991		mlpp = &mlp->ml_next;
1992		mlp = mlp->ml_next;
1993	}
1994	mlp = (struct mountlist *)malloc(sizeof (*mlp));
1995	strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
1996	mlp->ml_host[RPCMNT_NAMELEN] = '\0';
1997	strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
1998	mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
1999	mlp->ml_next = (struct mountlist *)NULL;
2000	*mlpp = mlp;
2001	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
2002		syslog(LOG_ERR, "Can't update %s", _PATH_RMOUNTLIST);
2003		return;
2004	}
2005	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2006	fclose(mlfile);
2007}
2008
2009/*
2010 * This function is called via. SIGTERM when the system is going down.
2011 * It sends a broadcast RPCMNT_UMNTALL.
2012 */
2013void
2014send_umntall()
2015{
2016	(void) clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL,
2017		xdr_void, (caddr_t)0, xdr_void, (caddr_t)0, umntall_each);
2018	exit(0);
2019}
2020
2021int
2022umntall_each(resultsp, raddr)
2023	caddr_t resultsp;
2024	struct sockaddr_in *raddr;
2025{
2026	return (1);
2027}
2028
2029/*
2030 * Free up a group list.
2031 */
2032void
2033free_grp(grp)
2034	struct grouplist *grp;
2035{
2036	char **addrp;
2037
2038	if (grp->gr_type == GT_HOST) {
2039		if (grp->gr_ptr.gt_hostent->h_name) {
2040			addrp = grp->gr_ptr.gt_hostent->h_addr_list;
2041			while (addrp && *addrp)
2042				free(*addrp++);
2043			free((caddr_t)grp->gr_ptr.gt_hostent->h_addr_list);
2044			free(grp->gr_ptr.gt_hostent->h_name);
2045		}
2046		free((caddr_t)grp->gr_ptr.gt_hostent);
2047	} else if (grp->gr_type == GT_NET) {
2048		if (grp->gr_ptr.gt_net.nt_name)
2049			free(grp->gr_ptr.gt_net.nt_name);
2050	}
2051#ifdef ISO
2052	else if (grp->gr_type == GT_ISO)
2053		free((caddr_t)grp->gr_ptr.gt_isoaddr);
2054#endif
2055	free((caddr_t)grp);
2056}
2057
2058#ifdef DEBUG
2059void
2060SYSLOG(int pri, const char *fmt, ...)
2061{
2062	va_list ap;
2063
2064	va_start(ap, fmt);
2065	vfprintf(stderr, fmt, ap);
2066	va_end(ap);
2067}
2068#endif /* DEBUG */
2069
2070/*
2071 * Check options for consistency.
2072 */
2073int
2074check_options(dp)
2075	struct dirlist *dp;
2076{
2077
2078	if (dp == (struct dirlist *)NULL)
2079	    return (1);
2080	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL) ||
2081	    (opt_flags & (OP_MAPROOT | OP_KERB)) == (OP_MAPROOT | OP_KERB) ||
2082	    (opt_flags & (OP_MAPALL | OP_KERB)) == (OP_MAPALL | OP_KERB)) {
2083	    syslog(LOG_ERR, "-mapall, -maproot and -kerb mutually exclusive");
2084	    return (1);
2085	}
2086	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
2087	    syslog(LOG_ERR, "-mask requires -net");
2088	    return (1);
2089	}
2090	if ((opt_flags & (OP_NET | OP_ISO)) == (OP_NET | OP_ISO)) {
2091	    syslog(LOG_ERR, "-net and -iso mutually exclusive");
2092	    return (1);
2093	}
2094	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
2095	    syslog(LOG_ERR, "-alldir has multiple directories");
2096	    return (1);
2097	}
2098	return (0);
2099}
2100
2101/*
2102 * Check an absolute directory path for any symbolic links. Return true
2103 * if no symbolic links are found.
2104 */
2105int
2106check_dirpath(dirp)
2107	char *dirp;
2108{
2109	char *cp;
2110	int ret = 1;
2111	struct stat sb;
2112
2113	cp = dirp + 1;
2114	while (*cp && ret) {
2115		if (*cp == '/') {
2116			*cp = '\0';
2117			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2118				ret = 0;
2119			*cp = '/';
2120		}
2121		cp++;
2122	}
2123	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2124		ret = 0;
2125	return (ret);
2126}
2127
2128/*
2129 * Just translate an ascii string to an integer.
2130 */
2131int
2132get_num(cp)
2133	register char *cp;
2134{
2135	register int res = 0;
2136
2137	while (*cp) {
2138		if (*cp < '0' || *cp > '9')
2139			return (-1);
2140		res = res * 10 + (*cp++ - '0');
2141	}
2142	return (res);
2143}
2144