Index: qemu/nbd.c =================================================================== --- qemu.orig/nbd.c 2008-06-18 17:05:46.000000000 +0200 +++ qemu/nbd.c 2008-06-19 10:47:46.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 @@ -388,8 +448,8 @@ int nbd_trip(BlockDriverState *bs, int c } if (len > sizeof(data)) { - LOG("len (%u) is larger than max len (%u)", - len, sizeof(data)); + LOG("len (%u) is larger than max len (%lu)", + len, (unsigned long)sizeof(data)); errno = EINVAL; return -1; } Index: qemu/nbd.h =================================================================== --- qemu.orig/nbd.h 2008-06-18 17:05:46.000000000 +0200 +++ qemu/nbd.h 2008-06-19 10:47:46.000000000 +0200 @@ -27,6 +27,8 @@ #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); Index: qemu/qemu-nbd.c =================================================================== --- qemu.orig/qemu-nbd.c 2008-06-18 17:05:46.000000000 +0200 +++ qemu/qemu-nbd.c 2008-06-19 10:48:49.000000000 +0200 @@ -21,15 +21,18 @@ #include "block_int.h" #include "nbd.h" -#include #include #include #include #include +#include #include #include #include #include +#include + +//#define DEBUG int verbose; @@ -44,6 +47,8 @@ static void usage(const char *name) " -b, --bind=IFACE interface to bind to (default `0.0.0.0')\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" @@ -145,19 +150,39 @@ 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; + char sockpath[128]; + const char *sopt = "hVbo:p:rsP:c:dv?"; struct option lopt[] = { { "help", 0, 0, 'h' }, { "version", 0, 0, 'V' }, @@ -166,8 +191,11 @@ int main(int argc, char **argv) { "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' }, + { 0, 0, 0, 0 } }; int ch; int opt_ind = 0; @@ -175,6 +203,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 +243,12 @@ int main(int argc, char **argv) if (partition < 1 || partition > 8) errx(EINVAL, "Invalid partition %d", partition); break; + case 'd': + disconnect = true; + break; + case 'c': + device = optarg; + break; case 'v': verbose = 1; break; @@ -236,6 +272,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 +301,60 @@ 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 */ + + sprintf(sockpath, "/var/lock/qemu-img-%s", basename(device)); + + pid = fork(); + if (pid < 0) + return 1; + if (pid != 0) { + ret = 0; + bdrv_close(bs); + + do { + sock = unix_socket_outgoing(sockpath); + 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(fd, sock); + if (ret == -1) + goto exit; + + printf("NBD device %s is now connected to file %s\n", + device, argv[optind]); + + /* if supported, update partition table */ + + show_parts(device); + + nbd_client(fd, sock); + close(fd); +exit: + kill(pid, SIGTERM); + unlink(sockpath); + + return ret; + } + /* children */ + sock = unix_socket_incoming(sockpath); + } else { + sock = tcp_socket_incoming(bindto, port); + } + if (sock == -1) return 1;