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 - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include - - -#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 #include #include -#include #include #include #include #include #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 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 . + * Copyright (C) 2009 Jeff Layton + * + * 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);