nbd-client.c

Go to the documentation of this file.
00001 /*
00002  * Open connection for network block device
00003  *
00004  * Copyright 1997,1998 Pavel Machek, distribute under GPL
00005  *  <pavel@atrey.karlin.mff.cuni.cz>
00006  *
00007  * Version 1.0 - 64bit issues should be fixed, now
00008  * Version 1.1 - added bs (blocksize) option (Alexey Guzeev, aga@permonline.ru)
00009  * Version 1.2 - I added new option '-d' to send the disconnect request
00010  * Version 2.0 - Version synchronised with server
00011  * Version 2.1 - Check for disconnection before INIT_PASSWD is received
00012  *      to make errormsg a bit more helpful in case the server can't
00013  *      open the exported file.
00014  */
00015 
00016 #include "config.h"
00017 #include "lfs.h"
00018 
00019 #include <sys/ioctl.h>
00020 #include <sys/socket.h>
00021 #include <sys/types.h>
00022 #include <unistd.h>
00023 #include <netinet/tcp.h>
00024 #include <netinet/in.h>         /* sockaddr_in, htons, in_addr */
00025 #include <netdb.h>              /* hostent, gethostby*, getservby* */
00026 #include <stdio.h>
00027 #include <fcntl.h>
00028 #include <syslog.h>
00029 #include <stdlib.h>
00030 
00031 #ifndef __GNUC__
00032 #error I need GCC to work
00033 #endif
00034 
00035 #include <linux/ioctl.h>
00036 #define MY_NAME "nbd_client"
00037 #include "cliserv.h"
00038 
00039 int opennet(char *name, int port) {
00040         int sock;
00041         struct sockaddr_in xaddrin;
00042         int xaddrinlen = sizeof(xaddrin);
00043         struct hostent *hostn;
00044 
00045         hostn = gethostbyname(name);
00046         if (!hostn)
00047                 err("Gethostname failed: %h\n");
00048 
00049         if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
00050                 err("Socket failed: %m");
00051 
00052         xaddrin.sin_family = AF_INET;
00053         xaddrin.sin_port = htons(port);
00054         xaddrin.sin_addr.s_addr = *((int *) hostn->h_addr);
00055         if ((connect(sock, (struct sockaddr *) &xaddrin, xaddrinlen) < 0))
00056                 err("Connect: %m");
00057 
00058         setmysockopt(sock);
00059         return sock;
00060 }
00061 
00062 u64 negotiate(int sock, int blocksize) {
00063         u64 magic, size64;
00064         char buf[256] = "\0\0\0\0\0\0\0\0\0";
00065 
00066         printf("Negotiation: ");
00067         if (read(sock, buf, 8) < 0)
00068                 err("Failed/1: %m");
00069         if (strlen(buf)==0)
00070                 err("Server closed connection");
00071         if (strcmp(buf, INIT_PASSWD))
00072                 err("INIT_PASSWD bad");
00073         printf(".");
00074         if (read(sock, &magic, sizeof(magic)) < 0)
00075                 err("Failed/2: %m");
00076         magic = ntohll(magic);
00077         if (magic != cliserv_magic)
00078                 err("Not enough cliserv_magic");
00079         printf(".");
00080 
00081         if (read(sock, &size64, sizeof(size64)) < 0)
00082                 err("Failed/3: %m\n");
00083         size64 = ntohll(size64);
00084 
00085 #ifdef NBD_SET_SIZE_BLOCKS
00086         if ((size64>>10) > (~0UL >> 1)) {
00087                 printf("size = %luMB", (unsigned long)(size64>>20));
00088                 err("Exported device is too big for me. Get 64-bit machine :-(\n");
00089         } else
00090                 printf("size = %luKB", (unsigned long)(size64>>10));
00091 #else
00092         if (size64 > (~0UL >> 1)) {
00093                 printf("size = %luKB", (unsigned long)(size64>>10));
00094                 err("Exported device is too big. Get 64-bit machine or newer kernel :-(\n");
00095         } else
00096                 printf("size = %lu", (unsigned long)(size64));
00097 #endif
00098 
00099         if (read(sock, &buf, 128) < 0)
00100                 err("Failed/4: %m\n");
00101         printf("\n");
00102 
00103         return size64;
00104 }
00105 
00106 void setsizes(int nbd, u64 size64, int blocksize) {
00107         unsigned long size;
00108 
00109 #ifdef NBD_SET_SIZE_BLOCKS
00110         if (size64/blocksize > (~0UL >> 1))
00111                 err("Device too large.\n");
00112         else {
00113                 int er;
00114                 if (ioctl(nbd, NBD_SET_BLKSIZE, (unsigned long)blocksize) < 0)
00115                         err("Ioctl/1.1a failed: %m\n");
00116                 size = (unsigned long)(size64/blocksize);
00117                 if ((er = ioctl(nbd, NBD_SET_SIZE_BLOCKS, size)) < 0)
00118                         err("Ioctl/1.1b failed: %m\n");
00119                 fprintf(stderr, "bs=%d, sz=%lu\n", blocksize, size);
00120         }
00121 #else
00122         if (size64 > (~0UL >> 1)) {
00123                 err("Device too large.\n");
00124         } else {
00125                 size = (unsigned long)size64;
00126                 if (ioctl(nbd, NBD_SET_SIZE, size) < 0)
00127                         err("Ioctl NBD_SET_SIZE failed: %m\n");
00128         }
00129 #endif
00130 
00131         ioctl(nbd, NBD_CLEAR_SOCK);
00132 }
00133 
00134 void finish_sock(int sock, int nbd, int swap) {
00135         if (ioctl(nbd, NBD_SET_SOCK, sock) < 0)
00136                 err("Ioctl NBD_SET_SOCK failed: %m\n");
00137 
00138 #ifndef SO_SWAPPING
00139         if (swap)
00140                 err("You have to compile me on machine with swapping patch enabled in order to use it later.");
00141 #else
00142         if (swap)
00143                 if (setsockopt(sock, SOL_SOCKET, SO_SWAPPING, &one, sizeof(int)) < 0)
00144                         err("Could not enable swapping: %m");
00145 #endif
00146 }
00147 
00148 int main(int argc, char *argv[]) {
00149         int port, sock, nbd;
00150         int blocksize=1024;
00151         char *hostname, *nbddev;
00152         int swap=0;
00153         int cont=0;
00154         u64 size64;
00155 
00156         logging();
00157 
00158         if (argc < 3) {
00159         errmsg:
00160                 fprintf(stderr, "nbd-client version %s\n", PACKAGE_VERSION);
00161                 fprintf(stderr, "Usage: nbd-client [bs=blocksize] host port nbd_device [-swap] [-persist]\n");
00162                 fprintf(stderr, "Or   : nbd-client -d nbd_device\n");
00163                 fprintf(stderr, "Default value for blocksize is 1024 (recommended for ethernet)\n");
00164                 fprintf(stderr, "Allowed values for blocksize are 512,1024,2048,4096\n"); /* will be checked in kernel :) */
00165                 fprintf(stderr, "Note, that kernel 2.4.2 and older ones do not work correctly with\n");
00166                 fprintf(stderr, "blocksizes other than 1024 without patches\n");
00167                 return 1;
00168         }
00169 
00170         ++argv; --argc; /* skip programname */
00171         
00172         if (strcmp(argv[0], "-d")==0) {
00173                 nbd = open(argv[1], O_RDWR);
00174                 if (nbd < 0)
00175                         err("Can not open NBD: %m");
00176                 printf("Disconnecting: que, ");
00177                 if (ioctl(nbd, NBD_CLEAR_QUE)< 0)
00178                         err("Ioctl failed: %m\n");
00179                 printf("disconnect, ");
00180 #ifdef NBD_DISCONNECT
00181                 if (ioctl(nbd, NBD_DISCONNECT)<0)
00182                         err("Ioctl failed: %m\n");
00183                 printf("sock, ");
00184 #else
00185                 fprintf(stderr, "Can't disconnect: I was not compiled with disconnect support!\n" );
00186                 exit(1);
00187 #endif
00188                 if (ioctl(nbd, NBD_CLEAR_SOCK)<0)
00189                         err("Ioctl failed: %m\n");
00190                 printf("done\n");
00191                 return 0;
00192         }
00193         
00194         if (strncmp(argv[0], "bs=", 3)==0) {
00195                 blocksize=atoi(argv[0]+3);
00196                 ++argv; --argc; /* skip blocksize */
00197         }
00198 
00199         if (argc==0) goto errmsg;
00200         hostname=argv[0];
00201         ++argv; --argc; /* skip hostname */
00202 
00203         if (argc==0) goto errmsg;
00204         port = atoi(argv[0]);
00205         ++argv; --argc; /* skip port */
00206 
00207         if (argc==0) goto errmsg;
00208         sock = opennet(hostname, port);
00209         nbddev = argv[0];
00210         nbd = open(nbddev, O_RDWR);
00211         if (nbd < 0)
00212           err("Can not open NBD: %m");
00213         ++argv; --argc; /* skip device */
00214 
00215         if (argc>2) goto errmsg;
00216         if (argc!=0) {
00217                 if(strncmp(argv[0], "-swap", 5)==0) {
00218                         swap=1;
00219                         ++argv;--argc;
00220                 }
00221         }
00222         if (argc!=0) {
00223                 if(strncmp(argv[0], "-persist", 8)==0) {
00224                         cont=1;
00225                         ++argv;--argc;
00226                 }
00227         }
00228         argv=NULL; argc=0; /* don't use it later suddenly */
00229 
00230         size64 = negotiate(sock, blocksize);
00231         setsizes(nbd, size64, blocksize);
00232         finish_sock(sock, nbd, swap);
00233 
00234         /* Go daemon */
00235         
00236         chdir("/");
00237 #ifndef NOFORK
00238         if (fork())
00239                 exit(0);
00240 #endif
00241 
00242         do {
00243                 if (ioctl(nbd, NBD_DO_IT) < 0) {
00244                         fprintf(stderr, "Kernel call returned: %m");
00245                         if(errno==EBADR) {
00246                                 /* The user probably did 'nbd-client -d' on us.
00247                                  * quit */
00248                                 cont=0;
00249                         } else {
00250                                 if(cont) {
00251                                         fprintf(stderr, " Reconnecting\n");
00252                                         close(sock); close(nbd);
00253                                         sock = opennet(hostname, port);
00254                                         nbd = open(nbddev, O_RDWR);
00255                                         if(size64!=negotiate(sock,blocksize)) {
00256                                                 err("Size of the device changed. Bye");
00257                                         }
00258                                         setsizes(nbd, size64, blocksize);
00259                                         finish_sock(sock,nbd,swap);
00260                                 }
00261                         }
00262                 } else {
00263                         /* We're on 2.4. It's not clearly defined what exactly
00264                          * happened at this point. Probably best to quit, now
00265                          */
00266                         fprintf(stderr, "Kernel call returned.");
00267                         cont=0;
00268                 }
00269         } while(cont);
00270         printf("Closing: que, ");
00271         ioctl(nbd, NBD_CLEAR_QUE);
00272         printf("sock, ");
00273         ioctl(nbd, NBD_CLEAR_SOCK);
00274         printf("done\n");
00275         return 0;
00276 }

Generated on Fri Sep 21 19:49:46 2007 for Network Block Device by  doxygen 1.5.3