From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1KCBjH-0002Xk-61 for qemu-devel@nongnu.org; Fri, 27 Jun 2008 07:02:59 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1KCBjF-0002Vw-La for qemu-devel@nongnu.org; Fri, 27 Jun 2008 07:02:58 -0400 Received: from [199.232.76.173] (port=36761 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KCBjF-0002Vg-8z for qemu-devel@nongnu.org; Fri, 27 Jun 2008 07:02:57 -0400 Received: from ecfrec.frec.bull.fr ([129.183.4.8]:52970) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1KCBjE-0008DC-Fq for qemu-devel@nongnu.org; Fri, 27 Jun 2008 07:02:57 -0400 Message-Id: <20080627110247.660215428@bull.net> References: <20080627110204.818732368@bull.net> Date: Fri, 27 Jun 2008 13:02:06 +0200 From: Laurent.Vivier@bull.net Content-Disposition: inline; filename=qemu-nbd-local.patch Subject: [Qemu-devel] [patch 2/5] qemu-nbd: merge NBD client/server Reply-To: qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: Laurent.Vivier@bull.net This patch allows to connect directly a disk image file to an NBD device. It introduces the use of a unix socket (instead of inet). - To connect a file to a device: # qemu-nbd --connect=/dev/nbd0 my_disk.qcow2 Then you can see directly your disk (no need of nbd-client): # fdisk -l /dev/nbd0 Disk /dev/nbd0: 4294 MB, 4294967296 bytes 255 heads, 63 sectors/track, 522 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Device Boot Start End Blocks Id System /dev/nbd0p1 * 1 492 3951958+ 83 Linux /dev/nbd0p2 493 522 240975 5 Extended /dev/nbd0p5 493 522 240943+ 82 Linux swap / Solaris - To disconnect the file from the device: # qemu-nbd --disconnect /dev/nbd0 Changelog: - v2: call show_parts() from client and avoid the sleep(1). Thank you to Avi and Anthony. Include my cleanup patch and comments from Carlo Marcelo Arenas Belon. - v3: allow to specify unix socket name with "--socket" and update documentation. --- nbd.c | 74 ++++++++++++++++++++++++++++--- nbd.h | 5 +- qemu-nbd.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- qemu-nbd.texi | 6 ++ 4 files changed, 212 insertions(+), 10 deletions(-) Index: qemu/nbd.c =================================================================== --- qemu.orig/nbd.c 2008-06-27 11:34:28.000000000 +0200 +++ qemu/nbd.c 2008-06-27 11:34:35.000000000 +0200 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -183,6 +184,65 @@ error: return -1; } +int unix_socket_incoming(const char *path) +{ + int s; + struct sockaddr_un addr; + int serrno; + + s = socket(PF_UNIX, SOCK_STREAM, 0); + if (s == -1) { + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + pstrcpy(addr.sun_path, sizeof(addr.sun_path), path); + + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + goto error; + } + + if (listen(s, 128) == -1) { + goto error; + } + + return s; +error: + serrno = errno; + close(s); + errno = serrno; + return -1; +} + +int unix_socket_outgoing(const char *path) +{ + int s; + struct sockaddr_un addr; + int serrno; + + s = socket(PF_UNIX, SOCK_STREAM, 0); + if (s == -1) { + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + pstrcpy(addr.sun_path, sizeof(addr.sun_path), path); + + if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + goto error; + } + + return s; +error: + serrno = errno; + close(s); + errno = serrno; + return -1; +} + + /* Basic flow Server Client @@ -225,12 +285,10 @@ int nbd_negotiate(BlockDriverState *bs, return 0; } -int nbd_receive_negotiate(int fd, int csock) +int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize) { char buf[8 + 8 + 8 + 128]; uint64_t magic; - off_t size; - size_t blocksize; TRACE("Receiving negotation."); @@ -241,8 +299,8 @@ int nbd_receive_negotiate(int fd, int cs } magic = be64_to_cpup((uint64_t*)(buf + 8)); - size = be64_to_cpup((uint64_t*)(buf + 16)); - blocksize = 1024; + *size = be64_to_cpup((uint64_t*)(buf + 16)); + *blocksize = 1024; TRACE("Magic is %c%c%c%c%c%c%c%c", isprint(buf[0]) ? buf[0] : '.', @@ -254,7 +312,7 @@ int nbd_receive_negotiate(int fd, int cs isprint(buf[6]) ? buf[6] : '.', isprint(buf[7]) ? buf[7] : '.'); TRACE("Magic is 0x%" PRIx64, magic); - TRACE("Size is %" PRIu64, size); + TRACE("Size is %" PRIu64, *size); if (memcmp(buf, "NBDMAGIC", 8) != 0) { LOG("Invalid magic received"); @@ -269,7 +327,11 @@ int nbd_receive_negotiate(int fd, int cs errno = EINVAL; return -1; } + return 0; +} +int nbd_init(int fd, int csock, off_t size, size_t blocksize) +{ TRACE("Setting block size to %lu", (unsigned long)blocksize); if (ioctl(fd, NBD_SET_BLKSIZE, blocksize) == -1) { Index: qemu/nbd.h =================================================================== --- qemu.orig/nbd.h 2008-06-27 11:34:18.000000000 +0200 +++ qemu/nbd.h 2008-06-27 11:34:35.000000000 +0200 @@ -27,9 +27,12 @@ #include "block_int.h" int tcp_socket_incoming(const char *address, uint16_t port); +int unix_socket_outgoing(const char *path); +int unix_socket_incoming(const char *path); int nbd_negotiate(BlockDriverState *bs, int csock, off_t size); -int nbd_receive_negotiate(int fd, int csock); +int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize); +int nbd_init(int fd, int csock, off_t size, size_t blocksize); int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, off_t *offset, bool readonly); int nbd_client(int fd, int csock); int nbd_disconnect(int fd); Index: qemu/qemu-nbd.c =================================================================== --- qemu.orig/qemu-nbd.c 2008-06-27 11:34:28.000000000 +0200 +++ qemu/qemu-nbd.c 2008-06-27 11:34:35.000000000 +0200 @@ -25,10 +25,14 @@ #include #include #include +#include #include #include #include #include +#include + +#define SOCKET_PATH "/var/lock/qemu-nbd-%s" int verbose; @@ -41,14 +45,18 @@ static void usage(const char *name) " -p, --port=PORT port to listen on (default `1024')\n" " -o, --offset=OFFSET offset into the image\n" " -b, --bind=IFACE interface to bind to (default `0.0.0.0')\n" +" -k, --socket=PATH path to the unix socket\n" +" (default '"SOCKET_PATH"')\n" " -r, --read-only export read-only\n" " -P, --partition=NUM only expose partition NUM\n" +" -c, --connect=DEV connect FILE to the local NBD device DEV\n" +" -d, --disconnect disconnect the specified device\n" " -v, --verbose display extra debugging information\n" " -h, --help display this help and exit\n" " -V, --version output version information and exit\n" "\n" "Report bugs to \n" - , name); + , name, "DEVICE"); } static void version(const char *name) @@ -144,27 +152,51 @@ static int find_partition(BlockDriverSta return -1; } +static void show_parts(const char *device) +{ + if (fork() == 0) { + int nbd; + + /* linux just needs an open() to trigger + * the partition table update + * but remember to load the module with max_part != 0 : + * modprobe nbd max_part=63 + */ + nbd = open(device, O_RDWR); + if (nbd != -1) + close(nbd); + exit(0); + } +} + int main(int argc, char **argv) { BlockDriverState *bs; off_t dev_offset = 0; off_t offset = 0; bool readonly = false; + bool disconnect = false; const char *bindto = "0.0.0.0"; int port = 1024; int sock, csock; struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); off_t fd_size; - const char *sopt = "hVbo:p:rsP:v"; + char *device = NULL; + char *socket = NULL; + char sockpath[128]; + const char *sopt = "hVbo:p:rsP:c:dvk:"; struct option lopt[] = { { "help", 0, 0, 'h' }, { "version", 0, 0, 'V' }, { "bind", 1, 0, 'b' }, { "port", 1, 0, 'p' }, + { "socket", 1, 0, 'k' }, { "offset", 1, 0, 'o' }, { "read-only", 0, 0, 'r' }, { "partition", 1, 0, 'P' }, + { "connect", 1, 0, 'c' }, + { "disconnect", 0, 0, 'd' }, { "snapshot", 0, 0, 's' }, { "verbose", 0, 0, 'v' }, { NULL, 0, 0, 0 } @@ -175,6 +207,8 @@ int main(int argc, char **argv) char *end; bool snapshot = false; int partition = -1; + int fd; + int ret; while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { switch (ch) { @@ -213,6 +247,17 @@ int main(int argc, char **argv) if (partition < 1 || partition > 8) errx(EINVAL, "Invalid partition %d", partition); break; + case 'k': + socket = optarg; + if (socket[0] != '/') + errx(EINVAL, "socket path must be absolute\n"); + break; + case 'd': + disconnect = true; + break; + case 'c': + device = optarg; + break; case 'v': verbose = 1; break; @@ -236,6 +281,20 @@ int main(int argc, char **argv) argv[0]); } + if (disconnect) { + fd = open(argv[optind], O_RDWR); + if (fd == -1) + errx(errno, "Cannot open %s", argv[optind]); + + nbd_disconnect(fd); + + close(fd); + + printf("%s disconnected\n", argv[optind]); + + return 0; + } + bdrv_init(); bs = bdrv_new("hda"); @@ -251,7 +310,77 @@ int main(int argc, char **argv) find_partition(bs, partition, &dev_offset, &fd_size)) errx(errno, "Could not find partition %d", partition); - sock = tcp_socket_incoming(bindto, port); + if (device) { + pid_t pid; + if (!verbose) + daemon(0, 0); /* detach client and server */ + + if (socket == NULL) { + sprintf(sockpath, SOCKET_PATH, basename(device)); + socket = sockpath; + } + + pid = fork(); + if (pid < 0) + return 1; + if (pid != 0) { + off_t size; + size_t blocksize; + + ret = 0; + bdrv_close(bs); + + do { + sock = unix_socket_outgoing(socket); + if (sock == -1) { + if (errno != ENOENT && errno != ECONNREFUSED) + goto exit; + sleep(1); /* wait children */ + } + } while (sock == -1); + + fd = open(device, O_RDWR); + if (fd == -1) { + ret = 1; + goto exit; + } + + ret = nbd_receive_negotiate(sock, &size, &blocksize); + if (ret == -1) { + ret = 1; + goto exit; + } + + ret = nbd_init(fd, sock, size, blocksize); + if (ret == -1) { + ret = 1; + goto exit; + } + + printf("NBD device %s is now connected to file %s\n", + device, argv[optind]); + + /* update partition table */ + + show_parts(device); + + nbd_client(fd, sock); + close(fd); +exit: + kill(pid, SIGTERM); + unlink(socket); + + return ret; + } + /* children */ + } + + if (socket) { + sock = unix_socket_incoming(socket); + } else { + sock = tcp_socket_incoming(bindto, port); + } + if (sock == -1) return 1; @@ -270,6 +399,8 @@ int main(int argc, char **argv) close(csock); close(sock); bdrv_close(bs); + if (socket) + unlink(socket); return 0; } Index: qemu/qemu-nbd.texi =================================================================== --- qemu.orig/qemu-nbd.texi 2008-06-27 11:34:18.000000000 +0200 +++ qemu/qemu-nbd.texi 2008-06-27 11:34:35.000000000 +0200 @@ -20,10 +20,16 @@ Export Qemu disk image using NBD protoco offset into the image @item -b, --bind=IFACE interface to bind to (default `0.0.0.0') +@item -k, --socket=PATH + Use a unix socket with path PATH @item -r, --read-only export read-only @item -P, --partition=NUM only expose partition NUM +@item -c, --connect + connect FILE to NBD device DEV +@item -d, --disconnect + disconnect the specified device @item -v, --verbose display extra debugging information @item -h, --help --