Subject: NFS diskless boot improvement
To: None <tech-kern@sun-lamp.cs.berkeley.edu>
From: Gordon W. Ross <gwr@jericho.mc.com>
List: tech-kern
Date: 03/04/1994 10:30:22
Here are some changes and new code to let you specify the NFS diskless
boot parameters in terms of server path names instead of file handles.
This avoids the need to provide the kernel with NFS file handles,
and is particularly convenient when RB_ASKNAME is set (boot -a).
When RB_ASKNAME set, nfs_mountroot will ask for the root and swap
server addresses and path names, as well as the ifconfig data.

Using server path names instead of file handles also makes the job of
the boot program a little easier.  Both diskless boot protocols
(BOOTP, bootparamd) provide the client with the server path names and
expect the client to do RPCs to mountd to convert these path names to
file handles.  With these changes, nfs_mountroot will call mountd
to get the root and swap file handles.

I also updated mountroot to add its default route the new way.

Goron Ross

# Here is a sample config file section to show how to "wire in"
# default parameters for the nfsdiskless structure.  I still use
# this because I don't feel like porting the boot program yet.
# (Include something like this from your conf file.)

options NFSDISKLESS_HARDWIRE
options NFSDISKLESS_IF_NAME = "\"le0\""
#  Socket address fields:      PORT  IP_ADDRESS
options NFSDISKLESS_IF_ADDR ="192, 233, 20,  193"
options NFSDISKLESS_IF_BADDR="192, 233, 20,  255"
options NFSDISKLESS_IF_MASK ="255, 255, 255, 192"
#options NFSDISKLESS_GATEWAY="192, 233, 20, 194"

# Server address and paths
options NFSDISKLESS_SWAP_SADDR = "192, 233, 20, 194"
options NFSDISKLESS_SWAP_PATH  = "\"/home/rah-swap\""
options NFSDISKLESS_ROOT_SADDR = "192, 233, 20, 194"
options NFSDISKLESS_ROOT_PATH  = "\"/home/rah-root\""

Here are the changes:  (from diff -c)

*** sys/nfs/nfsdiskless.h.orig	Fri Dec 17 02:02:38 1993
--- sys/nfs/nfsdiskless.h	Tue Mar  1 23:48:04 1994
***************
*** 35,40 ****
--- 35,41 ----
   *
   *	from: @(#)nfsdiskless.h	7.1 (Berkeley) 3/4/91
   *	$Id: nfsdiskless.h,v 1.4 1993/07/07 12:06:39 cgd Exp $
+  *  Replaced NFS file handles with path names. -gwr
   */
  
  #ifndef _NFS_NFSDISKLESS_H_
***************
*** 46,65 ****
   * This structure is used by nfs_mountroot() to set up the root and swap
   * vnodes plus do a partial ifconfig(8) and route(8) so that the critical net
   * interface can communicate with the server.
!  * For now it is statically initialized in swapvmunix.c, but someday a primary
!  * bootstrap should fill it in.
   */
  struct nfs_diskless {
! 	struct ifaliasreq myif;		/* Info. for partial ifconfig */
! 	struct sockaddr	mygateway;	/* Default gateway for "route add" */
! 	struct nfs_args	swap_args;	/* Mount args for swap file */
! 	u_char		swap_fh[NFS_FHSIZE]; /* Swap file's file handle */
! 	struct sockaddr	swap_saddr;	/* Address of swap server */
! 	char		swap_hostnam[NFS_NAMELEN]; /* Host name for mount pt */
! 	struct nfs_args	root_args;	/* Mount args for root fs */
! 	u_char		root_fh[NFS_FHSIZE]; /* File handle of root dir */
! 	struct sockaddr	root_saddr;	/* Address of root server */
! 	char		root_hostnam[NFS_NAMELEN]; /* Host name for mount pt */
  };
  
  #endif /* !_NFS_NFSDISKLESS_H_ */
--- 47,87 ----
   * This structure is used by nfs_mountroot() to set up the root and swap
   * vnodes plus do a partial ifconfig(8) and route(8) so that the critical net
   * interface can communicate with the server.
!  *
!  * This structure can be initialized in one of several ways:
!  *  	ask the human (boot -a ...)
!  *  	automatically via BOOTP (see libnetboot)
!  *  	automatically via BOOTPARAMD (someday)
!  *  	statically (from default in swapvnfs.c)
!  * In all cases the initial data provided to the kernel are:
!  * server network address, server path name, and mount args.
!  * The nfs_mountroot function does RPCs to mountd to get the
!  * NFS file handles needed for the initial mounts.
!  * (See: nfs_getfh.c: nfs_getfh, nfs_callrpc, ...)
!  * (Also: swapnfs.c:nfs_diskless )
   */
  struct nfs_diskless {
! 	/**** Info. about the booting host: */
! 	struct ifaliasreq myif; 		/* Info. for partial ifconfig */
! 	struct sockaddr	mygateway;  	/* Default gateway for "route add" */
! 	/**** Info. about the swap server: */
! 	struct nfs_args	swap_args;  	/* Mount args for swap file */
! 	struct sockaddr	swap_saddr; 	/* Address of swap server */
! 	char swap_path[NFS_NAMELEN];	/* Path name on server */
! 	/**** Info. about the root server: */
! 	struct nfs_args	root_args;  	/* Mount args for root dir */
! 	struct sockaddr	root_saddr; 	/* Address of root server */
! 	char root_path[NFS_NAMELEN];	/* Path name on server */
  };
+ 
+ #ifdef KERNEL
+ extern struct nfs_diskless nfs_diskless;
+ /* Function used by mountroot to get its first file handles. */
+ int nfs_boot_getfh(
+ 	struct sockaddr *sa,
+ 	char *path,
+ 	u_char *fhp);
+ void nfs_boot_init();
+ #endif /* KERNEL */
  
  #endif /* !_NFS_NFSDISKLESS_H_ */

*** sys/nfs/swapnfs.c.orig	Tue Mar  1 06:23:05 1994
--- sys/nfs/swapnfs.c	Tue Mar  1 20:08:14 1994
***************
*** 35,40 ****
--- 35,41 ----
   *
   *	from: @(#)nfsswapvmunix.c	7.1 (Berkeley) 3/4/91
   *	$Id: swapnfs.c,v 1.5 1994/03/01 08:00:02 glass Exp $
+  *  Replaced NFS file handles with path names. -gwr
   */
  
  /*
***************
*** 51,56 ****
--- 52,58 ----
  #include <net/if.h>
  
  #include <nfs/nfsv2.h>
+ #include <nfs/nfs.h>
  #include <nfs/nfsdiskless.h>
  
  dev_t	rootdev = NODEV;
***************
*** 63,71 ****
  };
  
  extern int nfs_mountroot();
  
! /* We start with transfer sizes of 4K during boot			*/
! /* as the WD8003 has problems to support 8K of back to back packets	*/
  struct nfs_diskless nfs_diskless = {
  	{ 0 },		/* myif */
  	{ 0 },		/* mygateway */
--- 65,78 ----
  };
  
  extern int nfs_mountroot();
+ int (*mountroot)() = nfs_mountroot;
  
! #ifndef NFSDISKLESS_HARDWIRE
! /*
!  * Be conservative about transfer sizes during boot.
!  * Some ethernet boards (i.e. the WD8003) have problems
!  * supporting 8K of back to back packets
!  */
  struct nfs_diskless nfs_diskless = {
  	{ 0 },		/* myif */
  	{ 0 },		/* mygateway */
***************
*** 81,89 ****
  	    0,		/* retrans */
  	    0		/* hostname */
  	},
- 	{ 0 },		/* swap_fh */
  	{ 0 },		/* swap_saddr */
! 	{ 0 },		/* swap_hostnam */
  	{		/* root_args */
  	    0,		/* addr */
  	    0,		/* sotype */
--- 88,95 ----
  	    0,		/* retrans */
  	    0		/* hostname */
  	},
  	{ 0 },		/* swap_saddr */
! 	{ 0 },		/* swap_path */
  	{		/* root_args */
  	    0,		/* addr */
  	    0,		/* sotype */
***************
*** 96,168 ****
  	    0,		/* retrans */
  	    0		/* hostname */
  	},
- 	{ 0 },		/* root_fh */
  	{ 0 },		/* root_saddr */
! 	{ 0 }		/* root_hostnam */
  };
  
! #ifndef NFSDISKLESS_HARDWIRE
  
! int (*mountroot)() = nfs_mountroot;
!      
! #else
  
! int nfs_hack_mountroot();
! int (*mountroot)() = nfs_hack_mountroot;
! 
! #define NFS_SOCKET 2049
! 
! /* this is an egregious hack necessitated by many unfortunate circumstances*/
! 
! int nfs_hack_mountroot()
! {
!     struct ifaliasreq diskless_if = {
! 	"le0",			/* temporarily */
! 	NFSDISKLESS_IF_ADDR,
! 	NFSDISKLESS_IF_BADDR,
! 	NFSDISKLESS_IF_MASK
! 	};
! #ifdef NFSDISKLESS_GATEWAY
!     struct sockaddr diskless_gateway = NFSDISKLESS_GATEWAY;
  #endif
!     u_char diskless_swap_fh[NFS_FHSIZE] = NFSDISKLESS_SWAP_FH;
!     struct sockaddr diskless_swap_saddr = NFSDISKLESS_SWAP_SADDR;
!     u_char diskless_root_fh[NFS_FHSIZE] = NFSDISKLESS_ROOT_FH;
!     struct sockaddr diskless_root_saddr = NFSDISKLESS_ROOT_SADDR;
!     char *diskless_swap_hostnam = "solipsist";
!     char *diskless_root_hostnam = "solipsist";
  
!     nfs_diskless.swap_saddr.sa_data[0] = nfs_diskless.root_saddr.sa_data[0]
!                 = NFS_SOCKET >> 8;
!     nfs_diskless.swap_saddr.sa_data[1] = nfs_diskless.root_saddr.sa_data[1]
!                 = NFS_SOCKET & 0x00FF;
!     bcopy(&diskless_if, &nfs_diskless.myif, sizeof(diskless_if));
! #ifdef NFSDISKLESS_GATEWAY
!     bcopy(&diskless_gateway, &nfs_diskless.mygateway,
! 	  sizeof(diskless_gateway));
! #endif
!     bcopy(&diskless_swap_saddr, &nfs_diskless.swap_saddr,
! 	  sizeof(diskless_swap_saddr));
!     bcopy(&diskless_root_saddr, &nfs_diskless.root_saddr,
! 	  sizeof(diskless_root_saddr));
!     bcopy(diskless_root_fh, nfs_diskless.root_fh, NFS_FHSIZE);
!     bcopy(diskless_swap_fh, nfs_diskless.swap_fh, NFS_FHSIZE);
!     strcpy(nfs_diskless.swap_hostnam, diskless_swap_hostnam);
!     strcpy(nfs_diskless.root_hostnam, diskless_root_hostnam);
!     nfs_diskless.swap_args.addr = &nfs_diskless.swap_saddr;
!     nfs_diskless.swap_args.fh = (nfsv2fh_t *) nfs_diskless.swap_fh;
!     nfs_diskless.swap_args.sotype = SOCK_DGRAM;
!     nfs_diskless.swap_args.timeo = 10;
!     nfs_diskless.swap_args.retrans = 100;
!     nfs_diskless.swap_args.hostname = nfs_diskless.swap_hostnam;
!     nfs_diskless.root_args.addr = &nfs_diskless.root_saddr;
!     nfs_diskless.root_args.fh = (nfsv2fh_t *) nfs_diskless.root_fh;
!     nfs_diskless.root_args.sotype = SOCK_DGRAM;
!     nfs_diskless.root_args.timeo = 10;
!     nfs_diskless.root_args.retrans = 100;
!     nfs_diskless.root_args.hostname = nfs_diskless.root_hostnam;
! 
!     return nfs_mountroot();
! }
! 
! #endif     
--- 102,160 ----
  	    0,		/* retrans */
  	    0		/* hostname */
  	},
  	{ 0 },		/* root_saddr */
! 	{ 0 }		/* root_path */
  };
  
! #else	/* NFSDISKLESS_HARDWIRE */
  
! /*
!  * Let the config file specify the nfs_diskless info.
!  * If NFSDISKLESS_HARDWIRE is defined, requires all of them.
!  */
  
! struct nfs_diskless nfs_diskless = {
! 	{		/* myif */
! 		NFSDISKLESS_IF_NAME,
! 		{ 16, 2, { 0, 0, NFSDISKLESS_IF_ADDR  }},
! 		{ 16, 2, { 0, 0, NFSDISKLESS_IF_BADDR }},
! 		{ 16, 2, { 0, 0, NFSDISKLESS_IF_MASK  }}
! 	},
! #ifdef	NFSDISKLESS_GATEWAY
! 	{ 16, 2, { 0, 0, NFSDISKLESS_GATEWAY }},
! #else
! 	{ 0 },
  #endif
! 	{		/* swap_args */
! 	    0,		/* addr (set in mountroot) */
! 	    SOCK_DGRAM, /* sotype */
! 	    0,		/* proto */
! 	    0,		/* fh (set in mountroot) */
! 	    NFSMNT_WSIZE|NFSMNT_RSIZE,	/* flags */
! 	    4096,	/* wsize */
! 	    4096,	/* rsize */
! 		NFS_TIMEO,		/* timeo */
! 	    NFS_MAXREXMIT,	/* retrans */
! 	    0		/* hostname (set in mountroot) */
! 	},
! 	/*   Port: 8, 1 is the NFS port (2049) */
! 	{ 16, 2, { 0, 0, NFSDISKLESS_SWAP_SADDR }},
! 	{                NFSDISKLESS_SWAP_PATH },
! 	{		/* root_args */
! 	    0,		/* addr (set in mountroot) */
! 	    SOCK_DGRAM, /* sotype */
! 	    0,		/* proto */
! 	    0,		/* fh (set in mountroot) */
! 	    NFSMNT_WSIZE|NFSMNT_RSIZE,	/* flags */
! 	    4096,	/* wsize */
! 	    4096,	/* rsize */
! 	    NFS_TIMEO,	/* timeo */
! 	    NFS_MAXREXMIT,	/* retrans */
! 	    0		/* hostname (set in mountroot) */
! 	},
! 	/*   Port: 8, 1 is the NFS port (2049) */
! 	{ 16, 2, { 0, 0, NFSDISKLESS_ROOT_SADDR }},
! 	{                NFSDISKLESS_ROOT_PATH }
! };
  
! #endif	/* NFSDISKLESS_HARDWIRE */

*** sys/nfs/nfs_vfsops.c.orig	Tue Dec 21 07:22:50 1993
--- sys/nfs/nfs_vfsops.c	Tue Mar  1 23:47:28 1994
***************
*** 35,40 ****
--- 35,41 ----
   *
   *	from: @(#)nfs_vfsops.c	7.31 (Berkeley) 5/6/91
   *	$Id: nfs_vfsops.c,v 1.11 1993/12/21 08:07:09 cgd Exp $
+  *  Added calls to the new nfs_boot module. -gwr
   */
  
  #include <sys/param.h>
***************
*** 140,267 ****
  
  /*
   * Mount a remote root fs via. nfs. This depends on the info in the
!  * nfs_diskless structure that has been filled in properly by some primary
!  * bootstrap.
   * It goes something like this:
   * - do enough of "ifconfig" by calling ifioctl() so that the system
   *   can talk to the server
   * - If nfs_diskless.mygateway is filled in, use that address as
   *   a default gateway.
-  *   (This is done the 4.3 way with rtioctl() and should be changed)
-  * - hand craft the swap nfs vnode hanging off a fake mount point
   * - build the rootfs mount point and call mountnfs() to do the rest.
   */
  nfs_mountroot()
  {
  	register struct mount *mp;
! 	register struct mbuf *m;
  	struct socket *so;
  	struct vnode *vp;
  	int error;
  
  	/*
  	 * Do enough of ifconfig(8) so that critical net interface can
  	 * talk to the server.
  	 */
! 	if (socreate(nfs_diskless.myif.ifra_addr.sa_family, &so, SOCK_DGRAM, 0))
! 		panic("nfs ifconf");
  	if (ifioctl(so, SIOCAIFADDR, &nfs_diskless.myif))
! 		panic("nfs ifconf2");
! 	soclose(so);
  
  	/*
  	 * If the gateway field is filled in, set it as the default route.
  	 */
! #ifdef COMPAT_43
! 	if (nfs_diskless.mygateway.sa_family == AF_INET) {
! 		struct ortentry rt;
! 		struct sockaddr_in *sin;
! 
! 		sin = (struct sockaddr_in *) &rt.rt_dst;
! 		sin->sin_len = sizeof (struct sockaddr_in);
! 		sin->sin_family = AF_INET;
! 		sin->sin_addr.s_addr = 0;	/* default */
! 		bcopy((caddr_t)&nfs_diskless.mygateway, (caddr_t)&rt.rt_gateway,
! 			sizeof (struct sockaddr_in));
! 		rt.rt_flags = (RTF_UP | RTF_GATEWAY);
! 		if (rtioctl(SIOCADDRT, (caddr_t)&rt, curproc))
! 			panic("nfs root route");
  	}
! #endif	/* COMPAT_43 */
  
  	/*
- 	 * If swapping to an nfs node (indicated by swdevt[0].sw_dev == NODEV):
- 	 * Create a fake mount point just for the swap vnode so that the
- 	 * swap file can be on a different server from the rootfs.
- 	 */
- 	if (swdevt[0].sw_dev == NODEV) {
- 		mp = (struct mount *)malloc((u_long)sizeof(struct mount),
- 			M_MOUNT, M_NOWAIT);
- 		if (mp == NULL)
- 			panic("nfs root mount");
- 		mp->mnt_op = &nfs_vfsops;
- 		mp->mnt_flag = 0;
- 		mp->mnt_exroot = 0;
- 		mp->mnt_mounth = NULLVP;
- 	
- 		/*
- 		 * Set up the diskless nfs_args for the swap mount point
- 		 * and then call mountnfs() to mount it.
- 		 * Since the swap file is not the root dir of a file system,
- 		 * hack it to a regular file.
- 		 */
- 		nfs_diskless.swap_args.fh = (nfsv2fh_t *)nfs_diskless.swap_fh;
- 		MGET(m, MT_SONAME, M_DONTWAIT);
- 		if (m == NULL)
- 			panic("nfs root mbuf");
- 		bcopy((caddr_t)&nfs_diskless.swap_saddr, mtod(m, caddr_t),
- 			nfs_diskless.swap_saddr.sa_len);
- 		m->m_len = nfs_diskless.swap_saddr.sa_len;
- 		if (mountnfs(&nfs_diskless.swap_args, mp, m, "/swap",
- 			nfs_diskless.swap_hostnam, &vp))
- 			panic("nfs swap");
- 		vp->v_type = VREG;
- 		vp->v_flag = 0;
- 		swapdev_vp = vp;
- 		VREF(vp);
- 		swdevt[0].sw_vp = vp;
- 		{
- 			struct vattr attr;
- 
- 			if (nfs_dogetattr(vp,&attr,NOCRED,0,0)) {
- 			    panic("nfs swap");
- 			}
- 			swdevt[0].sw_nblks = attr.va_size / DEV_BSIZE;
- 		}
- 	}
- 
- 	/*
  	 * Create the rootfs mount point.
  	 */
! 	mp = (struct mount *)malloc((u_long)sizeof(struct mount),
! 		M_MOUNT, M_NOWAIT);
! 	if (mp == NULL)
! 		panic("nfs root mount2");
  	mp->mnt_op = &nfs_vfsops;
  	mp->mnt_flag = MNT_RDONLY;
  	mp->mnt_exroot = 0;
- 	mp->mnt_mounth = NULLVP;
  
  	/*
! 	 * Set up the root fs args and call mountnfs() to do the rest.
  	 */
! 	nfs_diskless.root_args.fh = (nfsv2fh_t *)nfs_diskless.root_fh;
! 	MGET(m, MT_SONAME, M_DONTWAIT);
! 	if (m == NULL)
! 		panic("nfs root mbuf2");
! 	bcopy((caddr_t)&nfs_diskless.root_saddr, mtod(m, caddr_t),
! 		nfs_diskless.root_saddr.sa_len);
! 	m->m_len = nfs_diskless.root_saddr.sa_len;
! 	if (mountnfs(&nfs_diskless.root_args, mp, m, "/",
! 		nfs_diskless.root_hostnam, &vp))
! 		panic("nfs root");
  	if (vfs_lock(mp))
! 		panic("nfs root2");
  	rootfs = mp;
  	mp->mnt_next = mp;
  	mp->mnt_prev = mp;
--- 141,239 ----
  
  /*
   * Mount a remote root fs via. nfs. This depends on the info in the
!  * nfs_diskless structure that has been filled in previously.
!  * (see nfsdiskless.h and swapnfs.c)
   * It goes something like this:
   * - do enough of "ifconfig" by calling ifioctl() so that the system
   *   can talk to the server
   * - If nfs_diskless.mygateway is filled in, use that address as
   *   a default gateway.
   * - build the rootfs mount point and call mountnfs() to do the rest.
+  * - hand craft the swap nfs vnode hanging off a fake mount point
   */
  nfs_mountroot()
  {
  	register struct mount *mp;
! 	register struct mbuf *nam;
! 	struct sockaddr *sa;
  	struct socket *so;
  	struct vnode *vp;
+ 	struct nfs_args *argp;
+ 	char server_name[MNAMELEN];
+ 	u_char root_fh[NFS_FHSIZE];
+ 	u_char swap_fh[NFS_FHSIZE];
+ 	int af;				/* address family */
  	int error;
  
+ 	/* Ask the user for the nfs_diskless data (maybe). */
+ 	nfs_boot_init();
+ 
  	/*
  	 * Do enough of ifconfig(8) so that critical net interface can
  	 * talk to the server.
  	 */
! 	af = nfs_diskless.myif.ifra_addr.sa_family;
! 	if (socreate(af, &so, SOCK_DGRAM, 0))
! 		panic("nfs_mountroot: socreate");
  	if (ifioctl(so, SIOCAIFADDR, &nfs_diskless.myif))
! 		panic("nfs_mountroot: SIOCAIFADDR");
  
  	/*
  	 * If the gateway field is filled in, set it as the default route.
  	 */
! 	sa = &nfs_diskless.mygateway;
! 	if (sa->sa_len > 0) {
! 		static struct sockaddr dst; /* default */
! 		dst.sa_len = sizeof(struct sockaddr);
! 		dst.sa_family = af;
! 		/* add, dest, gw, mask, flags, 0 */
! 		error = rtrequest(RTM_ADD, &dst, sa,
! 						   &nfs_diskless.myif.ifra_mask,
! 						   (RTF_UP | RTF_GATEWAY), NULL);
! 		if (error) printf("nfs root route error=%d\n", error);
  	}
! 	soclose(so);
  
  	/*
  	 * Create the rootfs mount point.
+ 	 *
+ 	 * I moved this before the swap mount just so I could do a
+ 	 * return to skip the swap mount instead of having all the
+ 	 * swap mount code inside a big if statement. -gwr
  	 */
! 	MALLOC(mp, struct mount *, sizeof(*mp), M_MOUNT, M_WAITOK);
! 	if (mp == NULL) panic("nfs_mountroot: malloc mount for root");
! 	bzero((caddr_t)mp, sizeof(*mp));
  	mp->mnt_op = &nfs_vfsops;
+ 	mp->mnt_mounth = NULLVP;
  	mp->mnt_flag = MNT_RDONLY;
  	mp->mnt_exroot = 0;
  
+ 	/* Get the root file handle. */
+ 	sa = &nfs_diskless.root_saddr;
+ 	if (nfs_boot_getfh(sa, nfs_diskless.root_path, root_fh))
+ 		panic("nfs_mountroot: getfh for root");
+ 
+ 	/* Setup server address mbuf. */
+ 	nam = m_get(M_WAIT, MT_SONAME);	/* Note: type is LAST! */
+ 	if (nam == NULL) panic("nfs_mountroot: soname for root");
+ 	bcopy((caddr_t)sa, mtod(nam, caddr_t), sa->sa_len);
+ 	nam->m_len = sa->sa_len;
+ 
  	/*
! 	 * Set up the root fs args and call mountnfs()
  	 */
! 	argp = &nfs_diskless.root_args;
! 	argp->addr = sa;
! 	argp->sotype = SOCK_DGRAM;
! 	argp->fh = (nfsv2fh_t *)root_fh;
! 	bzero(server_name, sizeof(server_name));
! 	strcpy(server_name, "root-server");
! 	if (mountnfs(argp, mp, nam, "/", server_name, &vp))
! 		panic("nfs_mountroot: mountnfs root");
! 	nam = NULL; /* mountnfs keeps it... */
  	if (vfs_lock(mp))
! 		panic("nfs_mountroot: vfs_lock root");
  	rootfs = mp;
  	mp->mnt_next = mp;
  	mp->mnt_prev = mp;
***************
*** 269,274 ****
--- 241,306 ----
  	vfs_unlock(mp);
  	rootvp = vp;
  	inittodr((time_t)0);	/* There is no time in the nfs fsstat so ?? */
+ 
+ 	/*
+ 	 * If swapping to an nfs node (indicated by swdevt[0].sw_dev == NODEV):
+ 	 * Create a fake mount point just for the swap vnode so that the
+ 	 * swap file can be on a different server from the rootfs.
+ 	 * The mechanics are the same as for the root mount above.
+ 	 */
+ 	if (swdevt[0].sw_dev != NODEV)
+ 		return 0;
+ 
+ 	/*
+ 	 * Create the swap mount point.
+ 	 */
+ 	MALLOC(mp, struct mount *, sizeof(*mp), M_MOUNT, M_WAITOK);
+ 	if (mp == NULL) panic("nfs_mountroot: malloc mount for swap");
+ 	bzero((caddr_t)mp, sizeof(*mp));
+ 	mp->mnt_op = &nfs_vfsops;
+ 	mp->mnt_mounth = NULLVP;
+ 	mp->mnt_flag = 0;
+ 	mp->mnt_exroot = 0;
+ 
+ 	/* Get the swap file handle. */
+ 	sa = &nfs_diskless.swap_saddr;
+ 	if (nfs_boot_getfh(sa, nfs_diskless.swap_path, swap_fh))
+ 		panic("nfs_mountroot: getfh for swap");
+ 
+ 	/* Setup server address mbuf. */
+ 	nam = m_get(M_WAIT, MT_SONAME);	/* Note: type is LAST! */
+ 	if (nam == NULL) panic("nfs_mountroot: soname for swap");
+ 	bcopy((caddr_t)sa, mtod(nam, caddr_t), sa->sa_len);
+ 	nam->m_len = sa->sa_len;
+ 
+ 	/*
+ 	 * Set up the diskless nfs_args for the swap mount point
+ 	 * and then call mountnfs() to mount it.
+ 	 * Since the swap file is not the root dir of a file system,
+ 	 * hack it to a regular file.
+ 	 */
+ 	argp = &nfs_diskless.swap_args;
+ 	argp->addr = sa;
+ 	argp->sotype = SOCK_DGRAM;
+ 	argp->fh = (nfsv2fh_t *)swap_fh;
+ 	bzero(server_name, sizeof(server_name));
+ 	strcpy(server_name, "swap-server");
+ 	if (mountnfs(argp, mp, nam, "/swap", server_name, &vp))
+ 		panic("nfs_mountroot: mountnfs swap");
+ 	nam = NULL; /* mountnfs keeps it... */
+ 	vp->v_type = VREG;
+ 	vp->v_flag = 0;
+ 	swapdev_vp = vp;
+ 	VREF(vp);
+ 	swdevt[0].sw_vp = vp;
+ 	{
+ 		struct vattr attr;
+ 		if (nfs_dogetattr(vp, &attr, NOCRED, 0, 0)) {
+ 			panic("nfs_mountroot: getattr swap");
+ 		}
+ 		swdevt[0].sw_nblks = attr.va_size / DEV_BSIZE;
+ 	}
+ 
  	return (0);
  }

*** sys/conf/files.newconf.orig	Fri Feb 25 05:59:20 1994
--- sys/conf/files.newconf	Tue Mar  1 23:55:30 1994
***************
*** 238,243 ****
--- 238,244 ----
  file netns/spp_debug.c	 	ns
  file netns/spp_usrreq.c	 	ns
  file nfs/nfs_bio.c		nfsclient
+ file nfs/nfs_boot.c		nfsclient
  file nfs/nfs_node.c		nfsclient
  file nfs/nfs_serv.c		nfsserver
  file nfs/nfs_socket.c	 	nfsserver nfsclient

*** nfs_boot.c.orig	Tue Feb 22 00:37:53 1994
--- nfs_boot.c	Thu Mar  3 20:18:08 1994
***************
*** 0 ****
--- 1,649 ----
+ /*
+  * Copyright (c) 1989 The Regents of the University of California.
+  * All rights reserved.
+  *
+  * This code is derived from software contributed to Berkeley by
+  * Rick Macklem at The University of Guelph.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  * 1. Redistributions of source code must retain the above copyright
+  *    notice, this list of conditions and the following disclaimer.
+  * 2. Redistributions in binary form must reproduce the above copyright
+  *    notice, this list of conditions and the following disclaimer in the
+  *    documentation and/or other materials provided with the distribution.
+  * 3. All advertising materials mentioning features or use of this software
+  *    must display the following acknowledgement:
+  *	This product includes software developed by the University of
+  *	California, Berkeley and its contributors.
+  * 4. Neither the name of the University nor the names of its contributors
+  *    may be used to endorse or promote products derived from this software
+  *    without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+  * SUCH DAMAGE.
+  *
+  *	from: @(#) ?
+  *	$Id: nfs_boot.c $
+  */
+ 
+ /* Much of this was taken from sys/lib/libnetboot/rpc.c */
+ 
+ #include <sys/param.h>
+ #include <sys/conf.h>
+ #include <sys/ioctl.h>
+ #include <sys/proc.h>	/* XXX */
+ #include <sys/mount.h>
+ #include <sys/mbuf.h>
+ #include <sys/socket.h>
+ #include <sys/systm.h>
+ #include <sys/reboot.h>
+ 
+ #include <net/if.h>
+ /* #include <net/route.h> */
+ 
+ #include <netinet/in.h>
+ 
+ #include <nfs/rpcv2.h>
+ #include <nfs/nfsv2.h>
+ #include <nfs/nfsdiskless.h>
+ 
+ /* #define DEBUG 1 */
+ 
+ static int nfs_callrpc(
+ 	struct sockaddr *sa,
+ 	u_long prog,
+ 	u_long vers,
+ 	u_long proc,
+ 	struct mbuf **data,
+ 	int rlen);
+ static int nfs_getport(
+ 	struct sockaddr *sa,
+ 	u_long prog,
+ 	u_long vers,
+ 	u_short *port);
+ 
+ #ifdef DEBUG
+ static void dump_mbuf(struct mbuf *m);
+ #endif
+ 
+ 
+ /*
+  * Get a file handle given a path name.
+  * This is used by mountroot to get its first handles.
+  */
+ int
+ nfs_boot_getfh(sa, path, fhp)
+ 	struct sockaddr *sa;		/* server address */
+ 	char *path;
+ 	u_char *fhp;
+ {
+ 	/* The RPC structures */
+ 	struct sdata {
+ 		u_long  len;
+ 		u_char  path[4];	/* longer, of course */
+ 	} *sdata;
+ 	struct rdata {
+ 		u_long	errno;
+ 		u_char	fh[NFS_FHSIZE];
+ 	} *rdata;
+ 	struct sockaddr_in *sin;
+ 	struct mbuf *m;
+ 	int error, mlen, slen;
+ 
+ #ifdef DEBUG
+ 	printf("nfs_boot_getfh: path=%s\n", path);
+ #endif
+ 
+ 	/*
+ 	 * Validate address family.
+ 	 * Sorry, this is INET specific...
+ 	 */
+ 	if (sa->sa_family != AF_INET) {
+ 		printf("nfs_boot_getfh: address not AF_INET\n");
+ 		return (EAFNOSUPPORT);
+ 	}
+ 
+ 	slen = strlen(path);
+ 	if (slen > (MLEN-4))
+ 		slen = (MLEN-4);
+ 	mlen = 4 + ((slen + 3) & ~3);
+ 
+ 	m = m_get(M_WAIT, MT_DATA);
+ 	if (m == NULL) return ENOBUFS;
+ 	m->m_len = mlen;
+ 	sdata = mtod(m, struct sdata *);
+ 	sdata->len = htonl(slen);
+ 	bcopy(path, sdata->path, slen);
+ 
+ 	/* Do RPC to mountd. */
+ 	error = nfs_callrpc(sa, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT,
+ 						&m, sizeof(*rdata));
+ 	if (error) {
+ 		printf("nfs_boot_getfh: callrpc error=%d\n", error);
+ 		return error;
+ 	}
+ 
+ #ifdef DEBUG
+ 	printf("nfs_boot_getfh: result fh:\n");
+ 	dump_mbuf(m);
+ #endif
+ 	rdata = mtod(m, struct rdata *);
+ 	error = ntohl(rdata->errno);
+ 	if (error)
+ 		printf("nfs_boot_getfh: reply errno=%d\n", error);
+ 	else 
+ 		bcopy(rdata->fh, fhp, NFS_FHSIZE);
+ 	m_freem(m);
+ 
+ 	/*
+ 	 * Set the port to use to reach the NFS server.
+ 	 */
+ 	if (!error) {
+ 		sin = (struct sockaddr_in *)sa;
+ 		error = nfs_getport(sa, NFS_PROG, NFS_VER2, &sin->sin_port);
+ 		if (error) {
+ 			printf("nfs_boot_getfh: getport rc=%d\n", error);
+ 		}
+ 	}
+ 
+ 	return error;
+ }
+ 
+ 
+ /*
+  * Generic RPC headers
+  */
+ struct auth_info {
+ 	int	rp_atype;			/* auth type */
+ 	u_long	rp_alen;		/* auth length */
+ };
+ struct rpc_call {
+ 	u_long	rp_xid;			/* request transaction id */
+ 	int 	rp_direction;	/* call direction (0) */
+ 	u_long	rp_rpcvers;		/* rpc version (2) */
+ 	u_long	rp_prog;		/* program */
+ 	u_long	rp_vers;		/* version */
+ 	u_long	rp_proc;		/* procedure */
+ 	struct	auth_info rp_auth;
+ 	struct	auth_info rp_verf;
+ };
+ struct rpc_reply {
+ 	u_long	rp_xid;			/* request transaction id */
+ 	int	rp_direction;		/* call direction (1) */
+ 	int	rp_astatus;			/* accept status (0: accepted) */
+ 	union {
+ 		u_long	rpu_errno;
+ 		struct {
+ 			struct auth_info rp_auth;
+ 			u_long	rp_rstatus;		/* reply status */
+ 		} rpu_ok;
+ 	} rp_u;
+ };
+ #define MIN_REPLY_HDR 16	/* xid, dir, astat, errno */
+ 
+ /*
+  * Do a remote procedure call (RPC) and wait for its reply.
+  */
+ static int
+ nfs_callrpc(sa, prog, vers, func, data, want)
+ 	struct sockaddr *sa;
+ 	u_long prog, vers, func;
+ 	struct mbuf **data;		/* input/output */
+ 	int want;		/* required response data length */
+ {
+ 	struct socket *so;
+ 	struct sockaddr_in *sin;
+ 	struct timeval *tv;
+ 	struct mbuf *m, *nam, *mhead;
+ 	struct rpc_call *call;
+ 	struct rpc_reply *reply;
+ 	struct uio auio;
+ 	int error, rcvflg, timo, secs, len;
+ 	static u_long xid = ~0xFF;
+ 
+ #if 0 /* def DEBUG */
+ 	printf("nfs_callrpc entered: prog=%d vers=%d pgrm=%d\n",
+ 		   prog, vers, func);
+ #endif
+ 
+ 	/*
+ 	 * Validate address family.
+ 	 * Sorry, this is INET specific...
+ 	 */
+ 	if (sa->sa_family != AF_INET) {
+ 		printf("nfs_callrpc: address not AF_INET\n");
+ 		return (EAFNOSUPPORT);
+ 	}
+ 
+ 	/* Free at end if not null. */
+ 	nam = mhead = NULL;
+ 
+ 	/*
+ 	 * Create socket and set its recieve timeout.
+ 	 */
+ 	if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0))) {
+ 		printf("nfs_callrpc: socreate err=%d\n", error);
+ 		goto out;
+ 	}
+ 	m = m_get(M_WAIT, MT_SOOPTS);
+ 	if (m == NULL) { error = ENOBUFS; goto out;	}
+ 	tv = mtod(m, struct timeval *);
+ 	m->m_len = sizeof(*tv);
+ 	tv->tv_sec = 1;
+ 	tv->tv_usec = 0;
+ 	if ((error = sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m))) {
+ 		printf("nfs_callrpc: sosetopt SO_RCVTIMEO err=%d\n", error);
+ 		goto out;
+ 	}
+ 
+ 	/*
+ 	 * Setup socket address for the server.
+ 	 */
+ 	nam = m_get(M_WAIT, MT_SONAME);
+ 	if (nam == NULL) { error = ENOBUFS; goto out; }
+ 	sin = mtod(nam, struct sockaddr_in *);
+ 	bcopy((caddr_t)sa, (caddr_t)sin, (nam->m_len = sa->sa_len));
+ 
+ 	/*
+ 	 * Set the port number nfs_request() will use.
+ 	 */
+ 	if ((error = nfs_getport(sa, prog, vers, &sin->sin_port))) {
+ 		printf("nfs_callrpc: getport rc=%d\n", error);
+ 		goto out;
+ 	}
+ #ifdef DEBUG
+ 	/* Is this worth checking? -gwr */
+ 	if (MHLEN < sizeof(*call)) {
+ 		printf("nfs_callrpc: MHLEN < %d\n", sizeof(*call));
+ 		goto out;
+ 	}
+ #endif
+ 
+ 	/*
+ 	 * Build the RPC message header.
+ 	 */
+ 	mhead = m_gethdr(M_WAIT, MT_DATA);
+ 	if (mhead == NULL) { error = ENOBUFS; goto out;	}
+ 	mhead->m_len = sizeof(*call);
+ 	call = mtod(mhead, struct rpc_call *);
+ 	bzero((caddr_t)call, sizeof(*call));
+ 	call->rp_xid = ++xid;	/* no need to put in network order */
+ 	/* call->rp_direction = 0; */
+ 	call->rp_rpcvers = htonl(2);
+ 	call->rp_prog = htonl(prog);
+ 	call->rp_vers = htonl(vers);
+ 	call->rp_proc = htonl(func);
+ 	/* call->rp_auth = 0; */
+ 	/* call->rp_verf = 0; */
+ 
+ 	/*
+ 	 * Prepend RPC header and setup packet header.
+ 	 */
+ 	for (len = 0, m = *data; m ; m = m->m_next)
+ 		len += m->m_len;
+ 	mhead->m_next = *data;
+ 	mhead->m_pkthdr.len = mhead->m_len + len;
+ 	mhead->m_pkthdr.rcvif = NULL;
+ 	*data = NULL;
+ 
+ #if 0 /* def DEBUG */
+ 	/* This makes startup rather verbose. */
+ 	printf("nfs_callrpc: sending request:\n");
+ 	dump_mbuf(mhead);
+ #endif
+ 
+ 	/*
+ 	 * Send it, repeatedly, until a reply is received, but
+ 	 * delay each send by an increasing amount. (10 sec. max)
+ 	 * When the send delay hits 10 sec. start complaining.
+ 	 */
+ 	timo = 0;
+ 	for (;;) {
+ 		/* Send RPC request (or re-send). */
+ 		m = m_copym(mhead, 0, M_COPYALL, M_WAIT);
+ 		error = sosend(so, nam, NULL, m, NULL, 0);
+ 		if (error) {
+ 			printf("nfs_callrpc: sosend err=%d\n", error);
+ 			goto out;
+ 		}
+ 		m = NULL;
+ 
+ 		/* Determine new timeout (1 to 10) */
+ 		if (timo < 10)
+ 			timo++;
+ 		else
+ 			printf("RPC timeout for server 0x%X\n",
+ 				   ntohl(sin->sin_addr.s_addr));
+ 
+ 		/* Wait for up to timo seconds for a reply. */
+ 		/* The socket receive timeout was set to 1 second. */
+ 		secs = timo;
+ 		while (secs > 0) {
+ 			auio.uio_resid = len = 1<<16;
+ 			rcvflg = 0;
+ 			error = soreceive(so, NULL, &auio, &m, NULL, &rcvflg);
+ 			if (error == EWOULDBLOCK) {
+ 				secs--;
+ 				continue;
+ 			}
+ 			if (error) {
+ 				printf("nfs_callrpc: soreceive err=%d\n", error);
+ 				goto out;
+ 			}
+ 			len -= auio.uio_resid;
+ 
+ 			/* Is the reply complete and the right one? */
+ 			if (len < MIN_REPLY_HDR) {
+ 				printf("nfs_callrpc: short reply!\n");
+ 				m_freem(m);
+ 				continue;
+ 			}
+ 			if (m->m_len < MIN_REPLY_HDR) {
+ 				m = m_pullup(m, MIN_REPLY_HDR);
+ 				if (!m) {
+ 					printf("nfs_callrpc: m_pullup header\n");
+ 					continue;
+ 				}
+ 			}
+ 			reply = mtod(m, struct rpc_reply *);
+ 			if ((reply->rp_direction == htonl(RPC_REPLY)) &&
+ 				(reply->rp_xid == xid))
+ 			{
+ 				goto gotreply;	/* break two levels */
+ 			}
+ #ifdef DEBUG
+ 			printf("nfs_callrpc: received junk:\n");
+ 			dump_mbuf(m);
+ #endif
+ 		} /* while secs */
+ #ifdef DEBUG
+ 		printf("nfs_callrpc: re-sending xid=%d\n", xid);
+ #endif
+ 	} /* forever send/receive */
+  gotreply:
+ 
+ #if 0 /* def DEBUG */
+ 	/* This makes startup rather verbose. */
+ 	printf("callrpc nfs_request got reply:\n");
+ 	dump_mbuf(m);
+ #endif
+ 
+ 	/*
+ 	 * Got the reply.  Check and strip header.
+ 	 */
+ 	if (reply->rp_astatus != 0) {
+ 		error = reply->rp_u.rpu_errno;
+ 		m_freem(m);
+ 		printf("nfs_callrpc: rpc denied, errno=%d\n", error);
+ 		goto out;
+ 	}
+ 	len = sizeof(*reply);
+ 	if (reply->rp_u.rpu_ok.rp_auth.rp_atype != 0) {
+ 		printf("nfs_callrpc: not auth null?\n");
+ 		len += ntohl(reply->rp_u.rpu_ok.rp_auth.rp_alen);
+ 		len = (len + 3) & ~3;
+ 	}
+ 	m_adj(m, len);
+ 	if (m == NULL) {
+ 		error = ENOBUFS;
+ 		printf("nfs_callrpc: adjust failed\n");
+ 		goto out;
+ 	}
+ 	if (m->m_len < want) {
+ 		m = m_pullup(m, want);
+ 		if (m == NULL) {
+ 			printf("nfs_callrpc: pullup data\n");
+ 			error = ENOBUFS;
+ 			goto out;
+ 		}
+ 	}
+ 	/* result */
+ 	*data = m;
+ 
+  out:
+ 	if (nam) m_freem(nam);
+ 	if (mhead) m_freem(mhead);
+ 	soclose(so);
+ 	return error;
+ }
+ 
+ /* XXX defines we can't easily get from system includes */
+ #define	PMAPPORT		111
+ #define	PMAPPROG		100000
+ #define	PMAPVERS		2
+ #define	PMAPPROC_GETPORT	3
+ 
+ /*
+  * Call portmap to lookup a port number.
+  * Returns port number in host order,
+  * or ZERO if it could not find the port.
+  */
+ static int
+ nfs_getport(sa,  prog, vers, portp)
+ 	struct sockaddr *sa;		/* server address */
+ 	u_long prog, vers;	/* host order */
+ 	u_short *portp;		/* network order */
+ {
+ 	struct sdata {
+ 		u_long	prog;		/* call program */
+ 		u_long	vers;		/* call version */
+ 		u_long	proto;		/* call protocol */
+ 		u_long	port;		/* call port (unused) */
+ 	} *sdata;
+ 	struct rdata {
+ 		u_short pad;
+ 		u_short port;
+ 	} *rdata;
+ 	struct mbuf *m;
+ 	int error;
+ 
+ 	/* The portmapper port is fixed. */
+ 	if (prog == PMAPPROG) {
+ 		*portp = htons(PMAPPORT);
+ 		return 0;
+ 	}
+ 
+ 	m = m_get(M_WAIT, MT_DATA);
+ 	if (m == NULL) return ENOBUFS;
+ 	m->m_len = sizeof(*sdata);
+ 	sdata = mtod(m, struct sdata *);
+ 
+ 	/* Do the RPC to get it. */
+ 	sdata->prog = htonl(prog);
+ 	sdata->vers = htonl(vers);
+ 	sdata->proto = htonl(IPPROTO_UDP);
+ 	sdata->port = 0;
+ 
+ 	error = nfs_callrpc(sa, PMAPPROG, PMAPVERS, PMAPPROC_GETPORT,
+ 						&m, sizeof(*rdata));
+ 	if (error) {
+ 		printf("nfs_getport: callrpc error=%d\n", error);
+ 		return error;
+ 	}
+ 	rdata = mtod(m, struct rdata *);
+ 	*portp = rdata->port;
+ 
+ #ifdef DEBUG
+ 	printf("nfs_getport: prog=%d vers=%d --> port %d\n",
+ 		   prog, vers, ntohs(rdata->port));
+ #endif
+ 
+ 	m_freem(m);
+ 	return 0;
+ }
+ 
+ #ifdef DEBUG
+ static void
+ dump_mbuf(m)
+ 	struct mbuf *m;
+ {
+ 	extern char *hexstr();
+ 	int len, i=0;
+ 	caddr_t p;
+ 
+ 	if (!m) return;
+ 
+ 	len = m->m_len;
+ 	p = mtod(m, caddr_t);
+ 	while (len > 0) {
+ 		printf(" %s", hexstr(*p, 2));
+ 		len--; p++; i++;
+ 		if ((i & 0xF) == 0)
+ 			printf("\n");
+ 		if (len <= 0) {
+ 			m = m->m_next;
+ 			if (!m) break;
+ 			len = m->m_len;
+ 			p = mtod(m, caddr_t);
+ 		}
+ 	}
+ 	if ((i&0xF)) printf("\n");
+ }
+ #endif /* DEBUG */
+ 
+ 
+ static void ask_ipa(char *prompt, struct sockaddr *sa);
+ static void ask_str(char *prompt, char *s, int len);
+ static void getsn(char *s, int len);
+ 
+ /*
+  * Give the user a chance to modify the diskless boot info.
+  */
+ void
+ nfs_boot_init()
+ {
+ 	struct nfs_diskless *dl;
+ 	char ok[4];
+ 
+ 	if ((boothowto & RB_ASKNAME) == 0)
+ 		return;
+ 
+ 	dl = &nfs_diskless;
+ 	do {
+ 	    ask_ipa("client IP addr", &dl->myif.ifra_addr);
+ 		ask_ipa("client netmask", &dl->myif.ifra_mask);
+ 		ask_ipa("broadcast addr", &dl->myif.ifra_broadaddr);
+ 		ask_ipa("  gateway addr", &dl->mygateway);
+ 
+ 		ask_ipa("root server IP", &dl->root_saddr);
+ 		ask_str("root  pathname", dl->root_path, NFS_NAMELEN);
+ 
+ 		ask_ipa("swap server IP", &dl->swap_saddr);
+ 		ask_str("swap  pathname", dl->swap_path, NFS_NAMELEN);
+ 
+ 		printf("correct? [y]");
+ 		getsn(ok, sizeof(ok));
+ 	} while (ok[0] && (ok[0] != 'y'));
+ }
+ 
+ /* This is IP specific (sorry) */
+ static void
+ ask_ipa(prompt, sa)
+ 	char *prompt;
+ 	struct sockaddr *sa;
+ {
+ 	struct sockaddr_in *sin;
+ 	char buf[32];
+ 	u_char *a;
+ 	char *p;
+ 	int n;
+ 
+ 	sin = (struct sockaddr_in *)sa;
+ 	a = (u_char*)&sin->sin_addr;
+ 
+ 	sprintf(buf, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
+ 	ask_str(prompt, buf, sizeof(buf));
+ 
+ 	p = buf;
+ 	while (*p == ' ') p++;
+ 	for (;;) {
+ 		n = 0;
+ 		while ((*p >= '0') && (*p <= '9')) {
+ 			n = (n * 10) + (*p - '0');
+ 			p++;
+ 		}
+ 		*a++ = (n & 0xFF);
+ 		if (*p != '.') break;
+ 		p++;
+ 	}
+ 	if (sin->sin_addr.s_addr != 0) {
+ 		sin->sin_len = sizeof(*sa);
+ 		sin->sin_family = AF_INET;
+ 	}
+ }
+ 
+ static void
+ ask_str(prompt, str, len)
+ 	char *prompt, *str;
+ 	int len;
+ {
+ 	char buf[NFS_NAMELEN];
+ 
+ 	if (len > NFS_NAMELEN) len = NFS_NAMELEN;
+ 	printf("%s: [%s] ", prompt, str);
+ 	getsn(buf, len);
+ 	if (buf[0])
+ 		strncpy(str, buf, len);
+ 	return;
+ }
+ 
+ /* Isn't there something like "gets()" provided somewhere? */
+ static void
+ getsn(cp, len)
+ 	char *cp;
+ 	int len;
+ {
+ 	char *lp;
+ 	int c;
+ 
+ 	len--;	/* leave room for the null */
+ 	lp = cp;
+ 	for (;;) {
+ 		c = cngetc();
+ 		switch (c) {
+ 
+ 		case '\r':
+ 		case '\n':
+ 			*lp = '\0';
+ 			cnputc('\n');
+ 			return;
+ 
+ 		case '\b':
+ 		case '\177':
+ 			if (lp > cp) {
+ 				lp--;
+ 				cnputc('\b');
+ 				cnputc(' ');
+ 				cnputc('\b');
+ 			}
+ 			break;
+ 
+ 		default:
+ 			if (lp < (cp + len)) {
+ 				cnputc(c);
+ 				*lp++ = c;
+ 				continue;
+ 			}
+ 			cnputc('\007');
+ 			break;
+ 
+ 		}
+ 	}
+ }
+ 
+ /*
+  * Local Variables:
+  * tab-width: 4
+  * End:
+  */

------------------------------------------------------------------------------