mirror of https://github.com/xqemu/xqemu.git
nbd: Accept -drive options for the network connection
The existing parsers for the file name now parse everything into the bdrv_open() options QDict. Instead of using these parsers, you can now directly specify the options on the command line, like this: qemu-system-x86_64 -drive file=nbd:,file.port=1234,file.host=::1 Clearly the file=... part could use further improvement, but it's a start. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
parent
197a4859b9
commit
f53a1febcd
131
block/nbd.c
131
block/nbd.c
|
@ -32,6 +32,8 @@
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qemu/sockets.h"
|
#include "qemu/sockets.h"
|
||||||
|
#include "qapi/qmp/qjson.h"
|
||||||
|
#include "qapi/qmp/qint.h"
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
@ -65,20 +67,19 @@ typedef struct BDRVNBDState {
|
||||||
Coroutine *recv_coroutine[MAX_NBD_REQUESTS];
|
Coroutine *recv_coroutine[MAX_NBD_REQUESTS];
|
||||||
struct nbd_reply reply;
|
struct nbd_reply reply;
|
||||||
|
|
||||||
int is_unix;
|
bool is_unix;
|
||||||
char *unix_path;
|
QemuOpts *socket_opts;
|
||||||
|
|
||||||
InetSocketAddress *inet_addr;
|
|
||||||
|
|
||||||
char *export_name; /* An NBD server may export several devices */
|
char *export_name; /* An NBD server may export several devices */
|
||||||
} BDRVNBDState;
|
} BDRVNBDState;
|
||||||
|
|
||||||
static int nbd_parse_uri(BDRVNBDState *s, const char *filename)
|
static int nbd_parse_uri(const char *filename, QDict *options)
|
||||||
{
|
{
|
||||||
URI *uri;
|
URI *uri;
|
||||||
const char *p;
|
const char *p;
|
||||||
QueryParams *qp = NULL;
|
QueryParams *qp = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
bool is_unix;
|
||||||
|
|
||||||
uri = uri_parse(filename);
|
uri = uri_parse(filename);
|
||||||
if (!uri) {
|
if (!uri) {
|
||||||
|
@ -87,11 +88,11 @@ static int nbd_parse_uri(BDRVNBDState *s, const char *filename)
|
||||||
|
|
||||||
/* transport */
|
/* transport */
|
||||||
if (!strcmp(uri->scheme, "nbd")) {
|
if (!strcmp(uri->scheme, "nbd")) {
|
||||||
s->is_unix = false;
|
is_unix = false;
|
||||||
} else if (!strcmp(uri->scheme, "nbd+tcp")) {
|
} else if (!strcmp(uri->scheme, "nbd+tcp")) {
|
||||||
s->is_unix = false;
|
is_unix = false;
|
||||||
} else if (!strcmp(uri->scheme, "nbd+unix")) {
|
} else if (!strcmp(uri->scheme, "nbd+unix")) {
|
||||||
s->is_unix = true;
|
is_unix = true;
|
||||||
} else {
|
} else {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -100,24 +101,26 @@ static int nbd_parse_uri(BDRVNBDState *s, const char *filename)
|
||||||
p = uri->path ? uri->path : "/";
|
p = uri->path ? uri->path : "/";
|
||||||
p += strspn(p, "/");
|
p += strspn(p, "/");
|
||||||
if (p[0]) {
|
if (p[0]) {
|
||||||
s->export_name = g_strdup(p);
|
qdict_put(options, "export", qstring_from_str(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
qp = query_params_parse(uri->query);
|
qp = query_params_parse(uri->query);
|
||||||
if (qp->n > 1 || (s->is_unix && !qp->n) || (!s->is_unix && qp->n)) {
|
if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->is_unix) {
|
if (is_unix) {
|
||||||
/* nbd+unix:///export?socket=path */
|
/* nbd+unix:///export?socket=path */
|
||||||
if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) {
|
if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
s->unix_path = g_strdup(qp->p[0].value);
|
qdict_put(options, "path", qstring_from_str(qp->p[0].value));
|
||||||
} else {
|
} else {
|
||||||
/* nbd[+tcp]://host:port/export */
|
/* nbd[+tcp]://host:port/export */
|
||||||
|
char *port_str;
|
||||||
|
|
||||||
if (!uri->server) {
|
if (!uri->server) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -126,11 +129,10 @@ static int nbd_parse_uri(BDRVNBDState *s, const char *filename)
|
||||||
uri->port = NBD_DEFAULT_PORT;
|
uri->port = NBD_DEFAULT_PORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->inet_addr = g_new0(InetSocketAddress, 1);
|
port_str = g_strdup_printf("%d", uri->port);
|
||||||
*s->inet_addr = (InetSocketAddress) {
|
qdict_put(options, "host", qstring_from_str(uri->server));
|
||||||
.host = g_strdup(uri->server),
|
qdict_put(options, "port", qstring_from_str(port_str));
|
||||||
.port = g_strdup_printf("%d", uri->port),
|
g_free(port_str);
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -141,17 +143,17 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nbd_config(BDRVNBDState *s, const char *filename)
|
static int nbd_parse_filename(const char *filename, QDict *options)
|
||||||
{
|
{
|
||||||
char *file;
|
char *file;
|
||||||
char *export_name;
|
char *export_name;
|
||||||
const char *host_spec;
|
const char *host_spec;
|
||||||
const char *unixpath;
|
const char *unixpath;
|
||||||
int err = -EINVAL;
|
int ret = -EINVAL;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
if (strstr(filename, "://")) {
|
if (strstr(filename, "://")) {
|
||||||
return nbd_parse_uri(s, filename);
|
return nbd_parse_uri(filename, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
file = g_strdup(filename);
|
file = g_strdup(filename);
|
||||||
|
@ -163,7 +165,8 @@ static int nbd_config(BDRVNBDState *s, const char *filename)
|
||||||
}
|
}
|
||||||
export_name[0] = 0; /* truncate 'file' */
|
export_name[0] = 0; /* truncate 'file' */
|
||||||
export_name += strlen(EN_OPTSTR);
|
export_name += strlen(EN_OPTSTR);
|
||||||
s->export_name = g_strdup(export_name);
|
|
||||||
|
qdict_put(options, "export", qstring_from_str(export_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* extract the host_spec - fail if it's not nbd:... */
|
/* extract the host_spec - fail if it's not nbd:... */
|
||||||
|
@ -171,32 +174,65 @@ static int nbd_config(BDRVNBDState *s, const char *filename)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!*host_spec) {
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* are we a UNIX or TCP socket? */
|
/* are we a UNIX or TCP socket? */
|
||||||
if (strstart(host_spec, "unix:", &unixpath)) {
|
if (strstart(host_spec, "unix:", &unixpath)) {
|
||||||
s->is_unix = true;
|
qdict_put(options, "path", qstring_from_str(unixpath));
|
||||||
s->unix_path = g_strdup(unixpath);
|
|
||||||
} else {
|
} else {
|
||||||
s->is_unix = false;
|
InetSocketAddress *addr = NULL;
|
||||||
s->inet_addr = inet_parse(host_spec, &local_err);
|
|
||||||
|
addr = inet_parse(host_spec, &local_err);
|
||||||
if (local_err != NULL) {
|
if (local_err != NULL) {
|
||||||
qerror_report_err(local_err);
|
qerror_report_err(local_err);
|
||||||
error_free(local_err);
|
error_free(local_err);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qdict_put(options, "host", qstring_from_str(addr->host));
|
||||||
|
qdict_put(options, "port", qstring_from_str(addr->port));
|
||||||
|
qapi_free_InetSocketAddress(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = 0;
|
ret = 1;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
g_free(file);
|
g_free(file);
|
||||||
if (err != 0) {
|
return ret;
|
||||||
g_free(s->export_name);
|
|
||||||
g_free(s->unix_path);
|
|
||||||
qapi_free_InetSocketAddress(s->inet_addr);
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nbd_config(BDRVNBDState *s, QDict *options)
|
||||||
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
if (qdict_haskey(options, "path")) {
|
||||||
|
s->is_unix = true;
|
||||||
|
} else if (qdict_haskey(options, "host")) {
|
||||||
|
s->is_unix = false;
|
||||||
|
} else {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->socket_opts = qemu_opts_create_nofail(&socket_optslist);
|
||||||
|
|
||||||
|
qemu_opts_absorb_qdict(s->socket_opts, options, &local_err);
|
||||||
|
if (error_is_set(&local_err)) {
|
||||||
|
qerror_report_err(local_err);
|
||||||
|
error_free(local_err);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->export_name = g_strdup(qdict_get_try_str(options, "export"));
|
||||||
|
if (s->export_name) {
|
||||||
|
qdict_del(options, "export");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void nbd_coroutine_start(BDRVNBDState *s, struct nbd_request *request)
|
static void nbd_coroutine_start(BDRVNBDState *s, struct nbd_request *request)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -343,24 +379,9 @@ static int nbd_establish_connection(BlockDriverState *bs)
|
||||||
size_t blocksize;
|
size_t blocksize;
|
||||||
|
|
||||||
if (s->is_unix) {
|
if (s->is_unix) {
|
||||||
sock = unix_socket_outgoing(s->unix_path);
|
sock = unix_socket_outgoing(qemu_opt_get(s->socket_opts, "path"));
|
||||||
} else {
|
} else {
|
||||||
QemuOpts *opts = qemu_opts_create_nofail(&socket_optslist);
|
sock = tcp_socket_outgoing_opts(s->socket_opts);
|
||||||
|
|
||||||
qemu_opt_set(opts, "host", s->inet_addr->host);
|
|
||||||
qemu_opt_set(opts, "port", s->inet_addr->port);
|
|
||||||
if (s->inet_addr->has_to) {
|
|
||||||
qemu_opt_set_number(opts, "to", s->inet_addr->to);
|
|
||||||
}
|
|
||||||
if (s->inet_addr->has_ipv4) {
|
|
||||||
qemu_opt_set_number(opts, "ipv4", s->inet_addr->ipv4);
|
|
||||||
}
|
|
||||||
if (s->inet_addr->has_ipv6) {
|
|
||||||
qemu_opt_set_number(opts, "ipv6", s->inet_addr->ipv6);
|
|
||||||
}
|
|
||||||
|
|
||||||
sock = tcp_socket_outgoing_opts(opts);
|
|
||||||
qemu_opts_del(opts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Failed to establish connection */
|
/* Failed to establish connection */
|
||||||
|
@ -416,7 +437,12 @@ static int nbd_open(BlockDriverState *bs, const char* filename,
|
||||||
qemu_co_mutex_init(&s->free_sema);
|
qemu_co_mutex_init(&s->free_sema);
|
||||||
|
|
||||||
/* Pop the config into our state object. Exit if invalid. */
|
/* Pop the config into our state object. Exit if invalid. */
|
||||||
result = nbd_config(s, filename);
|
result = nbd_parse_filename(filename, options);
|
||||||
|
if (result < 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = nbd_config(s, options);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -580,8 +606,7 @@ static void nbd_close(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVNBDState *s = bs->opaque;
|
BDRVNBDState *s = bs->opaque;
|
||||||
g_free(s->export_name);
|
g_free(s->export_name);
|
||||||
g_free(s->unix_path);
|
qemu_opts_del(s->socket_opts);
|
||||||
qapi_free_InetSocketAddress(s->inet_addr);
|
|
||||||
|
|
||||||
nbd_teardown_connection(bs);
|
nbd_teardown_connection(bs);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue