X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/73104fd399c6778112f64fe0d439319f24508d9a..3d4b2f9c9449ee1acdb1a488177204ebfdaccd0d:/qemu-nbd.c diff --git a/qemu-nbd.c b/qemu-nbd.c index ac1e5d6f77..9710a26176 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -16,25 +16,22 @@ * along with this program; if not, see . */ +#include "qemu/osdep.h" #include "qemu-common.h" #include "sysemu/block-backend.h" #include "block/block_int.h" #include "block/nbd.h" #include "qemu/main-loop.h" -#include "qemu/sockets.h" #include "qemu/error-report.h" +#include "qemu/config-file.h" #include "block/snapshot.h" #include "qapi/util.h" +#include "qapi/qmp/qstring.h" +#include "qom/object_interfaces.h" +#include "io/channel-socket.h" -#include -#include #include -#include #include -#include -#include -#include -#include #include #include #include @@ -44,15 +41,19 @@ #define QEMU_NBD_OPT_AIO 2 #define QEMU_NBD_OPT_DISCARD 3 #define QEMU_NBD_OPT_DETECT_ZEROES 4 +#define QEMU_NBD_OPT_OBJECT 5 static NBDExport *exp; +static bool newproto; static int verbose; static char *srcpath; -static char *sockpath; +static SocketAddress *saddr; static int persistent = 0; static enum { RUNNING, TERMINATE, TERMINATING, TERMINATED } state; static int shared = 1; static int nb_fds; +static QIOChannelSocket *server_ioc; +static int server_watch = -1; static void usage(const char *name) { @@ -76,6 +77,9 @@ static void usage(const char *name) " -o, --offset=OFFSET offset into the image\n" " -P, --partition=NUM only expose partition NUM\n" "\n" +"General purpose options:\n" +" --object type,id=ID,... define an object such as 'secret' for providing\n" +" passwords and/or encryption keys\n" #ifdef __linux__ "Kernel NBD client support:\n" " -c, --connect=DEV connect FILE to the local NBD device DEV\n" @@ -96,11 +100,9 @@ static void usage(const char *name) " '[ID_OR_NAME]'\n" " -n, --nocache disable host cache\n" " --cache=MODE set cache mode (none, writeback, ...)\n" -#ifdef CONFIG_LINUX_AIO " --aio=MODE set AIO mode (native or threads)\n" -#endif " --discard=MODE set discard mode (ignore, unmap)\n" -" --detect-zeroes=MODE set detect-zeroes mode (off, on, discard)\n" +" --detect-zeroes=MODE set detect-zeroes mode (off, on, unmap)\n" "\n" "Report bugs to \n" , name, NBD_DEFAULT_PORT, "DEVICE"); @@ -142,8 +144,9 @@ static void read_partition(uint8_t *p, struct partition_record *r) r->end_head = p[5]; r->end_cylinder = p[7] | ((p[6] << 2) & 0x300); r->end_sector = p[6] & 0x3f; - r->start_sector_abs = p[8] | p[9] << 8 | p[10] << 16 | p[11] << 24; - r->nb_sectors_abs = p[12] | p[13] << 8 | p[14] << 16 | p[15] << 24; + + r->start_sector_abs = le32_to_cpup((uint32_t *)(p + 8)); + r->nb_sectors_abs = le32_to_cpup((uint32_t *)(p + 12)); } static int find_partition(BlockBackend *blk, int partition, @@ -156,8 +159,8 @@ static int find_partition(BlockBackend *blk, int partition, int ret; if ((ret = blk_read(blk, 0, data, 1)) < 0) { - errno = -ret; - err(EXIT_FAILURE, "error while reading"); + error_report("error while reading: %s", strerror(-ret)); + exit(EXIT_FAILURE); } if (data[510] != 0x55 || data[511] != 0xaa) { @@ -167,8 +170,9 @@ static int find_partition(BlockBackend *blk, int partition, for (i = 0; i < 4; i++) { read_partition(&data[446 + 16 * i], &mbr[i]); - if (!mbr[i].nb_sectors_abs) + if (!mbr[i].system || !mbr[i].nb_sectors_abs) { continue; + } if (mbr[i].system == 0xF || mbr[i].system == 0x5) { struct partition_record ext[4]; @@ -176,14 +180,15 @@ static int find_partition(BlockBackend *blk, int partition, int j; if ((ret = blk_read(blk, mbr[i].start_sector_abs, data1, 1)) < 0) { - errno = -ret; - err(EXIT_FAILURE, "error while reading"); + error_report("error while reading: %s", strerror(-ret)); + exit(EXIT_FAILURE); } for (j = 0; j < 4; j++) { read_partition(&data1[446 + 16 * j], &ext[j]); - if (!ext[j].nb_sectors_abs) + if (!ext[j].system || !ext[j].nb_sectors_abs) { continue; + } if ((ext_partnum + j + 1) == partition) { *offset = (uint64_t)ext[j].start_sector_abs << 9; @@ -208,55 +213,6 @@ static void termsig_handler(int signum) qemu_notify_event(); } -static void combine_addr(char *buf, size_t len, const char* address, - uint16_t port) -{ - /* If the address-part contains a colon, it's an IPv6 IP so needs [] */ - if (strstr(address, ":")) { - snprintf(buf, len, "[%s]:%u", address, port); - } else { - snprintf(buf, len, "%s:%u", address, port); - } -} - -static int tcp_socket_incoming(const char *address, uint16_t port) -{ - char address_and_port[128]; - Error *local_err = NULL; - - combine_addr(address_and_port, 128, address, port); - int fd = inet_listen(address_and_port, NULL, 0, SOCK_STREAM, 0, &local_err); - - if (local_err != NULL) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); - } - return fd; -} - -static int unix_socket_incoming(const char *path) -{ - Error *local_err = NULL; - int fd = unix_listen(path, NULL, 0, &local_err); - - if (local_err != NULL) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); - } - return fd; -} - -static int unix_socket_outgoing(const char *path) -{ - Error *local_err = NULL; - int fd = unix_connect(path, &local_err); - - if (local_err != NULL) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); - } - return fd; -} static void *show_parts(void *arg) { @@ -279,24 +235,26 @@ static void *nbd_client_thread(void *arg) { char *device = arg; off_t size; - size_t blocksize; uint32_t nbdflags; - int fd, sock; + QIOChannelSocket *sioc; + int fd; int ret; pthread_t show_parts_thread; Error *local_error = NULL; - sock = unix_socket_outgoing(sockpath); - if (sock < 0) { + sioc = qio_channel_socket_new(); + if (qio_channel_socket_connect_sync(sioc, + saddr, + &local_error) < 0) { + error_report_err(local_error); goto out; } - ret = nbd_receive_negotiate(sock, NULL, &nbdflags, - &size, &blocksize, &local_error); + ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, &nbdflags, + &size, &local_error); if (ret < 0) { if (local_error) { - fprintf(stderr, "%s\n", error_get_pretty(local_error)); - error_free(local_error); + error_report_err(local_error); } goto out_socket; } @@ -304,11 +262,11 @@ static void *nbd_client_thread(void *arg) fd = open(device, O_RDWR); if (fd < 0) { /* Linux-only, we can use %m in printf. */ - fprintf(stderr, "Failed to open %s: %m\n", device); + error_report("Failed to open %s: %m", device); goto out_socket; } - ret = nbd_init(fd, sock, nbdflags, size, blocksize); + ret = nbd_init(fd, sioc, nbdflags, size); if (ret < 0) { goto out_fd; } @@ -329,19 +287,20 @@ static void *nbd_client_thread(void *arg) goto out_fd; } close(fd); + object_unref(OBJECT(sioc)); kill(getpid(), SIGTERM); return (void *) EXIT_SUCCESS; out_fd: close(fd); out_socket: - closesocket(sock); + object_unref(OBJECT(sioc)); out: kill(getpid(), SIGTERM); return (void *) EXIT_FAILURE; } -static int nbd_can_accept(void *opaque) +static int nbd_can_accept(void) { return nb_fds < shared; } @@ -352,41 +311,95 @@ static void nbd_export_closed(NBDExport *exp) state = TERMINATED; } +static void nbd_update_server_watch(void); + static void nbd_client_closed(NBDClient *client) { nb_fds--; if (nb_fds == 0 && !persistent && state == RUNNING) { state = TERMINATE; } - qemu_notify_event(); + nbd_update_server_watch(); nbd_client_put(client); } -static void nbd_accept(void *opaque) +static gboolean nbd_accept(QIOChannel *ioc, GIOCondition cond, gpointer opaque) { - int server_fd = (uintptr_t) opaque; - struct sockaddr_in addr; - socklen_t addr_len = sizeof(addr); + QIOChannelSocket *cioc; - int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len); - if (fd < 0) { - perror("accept"); - return; + cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), + NULL); + if (!cioc) { + return TRUE; } if (state >= TERMINATE) { - close(fd); - return; + object_unref(OBJECT(cioc)); + return TRUE; + } + + nb_fds++; + nbd_update_server_watch(); + nbd_client_new(newproto ? NULL : exp, cioc, nbd_client_closed); + object_unref(OBJECT(cioc)); + + return TRUE; +} + +static void nbd_update_server_watch(void) +{ + if (nbd_can_accept()) { + if (server_watch == -1) { + server_watch = qio_channel_add_watch(QIO_CHANNEL(server_ioc), + G_IO_IN, + nbd_accept, + NULL, NULL); + } + } else { + if (server_watch != -1) { + g_source_remove(server_watch); + server_watch = -1; + } } +} + + +static SocketAddress *nbd_build_socket_address(const char *sockpath, + const char *bindto, + const char *port) +{ + SocketAddress *saddr; - if (nbd_client_new(exp, fd, nbd_client_closed)) { - nb_fds++; + saddr = g_new0(SocketAddress, 1); + if (sockpath) { + saddr->type = SOCKET_ADDRESS_KIND_UNIX; + saddr->u.q_unix = g_new0(UnixSocketAddress, 1); + saddr->u.q_unix->path = g_strdup(sockpath); } else { - shutdown(fd, 2); - close(fd); + saddr->type = SOCKET_ADDRESS_KIND_INET; + saddr->u.inet = g_new0(InetSocketAddress, 1); + saddr->u.inet->host = g_strdup(bindto); + if (port) { + saddr->u.inet->port = g_strdup(port); + } else { + saddr->u.inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT); + } } + + return saddr; } + +static QemuOptsList qemu_object_opts = { + .name = "object", + .implied_opt_name = "qom-type", + .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head), + .desc = { + { } + }, +}; + + int main(int argc, char **argv) { BlockBackend *blk; @@ -395,12 +408,13 @@ int main(int argc, char **argv) uint32_t nbdflags = 0; bool disconnect = false; const char *bindto = "0.0.0.0"; + const char *port = NULL; + char *sockpath = NULL; char *device = NULL; - int port = NBD_DEFAULT_PORT; off_t fd_size; QemuOpts *sn_opts = NULL; const char *sn_id_or_name = NULL; - const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:"; + const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:"; struct option lopt[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, @@ -416,35 +430,32 @@ int main(int argc, char **argv) { "load-snapshot", 1, NULL, 'l' }, { "nocache", 0, NULL, 'n' }, { "cache", 1, NULL, QEMU_NBD_OPT_CACHE }, -#ifdef CONFIG_LINUX_AIO { "aio", 1, NULL, QEMU_NBD_OPT_AIO }, -#endif { "discard", 1, NULL, QEMU_NBD_OPT_DISCARD }, { "detect-zeroes", 1, NULL, QEMU_NBD_OPT_DETECT_ZEROES }, { "shared", 1, NULL, 'e' }, { "format", 1, NULL, 'f' }, { "persistent", 0, NULL, 't' }, { "verbose", 0, NULL, 'v' }, + { "object", 1, NULL, QEMU_NBD_OPT_OBJECT }, + { "export-name", 1, NULL, 'x' }, { NULL, 0, NULL, 0 } }; int ch; int opt_ind = 0; - int li; char *end; int flags = BDRV_O_RDWR; int partition = -1; int ret = 0; - int fd; bool seen_cache = false; bool seen_discard = false; -#ifdef CONFIG_LINUX_AIO bool seen_aio = false; -#endif pthread_t client_thread; const char *fmt = NULL; Error *local_err = NULL; BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; QDict *options = NULL; + const char *export_name = NULL; /* The client thread uses SIGTERM to interrupt the server. A signal * handler ensures that "qemu-nbd -v -c" exits with a nice status code. @@ -453,6 +464,8 @@ int main(int argc, char **argv) memset(&sa_sigterm, 0, sizeof(sa_sigterm)); sa_sigterm.sa_handler = termsig_handler; sigaction(SIGTERM, &sa_sigterm, NULL); + module_call_init(MODULE_INIT_QOM); + qemu_add_opts(&qemu_object_opts); qemu_init_exec_dir(argv[0]); while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { @@ -465,17 +478,19 @@ int main(int argc, char **argv) /* fallthrough */ case QEMU_NBD_OPT_CACHE: if (seen_cache) { - errx(EXIT_FAILURE, "-n and --cache can only be specified once"); + error_report("-n and --cache can only be specified once"); + exit(EXIT_FAILURE); } seen_cache = true; if (bdrv_parse_cache_flags(optarg, &flags) == -1) { - errx(EXIT_FAILURE, "Invalid cache mode `%s'", optarg); + error_report("Invalid cache mode `%s'", optarg); + exit(EXIT_FAILURE); } break; -#ifdef CONFIG_LINUX_AIO case QEMU_NBD_OPT_AIO: if (seen_aio) { - errx(EXIT_FAILURE, "--aio can only be specified once"); + error_report("--aio can only be specified once"); + exit(EXIT_FAILURE); } seen_aio = true; if (!strcmp(optarg, "native")) { @@ -483,64 +498,65 @@ int main(int argc, char **argv) } else if (!strcmp(optarg, "threads")) { /* this is the default */ } else { - errx(EXIT_FAILURE, "invalid aio mode `%s'", optarg); + error_report("invalid aio mode `%s'", optarg); + exit(EXIT_FAILURE); } break; -#endif case QEMU_NBD_OPT_DISCARD: if (seen_discard) { - errx(EXIT_FAILURE, "--discard can only be specified once"); + error_report("--discard can only be specified once"); + exit(EXIT_FAILURE); } seen_discard = true; if (bdrv_parse_discard_flags(optarg, &flags) == -1) { - errx(EXIT_FAILURE, "Invalid discard mode `%s'", optarg); + error_report("Invalid discard mode `%s'", optarg); + exit(EXIT_FAILURE); } break; case QEMU_NBD_OPT_DETECT_ZEROES: detect_zeroes = qapi_enum_parse(BlockdevDetectZeroesOptions_lookup, optarg, - BLOCKDEV_DETECT_ZEROES_OPTIONS_MAX, + BLOCKDEV_DETECT_ZEROES_OPTIONS__MAX, BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, &local_err); if (local_err) { - errx(EXIT_FAILURE, "Failed to parse detect_zeroes mode: %s", - error_get_pretty(local_err)); + error_reportf_err(local_err, + "Failed to parse detect_zeroes mode: "); + exit(EXIT_FAILURE); } if (detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP && !(flags & BDRV_O_UNMAP)) { - errx(EXIT_FAILURE, "setting detect-zeroes to unmap is not allowed " - "without setting discard operation to unmap"); + error_report("setting detect-zeroes to unmap is not allowed " + "without setting discard operation to unmap"); + exit(EXIT_FAILURE); } break; case 'b': bindto = optarg; break; case 'p': - li = strtol(optarg, &end, 0); - if (*end) { - errx(EXIT_FAILURE, "Invalid port `%s'", optarg); - } - if (li < 1 || li > 65535) { - errx(EXIT_FAILURE, "Port out of range `%s'", optarg); - } - port = (uint16_t)li; + port = optarg; break; case 'o': dev_offset = strtoll (optarg, &end, 0); if (*end) { - errx(EXIT_FAILURE, "Invalid offset `%s'", optarg); + error_report("Invalid offset `%s'", optarg); + exit(EXIT_FAILURE); } if (dev_offset < 0) { - errx(EXIT_FAILURE, "Offset must be positive `%s'", optarg); + error_report("Offset must be positive `%s'", optarg); + exit(EXIT_FAILURE); } break; case 'l': if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) { - sn_opts = qemu_opts_parse(&internal_snapshot_opts, optarg, 0); + sn_opts = qemu_opts_parse_noisily(&internal_snapshot_opts, + optarg, false); if (!sn_opts) { - errx(EXIT_FAILURE, "Failed in parsing snapshot param `%s'", - optarg); + error_report("Failed in parsing snapshot param `%s'", + optarg); + exit(EXIT_FAILURE); } } else { sn_id_or_name = optarg; @@ -553,16 +569,19 @@ int main(int argc, char **argv) case 'P': partition = strtol(optarg, &end, 0); if (*end) { - errx(EXIT_FAILURE, "Invalid partition `%s'", optarg); + error_report("Invalid partition `%s'", optarg); + exit(EXIT_FAILURE); } if (partition < 1 || partition > 8) { - errx(EXIT_FAILURE, "Invalid partition %d", partition); + error_report("Invalid partition %d", partition); + exit(EXIT_FAILURE); } break; case 'k': sockpath = optarg; if (sockpath[0] != '/') { - errx(EXIT_FAILURE, "socket path must be absolute\n"); + error_report("socket path must be absolute"); + exit(EXIT_FAILURE); } break; case 'd': @@ -574,10 +593,12 @@ int main(int argc, char **argv) case 'e': shared = strtol(optarg, &end, 0); if (*end) { - errx(EXIT_FAILURE, "Invalid shared device number '%s'", optarg); + error_report("Invalid shared device number '%s'", optarg); + exit(EXIT_FAILURE); } if (shared < 1) { - errx(EXIT_FAILURE, "Shared device number must be greater than 0\n"); + error_report("Shared device number must be greater than 0"); + exit(EXIT_FAILURE); } break; case 'f': @@ -586,6 +607,9 @@ int main(int argc, char **argv) case 't': persistent = 1; break; + case 'x': + export_name = optarg; + break; case 'v': verbose = 1; break; @@ -598,25 +622,42 @@ int main(int argc, char **argv) exit(0); break; case '?': - errx(EXIT_FAILURE, "Try `%s --help' for more information.", - argv[0]); + error_report("Try `%s --help' for more information.", argv[0]); + exit(EXIT_FAILURE); + case QEMU_NBD_OPT_OBJECT: { + QemuOpts *opts; + opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!opts) { + exit(EXIT_FAILURE); + } + } break; } } if ((argc - optind) != 1) { - errx(EXIT_FAILURE, "Invalid number of argument.\n" - "Try `%s --help' for more information.", - argv[0]); + error_report("Invalid number of arguments"); + error_printf("Try `%s --help' for more information.\n", argv[0]); + exit(EXIT_FAILURE); + } + + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, &local_err)) { + error_report_err(local_err); + exit(EXIT_FAILURE); } if (disconnect) { - fd = open(argv[optind], O_RDWR); - if (fd < 0) { - err(EXIT_FAILURE, "Cannot open %s", argv[optind]); + int nbdfd = open(argv[optind], O_RDWR); + if (nbdfd < 0) { + error_report("Cannot open %s: %s", argv[optind], + strerror(errno)); + exit(EXIT_FAILURE); } - nbd_disconnect(fd); + nbd_disconnect(nbdfd); - close(fd); + close(nbdfd); printf("%s disconnected\n", argv[optind]); @@ -629,21 +670,27 @@ int main(int argc, char **argv) int ret; if (qemu_pipe(stderr_fd) < 0) { - err(EXIT_FAILURE, "Error setting up communication pipe"); + error_report("Error setting up communication pipe: %s", + strerror(errno)); + exit(EXIT_FAILURE); } /* Now daemonize, but keep a communication channel open to * print errors and exit with the proper status code. */ pid = fork(); - if (pid == 0) { + if (pid < 0) { + error_report("Failed to fork: %s", strerror(errno)); + exit(EXIT_FAILURE); + } else if (pid == 0) { close(stderr_fd[0]); ret = qemu_daemon(1, 0); /* Temporarily redirect stderr to the parent's pipe... */ dup2(stderr_fd[1], STDERR_FILENO); if (ret < 0) { - err(EXIT_FAILURE, "Failed to daemonize"); + error_report("Failed to daemonize: %s", strerror(errno)); + exit(EXIT_FAILURE); } /* ... close the descriptor we inherited and go on. */ @@ -665,7 +712,9 @@ int main(int argc, char **argv) } } if (ret < 0) { - err(EXIT_FAILURE, "Cannot read from daemon"); + error_report("Cannot read from daemon: %s", + strerror(errno)); + exit(EXIT_FAILURE); } /* Usually the daemon should not print any message. @@ -680,9 +729,10 @@ int main(int argc, char **argv) snprintf(sockpath, 128, SOCKET_PATH, basename(device)); } + saddr = nbd_build_socket_address(sockpath, bindto, port); + if (qemu_init_main_loop(&local_err)) { - error_report("%s", error_get_pretty(local_err)); - error_free(local_err); + error_report_err(local_err); exit(EXIT_FAILURE); } bdrv_init(); @@ -696,8 +746,9 @@ int main(int argc, char **argv) srcpath = argv[optind]; blk = blk_new_open("hda", srcpath, NULL, options, flags, &local_err); if (!blk) { - errx(EXIT_FAILURE, "Failed to blk_new_open '%s': %s", argv[optind], - error_get_pretty(local_err)); + error_reportf_err(local_err, "Failed to blk_new_open '%s': ", + argv[optind]); + exit(EXIT_FAILURE); } bs = blk_bs(blk); @@ -711,32 +762,42 @@ int main(int argc, char **argv) &local_err); } if (ret < 0) { - errno = -ret; - err(EXIT_FAILURE, - "Failed to load snapshot: %s", - error_get_pretty(local_err)); + error_reportf_err(local_err, "Failed to load snapshot: "); + exit(EXIT_FAILURE); } bs->detect_zeroes = detect_zeroes; fd_size = blk_getlength(blk); + if (fd_size < 0) { + error_report("Failed to determine the image length: %s", + strerror(-fd_size)); + exit(EXIT_FAILURE); + } if (partition != -1) { ret = find_partition(blk, partition, &dev_offset, &fd_size); if (ret < 0) { - errno = -ret; - err(EXIT_FAILURE, "Could not find partition %d", partition); + error_report("Could not find partition %d: %s", partition, + strerror(-ret)); + exit(EXIT_FAILURE); } } - exp = nbd_export_new(blk, dev_offset, fd_size, nbdflags, nbd_export_closed); - - if (sockpath) { - fd = unix_socket_incoming(sockpath); - } else { - fd = tcp_socket_incoming(bindto, port); + exp = nbd_export_new(blk, dev_offset, fd_size, nbdflags, nbd_export_closed, + &local_err); + if (!exp) { + error_report_err(local_err); + exit(EXIT_FAILURE); + } + if (export_name) { + nbd_export_set_name(exp, export_name); + newproto = true; } - if (fd < 0) { + server_ioc = qio_channel_socket_new(); + if (qio_channel_socket_listen_sync(server_ioc, saddr, &local_err) < 0) { + object_unref(OBJECT(server_ioc)); + error_report_err(local_err); return 1; } @@ -745,21 +806,22 @@ int main(int argc, char **argv) ret = pthread_create(&client_thread, NULL, nbd_client_thread, device); if (ret != 0) { - errx(EXIT_FAILURE, "Failed to create client thread: %s", - strerror(ret)); + error_report("Failed to create client thread: %s", strerror(ret)); + exit(EXIT_FAILURE); } } else { /* Shut up GCC warnings. */ memset(&client_thread, 0, sizeof(client_thread)); } - qemu_set_fd_handler2(fd, nbd_can_accept, nbd_accept, NULL, - (void *)(uintptr_t)fd); + nbd_update_server_watch(); /* now when the initialization is (almost) complete, chdir("/") * to free any busy filesystems */ if (chdir("/") < 0) { - err(EXIT_FAILURE, "Could not chdir to root directory"); + error_report("Could not chdir to root directory: %s", + strerror(errno)); + exit(EXIT_FAILURE); } state = RUNNING;