Network Block Device 2.9.20
|
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 * 16/03/2010 - Add IPv6 support. 00015 * Kitt Tientanopajai <kitt@kitty.in.th> 00016 * Neutron Soutmun <neo.neutron@gmail.com> 00017 * Suriya Soutmun <darksolar@gmail.com> 00018 */ 00019 00020 #include "config.h" 00021 #include "lfs.h" 00022 00023 #include <sys/ioctl.h> 00024 #include <sys/socket.h> 00025 #include <sys/types.h> 00026 #include <unistd.h> 00027 #include <netinet/tcp.h> 00028 #include <netinet/in.h> 00029 #include <netdb.h> 00030 #include <stdio.h> 00031 #include <fcntl.h> 00032 #include <syslog.h> 00033 #include <stdlib.h> 00034 #include <sys/mount.h> 00035 #include <sys/mman.h> 00036 #include <errno.h> 00037 #include <getopt.h> 00038 #include <stdarg.h> 00039 00040 #include <linux/ioctl.h> 00041 #define MY_NAME "nbd_client" 00042 #include "cliserv.h" 00043 00044 #ifdef WITH_SDP 00045 #include <sdp_inet.h> 00046 #endif 00047 00048 int check_conn(char* devname, int do_print) { 00049 char buf[256]; 00050 char* p; 00051 int fd; 00052 int len; 00053 00054 if(!strncmp(devname, "/dev/", 5)) { 00055 devname+=5; 00056 } 00057 if((p=strchr(devname, 'p'))) { 00058 /* We can't do checks on partitions. */ 00059 *p='\0'; 00060 } 00061 snprintf(buf, 256, "/sys/block/%s/pid", devname); 00062 if((fd=open(buf, O_RDONLY))<0) { 00063 if(errno==ENOENT) { 00064 return 1; 00065 } else { 00066 return 2; 00067 } 00068 } 00069 len=read(fd, buf, 256); 00070 buf[len-1]='\0'; 00071 if(do_print) printf("%s\n", buf); 00072 close (fd); 00073 return 0; 00074 } 00075 00076 int opennet(char *name, char* portstr, int sdp) { 00077 int sock; 00078 struct addrinfo hints; 00079 struct addrinfo *ai = NULL; 00080 struct addrinfo *rp = NULL; 00081 int e; 00082 00083 memset(&hints,'\0',sizeof(hints)); 00084 hints.ai_family = AF_UNSPEC; 00085 hints.ai_socktype = SOCK_STREAM; 00086 hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; 00087 hints.ai_protocol = IPPROTO_TCP; 00088 00089 e = getaddrinfo(name, portstr, &hints, &ai); 00090 00091 if(e != 0) { 00092 fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(e)); 00093 freeaddrinfo(ai); 00094 exit(EXIT_FAILURE); 00095 } 00096 00097 if(sdp) { 00098 #ifdef WITH_SDP 00099 if (ai->ai_family == AF_INET) 00100 ai->ai_family = AF_INET_SDP; 00101 else (ai->ai_family == AF_INET6) 00102 ai->ai_family = AF_INET6_SDP; 00103 #else 00104 err("Can't do SDP: I was not compiled with SDP support!"); 00105 #endif 00106 } 00107 00108 for(rp = ai; rp != NULL; rp = rp->ai_next) { 00109 sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); 00110 00111 if(sock == -1) 00112 continue; /* error */ 00113 00114 if(connect(sock, rp->ai_addr, rp->ai_addrlen) != -1) 00115 break; /* success */ 00116 } 00117 00118 if (rp == NULL) 00119 err("Socket failed: %m"); 00120 00121 setmysockopt(sock); 00122 00123 freeaddrinfo(ai); 00124 return sock; 00125 } 00126 00127 void negotiate(int sock, u64 *rsize64, u32 *flags, char* name) { 00128 u64 magic, size64; 00129 uint16_t tmp; 00130 char buf[256] = "\0\0\0\0\0\0\0\0\0"; 00131 00132 printf("Negotiation: "); 00133 if (read(sock, buf, 8) < 0) 00134 err("Failed/1: %m"); 00135 if (strlen(buf)==0) 00136 err("Server closed connection"); 00137 if (strcmp(buf, INIT_PASSWD)) 00138 err("INIT_PASSWD bad"); 00139 printf("."); 00140 if (read(sock, &magic, sizeof(magic)) < 0) 00141 err("Failed/2: %m"); 00142 magic = ntohll(magic); 00143 if(name) { 00144 uint32_t opt; 00145 uint32_t namesize; 00146 uint32_t reserved = 0; 00147 00148 if (magic != opts_magic) 00149 err("Not enough opts_magic"); 00150 printf("."); 00151 if(read(sock, &tmp, sizeof(uint16_t)) < 0) { 00152 err("Failed reading flags: %m"); 00153 } 00154 *flags = ((u32)ntohs(tmp)) << 16; 00155 00156 /* reserved for future use*/ 00157 if (write(sock, &reserved, sizeof(reserved)) < 0) 00158 err("Failed/2.1: %m"); 00159 00160 /* Write the export name that we're after */ 00161 magic = ntohll(opts_magic); 00162 if (write(sock, &magic, sizeof(magic)) < 0) 00163 err("Failed/2.2: %m"); 00164 opt = ntohl(NBD_OPT_EXPORT_NAME); 00165 if (write(sock, &opt, sizeof(opt)) < 0) 00166 err("Failed/2.3: %m"); 00167 namesize = (u32)strlen(name); 00168 namesize = ntohl(namesize); 00169 if (write(sock, &namesize, sizeof(namesize)) < 0) 00170 err("Failed/2.4: %m"); 00171 if (write(sock, name, strlen(name)) < 0) 00172 err("Failed/2.4: %m"); 00173 } else { 00174 if (magic != cliserv_magic) 00175 err("Not enough cliserv_magic"); 00176 printf("."); 00177 } 00178 00179 if (read(sock, &size64, sizeof(size64)) < 0) 00180 err("Failed/3: %m\n"); 00181 size64 = ntohll(size64); 00182 00183 #ifdef NBD_SET_SIZE_BLOCKS 00184 if ((size64>>12) > (uint64_t)~0UL) { 00185 printf("size = %luMB", (unsigned long)(size64>>20)); 00186 err("Exported device is too big for me. Get 64-bit machine :-(\n"); 00187 } else 00188 printf("size = %luMB", (unsigned long)(size64>>20)); 00189 #else 00190 if (size64 > (~0UL >> 1)) { 00191 printf("size = %luKB", (unsigned long)(size64>>10)); 00192 err("Exported device is too big. Get 64-bit machine or newer kernel :-(\n"); 00193 } else 00194 printf("size = %lu", (unsigned long)(size64)); 00195 #endif 00196 00197 if(!name) { 00198 if (read(sock, flags, sizeof(*flags)) < 0) 00199 err("Failed/4: %m\n"); 00200 *flags = ntohl(*flags); 00201 } else { 00202 if(read(sock, &tmp, sizeof(tmp)) < 0) 00203 err("Failed/4: %m\n"); 00204 *flags |= (uint32_t)ntohs(tmp); 00205 } 00206 00207 if (read(sock, &buf, 124) < 0) 00208 err("Failed/5: %m\n"); 00209 printf("\n"); 00210 00211 *rsize64 = size64; 00212 } 00213 00214 void setsizes(int nbd, u64 size64, int blocksize, u32 flags) { 00215 unsigned long size; 00216 int read_only = (flags & NBD_FLAG_READ_ONLY) ? 1 : 0; 00217 00218 #ifdef NBD_SET_SIZE_BLOCKS 00219 if (size64>>12 > (uint64_t)~0UL) 00220 err("Device too large.\n"); 00221 else { 00222 int er; 00223 if (ioctl(nbd, NBD_SET_BLKSIZE, 4096UL) < 0) 00224 err("Ioctl/1.1a failed: %m\n"); 00225 size = (unsigned long)(size64>>12); 00226 if ((er = ioctl(nbd, NBD_SET_SIZE_BLOCKS, size)) < 0) 00227 err("Ioctl/1.1b failed: %m\n"); 00228 if (ioctl(nbd, NBD_SET_BLKSIZE, (unsigned long)blocksize) < 0) 00229 err("Ioctl/1.1c failed: %m\n"); 00230 fprintf(stderr, "bs=%d, sz=%llu bytes\n", blocksize, 4096ULL*size); 00231 } 00232 #else 00233 if (size64 > (~0UL >> 1)) { 00234 err("Device too large.\n"); 00235 } else { 00236 size = (unsigned long)size64; 00237 if (ioctl(nbd, NBD_SET_SIZE, size) < 0) 00238 err("Ioctl NBD_SET_SIZE failed: %m\n"); 00239 } 00240 #endif 00241 00242 ioctl(nbd, NBD_CLEAR_SOCK); 00243 00244 if (ioctl(nbd, BLKROSET, (unsigned long) &read_only) < 0) 00245 err("Unable to set read-only attribute for device"); 00246 } 00247 00248 void set_timeout(int nbd, int timeout) { 00249 if (timeout) { 00250 #ifdef NBD_SET_TIMEOUT 00251 if (ioctl(nbd, NBD_SET_TIMEOUT, (unsigned long)timeout) < 0) 00252 err("Ioctl NBD_SET_TIMEOUT failed: %m\n"); 00253 fprintf(stderr, "timeout=%d\n", timeout); 00254 #else 00255 err("Ioctl NBD_SET_TIMEOUT cannot be called when compiled on a system that does not support it\n"); 00256 #endif 00257 } 00258 } 00259 00260 void finish_sock(int sock, int nbd, int swap) { 00261 if (ioctl(nbd, NBD_SET_SOCK, sock) < 0) 00262 err("Ioctl NBD_SET_SOCK failed: %m\n"); 00263 00264 if (swap) 00265 mlockall(MCL_CURRENT | MCL_FUTURE); 00266 } 00267 00268 void usage(char* errmsg, ...) { 00269 if(errmsg) { 00270 char tmp[256]; 00271 va_list ap; 00272 va_start(ap, errmsg); 00273 snprintf(tmp, 256, "ERROR: %s\n\n", errmsg); 00274 vfprintf(stderr, errmsg, ap); 00275 va_end(ap); 00276 } else { 00277 fprintf(stderr, "nbd-client version %s\n", PACKAGE_VERSION); 00278 } 00279 fprintf(stderr, "Usage: nbd-client host port nbd_device [-block-size|-b block size] [-timeout|-t timeout] [-swap|-s] [-sdp|-S] [-persist|-p] [-nofork|-n] [-name|-N name]\n"); 00280 fprintf(stderr, "Or : nbd-client -d nbd_device\n"); 00281 fprintf(stderr, "Or : nbd-client -c nbd_device\n"); 00282 fprintf(stderr, "Or : nbd-client -h|--help\n"); 00283 fprintf(stderr, "Default value for blocksize is 1024 (recommended for ethernet)\n"); 00284 fprintf(stderr, "Allowed values for blocksize are 512,1024,2048,4096\n"); /* will be checked in kernel :) */ 00285 fprintf(stderr, "Note, that kernel 2.4.2 and older ones do not work correctly with\n"); 00286 fprintf(stderr, "blocksizes other than 1024 without patches\n"); 00287 } 00288 00289 void disconnect(char* device) { 00290 int nbd = open(device, O_RDWR); 00291 00292 if (nbd < 0) 00293 err("Cannot open NBD: %m\nPlease ensure the 'nbd' module is loaded."); 00294 printf("Disconnecting: que, "); 00295 if (ioctl(nbd, NBD_CLEAR_QUE)< 0) 00296 err("Ioctl failed: %m\n"); 00297 printf("disconnect, "); 00298 #ifdef NBD_DISCONNECT 00299 if (ioctl(nbd, NBD_DISCONNECT)<0) 00300 err("Ioctl failed: %m\n"); 00301 printf("sock, "); 00302 #else 00303 fprintf(stderr, "Can't disconnect: I was not compiled with disconnect support!\n" ); 00304 exit(1); 00305 #endif 00306 if (ioctl(nbd, NBD_CLEAR_SOCK)<0) 00307 err("Ioctl failed: %m\n"); 00308 printf("done\n"); 00309 } 00310 00311 int main(int argc, char *argv[]) { 00312 char* port=NULL; 00313 int sock, nbd; 00314 int blocksize=1024; 00315 char *hostname=NULL; 00316 char *nbddev=NULL; 00317 int swap=0; 00318 int cont=0; 00319 int timeout=0; 00320 int sdp=0; 00321 int nofork=0; 00322 u64 size64; 00323 u32 flags; 00324 int c; 00325 int nonspecial=0; 00326 char* name=NULL; 00327 struct option long_options[] = { 00328 { "block-size", required_argument, NULL, 'b' }, 00329 { "check", required_argument, NULL, 'c' }, 00330 { "disconnect", required_argument, NULL, 'd' }, 00331 { "help", no_argument, NULL, 'h' }, 00332 { "name", required_argument, NULL, 'N' }, 00333 { "nofork", no_argument, NULL, 'n' }, 00334 { "persist", no_argument, NULL, 'p' }, 00335 { "sdp", no_argument, NULL, 'S' }, 00336 { "swap", no_argument, NULL, 's' }, 00337 { "timeout", required_argument, NULL, 't' }, 00338 { 0, 0, 0, 0 }, 00339 }; 00340 00341 logging(); 00342 00343 while((c=getopt_long_only(argc, argv, "-b:c:d:hnN:pSst:", long_options, NULL))>=0) { 00344 switch(c) { 00345 case 1: 00346 // non-option argument 00347 if(strchr(optarg, '=')) { 00348 // old-style 'bs=' or 'timeout=' 00349 // argument 00350 fprintf(stderr, "WARNING: old-style command-line argument encountered. This is deprecated.\n"); 00351 if(!strncmp(optarg, "bs=", 3)) { 00352 optarg+=3; 00353 goto blocksize; 00354 } 00355 if(!strncmp(optarg, "timeout=", 8)) { 00356 optarg+=8; 00357 goto timeout; 00358 } 00359 usage("unknown option %s encountered", optarg); 00360 exit(EXIT_FAILURE); 00361 } 00362 switch(nonspecial++) { 00363 case 0: 00364 // host 00365 hostname=optarg; 00366 break; 00367 case 1: 00368 // port 00369 if(!strtol(optarg, NULL, 0)) { 00370 // not parseable as a number, assume it's the device and we have a name 00371 nbddev = optarg; 00372 nonspecial++; 00373 } else { 00374 port = optarg; 00375 if(name) { 00376 usage("port and name specified at the same time. This is not supported."); 00377 exit(EXIT_FAILURE); 00378 } 00379 } 00380 break; 00381 case 2: 00382 // device 00383 nbddev = optarg; 00384 break; 00385 default: 00386 usage("too many non-option arguments specified"); 00387 exit(EXIT_FAILURE); 00388 } 00389 break; 00390 case 'b': 00391 blocksize: 00392 blocksize=(int)strtol(optarg, NULL, 0); 00393 break; 00394 case 'c': 00395 return check_conn(optarg, 1); 00396 case 'd': 00397 disconnect(optarg); 00398 exit(EXIT_SUCCESS); 00399 case 'h': 00400 usage(NULL); 00401 exit(EXIT_SUCCESS); 00402 case 'n': 00403 nofork=1; 00404 break; 00405 case 'N': 00406 name=optarg; 00407 if(port) { 00408 usage("port and name specified at the same time. This is not supported."); 00409 exit(EXIT_FAILURE); 00410 } 00411 port = NBD_DEFAULT_PORT; 00412 break; 00413 case 'p': 00414 cont=1; 00415 break; 00416 case 's': 00417 swap=1; 00418 break; 00419 case 'S': 00420 sdp=1; 00421 break; 00422 case 't': 00423 timeout: 00424 timeout=strtol(optarg, NULL, 0); 00425 break; 00426 default: 00427 fprintf(stderr, "E: option eaten by 42 mice\n"); 00428 exit(EXIT_FAILURE); 00429 } 00430 } 00431 00432 if((!port && !name) || !hostname || !nbddev) { 00433 usage("not enough information specified"); 00434 exit(EXIT_FAILURE); 00435 } 00436 00437 nbd = open(nbddev, O_RDWR); 00438 if (nbd < 0) 00439 err("Cannot open NBD: %m\nPlease ensure the 'nbd' module is loaded."); 00440 ++argv; --argc; /* skip device */ 00441 00442 sock = opennet(hostname, port, sdp); 00443 00444 negotiate(sock, &size64, &flags, name); 00445 setsizes(nbd, size64, blocksize, flags); 00446 set_timeout(nbd, timeout); 00447 finish_sock(sock, nbd, swap); 00448 00449 /* Go daemon */ 00450 00451 #ifndef NOFORK 00452 if(!nofork) { 00453 if (daemon(0,0) < 0) 00454 err("Cannot detach from terminal"); 00455 } 00456 #endif 00457 do { 00458 #ifndef NOFORK 00459 if (fork()) { 00460 /* Due to a race, the kernel NBD driver cannot 00461 * call for a reread of the partition table 00462 * in the handling of the NBD_DO_IT ioctl(). 00463 * Therefore, this is done in the first open() 00464 * of the device. We therefore make sure that 00465 * the device is opened at least once after the 00466 * connection was made. This has to be done in a 00467 * separate process, since the NBD_DO_IT ioctl() 00468 * does not return until the NBD device has 00469 * disconnected. 00470 */ 00471 while(check_conn(nbddev, 0)) { 00472 sleep(1); 00473 } 00474 open(nbddev, O_RDONLY); 00475 exit(0); 00476 } 00477 #endif 00478 00479 if (ioctl(nbd, NBD_DO_IT) < 0) { 00480 fprintf(stderr, "Kernel call returned: %m"); 00481 if(errno==EBADR) { 00482 /* The user probably did 'nbd-client -d' on us. 00483 * quit */ 00484 cont=0; 00485 } else { 00486 if(cont) { 00487 u64 new_size; 00488 u32 new_flags; 00489 00490 fprintf(stderr, " Reconnecting\n"); 00491 close(sock); close(nbd); 00492 sock = opennet(hostname, port, sdp); 00493 nbd = open(nbddev, O_RDWR); 00494 negotiate(sock, &new_size, &new_flags, name); 00495 if (size64 != new_size) { 00496 err("Size of the device changed. Bye"); 00497 } 00498 setsizes(nbd, size64, blocksize, 00499 new_flags); 00500 00501 set_timeout(nbd, timeout); 00502 finish_sock(sock,nbd,swap); 00503 } 00504 } 00505 } else { 00506 /* We're on 2.4. It's not clearly defined what exactly 00507 * happened at this point. Probably best to quit, now 00508 */ 00509 fprintf(stderr, "Kernel call returned."); 00510 cont=0; 00511 } 00512 } while(cont); 00513 printf("Closing: que, "); 00514 ioctl(nbd, NBD_CLEAR_QUE); 00515 printf("sock, "); 00516 ioctl(nbd, NBD_CLEAR_SOCK); 00517 printf("done\n"); 00518 return 0; 00519 }