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:
Kevin Wolf 2013-03-07 16:15:11 +01:00
parent 197a4859b9
commit f53a1febcd
1 changed files with 78 additions and 53 deletions

View File

@ -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);
} }