nbd-client: Add support for setting /sys/block/nbdN/backend
[nbd/ericb.git] / nbd-client.c
blob2be3a4bc7e7dfd9a93758b7dc382339ae17f588c
1 /*
2 * Open connection for network block device
4 * Copyright 1997,1998 Pavel Machek, distribute under GPL
5 * <pavel@atrey.karlin.mff.cuni.cz>
6 * Copyright (c) 2002 - 2011 Wouter Verhelst <w@uter.be>
8 * Version 1.0 - 64bit issues should be fixed, now
9 * Version 1.1 - added bs (blocksize) option (Alexey Guzeev, aga@permonline.ru)
10 * Version 1.2 - I added new option '-d' to send the disconnect request
11 * Version 2.0 - Version synchronised with server
12 * Version 2.1 - Check for disconnection before INIT_PASSWD is received
13 * to make errormsg a bit more helpful in case the server can't
14 * open the exported file.
15 * 16/03/2010 - Add IPv6 support.
16 * Kitt Tientanopajai <kitt@kitty.in.th>
17 * Neutron Soutmun <neo.neutron@gmail.com>
18 * Suriya Soutmun <darksolar@gmail.com>
21 #include "config.h"
22 #include "lfs.h"
24 #include <sys/ioctl.h>
25 #include <sys/socket.h>
26 #include <sys/un.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 #include <netinet/tcp.h>
30 #include <netinet/in.h>
31 #include <netdb.h>
32 #include "netdb-compat.h"
33 #include <inttypes.h>
34 #include <stdio.h>
35 #include <fcntl.h>
36 #include <syslog.h>
37 #include <stdlib.h>
38 #include <sys/mount.h>
39 #include <sys/mman.h>
40 #include <signal.h>
41 #include <errno.h>
42 #include <getopt.h>
43 #include <stdarg.h>
44 #include <stdbool.h>
45 #include <time.h>
47 #include <linux/ioctl.h>
49 #if HAVE_NETLINK
50 #include "nbd-netlink.h"
51 #include <netlink/netlink.h>
52 #include <netlink/genl/genl.h>
53 #include <netlink/genl/ctrl.h>
54 #endif
56 #define MY_NAME "nbd_client"
57 #include "cliserv.h"
59 #if HAVE_GNUTLS && !defined(NOTLS)
60 #include "crypto-gnutls.h"
61 #endif
63 #include "nbdclt.h"
64 #include "nbdtab_parser.tab.h"
66 CLIENT* cur_client;
67 extern FILE *yyin, *yyout;
68 bool found_config = false;
69 bool parse_error = false;
71 #define SET_PROP(str, member, rval) if(!strcmp(property, str)) { cur_client->member = (rval); return; }
72 void nbdtab_set_property(char *property, char *val) {
73 if(found_config) return;
74 SET_PROP("port", port, val);
75 SET_PROP("certfile", cert, val);
76 SET_PROP("keyfile", key, val);
77 SET_PROP("cacertfile", cacert, val);
78 SET_PROP("tlshostname", tlshostn, val);
79 SET_PROP("priority", priority, val);
80 SET_PROP("bs", bs, strtol(val, NULL, 10));
81 SET_PROP("timeout", timeout, strtol(val, NULL, 10));
82 SET_PROP("conns", nconn, strtol(val, NULL, 10));
83 if(*property != '_') {
84 fprintf(stderr, "Warning: unknown option '%s' found in nbdtab file", property);
87 #undef SET_PROP
89 #define SET_FLAG(str, member) if(!strcmp(property, str)) { cur_client->member = true; return; }
90 void nbdtab_set_flag(char *property) {
91 if(found_config) return;
92 SET_FLAG("no_optgo", no_optgo);
93 SET_FLAG("persist", persist);
94 SET_FLAG("swap", swap);
95 SET_FLAG("unix", b_unix);
96 SET_FLAG("preinit", preinit);
97 SET_FLAG("tls", tls);
98 if(*property != '_') {
99 fprintf(stderr, "Warning: unknown option '%s' found in nbdtab file", property);
103 void nbdtab_commit_line(char *devn, char *hostn, char *exportname) {
104 if(!strncmp(devn, "/dev/", 5)) {
105 devn += 5;
107 if(!strcmp(cur_client->dev, devn)) {
108 found_config = true;
109 cur_client->hostn = hostn;
110 cur_client->name = exportname;
111 } else {
112 if(!found_config) {
113 char *tmp = cur_client->dev;
114 memset(cur_client, 0, sizeof(CLIENT));
115 cur_client->bs = 512;
116 cur_client->nconn = 1;
117 cur_client->dev = tmp;
120 return;
123 void yyerror(char *msg) {
124 parse_error = true;
125 fprintf(stderr, "parse error parsing " SYSCONFDIR "/nbdtab: %s", msg);
128 #undef SET_FLAG
130 #define NBDC_DO_LIST 1
132 #if HAVE_NETLINK
133 static int callback(struct nl_msg *msg, void *arg) {
134 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
135 struct nlattr *msg_attr[NBD_ATTR_MAX + 1];
136 int ret;
137 uint32_t index;
139 ret = nla_parse(msg_attr, NBD_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
140 genlmsg_attrlen(gnlh, 0), NULL);
141 if (ret)
142 err("Invalid response from the kernel\n");
143 if (!msg_attr[NBD_ATTR_INDEX])
144 err("Did not receive index from the kernel\n");
145 index = nla_get_u32(msg_attr[NBD_ATTR_INDEX]);
146 printf("Connected /dev/nbd%d\n", (int)index);
147 return NL_OK;
150 static struct nl_sock *get_nbd_socket(int *driver_id) {
151 struct nl_sock *socket;
153 socket = nl_socket_alloc();
154 if (!socket)
155 err("Couldn't allocate netlink socket\n");
157 if (genl_connect(socket))
158 err("Couldn't connect to the generic netlink socket\n");
159 *driver_id = genl_ctrl_resolve(socket, "nbd");
160 if (*driver_id < 0)
161 err("Couldn't resolve the nbd netlink family, make sure the nbd module is loaded and your nbd driver supports the netlink interface.\n");
162 return socket;
165 static void netlink_configure(int index, int *sockfds, int num_connects,
166 u64 size64, int blocksize, uint16_t flags,
167 int timeout, const char *identifier) {
168 struct nl_sock *socket;
169 struct nlattr *sock_attr;
170 struct nl_msg *msg;
171 int driver_id, i;
173 socket = get_nbd_socket(&driver_id);
174 nl_socket_modify_cb(socket, NL_CB_VALID, NL_CB_CUSTOM, callback, NULL);
176 msg = nlmsg_alloc();
177 if (!msg)
178 err("Couldn't allocate netlink message\n");
179 genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, driver_id, 0, 0,
180 NBD_CMD_CONNECT, 0);
181 if (index >= 0)
182 NLA_PUT_U32(msg, NBD_ATTR_INDEX, index);
183 NLA_PUT_U64(msg, NBD_ATTR_SIZE_BYTES, size64);
184 NLA_PUT_U64(msg, NBD_ATTR_BLOCK_SIZE_BYTES, blocksize);
185 NLA_PUT_U64(msg, NBD_ATTR_SERVER_FLAGS, flags);
186 if (timeout)
187 NLA_PUT_U64(msg, NBD_ATTR_TIMEOUT, timeout);
188 if (identifier)
189 NLA_PUT_STRING(msg, NBD_ATTR_BACKEND_IDENTIFIER, identifier);
191 sock_attr = nla_nest_start(msg, NBD_ATTR_SOCKETS);
192 if (!sock_attr)
193 err("Couldn't nest the sockets for our connection\n");
194 for (i = 0; i < num_connects; i++) {
195 struct nlattr *sock_opt;
196 sock_opt = nla_nest_start(msg, NBD_SOCK_ITEM);
197 if (!sock_opt)
198 err("Couldn't nest the sockets for our connection\n");
199 NLA_PUT_U32(msg, NBD_SOCK_FD, sockfds[i]);
200 nla_nest_end(msg, sock_opt);
202 nla_nest_end(msg, sock_attr);
204 if (nl_send_sync(socket, msg) < 0) {
205 if(geteuid() != 0) {
206 err("Failed to setup device. Are you root?\n");
207 } else {
208 err("Failed to setup device, check dmesg\n");
211 return;
212 nla_put_failure:
213 err("Failed to create netlink message\n");
216 static void netlink_disconnect(char *nbddev) {
217 struct nl_sock *socket;
218 struct nl_msg *msg;
219 int driver_id;
221 int index = -1;
222 if (nbddev) {
223 if (sscanf(nbddev, "/dev/nbd%d", &index) != 1)
224 err("Invalid nbd device target\n");
226 if (index < 0)
227 err("Invalid nbd device target\n");
229 socket = get_nbd_socket(&driver_id);
231 msg = nlmsg_alloc();
232 if (!msg)
233 err("Couldn't allocate netlink message\n");
234 genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, driver_id, 0, 0,
235 NBD_CMD_DISCONNECT, 0);
236 NLA_PUT_U32(msg, NBD_ATTR_INDEX, index);
237 if (nl_send_sync(socket, msg) < 0)
238 err("Failed to disconnect device, check dmsg\n");
239 nl_socket_free(socket);
240 return;
241 nla_put_failure:
242 err("Failed to create netlink message\n");
244 #else
245 static void netlink_configure(int index, int *sockfds, int num_connects,
246 u64 size64, int blocksize, uint16_t flags,
247 int timeout, const char *identifier)
251 static void netlink_disconnect(char *nbddev)
254 #endif /* HAVE_NETLINK */
256 int check_conn(char* devname, int do_print) {
257 char buf[256];
258 char* p;
259 int fd;
260 int len;
262 if( (p=strrchr(devname, '/')) ) {
263 devname=p+1;
265 if((p=strchr(devname, 'p'))) {
266 /* We can't do checks on partitions. */
267 *p='\0';
269 snprintf(buf, 256, "/sys/block/%s/pid", devname);
270 if((fd=open(buf, O_RDONLY))<0) {
271 if(errno==ENOENT) {
272 return 1;
273 } else {
274 return 2;
277 len=read(fd, buf, 256);
278 if(len < 0) {
279 perror("could not read from server");
280 close(fd);
281 return 2;
283 buf[(len < 256) ? len : 255]='\0';
284 if(do_print) printf("%s\n", buf);
285 close(fd);
286 return 0;
289 int opennet() {
290 int sock;
291 struct addrinfo hints;
292 struct addrinfo *ai = NULL;
293 struct addrinfo *rp = NULL;
294 int e;
296 memset(&hints,'\0',sizeof(hints));
297 hints.ai_family = AF_UNSPEC;
298 hints.ai_socktype = SOCK_STREAM;
299 hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
300 hints.ai_protocol = IPPROTO_TCP;
302 e = getaddrinfo(cur_client->hostn, cur_client->port, &hints, &ai);
304 if(e != 0) {
305 fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(e));
306 freeaddrinfo(ai);
307 return -1;
310 for(rp = ai; rp != NULL; rp = rp->ai_next) {
311 sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
313 if(sock == -1)
314 continue; /* error */
316 if(connect(sock, rp->ai_addr, rp->ai_addrlen) != -1)
317 break; /* success */
319 close(sock);
322 if (rp == NULL) {
323 err_nonfatal("Socket failed: %m");
324 sock = -1;
325 goto err;
328 setmysockopt(sock);
329 err:
330 freeaddrinfo(ai);
331 return sock;
334 int openunix() {
335 int sock;
336 char *path = cur_client->hostn;
337 struct sockaddr_un un_addr;
338 memset(&un_addr, 0, sizeof(un_addr));
340 un_addr.sun_family = AF_UNIX;
341 if (strnlen(path, sizeof(un_addr.sun_path)) == sizeof(un_addr.sun_path)) {
342 err_nonfatal("UNIX socket path too long");
343 return -1;
346 strncpy(un_addr.sun_path, path, sizeof(un_addr.sun_path) - 1);
348 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
349 err_nonfatal("SOCKET failed");
350 return -1;
353 if (connect(sock, (struct sockaddr*)&un_addr, sizeof(un_addr)) == -1) {
354 err_nonfatal("CONNECT failed");
355 close(sock);
356 return -1;
358 return sock;
361 void send_request(int sock, uint32_t opt, ssize_t datasize, void* data) {
362 struct {
363 uint64_t magic;
364 uint32_t opt;
365 uint32_t datasize;
366 } __attribute__((packed)) header = {
367 ntohll(opts_magic),
368 ntohl(opt),
369 ntohl(datasize),
371 if(datasize < 0) {
372 datasize = strlen((char*)data);
373 header.datasize = htonl(datasize);
375 writeit(sock, &header, sizeof(header));
376 if(data != NULL) {
377 writeit(sock, data, datasize);
381 void send_info_request(int sock, uint32_t opt, int n_reqs, uint16_t* reqs, char* name) {
382 uint16_t rlen = htons(n_reqs);
383 uint32_t nlen = htonl(strlen(name));
385 send_request(sock, opt, sizeof(uint32_t) + strlen(name) + sizeof(uint16_t) + n_reqs * sizeof(uint16_t), NULL);
386 writeit(sock, &nlen, sizeof(nlen));
387 writeit(sock, name, strlen(name));
388 writeit(sock, &rlen, sizeof(rlen));
389 if(n_reqs > 0) {
390 writeit(sock, reqs, n_reqs * sizeof(uint16_t));
394 struct reply {
395 uint64_t magic;
396 uint32_t opt;
397 uint32_t reply_type;
398 uint32_t datasize;
399 char data[];
400 } __attribute__((packed));
402 struct reply* read_reply(int sock) {
403 struct reply *retval = malloc(sizeof(struct reply));
405 if (retval == NULL)
406 return NULL;
408 if(readit(sock, retval, sizeof(*retval)) < 0) {
409 free(retval);
410 return NULL;
413 retval->magic = ntohll(retval->magic);
414 retval->opt = ntohl(retval->opt);
415 retval->reply_type = ntohl(retval->reply_type);
416 retval->datasize = ntohl(retval->datasize);
417 if (retval->magic != rep_magic) {
418 fprintf(stderr, "E: received invalid negotiation magic %" PRIu64 " (expected %" PRIu64 ")", retval->magic, rep_magic);
419 exit(EXIT_FAILURE);
421 if (retval->datasize > 0 && retval->datasize < 4096) {
422 struct reply *retval_r = realloc(retval, sizeof(struct reply) + retval->datasize);
423 if (retval_r == NULL) {
424 free(retval);
425 return NULL;
427 retval = retval_r;
428 readit(sock, &(retval->data), retval->datasize);
430 return retval;
433 void ask_list(int sock) {
434 uint32_t opt_server;
435 uint32_t len;
436 uint32_t lenn;
437 uint32_t reptype;
438 uint64_t magic;
439 int rlen;
440 const int BUF_SIZE = 1024;
441 char buf[BUF_SIZE];
443 send_request(sock, NBD_OPT_LIST, 0, NULL);
444 /* newline, move away from the "Negotiation:" line */
445 printf("\n");
446 do {
447 memset(buf, 0, 1024);
448 if(read(sock, &magic, sizeof(magic)) < 0) {
449 err("Reading magic from server: %m");
451 if(read(sock, &opt_server, sizeof(opt_server)) < 0) {
452 err("Reading option: %m");
454 if(read(sock, &reptype, sizeof(reptype)) <0) {
455 err("Reading reply from server: %m");
457 if(read(sock, &len, sizeof(len)) < 0) {
458 err("Reading length from server: %m");
460 magic=ntohll(magic);
461 len=ntohl(len);
462 reptype=ntohl(reptype);
463 if(magic != rep_magic) {
464 err("Not enough magic from server");
466 if(reptype & NBD_REP_FLAG_ERROR) {
467 switch(reptype) {
468 case NBD_REP_ERR_POLICY:
469 fprintf(stderr, "\nE: listing not allowed by server.\n");
470 break;
471 default:
472 fprintf(stderr, "\nE: unexpected error from server.\n");
473 break;
475 if(len > 0 && len < BUF_SIZE) {
476 if((rlen=read(sock, buf, len)) < 0) {
477 fprintf(stderr, "\nE: could not read error message from server\n");
478 } else {
479 buf[rlen] = '\0';
480 fprintf(stderr, "Server said: %s\n", buf);
483 exit(EXIT_FAILURE);
484 } else {
485 if(reptype != NBD_REP_ACK) {
486 if(reptype != NBD_REP_SERVER) {
487 err("Server sent us a reply we don't understand!");
489 if(read(sock, &lenn, sizeof(lenn)) < 0) {
490 fprintf(stderr, "\nE: could not read export name length from server\n");
491 exit(EXIT_FAILURE);
493 lenn=ntohl(lenn);
494 if (lenn >= BUF_SIZE) {
495 fprintf(stderr, "\nE: export name on server too long\n");
496 exit(EXIT_FAILURE);
498 if(read(sock, buf, lenn) < 0) {
499 fprintf(stderr, "\nE: could not read export name from server\n");
500 exit(EXIT_FAILURE);
502 buf[lenn] = 0;
503 printf("%s", buf);
504 len -= lenn;
505 len -= sizeof(lenn);
506 if(len > 0) {
507 if(len >= BUF_SIZE) {
508 fprintf(stderr, "\nE: export description read buffer overflow\n");
509 exit(EXIT_FAILURE);
511 if(read(sock, buf, len) < 0) {
512 fprintf(stderr, "\nE: could not read export description from server\n");
513 exit(EXIT_FAILURE);
515 buf[len] = 0;
516 printf(": %s\n", buf);
517 } else {
518 printf("\n");
522 } while(reptype != NBD_REP_ACK);
523 send_request(sock, NBD_OPT_ABORT, 0, NULL);
526 void parse_sizes(char *buf, uint64_t *size, uint16_t *flags) {
527 memcpy(size, buf, sizeof(*size));
528 *size = ntohll(*size);
529 buf += sizeof(*size);
530 memcpy(flags, buf, sizeof(*flags));
531 *flags = ntohs(*flags);
533 if ((*size>>12) > (uint64_t)~0UL) {
534 printf("size = %luMB", (unsigned long)(*size>>20));
535 err("Exported device is too big for me. Get 64-bit machine :-(\n");
536 } else {
537 printf("size = %luMB", (unsigned long)(*size>>20));
539 printf("\n");
542 void send_opt_exportname(int sock, uint16_t *flags, char* name, uint16_t global_flags) {
543 send_request(sock, NBD_OPT_EXPORT_NAME, -1, name);
544 char b[sizeof(*flags) + sizeof(cur_client->size64)];
545 if(readit(sock, b, sizeof(b)) < 0 && !cur_client->no_optgo) {
546 err("E: server does not support NBD_OPT_GO and dropped connection after sending NBD_OPT_EXPORT_NAME. Try -g.");
548 parse_sizes(b, &(cur_client->size64), flags);
549 if(!(global_flags & NBD_FLAG_NO_ZEROES)) {
550 char buf[125];
551 readit(sock, buf, 124);
555 void negotiate(int *sockp, uint16_t *flags, uint32_t needed_flags, uint32_t client_flags, uint32_t do_opts) {
556 u64 magic;
557 uint16_t tmp;
558 uint16_t global_flags;
559 char buf[256] = "\0\0\0\0\0\0\0\0\0";
560 int sock = *sockp;
562 printf("Negotiation: ");
563 readit(sock, buf, 8);
564 if (strcmp(buf, INIT_PASSWD))
565 err("INIT_PASSWD bad");
566 printf(".");
567 readit(sock, &magic, sizeof(magic));
568 magic = ntohll(magic);
569 if (magic != opts_magic) {
570 if(magic == cliserv_magic) {
571 err("It looks like you're trying to connect to an oldstyle server. This is no longer supported since nbd 3.10.");
574 printf(".");
575 readit(sock, &tmp, sizeof(uint16_t));
576 global_flags = ntohs(tmp);
577 if((needed_flags & global_flags) != needed_flags) {
578 /* There's currently really only one reason why this
579 * check could possibly fail, but we may need to change
580 * this error message in the future... */
581 fprintf(stderr, "\nE: Server does not support listing exports\n");
582 exit(EXIT_FAILURE);
585 if (global_flags & NBD_FLAG_NO_ZEROES) {
586 client_flags |= NBD_FLAG_C_NO_ZEROES;
588 client_flags = htonl(client_flags);
589 if (write(sock, &client_flags, sizeof(client_flags)) < 0)
590 err("Failed/2.1: %m");
592 #if HAVE_GNUTLS && !defined(NOTLS)
593 /* TLS */
594 if (cur_client->tls) {
595 int plainfd[2]; // [0] is used by the proxy, [1] is used by NBD
596 tlssession_t *s = NULL;
597 int ret;
598 uint32_t tmp32;
599 uint64_t tmp64;
601 send_request(sock, NBD_OPT_STARTTLS, 0, NULL);
603 if (read(sock, &tmp64, sizeof(tmp64)) < 0)
604 err("Could not read cliserv_magic: %m");
605 tmp64 = ntohll(tmp64);
606 if (tmp64 != NBD_OPT_REPLY_MAGIC) {
607 err("reply magic does not match");
609 if (read(sock, &tmp32, sizeof(tmp32)) < 0)
610 err("Could not read option type: %m");
611 tmp32 = ntohl(tmp32);
612 if (tmp32 != NBD_OPT_STARTTLS)
613 err("Reply to wrong option");
614 if (read(sock, &tmp32, sizeof(tmp32)) < 0)
615 err("Could not read option reply type: %m");
616 tmp32 = ntohl(tmp32);
617 if (tmp32 != NBD_REP_ACK) {
618 err("Option reply type != NBD_REP_ACK");
620 if (read(sock, &tmp32, sizeof(tmp32)) < 0) err(
621 "Could not read option data length: %m");
622 tmp32 = ntohl(tmp32);
623 if (tmp32 != 0) {
624 err("Option reply data length != 0");
626 s = tlssession_new(0,
627 cur_client->key,
628 cur_client->cert,
629 cur_client->cacert,
630 cur_client->tlshostn,
631 cur_client->priority,
632 !(cur_client->cacert) || !(cur_client->tlshostn), // insecure flag
633 #ifdef DODBG
634 1, // debug
635 #else
636 0, // debug
637 #endif
638 NULL, // quitfn
639 NULL, // erroutfn
640 NULL // opaque
642 if (!s)
643 err("Cannot establish TLS session");
645 if (socketpair(AF_UNIX, SOCK_STREAM, 0, plainfd) < 0)
646 err("Cannot get socket pair");
648 if (set_nonblocking(plainfd[0], 0) <0 ||
649 set_nonblocking(plainfd[1], 0) <0 ||
650 set_nonblocking(sock, 0) <0) {
651 close(plainfd[0]);
652 close(plainfd[1]);
653 err("Cannot set socket options");
656 ret = fork();
657 if (ret < 0)
658 err("Could not fork");
659 else if (ret == 0) {
660 // we are the child
661 if (daemon(0, 0) < 0) {
662 /* no one will see this */
663 fprintf(stderr, "Can't detach from the terminal");
664 exit(1);
666 signal (SIGPIPE, SIG_IGN);
667 close(plainfd[1]);
668 tlssession_mainloop(sock, plainfd[0], s);
669 close(sock);
670 close(plainfd[0]);
671 exit(0);
673 close(plainfd[0]);
674 close(sock);
675 sock = plainfd[1]; /* use the decrypted FD from now on */
676 *sockp = sock;
678 #else
679 if (cur_client->tls) {
680 err("TLS requested but support not compiled in");
682 #endif
684 if(do_opts & NBDC_DO_LIST) {
685 ask_list(sock);
686 exit(EXIT_SUCCESS);
689 struct reply *rep = NULL;
691 if(cur_client->no_optgo) {
692 send_opt_exportname(sock, flags, cur_client->name, global_flags);
693 return;
696 send_info_request(sock, NBD_OPT_GO, 0, NULL, cur_client->name);
698 do {
699 if(rep != NULL) free(rep);
700 rep = read_reply(sock);
701 if(rep == NULL) {
702 err("Unable to read reply.");
703 exit(EXIT_FAILURE);
705 if(rep->reply_type & NBD_REP_FLAG_ERROR) {
706 switch(rep->reply_type) {
707 case NBD_REP_ERR_UNSUP:
708 /* server doesn't support NBD_OPT_GO or NBD_OPT_INFO,
709 * fall back to NBD_OPT_EXPORT_NAME */
710 send_opt_exportname(sock, flags, cur_client->name, global_flags);
711 free(rep);
712 return;
713 case NBD_REP_ERR_POLICY:
714 if(rep->datasize > 0) {
715 char errstr[1024];
716 rep->data[rep->datasize - 1] = '\0';
717 snprintf(errstr, sizeof errstr, "Connection not allowed by server policy. Server said: %s", rep->data);
718 err(errstr);
719 } else {
720 err("Connection not allowed by server policy.");
722 free(rep);
723 exit(EXIT_FAILURE);
724 default:
725 if(rep->datasize > 0) {
726 char errstr[1024];
727 rep->data[rep->datasize - 1] = '\0';
728 snprintf(errstr, sizeof errstr, "Unknown error returned by server. Server said: %s", rep->data);
729 err(errstr);
730 } else {
731 err("Unknown error returned by server.");
733 free(rep);
734 exit(EXIT_FAILURE);
737 uint16_t info_type;
738 switch(rep->reply_type) {
739 case NBD_REP_INFO:
740 memcpy(&info_type, rep->data, 2);
741 info_type = htons(info_type);
742 switch(info_type) {
743 case NBD_INFO_EXPORT:
744 parse_sizes(rep->data + 2, &(cur_client->size64), flags);
745 break;
746 default:
747 // ignore these, don't need them
748 break;
750 break;
751 case NBD_REP_ACK:
752 break;
753 default:
754 err_nonfatal("Unknown reply to NBD_OPT_GO received, ignoring");
756 } while(rep->reply_type != NBD_REP_ACK);
757 free(rep);
760 bool get_from_config() {
761 bool retval = false;
762 yyin = fopen(SYSCONFDIR "/nbdtab", "r");
763 yyout = fopen("/dev/null", "w");
765 if(!strncmp(cur_client->dev, "/dev/", 5)) {
766 cur_client->dev += 5;
768 if(yyin == NULL) {
769 fprintf(stderr, "while opening %s: ", SYSCONFDIR "/nbdtab");
770 perror("could not open config file");
771 goto out;
773 yyparse();
775 if(!found_config || parse_error) {
776 goto out;
779 retval = true;
780 out:
781 if(yyin != NULL) {
782 fclose(yyin);
784 if(yyout != NULL) {
785 fclose(yyout);
787 return retval;
790 void setsizes(int nbd, u64 size64, int blocksize, u32 flags) {
791 unsigned long size;
792 int read_only = (flags & NBD_FLAG_READ_ONLY) ? 1 : 0;
794 if (size64>>12 > (uint64_t)~0UL)
795 err("Device too large.\n");
796 else {
797 int tmp_blocksize = 4096;
798 if (size64 / (u64)blocksize <= (uint64_t)~0UL)
799 tmp_blocksize = blocksize;
800 if (ioctl(nbd, NBD_SET_BLKSIZE, tmp_blocksize) < 0) {
801 fprintf(stderr, "Failed to set blocksize %d\n",
802 tmp_blocksize);
803 err("Ioctl/1.1a failed: %m\n");
805 size = (unsigned long)(size64 / (u64)tmp_blocksize);
806 if (ioctl(nbd, NBD_SET_SIZE_BLOCKS, size) < 0)
807 err("Ioctl/1.1b failed: %m\n");
808 if (tmp_blocksize != blocksize) {
809 if (ioctl(nbd, NBD_SET_BLKSIZE, (unsigned long)blocksize) < 0) {
810 fprintf(stderr, "Failed to set blocksize %d\n",
811 blocksize);
812 err("Ioctl/1.1c failed: %m\n");
815 fprintf(stderr, "bs=%d, sz=%" PRIu64 " bytes\n", blocksize, (u64)tmp_blocksize * size);
818 ioctl(nbd, NBD_CLEAR_SOCK);
820 /* ignore error as kernel may not support */
821 ioctl(nbd, NBD_SET_FLAGS, (unsigned long) flags);
823 if (ioctl(nbd, BLKROSET, (unsigned long) &read_only) < 0)
824 err("Unable to set read-only attribute for device");
827 void set_timeout(int nbd, int timeout) {
828 if (timeout) {
829 if (ioctl(nbd, NBD_SET_TIMEOUT, (unsigned long)timeout) < 0)
830 err("Ioctl NBD_SET_TIMEOUT failed: %m\n");
831 fprintf(stderr, "timeout=%d\n", timeout);
835 void finish_sock(int sock, int nbd, int swap) {
836 if (ioctl(nbd, NBD_SET_SOCK, sock) < 0) {
837 if (errno == EBUSY)
838 err("Kernel doesn't support multiple connections\n");
839 else
840 err("Ioctl NBD_SET_SOCK failed: %m\n");
843 #ifndef __ANDROID__
844 if (swap)
845 mlockall(MCL_CURRENT | MCL_FUTURE);
846 #endif
849 static int
850 oom_adjust(const char *file, const char *value)
852 int fd, rc;
853 size_t len;
855 fd = open(file, O_WRONLY);
856 if (fd < 0)
857 return -1;
858 len = strlen(value);
859 rc = write(fd, value, len) != (ssize_t) len;
860 close(fd);
861 return rc ? -1 : 0;
864 void usage(char* errmsg, ...) {
865 if(errmsg) {
866 char tmp[256];
867 va_list ap;
868 va_start(ap, errmsg);
869 snprintf(tmp, 256, "ERROR: %s\n\n", errmsg);
870 vfprintf(stderr, tmp, ap);
871 va_end(ap);
872 } else {
873 fprintf(stderr, "%s version %s\n", PROG_NAME, PACKAGE_VERSION);
875 #if HAVE_NETLINK
876 fprintf(stderr, "Usage: nbd-client -name|-N name host [port] nbd_device\n\t[-block-size|-b block size] [-timeout|-t timeout] [-swap|-s]\n\t[-persist|-p] [-nofork|-n] [-systemd-mark|-m] [-i ident|-nonetlink|-L]\n\t[-readonly|-R] [-size|-B bytes] [-preinit|-P]\n");
877 #else
878 fprintf(stderr, "Usage: nbd-client -name|-N name host [port] nbd_device\n\t[-block-size|-b block size] [-timeout|-t timeout] [-swap|-s]\n\t[-persist|-p] [-nofork|-n] [-systemd-mark|-m]\n\t[-readonly|-R] [-size|-B bytes] [-preinit|-P]\n");
879 #endif
880 fprintf(stderr, "Or : nbd-client -u (with same arguments as above)\n");
881 fprintf(stderr, "Or : nbd-client nbdX\n");
882 fprintf(stderr, "Or : nbd-client -d nbd_device\n");
883 fprintf(stderr, "Or : nbd-client -c nbd_device\n");
884 fprintf(stderr, "Or : nbd-client -h|--help\n");
885 fprintf(stderr, "Or : nbd-client -l|--list host\n");
886 fprintf(stderr, "Or : nbd-client -V|--version\n");
887 #if HAVE_GNUTLS && !defined(NOTLS)
888 fprintf(stderr, "All commands that connect to a host also take:\n\t[-F|-certfile certfile] [-K|-keyfile keyfile]\n\t[-A|-cacertfile cacertfile] [-H|-tlshostname hostname] [-x|-enable-tls]\n\t[-y|-priority gnutls-priority-string]\n");
889 #endif
890 fprintf(stderr, "Default value for blocksize is 512\n");
891 fprintf(stderr, "Allowed values for blocksize are 512,1024,2048,4096\n"); /* will be checked in kernel :) */
892 fprintf(stderr, "Note, that kernel 2.4.2 and older ones do not work correctly with\n");
893 fprintf(stderr, "blocksizes other than 1024 without patches\n");
894 fprintf(stderr, "Default value for port is 10809. Note that port must always be numeric\n");
895 fprintf(stderr, "Bug reports and general discussion should go to %s\n", PACKAGE_BUGREPORT);
898 void disconnect(char* device) {
899 int nbd = open(device, O_RDWR);
901 if (nbd < 0)
902 err("Cannot open NBD: %m\nPlease ensure the 'nbd' module is loaded.");
903 printf("disconnect, ");
904 if (ioctl(nbd, NBD_DISCONNECT)<0)
905 err("Ioctl failed: %m\n");
906 printf("sock, ");
907 if (ioctl(nbd, NBD_CLEAR_SOCK)<0)
908 err("Ioctl failed: %m\n");
909 printf("done\n");
910 close(nbd);
913 static const char *short_opts = "-B:b:c:d:gH:hlnN:PpRSst:uVxy:"
914 #if HAVE_NETLINK
915 "i:L"
916 #endif
917 #if HAVE_GNUTLS
918 "A:C:F:K:"
919 #endif
922 int main(int argc, char *argv[]) {
923 char* port=NBD_DEFAULT_PORT;
924 int sock, nbd;
925 int cont=0;
926 int G_GNUC_UNUSED nofork=0; // if -dNOFORK
927 pid_t main_pid;
928 uint16_t flags = 0;
929 int c;
930 int nonspecial=0;
931 uint16_t needed_flags=0;
932 uint32_t cflags=NBD_FLAG_C_FIXED_NEWSTYLE;
933 uint32_t opts=0;
934 sigset_t block, old;
935 struct sigaction sa;
936 char *identifier = NULL;
937 int netlink = HAVE_NETLINK;
938 int need_disconnect = 0;
939 int *sockfds;
940 struct option long_options[] = {
941 { "cacertfile", required_argument, NULL, 'A' },
942 { "block-size", required_argument, NULL, 'b' },
943 { "size", required_argument, NULL, 'B' },
944 { "check", required_argument, NULL, 'c' },
945 { "connections", required_argument, NULL, 'C'},
946 { "disconnect", required_argument, NULL, 'd' },
947 { "certfile", required_argument, NULL, 'F' },
948 { "no-optgo", no_argument, NULL, 'g' },
949 { "help", no_argument, NULL, 'h' },
950 { "tlshostname", required_argument, NULL, 'H' },
951 #if HAVE_NETLINK
952 { "identifier", required_argument, NULL, 'i' },
953 #endif
954 { "keyfile", required_argument, NULL, 'K' },
955 { "list", no_argument, NULL, 'l' },
956 #if HAVE_NETLINK
957 { "nonetlink", no_argument, NULL, 'L' },
958 #endif
959 { "systemd-mark", no_argument, NULL, 'm' },
960 { "nofork", no_argument, NULL, 'n' },
961 { "name", required_argument, NULL, 'N' },
962 { "persist", no_argument, NULL, 'p' },
963 { "preinit", no_argument, NULL, 'P' },
964 { "readonly", no_argument, NULL, 'R' },
965 { "swap", no_argument, NULL, 's' },
966 { "timeout", required_argument, NULL, 't' },
967 { "unix", no_argument, NULL, 'u' },
968 { "version", no_argument, NULL, 'V' },
969 { "enable-tls", no_argument, NULL, 'x' },
970 { "priority", required_argument, NULL, 'y' },
971 { 0, 0, 0, 0 },
973 int i;
975 logging(MY_NAME);
977 #if HAVE_GNUTLS && !defined(NOTLS)
978 tlssession_init();
979 #endif
980 cur_client = calloc(sizeof(CLIENT), 1);
981 cur_client->bs = 512;
982 cur_client->nconn = 1;
983 cur_client->port = NBD_DEFAULT_PORT;
985 while((c=getopt_long_only(argc, argv, short_opts, long_options, NULL))>=0) {
986 switch(c) {
987 case 1:
988 // non-option argument
989 if(strchr(optarg, '=')) {
990 // old-style 'bs=' or 'timeout='
991 // argument
992 fprintf(stderr, "WARNING: old-style command-line argument encountered. This is deprecated.\n");
993 if(!strncmp(optarg, "bs=", 3)) {
994 optarg+=3;
995 goto blocksize;
997 if(!strncmp(optarg, "timeout=", 8)) {
998 optarg+=8;
999 goto timeout;
1001 usage("unknown option %s encountered", optarg);
1002 exit(EXIT_FAILURE);
1004 switch(nonspecial++) {
1005 case 0:
1006 // host
1007 cur_client->hostn=optarg;
1008 break;
1009 case 1:
1010 // port
1011 if(!strtol(optarg, NULL, 0)) {
1012 // not parseable as a number, assume it's the device
1013 cur_client->dev = optarg;
1014 nonspecial++;
1015 } else {
1016 port = optarg;
1018 break;
1019 case 2:
1020 // device
1021 cur_client->dev = optarg;
1022 break;
1023 default:
1024 usage("too many non-option arguments specified");
1025 exit(EXIT_FAILURE);
1027 break;
1028 case 'b':
1029 blocksize:
1030 cur_client->bs=(int)strtol(optarg, NULL, 0);
1031 if(cur_client->bs == 0 || (cur_client->bs % 512) != 0) {
1032 fprintf(stderr, "E: blocksize is not a multiple of 512! This is not allowed\n");
1033 exit(EXIT_FAILURE);
1035 break;
1036 case 'B':
1037 cur_client->force_size64=(uint64_t)strtoull(optarg, NULL, 0);
1038 if(cur_client->force_size64 == 0) {
1039 fprintf(stderr, "E: Invalid size\n");
1040 exit(EXIT_FAILURE);
1042 break;
1043 case 'c':
1044 return check_conn(optarg, 1);
1045 case 'C':
1046 cur_client->nconn = (int)strtol(optarg, NULL, 0);
1047 break;
1048 case 'd':
1049 need_disconnect = 1;
1050 cur_client->dev = strdup(optarg);
1051 break;
1052 case 'g':
1053 cur_client->no_optgo = true;
1054 break;
1055 case 'h':
1056 usage(NULL);
1057 exit(EXIT_SUCCESS);
1058 #if HAVE_NETLINK
1059 case 'i':
1060 identifier = optarg;
1061 break;
1062 #endif
1063 case 'l':
1064 needed_flags |= NBD_FLAG_FIXED_NEWSTYLE;
1065 opts |= NBDC_DO_LIST;
1066 cur_client->dev="";
1067 break;
1068 #if HAVE_NETLINK
1069 case 'L':
1070 netlink = 0;
1071 break;
1072 #endif
1073 case 'm':
1074 argv[0][0] = '@';
1075 break;
1076 case 'n':
1077 nofork=1;
1078 break;
1079 case 'N':
1080 cur_client->name = optarg;
1081 break;
1082 case 'p':
1083 cont=1;
1084 break;
1085 case 'P':
1086 cur_client->preinit = true;
1087 break;
1088 case 'R':
1089 cur_client->force_ro = true;
1090 break;
1091 case 's':
1092 cur_client->swap = true;
1093 break;
1094 case 't':
1095 timeout:
1096 cur_client->timeout = strtol(optarg, NULL, 0);
1097 break;
1098 case 'u':
1099 cur_client->b_unix = 1;
1100 break;
1101 case 'V':
1102 printf("This is %s, from %s\n", PROG_NAME, PACKAGE_STRING);
1103 return 0;
1104 #if HAVE_GNUTLS && !defined(NOTLS)
1105 case 'x':
1106 cur_client->tls = true;
1107 break;
1108 case 'F':
1109 cur_client->cert=strdup(optarg);
1110 break;
1111 case 'K':
1112 cur_client->key=strdup(optarg);
1113 break;
1114 case 'A':
1115 cur_client->cacert=strdup(optarg);
1116 break;
1117 case 'H':
1118 cur_client->tlshostn=strdup(optarg);
1119 break;
1120 case 'y':
1121 cur_client->priority=strdup(optarg);
1122 break;
1123 #else
1124 case 'F':
1125 case 'K':
1126 case 'H':
1127 case 'A':
1128 case 'y':
1129 fprintf(stderr, "E: TLS support not compiled in\n");
1130 exit(EXIT_FAILURE);
1131 #endif
1132 default:
1133 fprintf(stderr, "E: option eaten by 42 mice\n");
1134 exit(EXIT_FAILURE);
1138 if (need_disconnect) {
1139 if (netlink)
1140 netlink_disconnect(cur_client->dev);
1141 else
1142 disconnect(cur_client->dev);
1143 exit(EXIT_SUCCESS);
1145 #ifdef __ANDROID__
1146 if (swap)
1147 err("swap option unsupported on Android because mlockall is unsupported.");
1148 #endif
1149 if(cur_client->hostn) {
1150 if((!cur_client->name || !cur_client->dev) && !(opts & NBDC_DO_LIST)) {
1151 if(!strncmp(cur_client->hostn, "nbd", 3) || !strncmp(cur_client->hostn, "/dev/nbd", 8)) {
1152 cur_client->dev = cur_client->hostn;
1153 if(!get_from_config()) {
1154 usage("no valid configuration for specified device found", cur_client->hostn);
1155 exit(EXIT_FAILURE);
1157 } else if (!netlink) {
1158 usage("not enough information specified, and argument didn't look like an nbd device");
1159 exit(EXIT_FAILURE);
1162 } else {
1163 usage("no information specified");
1164 exit(EXIT_FAILURE);
1167 if (cur_client->key && !cur_client->cert)
1168 cur_client->cert = strdup(cur_client->key);
1170 if (cur_client->cert != NULL || cur_client->key != NULL || cur_client->cacert != NULL || cur_client->tlshostn != NULL) {
1171 cur_client->tls = true;
1174 if (cur_client->preinit) {
1175 if (cur_client->tls) {
1176 fprintf(stderr, "E: preinit connection cannot be used with TLS\n");
1177 exit(EXIT_FAILURE);
1179 if (!cur_client->force_size64) {
1180 fprintf(stderr, "E: preinit connection requires specifying size\n");
1181 exit(EXIT_FAILURE);
1185 if (!cur_client->tlshostn && cur_client->hostn && !cur_client->b_unix)
1186 cur_client->tlshostn = strdup(cur_client->hostn);
1188 if (netlink)
1189 nofork = 1;
1190 else if (identifier) {
1191 fprintf(stderr, "E: identifier is only useful with netlink\n");
1192 exit(EXIT_FAILURE);
1195 if((cur_client->force_size64 % cur_client->bs) != 0) {
1196 fprintf(stderr, "E: size (%" PRIu64 " bytes) is not a multiple of blocksize (%d)!\n", cur_client->force_size64, cur_client->bs);
1197 exit(EXIT_FAILURE);
1200 if(!cur_client->name)
1201 cur_client->name = "";
1202 if((strlen(cur_client->name)==0) && !(opts & NBDC_DO_LIST)) {
1203 printf("Warning: the oldstyle protocol is no longer supported.\nThis method now uses the newstyle protocol with a default export\n");
1206 if(!(opts & NBDC_DO_LIST) && !netlink) {
1207 nbd = open(cur_client->dev, O_RDWR);
1208 if (nbd < 0)
1209 err("Cannot open NBD: %m\nPlease ensure the 'nbd' module is loaded.");
1212 if (netlink) {
1213 sockfds = malloc(sizeof(int) * cur_client->nconn);
1214 if (!sockfds)
1215 err("Cannot allocate the socket fd's array");
1218 for (i = 0; i < cur_client->nconn; i++) {
1219 if (cur_client->b_unix)
1220 sock = openunix(cur_client->hostn);
1221 else
1222 sock = opennet(cur_client->hostn, cur_client->port);
1223 if (sock < 0)
1224 exit(EXIT_FAILURE);
1226 if (!cur_client->preinit)
1227 negotiate(&sock, &flags, needed_flags, cflags, opts);
1228 if (cur_client->force_ro)
1229 flags |= NBD_FLAG_READ_ONLY;
1230 if (cur_client->force_size64)
1231 cur_client->size64 = cur_client->force_size64;
1232 if (netlink) {
1233 sockfds[i] = sock;
1234 continue;
1237 if (i == 0) {
1238 setsizes(nbd, cur_client->size64, cur_client->bs, flags);
1239 set_timeout(nbd, cur_client->timeout);
1241 finish_sock(sock, nbd, cur_client->swap);
1242 if (cur_client->swap) {
1243 if (cur_client->tls)
1244 fprintf(stderr, "Warning: using swap and TLS is prone to deadlock\n");
1245 /* try linux >= 2.6.36 interface first */
1246 if (oom_adjust("/proc/self/oom_score_adj", "-1000")) {
1247 /* fall back to linux <= 2.6.35 interface */
1248 oom_adjust("/proc/self/oom_adj", "-17");
1253 if (netlink) {
1254 int index = -1;
1255 if (cur_client->dev) {
1256 if (sscanf(cur_client->dev, "/dev/nbd%d", &index) != 1)
1257 err("Invalid nbd device target\n");
1259 netlink_configure(index, sockfds, cur_client->nconn,
1260 cur_client->size64, cur_client->bs, flags, cur_client->timeout,
1261 identifier);
1262 return 0;
1264 /* Go daemon */
1266 #ifndef NOFORK
1267 if(!nofork) {
1268 if (daemon(0,0) < 0)
1269 err("Cannot detach from terminal");
1272 memset(&sa, 0, sizeof(sa));
1273 sa.sa_handler = SIG_IGN;
1274 sigaction(SIGCHLD, &sa, NULL);
1275 #endif
1276 /* For child to check its parent */
1277 main_pid = getpid();
1278 do {
1279 #ifndef NOFORK
1281 sigfillset(&block);
1282 sigdelset(&block, SIGKILL);
1283 sigdelset(&block, SIGTERM);
1284 sigdelset(&block, SIGPIPE);
1285 sigprocmask(SIG_SETMASK, &block, &old);
1287 if (!fork()) {
1288 /* Due to a race, the kernel NBD driver cannot
1289 * call for a reread of the partition table
1290 * in the handling of the NBD_DO_IT ioctl().
1291 * Therefore, this is done in the first open()
1292 * of the device. We therefore make sure that
1293 * the device is opened at least once after the
1294 * connection was made. This has to be done in a
1295 * separate process, since the NBD_DO_IT ioctl()
1296 * does not return until the NBD device has
1297 * disconnected.
1299 struct timespec req = {
1300 .tv_sec = 0,
1301 .tv_nsec = 100000000,
1303 while(check_conn(cur_client->dev, 0)) {
1304 if (main_pid != getppid()) {
1305 /* check_conn() will not return 0 when nbd disconnected
1306 * and parent exited during this loop. So the child has to
1307 * explicitly check parent identity and exit if parent
1308 * exited */
1309 exit(0);
1311 nanosleep(&req, NULL);
1313 if(open(cur_client->dev, O_RDONLY) < 0) {
1314 perror("could not open device for updating partition table");
1316 exit(0);
1318 #endif
1320 if (ioctl(nbd, NBD_DO_IT) < 0) {
1321 int error = errno;
1322 fprintf(stderr, "nbd,%d: Kernel call returned: %s\n", main_pid, strerror(errno));
1323 if(error==EBADR) {
1324 /* The user probably did 'nbd-client -d' on us.
1325 * quit */
1326 cont=0;
1327 } else {
1328 if(cont) {
1329 uint64_t old_size = cur_client->size64;
1330 uint16_t new_flags;
1332 close(sock); close(nbd);
1333 for (;;) {
1334 fprintf(stderr, " Reconnecting\n");
1335 if (cur_client->b_unix)
1336 sock = openunix();
1337 else
1338 sock = opennet();
1339 if (sock >= 0)
1340 break;
1341 sleep (1);
1343 nbd = open(cur_client->dev, O_RDWR);
1344 if (nbd < 0)
1345 err("Cannot open NBD: %m");
1346 negotiate(&sock, &new_flags, needed_flags, cflags, opts);
1347 if (old_size != cur_client->size64) {
1348 err("Size of the device changed. Bye");
1350 setsizes(nbd, cur_client->size64, cur_client->bs,
1351 new_flags);
1353 set_timeout(nbd, cur_client->timeout);
1354 finish_sock(sock,nbd,cur_client->swap);
1357 } else {
1358 /* We're on 2.4. It's not clearly defined what exactly
1359 * happened at this point. Probably best to quit, now
1361 fprintf(stderr, "Kernel call returned.\n");
1362 cont=0;
1364 } while(cont);
1365 printf("sock, ");
1366 ioctl(nbd, NBD_CLEAR_SOCK);
1367 printf("done\n");
1368 return 0;