nfs-utils: convert nfssvc_setfds to use getaddrinfo
authorJeff Layton <jlayton@redhat.com>
Fri, 14 Aug 2009 17:30:04 +0000 (13:30 -0400)
committerSteve Dickson <steved@redhat.com>
Fri, 14 Aug 2009 17:30:04 +0000 (13:30 -0400)
Convert nfssvc_setfds to use getaddrinfo. Change the args that it takes
and fix up nfssvc function to pass in the proper args. The things that
nfssvc has to do to call the new nfssvc_setfds is a little cumbersome
for now, but that will eventually be cleaned up in a later patch.

nfs-utils: break up the nfssvc interface

Currently, the only public interface to the routines in nfssvc.c is
nfssvc(). This means that we do an awful lot of work after closing
stderr that could be done while it's still available.

Add prototypes to the header so that more functions in nfssvc.c can be
called individually, and change the nfsd program to call those routines
individually.

Signed-off-by: Steve Dickson <steved@redhat.com>
utils/nfsd/nfsd.c
utils/nfsd/nfssvc.c
utils/nfsd/nfssvc.h

index 1589a9f..c82249b 100644 (file)
@@ -45,21 +45,14 @@ static struct option longopts[] =
 unsigned int protobits = NFSCTL_ALLBITS;
 unsigned int versbits = NFSCTL_ALLBITS;
 int minorvers4 = NFSD_MAXMINORVERS4;           /* nfsv4 minor version */
-char *haddr = NULL;
 
 int
 main(int argc, char **argv)
 {
-       int     count = 1, c, error, port, fd, found_one;
-       struct servent *ent;
-       struct hostent *hp;
-       char *p, *progname;
-
-       ent = getservbyname ("nfs", "udp");
-       if (ent != NULL)
-               port = ntohs (ent->s_port);
-       else
-               port = 2049;
+       int     count = 1, c, error, portnum = 0, fd, found_one;
+       char *p, *progname, *port;
+       char *haddr = NULL;
+       int     socket_up = 0;
 
        progname = strdup(basename(argv[0]));
        if (!progname) {
@@ -67,6 +60,12 @@ main(int argc, char **argv)
                exit(1);
        }
 
+       port = strdup("nfs");
+       if (!port) {
+               fprintf(stderr, "%s: unable to allocate memory.\n", progname);
+               exit(1);
+       }
+
        xlog_syslog(0);
        xlog_stderr(1);
 
@@ -76,24 +75,34 @@ main(int argc, char **argv)
                        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",
-                                       progname, optarg);
-                               usage(progname);
+                       /*
+                        * 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",
                                        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':
                        switch((c = strtol(optarg, &p, 0))) {
@@ -169,10 +178,38 @@ main(int argc, char **argv)
                        count = 1;
                }
        }
-       /* 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 */
+
+       /* can only change number of threads if nfsd is already up */
+       if (nfssvc_inuse()) {
+               socket_up = 1;
+               goto set_threads;
+       }
+
+       /*
+        * 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, protobits, haddr, port);
+       if (!error)
+               socket_up = 1;
+
+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)
                xlog(L_ERROR, "Unable to open /dev/null: %m");
@@ -186,9 +223,11 @@ main(int argc, char **argv)
        }
        closeall(3);
 
-       if ((error = nfssvc(port, count, versbits, minorvers4, protobits, haddr)) < 0)
-               xlog(L_ERROR, "nfssvc: errno %d (%m)", errno);
-
+       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);
 }
index 7ecaea9..106f6e7 100644 (file)
@@ -10,7 +10,9 @@
 #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>
@@ -59,86 +61,150 @@ nfssvc_inuse(void)
        return (n > 0);
 }
 
-static void
-nfssvc_setfds(int port, unsigned int ctlbits, char *haddr)
+static int
+nfssvc_setfds(const struct addrinfo *hints, const char *node, const char *port)
 {
-       int fd, on=1;
-       int udpfd = -1, tcpfd = -1;
-       struct sockaddr_in sin;
-
-       if (nfssvc_inuse())
-               return;
+       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;
-       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) {
-                       xlog(L_ERROR, "unable to create UDP socket: "
-                               "errno %d (%m)", errno);
-                       exit(1);
-               }
-               if (bind(udpfd, (struct  sockaddr  *)&sin, sizeof(sin)) < 0){
-                       xlog(L_ERROR, "unable to bind UDP socket: "
-                               "errno %d (%m)", errno);
-                       exit(1);
-               }
+               return 0;
+
+       switch(hints->ai_family) {
+       case AF_INET:
+               family = "inet";
+               break;
+       default:
+               xlog(L_ERROR, "Unknown address family specified: %d\n",
+                               hints->ai_family);
+               rc = EAFNOSUPPORT;
+               goto error;
        }
 
-       if (NFSCTL_TCPISSET(ctlbits)) {
-               tcpfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-               if (tcpfd < 0) {
-                       xlog(L_ERROR, "unable to create TCP socket: "
-                               "errno %d (%m)", errno);
-                       exit(1);
+       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;
                }
-               if (setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
-                       xlog(L_ERROR, "unable to set SO_REUSEADDR: "
-                               "errno %d (%m)", errno);
-                       exit(1);
+
+               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;
                }
-               if (bind(tcpfd, (struct  sockaddr  *)&sin, sizeof(sin)) < 0){
-                       xlog(L_ERROR, "unable to bind TCP socket: "
-                               "errno %d (%m)", errno);
-                       exit(1);
+               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 (listen(tcpfd, 64) < 0){
+               if (addr->ai_protocol == IPPROTO_TCP && listen(sockfd, 64)) {
                        xlog(L_ERROR, "unable to create listening socket: "
                                "errno %d (%m)", errno);
-                       exit(1);
-               }
-       }
-       if (udpfd >= 0) {
-               snprintf(buf, sizeof(buf), "%d\n", udpfd); 
-               if (write(fd, buf, strlen(buf)) != strlen(buf)) {
-                       xlog(L_ERROR, 
-                              "writing fds to kernel failed: errno %d (%m)", 
-                              errno);
+                       rc = errno;
+                       goto error;
                }
-               close(fd);
-               fd = -1;
-       }
-       if (tcpfd >= 0) {
+
                if (fd < 0)
                        fd = open(NFSD_PORTS_FILE, O_WRONLY);
-               snprintf(buf, sizeof(buf), "%d\n", tcpfd); 
+
+               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)) {
-                       xlog(L_ERROR, 
-                              "writing fds to kernel failed: errno %d (%m)", 
-                              errno);
+                       /*
+                        * 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;
        }
-       close(fd);
+error:
+       if (fd >= 0)
+               close(fd);
+       if (sockfd >= 0)
+               close(sockfd);
+       if (addrhead)
+               freeaddrinfo(addrhead);
+       return rc;
+}
 
-       return;
+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);
 }
-static void
-nfssvc_versbits(unsigned int ctlbits, int minorvers4)
+
+void
+nfssvc_setvers(unsigned int ctlbits, int minorvers4)
 {
        int fd, n, off;
        char *ptr;
@@ -169,29 +235,22 @@ nfssvc_versbits(unsigned int ctlbits, int minorvers4)
 
        return;
 }
+
 int
-nfssvc(int port, int nrservs, unsigned int versbits, int minorvers4,
-       unsigned protobits, char *haddr)
+nfssvc_threads(unsigned short port, const int nrservs)
 {
        struct nfsctl_arg       arg;
+       struct servent *ent;
+       ssize_t n;
        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?
+                * Just write the number of threads.
                 */
-               int n;
                snprintf(buf, sizeof(buf), "%d\n", nrservs);
                n = write(fd, buf, strlen(buf));
                close(fd);
@@ -201,6 +260,14 @@ nfssvc(int port, int nrservs, unsigned int versbits, int minorvers4,
                        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;
index fe522ad..0c69bd6 100644 (file)
@@ -20,5 +20,8 @@
  *
  */
 
-int    nfssvc(int port, int nrservs, unsigned int versbits, int minorvers4,
-              unsigned int portbits, char *haddr);
+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);