mirror of
https://github.com/qemu/qemu.git
synced 2024-12-03 16:53:53 +08:00
f3313d23a0
The documentation to this monitor command tells, that 'writable' argument is optional and defaults to false. However, the code sets true as the default. But since some applications may already been using this, it's safer to fix the code and not documentation which would break those applications. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
134 lines
3.2 KiB
C
134 lines
3.2 KiB
C
/*
|
|
* Serving QEMU block devices via NBD
|
|
*
|
|
* Copyright (c) 2012 Red Hat, Inc.
|
|
*
|
|
* Author: Paolo Bonzini <pbonzini@redhat.com>
|
|
*
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or
|
|
* later. See the COPYING file in the top-level directory.
|
|
*/
|
|
|
|
#include "blockdev.h"
|
|
#include "hw/block-common.h"
|
|
#include "monitor.h"
|
|
#include "qerror.h"
|
|
#include "sysemu.h"
|
|
#include "qmp-commands.h"
|
|
#include "trace.h"
|
|
#include "nbd.h"
|
|
#include "qemu_socket.h"
|
|
|
|
static int server_fd = -1;
|
|
|
|
static void nbd_accept(void *opaque)
|
|
{
|
|
struct sockaddr_in addr;
|
|
socklen_t addr_len = sizeof(addr);
|
|
|
|
int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len);
|
|
if (fd >= 0) {
|
|
nbd_client_new(NULL, fd, nbd_client_put);
|
|
}
|
|
}
|
|
|
|
void qmp_nbd_server_start(SocketAddress *addr, Error **errp)
|
|
{
|
|
if (server_fd != -1) {
|
|
error_setg(errp, "NBD server already running");
|
|
return;
|
|
}
|
|
|
|
server_fd = socket_listen(addr, errp);
|
|
if (server_fd != -1) {
|
|
qemu_set_fd_handler2(server_fd, NULL, nbd_accept, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
/* Hook into the BlockDriverState notifiers to close the export when
|
|
* the file is closed.
|
|
*/
|
|
typedef struct NBDCloseNotifier {
|
|
Notifier n;
|
|
NBDExport *exp;
|
|
QTAILQ_ENTRY(NBDCloseNotifier) next;
|
|
} NBDCloseNotifier;
|
|
|
|
static QTAILQ_HEAD(, NBDCloseNotifier) close_notifiers =
|
|
QTAILQ_HEAD_INITIALIZER(close_notifiers);
|
|
|
|
static void nbd_close_notifier(Notifier *n, void *data)
|
|
{
|
|
NBDCloseNotifier *cn = DO_UPCAST(NBDCloseNotifier, n, n);
|
|
|
|
notifier_remove(&cn->n);
|
|
QTAILQ_REMOVE(&close_notifiers, cn, next);
|
|
|
|
nbd_export_close(cn->exp);
|
|
nbd_export_put(cn->exp);
|
|
g_free(cn);
|
|
}
|
|
|
|
static void nbd_server_put_ref(NBDExport *exp)
|
|
{
|
|
BlockDriverState *bs = nbd_export_get_blockdev(exp);
|
|
drive_put_ref(drive_get_by_blockdev(bs));
|
|
}
|
|
|
|
void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
|
|
Error **errp)
|
|
{
|
|
BlockDriverState *bs;
|
|
NBDExport *exp;
|
|
NBDCloseNotifier *n;
|
|
|
|
if (server_fd == -1) {
|
|
error_setg(errp, "NBD server not running");
|
|
return;
|
|
}
|
|
|
|
if (nbd_export_find(device)) {
|
|
error_setg(errp, "NBD server already exporting device '%s'", device);
|
|
return;
|
|
}
|
|
|
|
bs = bdrv_find(device);
|
|
if (!bs) {
|
|
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
|
return;
|
|
}
|
|
|
|
if (!has_writable) {
|
|
writable = false;
|
|
}
|
|
if (bdrv_is_read_only(bs)) {
|
|
writable = false;
|
|
}
|
|
|
|
exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY,
|
|
nbd_server_put_ref);
|
|
|
|
nbd_export_set_name(exp, device);
|
|
drive_get_ref(drive_get_by_blockdev(bs));
|
|
|
|
n = g_malloc0(sizeof(NBDCloseNotifier));
|
|
n->n.notify = nbd_close_notifier;
|
|
n->exp = exp;
|
|
bdrv_add_close_notifier(bs, &n->n);
|
|
QTAILQ_INSERT_TAIL(&close_notifiers, n, next);
|
|
}
|
|
|
|
void qmp_nbd_server_stop(Error **errp)
|
|
{
|
|
while (!QTAILQ_EMPTY(&close_notifiers)) {
|
|
NBDCloseNotifier *cn = QTAILQ_FIRST(&close_notifiers);
|
|
nbd_close_notifier(&cn->n, nbd_export_get_blockdev(cn->exp));
|
|
}
|
|
|
|
if (server_fd != -1) {
|
|
qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL);
|
|
close(server_fd);
|
|
server_fd = -1;
|
|
}
|
|
}
|