diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 06:03:02 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 06:03:02 +0000 |
commit | 4897093455a2bf08f3db3a1132cc2f6f5484d77c (patch) | |
tree | 9e6373544263f003139431fb4b08f9766e1ed530 /utils/nfsd/nfssvc.c | |
parent | Initial commit. (diff) | |
download | nfs-utils-4897093455a2bf08f3db3a1132cc2f6f5484d77c.tar.xz nfs-utils-4897093455a2bf08f3db3a1132cc2f6f5484d77c.zip |
Adding upstream version 1:2.6.4.upstream/1%2.6.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'utils/nfsd/nfssvc.c')
-rw-r--r-- | utils/nfsd/nfssvc.c | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/utils/nfsd/nfssvc.c b/utils/nfsd/nfssvc.c new file mode 100644 index 0000000..46452d9 --- /dev/null +++ b/utils/nfsd/nfssvc.c @@ -0,0 +1,438 @@ +/* + * 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 <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "nfslib.h" +#include "xlog.h" +#include "nfssvc.h" +#include "version.h" + +#ifndef NFSD_FS_DIR +#define NFSD_FS_DIR "/proc/fs/nfsd" +#endif + +#define NFSD_PORTS_FILE NFSD_FS_DIR "/portlist" +#define NFSD_VERS_FILE NFSD_FS_DIR "/versions" +#define NFSD_THREAD_FILE NFSD_FS_DIR "/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]; + +/* + * Using the "new" interfaces for nfsd requires that /proc/fs/nfsd is + * actually mounted. Make an attempt to mount it here if it doesn't appear + * to be. + */ +void +nfssvc_mount_nfsdfs(char *progname) +{ + int err; + struct stat statbuf; + + err = stat(NFSD_THREAD_FILE, &statbuf); + if (err == 0) + return; + + if (errno != ENOENT) { + xlog(L_ERROR, "Unable to stat %s: errno %d (%m)", + NFSD_THREAD_FILE, errno); + return; + } + + /* + * this call can return an error if modprobe is set up to automatically + * mount nfsdfs when nfsd.ko is plugged in. So, ignore the return + * code from it and just check for the "threads" file afterward. + */ + err = system("/bin/mount -t nfsd nfsd " NFSD_FS_DIR " >/dev/null 2>&1"); + + err = stat(NFSD_THREAD_FILE, &statbuf); + if (err == 0) + return; + + xlog(L_WARNING, "Unable to access " NFSD_FS_DIR " errno %d (%m)." + "\nPlease try, as root, 'mount -t nfsd nfsd " NFSD_FS_DIR + "' and then restart %s to correct the problem", errno, progname); + + return; +} + +/* + * 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, bounded = 0; + struct addrinfo *addrhead = NULL, *addr; + char *proto, *family; + + /* + * if file can't be opened, fail. + */ + fd = open(NFSD_PORTS_FILE, O_WRONLY); + if (fd < 0) + return 0; + + 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: %s", + node ? node : "ANYADDR", port, + 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; + } + + switch(addr->ai_addr->sa_family) { + case AF_INET: + family = "AF_INET"; + break; +#ifdef IPV6_SUPPORTED + case AF_INET6: + family = "AF_INET6"; + break; +#endif /* IPV6_SUPPORTED */ + default: + addr = addr->ai_next; + continue; + } + + /* open socket and prepare to hand it off to kernel */ + sockfd = socket(addr->ai_family, addr->ai_socktype, + addr->ai_protocol); + if (sockfd < 0) { + if (errno != EAFNOSUPPORT) { + xlog(L_ERROR, "unable to create %s %s socket: " + "errno %d (%m)", family, proto, errno); + rc = errno; + goto error; + } + addr = addr->ai_next; + continue; + } + + xlog(D_GENERAL, "Created %s %s socket.", family, proto); + +#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)) != (ssize_t)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; + } + bounded++; + + close(fd); + close(sockfd); + sockfd = fd = -1; + addr = addr->ai_next; + } +error: + if (fd >= 0) + close(fd); + if (sockfd >= 0) + close(sockfd); + nfs_freeaddrinfo(addrhead); + return (bounded ? 0 : rc); +} + +int +nfssvc_set_sockets(const unsigned int protobits, + const char *host, const char *port) +{ + struct addrinfo hints = { .ai_flags = AI_PASSIVE }; + +#ifdef IPV6_SUPPORTED + hints.ai_family = AF_UNSPEC; +#else /* IPV6_SUPPORTED */ + hints.ai_family = AF_INET; +#endif /* IPV6_SUPPORTED */ + + 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); +} + +int +nfssvc_set_rdmaport(const char *port) +{ + struct servent *sv = getservbyname(port, "tcp"); + int nport; + char buf[20]; + int ret; + int fd; + + if (sv) + nport = ntohs(sv->s_port); + else { + char *ep; + nport = strtol(port, &ep, 10); + if (!*port || *ep) { + xlog(L_ERROR, "unable to interpret port name %s", + port); + return 1; + } + } + + fd = open(NFSD_PORTS_FILE, O_WRONLY); + if (fd < 0) + return 1; + snprintf(buf, sizeof(buf), "rdma %d", nport); + ret = 0; + if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) { + ret= errno; + xlog(L_ERROR, "Unable to request RDMA services: %m"); + } + close(fd); + return ret; +} + +void +nfssvc_set_time(const char *type, const int seconds) +{ + char pathbuf[40]; + char nbuf[10]; + int fd; + + snprintf(pathbuf, sizeof(pathbuf), NFSD_FS_DIR "/nfsv4%stime", type); + snprintf(nbuf, sizeof(nbuf), "%d", seconds); + fd = open(pathbuf, O_WRONLY); + if (fd >= 0) { + if (write(fd, nbuf, strlen(nbuf)) != (ssize_t)strlen(nbuf)) + xlog(L_ERROR, "Unable to set nfsv4%stime: %m", type); + close(fd); + } + if (strcmp(type, "grace") == 0) { + /* set same value for lockd */ + fd = open("/proc/sys/fs/nfs/nlm_grace_period", O_WRONLY); + if (fd >= 0) { + if (write(fd, nbuf, strlen(nbuf)) != (ssize_t)strlen(nbuf)) + xlog(L_ERROR, "Unable to write nlm_grace_period : %m"); + close(fd); + } + } +} + +void +nfssvc_get_minormask(unsigned int *mask) +{ + int fd; + char *ptr = buf; + ssize_t size; + + fd = open(NFSD_VERS_FILE, O_RDONLY); + if (fd < 0) + return; + + size = read(fd, buf, sizeof(buf)); + if (size < 0) { + xlog(L_ERROR, "Getting versions failed: errno %d (%m)", errno); + goto out; + } + ptr[size] = '\0'; + for (;;) { + unsigned vers, minor = 0; + char *token = strtok(ptr, " "); + + if (!token) + break; + ptr = NULL; + if (*token != '+' && *token != '-') + continue; + if (sscanf(++token, "%u.%u", &vers, &minor) > 0 && + vers == 4 && minor <= NFS4_MAXMINOR) + NFSCTL_MINORSET(*mask, minor); + } +out: + close(fd); + return; +} + +static int +nfssvc_print_vers(char *ptr, unsigned size, unsigned vers, unsigned minorvers, + int isset, int force4dot0) +{ + char sign = isset ? '+' : '-'; + if (minorvers == 0) + if (linux_version_code() < MAKE_VERSION(4, 11, 0) || !force4dot0) + return snprintf(ptr, size, "%c%u ", sign, vers); + return snprintf(ptr, size, "%c%u.%u ", sign, vers, minorvers); +} + +void +nfssvc_setvers(unsigned int ctlbits, unsigned int minorvers, unsigned int minorversset, + int force4dot0) +{ + int fd, n, off; + + off = 0; + fd = open(NFSD_VERS_FILE, O_WRONLY); + if (fd < 0) + return; + + for (n = NFSD_MINVERS; n <= ((NFSD_MAXVERS < 3) ? NFSD_MAXVERS : 3); n++) + off += nfssvc_print_vers(&buf[off], sizeof(buf) - off, + n, 0, NFSCTL_VERISSET(ctlbits, n), 0); + + for (n = 0; n <= NFS4_MAXMINOR; n++) { + if (!NFSCTL_MINORISSET(minorversset, n)) + continue; + off += nfssvc_print_vers(&buf[off], sizeof(buf) - off, + 4, n, NFSCTL_MINORISSET(minorvers, n), + (n == 0) ? force4dot0 : 0); + } + if (!off--) + goto out; + buf[off] = '\0'; + xlog(D_GENERAL, "Writing version string to kernel: %s", buf); + snprintf(&buf[off], sizeof(buf) - off, "\n"); + if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) + xlog(L_ERROR, "Setting version failed: errno %d (%m)", errno); +out: + close(fd); + + return; +} + +int +nfssvc_threads(const int nrservs) +{ + 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 != (ssize_t)strlen(buf)) + return -1; + else + return 0; + } + return -1; +} |