- Add IPv6 support to nfsd - Allow nfssvc_setfds to properly deal with AF_INET6 - Convert nfssvc_setfds to use getaddrinfo - Move check for active knfsd to helper function - Declare a static common buffer for nfssvc.c routine - Convert rpc.nfsd to use xlog() and add --debug and --syslog options
		
			
				
	
	
		
			1004 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			1004 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| diff --git a/support/include/nfs/nfs.h b/support/include/nfs/nfs.h
 | |
| index 00b0028..a64eb0a 100644
 | |
| --- a/support/include/nfs/nfs.h
 | |
| +++ b/support/include/nfs/nfs.h
 | |
| @@ -42,14 +42,21 @@ struct nfs_fh_old {
 | |
|  #define NFSCTL_GETFD		7	/* get an fh by path (used by mountd) */
 | |
|  #define NFSCTL_GETFS		8	/* get an fh by path with max size (used by mountd) */
 | |
|  
 | |
| +#define NFSCTL_UDPBIT		      (1 << (17 - 1))
 | |
| +#define NFSCTL_TCPBIT		      (1 << (18 - 1))
 | |
| +
 | |
|  #define NFSCTL_VERUNSET(_cltbits, _v) ((_cltbits) &= ~(1 << ((_v) - 1))) 
 | |
| -#define NFSCTL_UDPUNSET(_cltbits)     ((_cltbits) &= ~(1 << (17 - 1))) 
 | |
| -#define NFSCTL_TCPUNSET(_cltbits)     ((_cltbits) &= ~(1 << (18 - 1))) 
 | |
| +#define NFSCTL_UDPUNSET(_cltbits)     ((_cltbits) &= ~NFSCTL_UDPBIT) 
 | |
| +#define NFSCTL_TCPUNSET(_cltbits)     ((_cltbits) &= ~NFSCTL_TCPBIT) 
 | |
|  
 | |
|  #define NFSCTL_VERISSET(_cltbits, _v) ((_cltbits) & (1 << ((_v) - 1))) 
 | |
| -#define NFSCTL_UDPISSET(_cltbits)     ((_cltbits) & (1 << (17 - 1))) 
 | |
| -#define NFSCTL_TCPISSET(_cltbits)     ((_cltbits) & (1 << (18 - 1))) 
 | |
| +#define NFSCTL_UDPISSET(_cltbits)     ((_cltbits) & NFSCTL_UDPBIT) 
 | |
| +#define NFSCTL_TCPISSET(_cltbits)     ((_cltbits) & NFSCTL_TCPBIT) 
 | |
| +
 | |
| +#define NFSCTL_UDPSET(_cltbits)       ((_cltbits) |= NFSCTL_UDPBIT)
 | |
| +#define NFSCTL_TCPSET(_cltbits)       ((_cltbits) |= NFSCTL_TCPBIT)
 | |
|  
 | |
| +#define NFSCTL_ANYPROTO(_cltbits)     ((_cltbits) & (NFSCTL_UDPBIT | NFSCTL_TCPBIT))
 | |
|  #define NFSCTL_ALLBITS (~0)
 | |
|  
 | |
|  /* SVC */
 | |
| diff --git a/support/include/nfslib.h b/support/include/nfslib.h
 | |
| index ae98650..537a31e 100644
 | |
| --- a/support/include/nfslib.h
 | |
| +++ b/support/include/nfslib.h
 | |
| @@ -130,7 +130,6 @@ int			wildmat(char *text, char *pattern);
 | |
|   * nfsd library functions.
 | |
|   */
 | |
|  int			nfsctl(int, struct nfsctl_arg *, union nfsctl_res *);
 | |
| -int			nfssvc(int port, int nrservs, unsigned int versbits, int minorvers4, unsigned int portbits, char *haddr);
 | |
|  int			nfsaddclient(struct nfsctl_client *clp);
 | |
|  int			nfsdelclient(struct nfsctl_client *clp);
 | |
|  int			nfsexport(struct nfsctl_export *exp);
 | |
| diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am
 | |
| index 86f52a1..096f56d 100644
 | |
| --- a/support/nfs/Makefile.am
 | |
| +++ b/support/nfs/Makefile.am
 | |
| @@ -2,7 +2,7 @@
 | |
|  
 | |
|  noinst_LIBRARIES = libnfs.a
 | |
|  libnfs_a_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \
 | |
| -		   xlog.c xcommon.c wildmat.c nfssvc.c nfsclient.c \
 | |
| +		   xlog.c xcommon.c wildmat.c nfsclient.c \
 | |
|  		   nfsexport.c getfh.c nfsctl.c rpc_socket.c getport.c \
 | |
|  		   svc_socket.c cacheio.c closeall.c nfs_mntent.c
 | |
|  
 | |
| diff --git a/support/nfs/nfssvc.c b/support/nfs/nfssvc.c
 | |
| deleted file mode 100644
 | |
| index 33c15a7..0000000
 | |
| --- a/support/nfs/nfssvc.c
 | |
| +++ /dev/null
 | |
| @@ -1,187 +0,0 @@
 | |
| -/*
 | |
| - * support/nfs/nfssvc.c
 | |
| - *
 | |
| - * Run an NFS daemon.
 | |
| - *
 | |
| - * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
 | |
| - */
 | |
| -
 | |
| -#ifdef HAVE_CONFIG_H
 | |
| -#include <config.h>
 | |
| -#endif
 | |
| -
 | |
| -#include <sys/socket.h>
 | |
| -#include <netinet/in.h>
 | |
| -#include <arpa/inet.h>
 | |
| -#include <unistd.h>
 | |
| -#include <fcntl.h>
 | |
| -#include <errno.h>
 | |
| -#include <syslog.h>
 | |
| -
 | |
| -
 | |
| -#include "nfslib.h"
 | |
| -
 | |
| -#define NFSD_PORTS_FILE     "/proc/fs/nfsd/portlist"
 | |
| -#define NFSD_VERS_FILE    "/proc/fs/nfsd/versions"
 | |
| -#define NFSD_THREAD_FILE  "/proc/fs/nfsd/threads"
 | |
| -
 | |
| -static void
 | |
| -nfssvc_setfds(int port, unsigned int ctlbits, char *haddr)
 | |
| -{
 | |
| -	int fd, n, on=1;
 | |
| -	char buf[BUFSIZ];
 | |
| -	int udpfd = -1, tcpfd = -1;
 | |
| -	struct sockaddr_in sin;
 | |
| -
 | |
| -	fd = open(NFSD_PORTS_FILE, O_RDONLY);
 | |
| -	if (fd < 0)
 | |
| -		return;
 | |
| -	n = read(fd, buf, BUFSIZ);
 | |
| -	close(fd);
 | |
| -	if (n != 0)
 | |
| -		return;
 | |
| -	/* there are no ports currently open, so it is safe to
 | |
| -	 * try to open some and pass them through.
 | |
| -	 * Note: If the user explicitly asked for 'udp', then
 | |
| -	 * we should probably check if that is open, and should
 | |
| -	 * open it if not.  However we don't yet.  All sockets
 | |
| -	 * have to be opened when the first daemon is started.
 | |
| -	 */
 | |
| -	fd = open(NFSD_PORTS_FILE, O_WRONLY);
 | |
| -	if (fd < 0)
 | |
| -		return;
 | |
| -	sin.sin_family = AF_INET;
 | |
| -	sin.sin_port   = htons(port);
 | |
| -	sin.sin_addr.s_addr =  inet_addr(haddr);
 | |
| -
 | |
| -	if (NFSCTL_UDPISSET(ctlbits)) {
 | |
| -		udpfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
 | |
| -		if (udpfd < 0) {
 | |
| -			syslog(LOG_ERR, "nfssvc: unable to create UPD socket: "
 | |
| -				"errno %d (%s)\n", errno, strerror(errno));
 | |
| -			exit(1);
 | |
| -		}
 | |
| -		if (bind(udpfd, (struct  sockaddr  *)&sin, sizeof(sin)) < 0){
 | |
| -			syslog(LOG_ERR, "nfssvc: unable to bind UPD socket: "
 | |
| -				"errno %d (%s)\n", errno, strerror(errno));
 | |
| -			exit(1);
 | |
| -		}
 | |
| -	}
 | |
| -
 | |
| -	if (NFSCTL_TCPISSET(ctlbits)) {
 | |
| -		tcpfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 | |
| -		if (tcpfd < 0) {
 | |
| -			syslog(LOG_ERR, "nfssvc: unable to createt tcp socket: "
 | |
| -				"errno %d (%s)\n", errno, strerror(errno));
 | |
| -			exit(1);
 | |
| -		}
 | |
| -		if (setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
 | |
| -			syslog(LOG_ERR, "nfssvc: unable to set SO_REUSEADDR: "
 | |
| -				"errno %d (%s)\n", errno, strerror(errno));
 | |
| -			exit(1);
 | |
| -		}
 | |
| -		if (bind(tcpfd, (struct  sockaddr  *)&sin, sizeof(sin)) < 0){
 | |
| -			syslog(LOG_ERR, "nfssvc: unable to bind TCP socket: "
 | |
| -				"errno %d (%s)\n", errno, strerror(errno));
 | |
| -			exit(1);
 | |
| -		}
 | |
| -		if (listen(tcpfd, 64) < 0){
 | |
| -			syslog(LOG_ERR, "nfssvc: unable to create listening socket: "
 | |
| -				"errno %d (%s)\n", errno, strerror(errno));
 | |
| -			exit(1);
 | |
| -		}
 | |
| -	}
 | |
| -	if (udpfd >= 0) {
 | |
| -		snprintf(buf, BUFSIZ,"%d\n", udpfd); 
 | |
| -		if (write(fd, buf, strlen(buf)) != strlen(buf)) {
 | |
| -			syslog(LOG_ERR, 
 | |
| -			       "nfssvc: writing fds to kernel failed: errno %d (%s)", 
 | |
| -			       errno, strerror(errno));
 | |
| -		}
 | |
| -		close(fd);
 | |
| -		fd = -1;
 | |
| -	}
 | |
| -	if (tcpfd >= 0) {
 | |
| -		if (fd < 0)
 | |
| -			fd = open(NFSD_PORTS_FILE, O_WRONLY);
 | |
| -		snprintf(buf, BUFSIZ,"%d\n", tcpfd); 
 | |
| -		if (write(fd, buf, strlen(buf)) != strlen(buf)) {
 | |
| -			syslog(LOG_ERR, 
 | |
| -			       "nfssvc: writing fds to kernel failed: errno %d (%s)", 
 | |
| -			       errno, strerror(errno));
 | |
| -		}
 | |
| -	}
 | |
| -	close(fd);
 | |
| -
 | |
| -	return;
 | |
| -}
 | |
| -static void
 | |
| -nfssvc_versbits(unsigned int ctlbits, int minorvers4)
 | |
| -{
 | |
| -	int fd, n, off;
 | |
| -	char buf[BUFSIZ], *ptr;
 | |
| -
 | |
| -	ptr = buf;
 | |
| -	off = 0;
 | |
| -	fd = open(NFSD_VERS_FILE, O_WRONLY);
 | |
| -	if (fd < 0)
 | |
| -		return;
 | |
| -
 | |
| -	for (n = NFSD_MINVERS; n <= NFSD_MAXVERS; n++) {
 | |
| -		if (NFSCTL_VERISSET(ctlbits, n))
 | |
| -		    off += snprintf(ptr+off, BUFSIZ - off, "+%d ", n);
 | |
| -		else
 | |
| -		    off += snprintf(ptr+off, BUFSIZ - off, "-%d ", n);
 | |
| -	}
 | |
| -	n = minorvers4 >= 0 ? minorvers4 : -minorvers4;
 | |
| -	if (n >= NFSD_MINMINORVERS4 && n <= NFSD_MAXMINORVERS4)
 | |
| -		    off += snprintf(ptr+off, BUFSIZ - off, "%c4.%d",
 | |
| -				    minorvers4 > 0 ? '+' : '-',
 | |
| -				    n);
 | |
| -	snprintf(ptr+off, BUFSIZ - off, "\n");
 | |
| -	if (write(fd, buf, strlen(buf)) != strlen(buf)) {
 | |
| -		syslog(LOG_ERR, "nfssvc: Setting version failed: errno %d (%s)", 
 | |
| -			errno, strerror(errno));
 | |
| -	}
 | |
| -	close(fd);
 | |
| -
 | |
| -	return;
 | |
| -}
 | |
| -int
 | |
| -nfssvc(int port, int nrservs, unsigned int versbits, int minorvers4,
 | |
| -	unsigned protobits, char *haddr)
 | |
| -{
 | |
| -	struct nfsctl_arg	arg;
 | |
| -	int fd;
 | |
| -
 | |
| -	/* Note: must set versions before fds so that
 | |
| -	 * the ports get registered with portmap against correct
 | |
| -	 * versions
 | |
| -	 */
 | |
| -	nfssvc_versbits(versbits, minorvers4);
 | |
| -	nfssvc_setfds(port, protobits, haddr);
 | |
| -
 | |
| -	fd = open(NFSD_THREAD_FILE, O_WRONLY);
 | |
| -	if (fd < 0)
 | |
| -		fd = open("/proc/fs/nfs/threads", O_WRONLY);
 | |
| -	if (fd >= 0) {
 | |
| -		/* 2.5+ kernel with nfsd filesystem mounted.
 | |
| -		 * Just write the number in.
 | |
| -		 * Cannot handle port number yet, but does anyone care?
 | |
| -		 */
 | |
| -		char buf[20];
 | |
| -		int n;
 | |
| -		snprintf(buf, 20,"%d\n", nrservs);
 | |
| -		n = write(fd, buf, strlen(buf));
 | |
| -		close(fd);
 | |
| -		if (n != strlen(buf))
 | |
| -			return -1;
 | |
| -		else
 | |
| -			return 0;
 | |
| -	}
 | |
| -
 | |
| -	arg.ca_version = NFSCTL_VERSION;
 | |
| -	arg.ca_svc.svc_nthreads = nrservs;
 | |
| -	arg.ca_svc.svc_port = port;
 | |
| -	return nfsctl(NFSCTL_SVC, &arg, NULL);
 | |
| -}
 | |
| diff --git a/utils/nfsd/Makefile.am b/utils/nfsd/Makefile.am
 | |
| index 445e3fd..c4c6fb0 100644
 | |
| --- a/utils/nfsd/Makefile.am
 | |
| +++ b/utils/nfsd/Makefile.am
 | |
| @@ -7,10 +7,8 @@ RPCPREFIX	= rpc.
 | |
|  KPREFIX		= @kprefix@
 | |
|  sbin_PROGRAMS	= nfsd
 | |
|  
 | |
| -nfsd_SOURCES = nfsd.c
 | |
| -nfsd_LDADD = ../../support/export/libexport.a \
 | |
| -	     ../../support/nfs/libnfs.a \
 | |
| -	     ../../support/misc/libmisc.a
 | |
| +nfsd_SOURCES = nfsd.c nfssvc.c
 | |
| +nfsd_LDADD = ../../support/nfs/libnfs.a
 | |
|  
 | |
|  MAINTAINERCLEANFILES = Makefile.in
 | |
|  
 | |
| diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c
 | |
| index e3c0094..650c593 100644
 | |
| --- a/utils/nfsd/nfsd.c
 | |
| +++ b/utils/nfsd/nfsd.c
 | |
| @@ -18,13 +18,14 @@
 | |
|  #include <string.h>
 | |
|  #include <errno.h>
 | |
|  #include <getopt.h>
 | |
| -#include <syslog.h>
 | |
|  #include <netdb.h>
 | |
|  #include <sys/socket.h>
 | |
|  #include <netinet/in.h>
 | |
|  #include <arpa/inet.h>
 | |
|  
 | |
|  #include "nfslib.h"
 | |
| +#include "nfssvc.h"
 | |
| +#include "xlog.h"
 | |
|  
 | |
|  static void	usage(const char *);
 | |
|  
 | |
| @@ -37,47 +38,116 @@ static struct option longopts[] =
 | |
|  	{ "no-udp", 0, 0, 'U' },
 | |
|  	{ "port", 1, 0, 'P' },
 | |
|  	{ "port", 1, 0, 'p' },
 | |
| +	{ "debug", 0, 0, 'd' },
 | |
| +	{ "syslog", 0, 0, 's' },
 | |
|  	{ NULL, 0, 0, 0 }
 | |
|  };
 | |
| -unsigned int protobits = NFSCTL_ALLBITS;
 | |
| -unsigned int versbits = NFSCTL_ALLBITS;
 | |
| -int minorvers4 = NFSD_MAXMINORVERS4;		/* nfsv4 minor version */
 | |
| -char *haddr = NULL;
 | |
| +
 | |
| +/* given a family and ctlbits, disable any that aren't listed in netconfig */
 | |
| +#ifdef HAVE_LIBTIRPC
 | |
| +static void
 | |
| +nfsd_enable_protos(unsigned int *proto4, unsigned int *proto6)
 | |
| +{
 | |
| +	struct netconfig *nconf;
 | |
| +	unsigned int *famproto;
 | |
| +	void *handle;
 | |
| +
 | |
| +	xlog(D_GENERAL, "Checking netconfig for visible protocols.");
 | |
| +
 | |
| +	handle = setnetconfig();
 | |
| +	while((nconf = getnetconfig(handle))) {
 | |
| +		if (!(nconf->nc_flag & NC_VISIBLE))
 | |
| +			continue;
 | |
| +
 | |
| +		if (!strcmp(nconf->nc_protofmly, NC_INET))
 | |
| +			famproto = proto4;
 | |
| +		else if (!strcmp(nconf->nc_protofmly, NC_INET6))
 | |
| +			famproto = proto6;
 | |
| +		else
 | |
| +			continue;
 | |
| +
 | |
| +		if (!strcmp(nconf->nc_proto, NC_TCP))
 | |
| +			NFSCTL_TCPSET(*famproto);
 | |
| +		else if (!strcmp(nconf->nc_proto, NC_UDP))
 | |
| +			NFSCTL_UDPSET(*famproto);
 | |
| +
 | |
| +		xlog(D_GENERAL, "Enabling %s %s.", nconf->nc_protofmly,
 | |
| +			nconf->nc_proto);
 | |
| +	}
 | |
| +	endnetconfig(handle);
 | |
| +	return;
 | |
| +}
 | |
| +#else /* HAVE_LIBTIRPC */
 | |
| +static void
 | |
| +nfsd_enable_protos(unsigned int *proto4, unsigned int *proto6)
 | |
| +{
 | |
| +	/* Enable all IPv4 protocols if no TIRPC support */
 | |
| +	*proto4 = NFSCTL_ALLBITS;
 | |
| +	*proto6 = 0;
 | |
| +}
 | |
| +#endif /* HAVE_LIBTIRPC */
 | |
|  
 | |
|  int
 | |
|  main(int argc, char **argv)
 | |
|  {
 | |
| -	int	count = 1, c, error, port, fd, found_one;
 | |
| -	struct servent *ent;
 | |
| -	struct hostent *hp;
 | |
| -	char *p;
 | |
| -
 | |
| -	ent = getservbyname ("nfs", "udp");
 | |
| -	if (ent != NULL)
 | |
| -		port = ntohs (ent->s_port);
 | |
| -	else
 | |
| -		port = 2049;
 | |
| -
 | |
| -	while ((c = getopt_long(argc, argv, "H:hN:p:P:TU", longopts, NULL)) != EOF) {
 | |
| +	int	count = 1, c, error = 0, portnum = 0, fd, found_one;
 | |
| +	char *p, *progname, *port;
 | |
| +	char *haddr = NULL;
 | |
| +	int	socket_up = 0;
 | |
| +	int minorvers4 = NFSD_MAXMINORVERS4;	/* nfsv4 minor version */
 | |
| +	unsigned int versbits = NFSCTL_ALLBITS;
 | |
| +	unsigned int protobits = NFSCTL_ALLBITS;
 | |
| +	unsigned int proto4 = 0;
 | |
| +	unsigned int proto6 = 0;
 | |
| +
 | |
| +	progname = strdup(basename(argv[0]));
 | |
| +	if (!progname) {
 | |
| +		fprintf(stderr, "%s: unable to allocate memory.\n", argv[0]);
 | |
| +		exit(1);
 | |
| +	}
 | |
| +
 | |
| +	port = strdup("nfs");
 | |
| +	if (!port) {
 | |
| +		fprintf(stderr, "%s: unable to allocate memory.\n", progname);
 | |
| +		exit(1);
 | |
| +	}
 | |
| +
 | |
| +	xlog_syslog(0);
 | |
| +	xlog_stderr(1);
 | |
| +
 | |
| +	while ((c = getopt_long(argc, argv, "dH:hN:p:P:sTU", longopts, NULL)) != EOF) {
 | |
|  		switch(c) {
 | |
| +		case 'd':
 | |
| +			xlog_config(D_ALL, 1);
 | |
| +			break;
 | |
|  		case 'H':
 | |
| -			if (inet_addr(optarg) != INADDR_NONE) {
 | |
| -				haddr = strdup(optarg);
 | |
| -			} else if ((hp = gethostbyname(optarg)) != NULL) {
 | |
| -				haddr = inet_ntoa((*(struct in_addr*)(hp->h_addr_list[0])));
 | |
| -			} else {
 | |
| -				fprintf(stderr, "%s: Unknown hostname: %s\n",
 | |
| -					argv[0], optarg);
 | |
| -				usage(argv [0]);
 | |
| +			/*
 | |
| +			 * for now, this only handles one -H option. Use the
 | |
| +			 * last one specified.
 | |
| +			 */
 | |
| +			free(haddr);
 | |
| +			haddr = strdup(optarg);
 | |
| +			if (!haddr) {
 | |
| +				fprintf(stderr, "%s: unable to allocate "
 | |
| +					"memory.\n", progname);
 | |
| +				exit(1);
 | |
|  			}
 | |
|  			break;
 | |
|  		case 'P':	/* XXX for nfs-server compatibility */
 | |
|  		case 'p':
 | |
| -			port = atoi(optarg);
 | |
| -			if (port <= 0 || port > 65535) {
 | |
| +			/* only the last -p option has any effect */
 | |
| +			portnum = atoi(optarg);
 | |
| +			if (portnum <= 0 || portnum > 65535) {
 | |
|  				fprintf(stderr, "%s: bad port number: %s\n",
 | |
| -					argv[0], optarg);
 | |
| -				usage(argv [0]);
 | |
| +					progname, optarg);
 | |
| +				usage(progname);
 | |
| +			}
 | |
| +			free(port);
 | |
| +			port = strdup(optarg);
 | |
| +			if (!port) {
 | |
| +				fprintf(stderr, "%s: unable to allocate "
 | |
| +						"memory.\n", progname);
 | |
| +				exit(1);
 | |
|  			}
 | |
|  			break;
 | |
|  		case 'N':
 | |
| @@ -96,80 +166,133 @@ main(int argc, char **argv)
 | |
|  				exit(1);
 | |
|  			}
 | |
|  			break;
 | |
| +		case 's':
 | |
| +			xlog_syslog(1);
 | |
| +			xlog_stderr(0);
 | |
| +			break;
 | |
|  		case 'T':
 | |
| -				NFSCTL_TCPUNSET(protobits);
 | |
| -				break;
 | |
| +			NFSCTL_TCPUNSET(protobits);
 | |
| +			break;
 | |
|  		case 'U':
 | |
| -				NFSCTL_UDPUNSET(protobits);
 | |
| -				break;
 | |
| +			NFSCTL_UDPUNSET(protobits);
 | |
| +			break;
 | |
|  		default:
 | |
|  			fprintf(stderr, "Invalid argument: '%c'\n", c);
 | |
|  		case 'h':
 | |
| -			usage(argv[0]);
 | |
| +			usage(progname);
 | |
|  		}
 | |
|  	}
 | |
| -	/*
 | |
| -	 * Do some sanity checking, if the ctlbits are set
 | |
| -	 */
 | |
| -	if (!NFSCTL_UDPISSET(protobits) && !NFSCTL_TCPISSET(protobits)) {
 | |
| -		fprintf(stderr, "invalid protocol specified\n");
 | |
| -		exit(1);
 | |
| +
 | |
| +	if (optind < argc) {
 | |
| +		if ((count = atoi(argv[optind])) < 0) {
 | |
| +			/* insane # of servers */
 | |
| +			fprintf(stderr,
 | |
| +				"%s: invalid server count (%d), using 1\n",
 | |
| +				argv[0], count);
 | |
| +			count = 1;
 | |
| +		} else if (count == 0) {
 | |
| +			/*
 | |
| +			 * don't bother setting anything else if the threads
 | |
| +			 * are coming down anyway.
 | |
| +			 */
 | |
| +			socket_up = 1;
 | |
| +			goto set_threads;
 | |
| +		}
 | |
|  	}
 | |
| +
 | |
| +	xlog_open(progname);
 | |
| +
 | |
| +	nfsd_enable_protos(&proto4, &proto6);
 | |
| +
 | |
| +	if (!NFSCTL_TCPISSET(protobits)) {
 | |
| +		NFSCTL_TCPUNSET(proto4);
 | |
| +		NFSCTL_TCPUNSET(proto6);
 | |
| +	}
 | |
| +
 | |
| +	if (!NFSCTL_UDPISSET(protobits)) {
 | |
| +		NFSCTL_UDPUNSET(proto4);
 | |
| +		NFSCTL_UDPUNSET(proto6);
 | |
| +	}
 | |
| +
 | |
| +	/* make sure that at least one version is enabled */
 | |
|  	found_one = 0;
 | |
|  	for (c = NFSD_MINVERS; c <= NFSD_MAXVERS; c++) {
 | |
|  		if (NFSCTL_VERISSET(versbits, c))
 | |
|  			found_one = 1;
 | |
|  	}
 | |
|  	if (!found_one) {
 | |
| -		fprintf(stderr, "no version specified\n");
 | |
| +		xlog(L_ERROR, "no version specified");
 | |
|  		exit(1);
 | |
|  	}			
 | |
|  
 | |
| -	if (NFSCTL_VERISSET(versbits, 4) && !NFSCTL_TCPISSET(protobits)) {
 | |
| -		fprintf(stderr, "version 4 requires the TCP protocol\n");
 | |
| +	if (NFSCTL_VERISSET(versbits, 4) &&
 | |
| +	    !NFSCTL_TCPISSET(proto4) &&
 | |
| +	    !NFSCTL_TCPISSET(proto6)) {
 | |
| +		xlog(L_ERROR, "version 4 requires the TCP protocol");
 | |
|  		exit(1);
 | |
|  	}
 | |
| -	if (haddr == NULL) {
 | |
| -		struct in_addr in = {INADDR_ANY}; 
 | |
| -		haddr = strdup(inet_ntoa(in));
 | |
| -	}
 | |
|  
 | |
|  	if (chdir(NFS_STATEDIR)) {
 | |
| -		fprintf(stderr, "%s: chdir(%s) failed: %s\n",
 | |
| -			argv [0], NFS_STATEDIR, strerror(errno));
 | |
| +		xlog(L_ERROR, "chdir(%s) failed: %m", NFS_STATEDIR);
 | |
|  		exit(1);
 | |
|  	}
 | |
|  
 | |
| -	if (optind < argc) {
 | |
| -		if ((count = atoi(argv[optind])) < 0) {
 | |
| -			/* insane # of servers */
 | |
| -			fprintf(stderr,
 | |
| -				"%s: invalid server count (%d), using 1\n",
 | |
| -				argv[0], count);
 | |
| -			count = 1;
 | |
| -		}
 | |
| +	/* can only change number of threads if nfsd is already up */
 | |
| +	if (nfssvc_inuse()) {
 | |
| +		socket_up = 1;
 | |
| +		goto set_threads;
 | |
|  	}
 | |
| -	/* KLUDGE ALERT:
 | |
| -	   Some kernels let nfsd kernel threads inherit open files
 | |
| -	   from the program that spawns them (i.e. us).  So close
 | |
| -	   everything before spawning kernel threads.  --Chip */
 | |
| +
 | |
| +	/*
 | |
| +	 * must set versions before the fd's so that the right versions get
 | |
| +	 * registered with rpcbind. Note that on older kernels w/o the right
 | |
| +	 * interfaces, these are a no-op.
 | |
| +	 */
 | |
| +	nfssvc_setvers(versbits, minorvers4);
 | |
| + 
 | |
| +	error = nfssvc_set_sockets(AF_INET, proto4, haddr, port);
 | |
| +	if (!error)
 | |
| +		socket_up = 1;
 | |
| +
 | |
| +#ifdef IPV6_SUPPORTED
 | |
| +	error = nfssvc_set_sockets(AF_INET6, proto6, haddr, port);
 | |
| +	if (!error)
 | |
| +		socket_up = 1;
 | |
| +#endif /* IPV6_SUPPORTED */
 | |
| +
 | |
| +set_threads:
 | |
| +	/* don't start any threads if unable to hand off any sockets */
 | |
| +	if (!socket_up) {
 | |
| +		xlog(L_ERROR, "unable to set any sockets for nfsd");
 | |
| +		goto out;
 | |
| +	}
 | |
| +	error = 0;
 | |
| +
 | |
| +	/*
 | |
| +	 * KLUDGE ALERT:
 | |
| +	 * Some kernels let nfsd kernel threads inherit open files
 | |
| +	 * from the program that spawns them (i.e. us).  So close
 | |
| +	 * everything before spawning kernel threads.  --Chip
 | |
| +	 */
 | |
|  	fd = open("/dev/null", O_RDWR);
 | |
|  	if (fd == -1)
 | |
| -		perror("/dev/null");
 | |
| +		xlog(L_ERROR, "Unable to open /dev/null: %m");
 | |
|  	else {
 | |
| +		/* switch xlog output to syslog since stderr is being closed */
 | |
| +		xlog_syslog(1);
 | |
| +		xlog_stderr(0);
 | |
|  		(void) dup2(fd, 0);
 | |
|  		(void) dup2(fd, 1);
 | |
|  		(void) dup2(fd, 2);
 | |
|  	}
 | |
|  	closeall(3);
 | |
|  
 | |
| -	openlog("nfsd", LOG_PID, LOG_DAEMON);
 | |
| -	if ((error = nfssvc(port, count, versbits, minorvers4, protobits, haddr)) < 0) {
 | |
| -		int e = errno;
 | |
| -		syslog(LOG_ERR, "nfssvc: %s", strerror(e));
 | |
| -		closelog();
 | |
| -	}
 | |
| -
 | |
| +	if ((error = nfssvc_threads(portnum, count)) < 0)
 | |
| +		xlog(L_ERROR, "error starting threads: errno %d (%m)", errno);
 | |
| +out:
 | |
| +	free(port);
 | |
| +	free(haddr);
 | |
| +	free(progname);
 | |
|  	return (error != 0);
 | |
|  }
 | |
|  
 | |
| @@ -177,7 +300,7 @@ static void
 | |
|  usage(const char *prog)
 | |
|  {
 | |
|  	fprintf(stderr, "Usage:\n"
 | |
| -		"%s [-H hostname] [-p|-P|--port port] [-N|--no-nfs-version version ] [-T|--no-tcp] [-U|--no-udp] nrservs\n", 
 | |
| +		"%s [-d|--debug] [-H hostname] [-p|-P|--port port] [-N|--no-nfs-version version ] [-s|--syslog] [-T|--no-tcp] [-U|--no-udp] nrservs\n", 
 | |
|  		prog);
 | |
|  	exit(2);
 | |
|  }
 | |
| diff --git a/utils/nfsd/nfsd.man b/utils/nfsd/nfsd.man
 | |
| index 4eb3e21..d8988d2 100644
 | |
| --- a/utils/nfsd/nfsd.man
 | |
| +++ b/utils/nfsd/nfsd.man
 | |
| @@ -13,8 +13,9 @@ The
 | |
|  program implements the user level part of the NFS service. The
 | |
|  main functionality is handled by the
 | |
|  .B nfsd
 | |
| -kernel module; the user space program merely starts the specified
 | |
| -number of kernel threads.
 | |
| +kernel module. The user space program merely specifies what sort of sockets
 | |
| +the kernel service should listen on, what NFS versions it should support, and
 | |
| +how many kernel threads it should use.
 | |
|  .P
 | |
|  The
 | |
|  .B rpc.mountd
 | |
| @@ -22,6 +23,9 @@ server provides an ancillary service needed to satisfy mount requests
 | |
|  by NFS clients.
 | |
|  .SH OPTIONS
 | |
|  .TP
 | |
| +.B \-d " or " \-\-debug
 | |
| +enable logging of debugging messages
 | |
| +.TP
 | |
|  .B \-H " or " \-\-host  hostname
 | |
|  specify a particular hostname (or address) that NFS requests will
 | |
|  be accepted on. By default,
 | |
| @@ -45,6 +49,14 @@ does not offer certain versions of NFS. The current version of
 | |
|  .B rpc.nfsd
 | |
|  can support both NFS version 2,3 and the newer version 4.
 | |
|  .TP
 | |
| +.B \-s " or " \-\-syslog
 | |
| +By default,
 | |
| +.B rpc.nfsd
 | |
| +logs error messages (and debug messages, if enabled) to stderr. This option makes 
 | |
| +.B rpc.nfsd
 | |
| +log these messages to syslog instead. Note that errors encountered during
 | |
| +option processing will still be logged to stderr regardless of this option.
 | |
| +.TP
 | |
|  .B \-T " or " \-\-no-tcp
 | |
|  Disable 
 | |
|  .B rpc.nfsd 
 | |
| @@ -75,12 +87,19 @@ In particular
 | |
|  .B rpc.nfsd 0
 | |
|  will stop all threads and thus close any open connections.
 | |
|  
 | |
| +.SH NOTES
 | |
| +If the program is built with TI-RPC support, it will enable any protocol and
 | |
| +address family combinations that are marked visible in the
 | |
| +.B netconfig
 | |
| +database.
 | |
| +
 | |
|  .SH SEE ALSO
 | |
|  .BR rpc.mountd (8),
 | |
|  .BR exports (5),
 | |
|  .BR exportfs (8),
 | |
|  .BR rpc.rquotad (8),
 | |
| -.BR nfsstat (8).
 | |
| +.BR nfsstat (8),
 | |
| +.BR netconfig(5).
 | |
|  .SH AUTHOR
 | |
|  Olaf Kirch, Bill Hawes, H. J. Lu, G. Allan Morris III,
 | |
|  and a host of others.
 | |
| diff --git a/utils/nfsd/nfssvc.c b/utils/nfsd/nfssvc.c
 | |
| new file mode 100644
 | |
| index 0000000..ee862b2
 | |
| --- /dev/null
 | |
| +++ b/utils/nfsd/nfssvc.c
 | |
| @@ -0,0 +1,289 @@
 | |
| +/*
 | |
| + * utils/nfsd/nfssvc.c
 | |
| + *
 | |
| + * Run an NFS daemon.
 | |
| + *
 | |
| + * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
 | |
| + */
 | |
| +
 | |
| +#ifdef HAVE_CONFIG_H
 | |
| +#include <config.h>
 | |
| +#endif
 | |
| +
 | |
| +#include <sys/types.h>
 | |
| +#include <sys/socket.h>
 | |
| +#include <netdb.h>
 | |
| +#include <netinet/in.h>
 | |
| +#include <arpa/inet.h>
 | |
| +#include <unistd.h>
 | |
| +#include <fcntl.h>
 | |
| +#include <errno.h>
 | |
| +
 | |
| +#include "nfslib.h"
 | |
| +#include "xlog.h"
 | |
| +
 | |
| +#define NFSD_PORTS_FILE     "/proc/fs/nfsd/portlist"
 | |
| +#define NFSD_VERS_FILE    "/proc/fs/nfsd/versions"
 | |
| +#define NFSD_THREAD_FILE  "/proc/fs/nfsd/threads"
 | |
| +
 | |
| +/*
 | |
| + * declaring a common static scratch buffer here keeps us from having to
 | |
| + * continually thrash the stack. The value of 128 bytes here is really just a
 | |
| + * SWAG and can be increased if necessary. It ought to be enough for the
 | |
| + * routines below however.
 | |
| + */
 | |
| +char buf[128];
 | |
| +
 | |
| +/*
 | |
| + * Are there already sockets configured? If not, then it is safe to try to
 | |
| + * open some and pass them through.
 | |
| + *
 | |
| + * Note: If the user explicitly asked for 'udp', then we should probably check
 | |
| + * if that is open, and should open it if not. However we don't yet. All
 | |
| + * sockets have to be opened when the first daemon is started.
 | |
| + */
 | |
| +int
 | |
| +nfssvc_inuse(void)
 | |
| +{
 | |
| +	int fd, n;
 | |
| +
 | |
| +	fd = open(NFSD_PORTS_FILE, O_RDONLY);
 | |
| +
 | |
| +	/* problem opening file, assume that nothing is configured */
 | |
| +	if (fd < 0)
 | |
| +		return 0;
 | |
| +
 | |
| +	n = read(fd, buf, sizeof(buf));
 | |
| +	close(fd);
 | |
| +
 | |
| +	xlog(D_GENERAL, "knfsd is currently %s", (n > 0) ? "up" : "down");
 | |
| +
 | |
| +	return (n > 0);
 | |
| +}
 | |
| +
 | |
| +static int
 | |
| +nfssvc_setfds(const struct addrinfo *hints, const char *node, const char *port)
 | |
| +{
 | |
| +	int fd, on = 1, fac = L_ERROR;
 | |
| +	int sockfd = -1, rc = 0;
 | |
| +	struct addrinfo *addrhead = NULL, *addr;
 | |
| +	char *proto, *family;
 | |
| +
 | |
| +	/*
 | |
| +	 * if file can't be opened, then assume that it's not available and
 | |
| +	 * that the caller should just fall back to the old nfsctl interface
 | |
| + 	 */
 | |
| +	fd = open(NFSD_PORTS_FILE, O_WRONLY);
 | |
| +	if (fd < 0)
 | |
| +		return 0;
 | |
| +
 | |
| +	switch(hints->ai_family) {
 | |
| +	case AF_INET:
 | |
| +		family = "inet";
 | |
| +		break;
 | |
| +#ifdef IPV6_SUPPORTED
 | |
| +	case AF_INET6:
 | |
| +		family = "inet6";
 | |
| +		break;
 | |
| +#endif /* IPV6_SUPPORTED */
 | |
| +	default:
 | |
| +		xlog(L_ERROR, "Unknown address family specified: %d\n",
 | |
| +				hints->ai_family);
 | |
| +		rc = EAFNOSUPPORT;
 | |
| +		goto error;
 | |
| +	}
 | |
| +
 | |
| +	rc = getaddrinfo(node, port, hints, &addrhead);
 | |
| +	if (rc == EAI_NONAME && !strcmp(port, "nfs")) {
 | |
| +		snprintf(buf, sizeof(buf), "%d", NFS_PORT);
 | |
| +		rc = getaddrinfo(node, buf, hints, &addrhead);
 | |
| +	}
 | |
| +
 | |
| +	if (rc != 0) {
 | |
| +		xlog(L_ERROR, "unable to resolve %s:%s to %s address: "
 | |
| +				"%s", node ? node : "ANYADDR", port, family,
 | |
| +				rc == EAI_SYSTEM ? strerror(errno) :
 | |
| +					gai_strerror(rc));
 | |
| +		goto error;
 | |
| +	}
 | |
| +
 | |
| +	addr = addrhead;
 | |
| +	while(addr) {
 | |
| +		/* skip non-TCP / non-UDP sockets */
 | |
| +		switch(addr->ai_protocol) {
 | |
| +		case IPPROTO_UDP:
 | |
| +			proto = "UDP";
 | |
| +			break;
 | |
| +		case IPPROTO_TCP:
 | |
| +			proto = "TCP";
 | |
| +			break;
 | |
| +		default:
 | |
| +			addr = addr->ai_next;
 | |
| +			continue;
 | |
| +		}
 | |
| +
 | |
| +		xlog(D_GENERAL, "Creating %s %s socket.", family, proto);
 | |
| +
 | |
| +		/* open socket and prepare to hand it off to kernel */
 | |
| +		sockfd = socket(addr->ai_family, addr->ai_socktype,
 | |
| +				addr->ai_protocol);
 | |
| +		if (sockfd < 0) {
 | |
| +			xlog(L_ERROR, "unable to create %s %s socket: "
 | |
| +				"errno %d (%m)", family, proto, errno);
 | |
| +			rc = errno;
 | |
| +			goto error;
 | |
| +		}
 | |
| +#ifdef IPV6_SUPPORTED
 | |
| +		if (addr->ai_family == AF_INET6 &&
 | |
| +		    setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on))) {
 | |
| +			xlog(L_ERROR, "unable to set IPV6_V6ONLY: "
 | |
| +				"errno %d (%m)\n", errno);
 | |
| +			rc = errno;
 | |
| +			goto error;
 | |
| +		}
 | |
| +#endif /* IPV6_SUPPORTED */
 | |
| +		if (addr->ai_protocol == IPPROTO_TCP &&
 | |
| +		    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) {
 | |
| +			xlog(L_ERROR, "unable to set SO_REUSEADDR on %s "
 | |
| +				"socket: errno %d (%m)", family, errno);
 | |
| +			rc = errno;
 | |
| +			goto error;
 | |
| +		}
 | |
| +		if (bind(sockfd, addr->ai_addr, addr->ai_addrlen)) {
 | |
| +			xlog(L_ERROR, "unable to bind %s %s socket: "
 | |
| +				"errno %d (%m)", family, proto, errno);
 | |
| +			rc = errno;
 | |
| +			goto error;
 | |
| +		}
 | |
| +		if (addr->ai_protocol == IPPROTO_TCP && listen(sockfd, 64)) {
 | |
| +			xlog(L_ERROR, "unable to create listening socket: "
 | |
| +				"errno %d (%m)", errno);
 | |
| +			rc = errno;
 | |
| +			goto error;
 | |
| +		}
 | |
| +
 | |
| +		if (fd < 0)
 | |
| +			fd = open(NFSD_PORTS_FILE, O_WRONLY);
 | |
| +
 | |
| +		if (fd < 0) {
 | |
| +			xlog(L_ERROR, "couldn't open ports file: errno "
 | |
| +				      "%d (%m)", errno);
 | |
| +			goto error;
 | |
| +		}
 | |
| +
 | |
| +		snprintf(buf, sizeof(buf), "%d\n", sockfd); 
 | |
| +		if (write(fd, buf, strlen(buf)) != strlen(buf)) {
 | |
| +			/*
 | |
| +			 * this error may be common on older kernels that don't
 | |
| +			 * support IPv6, so turn into a debug message.
 | |
| +			 */
 | |
| +			if (errno == EAFNOSUPPORT)
 | |
| +				fac = D_ALL;
 | |
| +			xlog(fac, "writing fd to kernel failed: errno %d (%m)",
 | |
| +				  errno);
 | |
| +			rc = errno;
 | |
| +			goto error;
 | |
| +		}
 | |
| +		close(fd);
 | |
| +		close(sockfd);
 | |
| +		sockfd = fd = -1;
 | |
| +		addr = addr->ai_next;
 | |
| +	}
 | |
| +error:
 | |
| +	if (fd >= 0)
 | |
| +		close(fd);
 | |
| +	if (sockfd >= 0)
 | |
| +		close(sockfd);
 | |
| +	if (addrhead)
 | |
| +		freeaddrinfo(addrhead);
 | |
| +	return rc;
 | |
| +}
 | |
| +
 | |
| +int
 | |
| +nfssvc_set_sockets(const int family, const unsigned int protobits,
 | |
| +		   const char *host, const char *port)
 | |
| +{
 | |
| +	struct addrinfo hints = { .ai_flags = AI_PASSIVE | AI_ADDRCONFIG };
 | |
| +
 | |
| +	hints.ai_family = family;
 | |
| +
 | |
| +	if (!NFSCTL_ANYPROTO(protobits))
 | |
| +		return EPROTOTYPE;
 | |
| +	else if (!NFSCTL_UDPISSET(protobits))
 | |
| +		hints.ai_protocol = IPPROTO_TCP;
 | |
| +	else if (!NFSCTL_TCPISSET(protobits))
 | |
| +		hints.ai_protocol = IPPROTO_UDP;
 | |
| +
 | |
| +	return nfssvc_setfds(&hints, host, port);
 | |
| +}
 | |
| +
 | |
| +void
 | |
| +nfssvc_setvers(unsigned int ctlbits, int minorvers4)
 | |
| +{
 | |
| +	int fd, n, off;
 | |
| +	char *ptr;
 | |
| +
 | |
| +	ptr = buf;
 | |
| +	off = 0;
 | |
| +	fd = open(NFSD_VERS_FILE, O_WRONLY);
 | |
| +	if (fd < 0)
 | |
| +		return;
 | |
| +
 | |
| +	for (n = NFSD_MINVERS; n <= NFSD_MAXVERS; n++) {
 | |
| +		if (NFSCTL_VERISSET(ctlbits, n))
 | |
| +		    off += snprintf(ptr+off, sizeof(buf) - off, "+%d ", n);
 | |
| +		else
 | |
| +		    off += snprintf(ptr+off, sizeof(buf) - off, "-%d ", n);
 | |
| +	}
 | |
| +	n = minorvers4 >= 0 ? minorvers4 : -minorvers4;
 | |
| +	if (n >= NFSD_MINMINORVERS4 && n <= NFSD_MAXMINORVERS4)
 | |
| +		    off += snprintf(ptr+off, sizeof(buf) - off, "%c4.%d",
 | |
| +				    minorvers4 > 0 ? '+' : '-',
 | |
| +				    n);
 | |
| +	xlog(D_GENERAL, "Writing version string to kernel: %s", buf);
 | |
| +	snprintf(ptr+off, sizeof(buf) - off, "\n");
 | |
| +	if (write(fd, buf, strlen(buf)) != strlen(buf))
 | |
| +		xlog(L_ERROR, "Setting version failed: errno %d (%m)", errno);
 | |
| +
 | |
| +	close(fd);
 | |
| +
 | |
| +	return;
 | |
| +}
 | |
| +
 | |
| +int
 | |
| +nfssvc_threads(unsigned short port, const int nrservs)
 | |
| +{
 | |
| +	struct nfsctl_arg	arg;
 | |
| +	struct servent *ent;
 | |
| +	ssize_t n;
 | |
| +	int fd;
 | |
| +
 | |
| +	fd = open(NFSD_THREAD_FILE, O_WRONLY);
 | |
| +	if (fd < 0)
 | |
| +		fd = open("/proc/fs/nfs/threads", O_WRONLY);
 | |
| +	if (fd >= 0) {
 | |
| +		/* 2.5+ kernel with nfsd filesystem mounted.
 | |
| +		 * Just write the number of threads.
 | |
| +		 */
 | |
| +		snprintf(buf, sizeof(buf), "%d\n", nrservs);
 | |
| +		n = write(fd, buf, strlen(buf));
 | |
| +		close(fd);
 | |
| +		if (n != strlen(buf))
 | |
| +			return -1;
 | |
| +		else
 | |
| +			return 0;
 | |
| +	}
 | |
| +
 | |
| +	if (!port) {
 | |
| +		ent = getservbyname("nfs", "udp");
 | |
| +		if (ent != NULL)
 | |
| +			port = ntohs(ent->s_port);
 | |
| +		else
 | |
| +			port = NFS_PORT;
 | |
| +	}
 | |
| +
 | |
| +	arg.ca_version = NFSCTL_VERSION;
 | |
| +	arg.ca_svc.svc_nthreads = nrservs;
 | |
| +	arg.ca_svc.svc_port = port;
 | |
| +	return nfsctl(NFSCTL_SVC, &arg, NULL);
 | |
| +}
 | |
| diff --git a/utils/nfsd/nfssvc.h b/utils/nfsd/nfssvc.h
 | |
| new file mode 100644
 | |
| index 0000000..0c69bd6
 | |
| --- /dev/null
 | |
| +++ b/utils/nfsd/nfssvc.h
 | |
| @@ -0,0 +1,27 @@
 | |
| +/*
 | |
| + *   utils/nfsd/nfssvc.h -- nfs service control routines for rpc.nfsd
 | |
| + *
 | |
| + *   Copyright (C) 2009 Red Hat, Inc <nfs@redhat.com>.
 | |
| + *   Copyright (C) 2009 Jeff Layton <jlayton@redhat.com>
 | |
| + *
 | |
| + * This program is free software; you can redistribute it and/or
 | |
| + * modify it under the terms of the GNU General Public License
 | |
| + * as published by the Free Software Foundation; either version 2
 | |
| + * of the License, or (at your option) any later version.
 | |
| + *
 | |
| + * This program is distributed in the hope that it will be useful,
 | |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| + * GNU General Public License for more details.
 | |
| + *
 | |
| + * You should have received a copy of the GNU General Public License along
 | |
| + * with this program; if not, write to the Free Software Foundation, Inc.,
 | |
| + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 | |
| + *
 | |
| + */
 | |
| +
 | |
| +int	nfssvc_inuse(void);
 | |
| +int	nfssvc_set_sockets(const int family, const unsigned int protobits,
 | |
| +			   const char *host, const char *port);
 | |
| +void	nfssvc_setvers(unsigned int ctlbits, int minorvers4);
 | |
| +int	nfssvc_threads(unsigned short port, int nrservs);
 |