r153: Add support for persistent connections (reconnecting if the connection drops
authoryoe <yoe>
Thu, 30 Mar 2006 20:11:21 +0000 (20:11 +0000)
committeryoe <yoe>
Thu, 30 Mar 2006 20:11:21 +0000 (20:11 +0000)
for some unknown reason)

nbd-client.c

index 8330906..ffda42c 100644 (file)
@@ -37,8 +37,7 @@
 #define MY_NAME "nbd_client"
 #include "cliserv.h"
 
-int opennet(char *name, int port)
-{
+int opennet(char *name, int port) {
        int sock;
        struct sockaddr_in xaddrin;
        int xaddrinlen = sizeof(xaddrin);
@@ -61,77 +60,9 @@ int opennet(char *name, int port)
        return sock;
 }
 
-int main(int argc, char *argv[])
-{
-       int port, sock, nbd;
+u64 negotiate(int sock, int blocksize) {
        u64 magic, size64;
-       unsigned long size;
        char buf[256] = "\0\0\0\0\0\0\0\0\0";
-       int blocksize=1024;
-       char *hostname;
-       int swap=0;
-
-       logging();
-
-       if (argc < 3) {
-       errmsg:
-               fprintf(stderr, "nbd-client version %s\n", PACKAGE_VERSION);
-               fprintf(stderr, "Usage: nbd-client [bs=blocksize] host port nbd_device [-swap]\n");
-               fprintf(stderr, "Or   : nbd-client -d nbd_device\n");
-               fprintf(stderr, "Default value for blocksize is 1024 (recommended for ethernet)\n");
-               fprintf(stderr, "Allowed values for blocksize are 512,1024,2048,4096\n"); /* will be checked in kernel :) */
-               fprintf(stderr, "Note, that kernel 2.4.2 and older ones do not work correctly with\n");
-               fprintf(stderr, "blocksizes other than 1024 without patches\n");
-               return 1;
-       }
-
-       ++argv; --argc; /* skip programname */
-       
-       if (strcmp(argv[0], "-d")==0) {
-               nbd = open(argv[1], O_RDWR);
-               if (nbd < 0)
-                       err("Can not open NBD: %m");
-               printf("Disconnecting: que, ");
-               if (ioctl(nbd, NBD_CLEAR_QUE)< 0)
-                       err("Ioctl failed: %m\n");
-               printf("disconnect, ");
-#ifdef NBD_DISCONNECT
-               if (ioctl(nbd, NBD_DISCONNECT)<0)
-                       err("Ioctl failed: %m\n");
-               printf("sock, ");
-#else
-               fprintf(stderr, "Can't disconnect: I was not compiled with disconnect support!\n" );
-               exit(1);
-#endif
-               if (ioctl(nbd, NBD_CLEAR_SOCK)<0)
-                       err("Ioctl failed: %m\n");
-               printf("done\n");
-               return 0;
-       }
-       
-       if (strncmp(argv[0], "bs=", 3)==0) {
-               blocksize=atoi(argv[0]+3);
-               ++argv; --argc; /* skip blocksize */
-       }
-       
-       if (argc==0) goto errmsg;
-       hostname=argv[0];
-       ++argv; --argc; /* skip hostname */
-       
-       if (argc==0) goto errmsg;
-       port = atoi(argv[0]);
-       ++argv; --argc; /* skip port */
-
-       if (argc==0) goto errmsg;
-       sock = opennet(hostname, port);
-       nbd = open(argv[0], O_RDWR);
-       if (nbd < 0)
-         err("Can not open NBD: %m");
-       ++argv; --argc; /* skip device */
-
-       if (argc>1) goto errmsg;
-       if (argc!=0) swap=1;
-       argv=NULL; argc=0; /* don't use it later suddenly */
 
        printf("Negotiation: ");
        if (read(sock, buf, 8) < 0)
@@ -170,6 +101,12 @@ int main(int argc, char *argv[])
                err("Failed/4: %m\n");
        printf("\n");
 
+       return size64;
+}
+
+void setsizes(int nbd, u64 size64, int blocksize) {
+       unsigned long size;
+
 #ifdef NBD_SET_SIZE_BLOCKS
        if (size64/blocksize > (~0UL >> 1))
                err("Device too large.\n");
@@ -180,7 +117,7 @@ int main(int argc, char *argv[])
                size = (unsigned long)(size64/blocksize);
                if ((er = ioctl(nbd, NBD_SET_SIZE_BLOCKS, size)) < 0)
                        err("Ioctl/1.1b failed: %m\n");
-fprintf(stderr, "bs=%d, sz=%lu\n", blocksize, size);
+               fprintf(stderr, "bs=%d, sz=%lu\n", blocksize, size);
        }
 #else
        if (size64 > (~0UL >> 1)) {
@@ -188,13 +125,16 @@ fprintf(stderr, "bs=%d, sz=%lu\n", blocksize, size);
        } else {
                size = (unsigned long)size64;
                if (ioctl(nbd, NBD_SET_SIZE, size) < 0)
-                       err("Ioctl/1 failed: %m\n");
+                       err("Ioctl NBD_SET_SIZE failed: %m\n");
        }
 #endif
 
        ioctl(nbd, NBD_CLEAR_SOCK);
+}
+
+void finish_sock(int sock, int nbd, int swap) {
        if (ioctl(nbd, NBD_SET_SOCK, sock) < 0)
-               err("Ioctl/2 failed: %m\n");
+               err("Ioctl NBD_SET_SOCK failed: %m\n");
 
 #ifndef SO_SWAPPING
        if (swap)
@@ -204,17 +144,127 @@ fprintf(stderr, "bs=%d, sz=%lu\n", blocksize, size);
                if (setsockopt(sock, SOL_SOCKET, SO_SWAPPING, &one, sizeof(int)) < 0)
                        err("Could not enable swapping: %m");
 #endif
+}
+
+int main(int argc, char *argv[]) {
+       int port, sock, nbd;
+       int blocksize=1024;
+       char *hostname;
+       int swap=0;
+       int cont=0;
+       u64 size64;
+
+       logging();
+
+       if (argc < 3) {
+       errmsg:
+               fprintf(stderr, "nbd-client version %s\n", PACKAGE_VERSION);
+               fprintf(stderr, "Usage: nbd-client [bs=blocksize] host port nbd_device [-swap] [-persist]\n");
+               fprintf(stderr, "Or   : nbd-client -d nbd_device\n");
+               fprintf(stderr, "Default value for blocksize is 1024 (recommended for ethernet)\n");
+               fprintf(stderr, "Allowed values for blocksize are 512,1024,2048,4096\n"); /* will be checked in kernel :) */
+               fprintf(stderr, "Note, that kernel 2.4.2 and older ones do not work correctly with\n");
+               fprintf(stderr, "blocksizes other than 1024 without patches\n");
+               return 1;
+       }
+
+       ++argv; --argc; /* skip programname */
+       
+       if (strcmp(argv[0], "-d")==0) {
+               nbd = open(argv[1], O_RDWR);
+               if (nbd < 0)
+                       err("Can not open NBD: %m");
+               printf("Disconnecting: que, ");
+               if (ioctl(nbd, NBD_CLEAR_QUE)< 0)
+                       err("Ioctl failed: %m\n");
+               printf("disconnect, ");
+#ifdef NBD_DISCONNECT
+               if (ioctl(nbd, NBD_DISCONNECT)<0)
+                       err("Ioctl failed: %m\n");
+               printf("sock, ");
+#else
+               fprintf(stderr, "Can't disconnect: I was not compiled with disconnect support!\n" );
+               exit(1);
+#endif
+               if (ioctl(nbd, NBD_CLEAR_SOCK)<0)
+                       err("Ioctl failed: %m\n");
+               printf("done\n");
+               return 0;
+       }
        
+       if (strncmp(argv[0], "bs=", 3)==0) {
+               blocksize=atoi(argv[0]+3);
+               ++argv; --argc; /* skip blocksize */
+       }
+
+       if (argc==0) goto errmsg;
+       hostname=argv[0];
+       ++argv; --argc; /* skip hostname */
+
+       if (argc==0) goto errmsg;
+       port = atoi(argv[0]);
+       ++argv; --argc; /* skip port */
+
+       if (argc==0) goto errmsg;
+       sock = opennet(hostname, port);
+       nbd = open(argv[0], O_RDWR);
+       if (nbd < 0)
+         err("Can not open NBD: %m");
+       ++argv; --argc; /* skip device */
+
+       if (argc>2) goto errmsg;
+       if (argc!=0) {
+               if(strncmp(argv[0], "-swap", 5)==0) {
+                       swap=1;
+                       ++argv;--argc;
+               }
+       }
+       if (argc!=0) {
+               if(strncmp(argv[0], "-persist", 8)==0) {
+                       cont=1;
+                       ++argv;--argc;
+               }
+       }
+       argv=NULL; argc=0; /* don't use it later suddenly */
+
+       size64 = negotiate(sock, blocksize);
+       setsizes(nbd, size64, blocksize);
+       finish_sock(sock, nbd, swap);
+
        /* Go daemon */
        
        chdir("/");
        if (fork())
                exit(0);
-       
-       if (ioctl(nbd, NBD_DO_IT) < 0)
-               fprintf(stderr, "Kernel call returned: %m");
-       else
-               fprintf(stderr, "Kernel call returned.");
+
+       do {
+               if (ioctl(nbd, NBD_DO_IT) < 0) {
+                       fprintf(stderr, "Kernel call returned: %m");
+                       if(errno==EBADR) {
+                               /* The user probably did 'nbd-client -d' on us.
+                                * quit */
+                               cont=0;
+                       } else {
+                               if(cont) {
+                                       fprintf(stderr, " Reconnecting\n");
+                                       close(sock); close(nbd);
+                                       sock = opennet(hostname, port);
+                                       nbd = open(argv[0], O_RDWR);
+                                       if(size64!=negotiate(sock,blocksize)) {
+                                               err("Size of the device changed. Bye");
+                                       }
+                                       setsizes(nbd, size64, blocksize);
+                                       finish_sock(sock,nbd,swap);
+                               }
+                       }
+               } else {
+                       /* We're on 2.4. It's not clearly defined what exactly
+                        * happened at this point. Probably best to quit, now
+                        */
+                       fprintf(stderr, "Kernel call returned.");
+                       cont=0;
+               }
+       } while(cont);
        printf("Closing: que, ");
        ioctl(nbd, NBD_CLEAR_QUE);
        printf("sock, ");