summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2016-11-01 11:50:21 +0000
committerPeter Maydell <peter.maydell@linaro.org>2016-11-01 11:50:21 +0000
commit38ab359644e7f26b1d77842ab29c4d425e5e540c (patch)
treeef2caf172f15ce19448dea348b1784db82611f37
parentbf99fd3983d7185178a0f65ce29bb94b1aecaed1 (diff)
parent586ef5dee77180fc32e33bc08051600030630239 (diff)
downloadqemu-38ab359644e7f26b1d77842ab29c4d425e5e540c.tar.gz
Merge remote-tracking branch 'remotes/mdroth/tags/qga-pull-2016-10-31-tag' into staging
qemu-ga patch queue for 2.8 * add guest-fstrim support for w32 * add support for using virtio-vsock as the communication channel # gpg: Signature made Tue 01 Nov 2016 00:55:40 GMT # gpg: using RSA key 0x3353C9CEF108B584 # gpg: Good signature from "Michael Roth <flukshun@gmail.com>" # gpg: aka "Michael Roth <mdroth@utexas.edu>" # gpg: aka "Michael Roth <mdroth@linux.vnet.ibm.com>" # Primary key fingerprint: CEAC C9E1 5534 EBAB B82D 3FA0 3353 C9CE F108 B584 * remotes/mdroth/tags/qga-pull-2016-10-31-tag: qga: add vsock-listen method sockets: add AF_VSOCK support qga: drop unnecessary GA_CHANNEL_UNIX_LISTEN checks qga: drop unused sockaddr in accept(2) call qga: minimal support for fstrim for Windows guests Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rwxr-xr-xconfigure31
-rw-r--r--qapi-schema.json23
-rw-r--r--qga/channel-posix.c36
-rw-r--r--qga/channel.h1
-rw-r--r--qga/commands-win32.c97
-rw-r--r--qga/main.c6
-rw-r--r--util/qemu-sockets.c227
7 files changed, 407 insertions, 14 deletions
diff --git a/configure b/configure
index 6b7acb1ccb..fd6f898cbc 100755
--- a/configure
+++ b/configure
@@ -4674,6 +4674,33 @@ if compile_prog "" "" ; then
have_rtnetlink=yes
fi
+##########################################
+# check for usable AF_VSOCK environment
+have_af_vsock=no
+cat > $TMPC << EOF
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#if !defined(AF_VSOCK)
+# error missing AF_VSOCK flag
+#endif
+#include <linux/vm_sockets.h>
+int main(void) {
+ int sock, ret;
+ struct sockaddr_vm svm;
+ socklen_t len = sizeof(svm);
+ sock = socket(AF_VSOCK, SOCK_STREAM, 0);
+ ret = getpeername(sock, (struct sockaddr *)&svm, &len);
+ if ((ret == -1) && (errno == ENOTCONN)) {
+ return 0;
+ }
+ return -1;
+}
+EOF
+if compile_prog "" "" ; then
+ have_af_vsock=yes
+fi
+
#################################################
# Sparc implicitly links with --relax, which is
# incompatible with -r, so --no-relax should be
@@ -5662,6 +5689,10 @@ if test "$replication" = "yes" ; then
echo "CONFIG_REPLICATION=y" >> $config_host_mak
fi
+if test "$have_af_vsock" = "yes" ; then
+ echo "CONFIG_AF_VSOCK=y" >> $config_host_mak
+fi
+
# Hold two types of flag:
# CONFIG_THREAD_SETNAME_BYTHREAD - we've got a way of setting the name on
# a thread we have a handle to
diff --git a/qapi-schema.json b/qapi-schema.json
index 8a7b527091..5dc96af469 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1063,12 +1063,14 @@
#
# @unix: unix socket
#
+# @vsock: vsock family (since 2.8)
+#
# @unknown: otherwise
#
# Since: 2.1
##
{ 'enum': 'NetworkAddressFamily',
- 'data': [ 'ipv4', 'ipv6', 'unix', 'unknown' ] }
+ 'data': [ 'ipv4', 'ipv6', 'unix', 'vsock', 'unknown' ] }
##
# @VncBasicInfo
@@ -3095,6 +3097,24 @@
'path': 'str' } }
##
+# @VsockSocketAddress
+#
+# Captures a socket address in the vsock namespace.
+#
+# @cid: unique host identifier
+# @port: port
+#
+# Note that string types are used to allow for possible future hostname or
+# service resolution support.
+#
+# Since 2.8
+##
+{ 'struct': 'VsockSocketAddress',
+ 'data': {
+ 'cid': 'str',
+ 'port': 'str' } }
+
+##
# @SocketAddress
#
# Captures the address of a socket, which could also be a named file descriptor
@@ -3105,6 +3125,7 @@
'data': {
'inet': 'InetSocketAddress',
'unix': 'UnixSocketAddress',
+ 'vsock': 'VsockSocketAddress',
'fd': 'String' } }
##
diff --git a/qga/channel-posix.c b/qga/channel-posix.c
index bb65d8ba17..71582e0c38 100644
--- a/qga/channel-posix.c
+++ b/qga/channel-posix.c
@@ -26,13 +26,10 @@ static gboolean ga_channel_listen_accept(GIOChannel *channel,
GAChannel *c = data;
int ret, client_fd;
bool accepted = false;
- struct sockaddr_un addr;
- socklen_t addrlen = sizeof(addr);
g_assert(channel != NULL);
- client_fd = qemu_accept(g_io_channel_unix_get_fd(channel),
- (struct sockaddr *)&addr, &addrlen);
+ client_fd = qemu_accept(g_io_channel_unix_get_fd(channel), NULL, NULL);
if (client_fd == -1) {
g_warning("error converting fd to gsocket: %s", strerror(errno));
goto out;
@@ -64,7 +61,6 @@ static void ga_channel_listen_add(GAChannel *c, int listen_fd, bool create)
static void ga_channel_listen_close(GAChannel *c)
{
- g_assert(c->method == GA_CHANNEL_UNIX_LISTEN);
g_assert(c->listen_channel);
g_io_channel_shutdown(c->listen_channel, true, NULL);
g_io_channel_unref(c->listen_channel);
@@ -80,7 +76,7 @@ static void ga_channel_client_close(GAChannel *c)
g_io_channel_shutdown(c->client_channel, true, NULL);
g_io_channel_unref(c->client_channel);
c->client_channel = NULL;
- if (c->method == GA_CHANNEL_UNIX_LISTEN && c->listen_channel) {
+ if (c->listen_channel) {
ga_channel_listen_add(c, 0, false);
}
}
@@ -197,6 +193,31 @@ static gboolean ga_channel_open(GAChannel *c, const gchar *path, GAChannelMethod
ga_channel_listen_add(c, fd, true);
break;
}
+ case GA_CHANNEL_VSOCK_LISTEN: {
+ Error *local_err = NULL;
+ SocketAddress *addr;
+ char *addr_str;
+ int fd;
+
+ addr_str = g_strdup_printf("vsock:%s", path);
+ addr = socket_parse(addr_str, &local_err);
+ g_free(addr_str);
+ if (local_err != NULL) {
+ g_critical("%s", error_get_pretty(local_err));
+ error_free(local_err);
+ return false;
+ }
+
+ fd = socket_listen(addr, &local_err);
+ qapi_free_SocketAddress(addr);
+ if (local_err != NULL) {
+ g_critical("%s", error_get_pretty(local_err));
+ error_free(local_err);
+ return false;
+ }
+ ga_channel_listen_add(c, fd, true);
+ break;
+ }
default:
g_critical("error binding/listening to specified socket");
return false;
@@ -258,8 +279,7 @@ GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path,
void ga_channel_free(GAChannel *c)
{
- if (c->method == GA_CHANNEL_UNIX_LISTEN
- && c->listen_channel) {
+ if (c->listen_channel) {
ga_channel_listen_close(c);
}
if (c->client_channel) {
diff --git a/qga/channel.h b/qga/channel.h
index ae8cf0f7e0..8fd0c8f72c 100644
--- a/qga/channel.h
+++ b/qga/channel.h
@@ -19,6 +19,7 @@ typedef enum {
GA_CHANNEL_VIRTIO_SERIAL,
GA_CHANNEL_ISA_SERIAL,
GA_CHANNEL_UNIX_LISTEN,
+ GA_CHANNEL_VSOCK_LISTEN,
} GAChannelMethod;
typedef gboolean (*GAChannelCallback)(GIOCondition condition, gpointer opaque);
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 9c9be12116..19d72b2411 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -840,8 +840,99 @@ static void guest_fsfreeze_cleanup(void)
GuestFilesystemTrimResponse *
qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
+ GuestFilesystemTrimResponse *resp;
+ HANDLE handle;
+ WCHAR guid[MAX_PATH] = L"";
+
+ handle = FindFirstVolumeW(guid, ARRAYSIZE(guid));
+ if (handle == INVALID_HANDLE_VALUE) {
+ error_setg_win32(errp, GetLastError(), "failed to find any volume");
+ return NULL;
+ }
+
+ resp = g_new0(GuestFilesystemTrimResponse, 1);
+
+ do {
+ GuestFilesystemTrimResult *res;
+ GuestFilesystemTrimResultList *list;
+ PWCHAR uc_path;
+ DWORD char_count = 0;
+ char *path, *out;
+ GError *gerr = NULL;
+ gchar * argv[4];
+
+ GetVolumePathNamesForVolumeNameW(guid, NULL, 0, &char_count);
+
+ if (GetLastError() != ERROR_MORE_DATA) {
+ continue;
+ }
+ if (GetDriveTypeW(guid) != DRIVE_FIXED) {
+ continue;
+ }
+
+ uc_path = g_malloc(sizeof(WCHAR) * char_count);
+ if (!GetVolumePathNamesForVolumeNameW(guid, uc_path, char_count,
+ &char_count) || !*uc_path) {
+ /* strange, but this condition could be faced even with size == 2 */
+ g_free(uc_path);
+ continue;
+ }
+
+ res = g_new0(GuestFilesystemTrimResult, 1);
+
+ path = g_utf16_to_utf8(uc_path, char_count, NULL, NULL, &gerr);
+
+ g_free(uc_path);
+
+ if (!path) {
+ res->has_error = true;
+ res->error = g_strdup(gerr->message);
+ g_error_free(gerr);
+ break;
+ }
+
+ res->path = path;
+
+ list = g_new0(GuestFilesystemTrimResultList, 1);
+ list->value = res;
+ list->next = resp->paths;
+
+ resp->paths = list;
+
+ memset(argv, 0, sizeof(argv));
+ argv[0] = (gchar *)"defrag.exe";
+ argv[1] = (gchar *)"/L";
+ argv[2] = path;
+
+ if (!g_spawn_sync(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
+ &out /* stdout */, NULL /* stdin */,
+ NULL, &gerr)) {
+ res->has_error = true;
+ res->error = g_strdup(gerr->message);
+ g_error_free(gerr);
+ } else {
+ /* defrag.exe is UGLY. Exit code is ALWAYS zero.
+ Error is reported in the output with something like
+ (x89000020) etc code in the stdout */
+
+ int i;
+ gchar **lines = g_strsplit(out, "\r\n", 0);
+ g_free(out);
+
+ for (i = 0; lines[i] != NULL; i++) {
+ if (g_strstr_len(lines[i], -1, "(0x") == NULL) {
+ continue;
+ }
+ res->has_error = true;
+ res->error = g_strdup(lines[i]);
+ break;
+ }
+ g_strfreev(lines);
+ }
+ } while (FindNextVolumeW(handle, guid, ARRAYSIZE(guid)));
+
+ FindVolumeClose(handle);
+ return resp;
}
typedef enum {
@@ -1416,7 +1507,7 @@ GList *ga_command_blacklist_init(GList *blacklist)
"guest-get-memory-blocks", "guest-set-memory-blocks",
"guest-get-memory-block-size",
"guest-fsfreeze-freeze-list",
- "guest-fstrim", NULL};
+ NULL};
char **p = (char **)list_unsupported;
while (*p) {
diff --git a/qga/main.c b/qga/main.c
index 0b9d04ea04..6caf215575 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -190,8 +190,8 @@ static void usage(const char *cmd)
"Usage: %s [-m <method> -p <path>] [<options>]\n"
"QEMU Guest Agent %s\n"
"\n"
-" -m, --method transport method: one of unix-listen, virtio-serial, or\n"
-" isa-serial (virtio-serial is the default)\n"
+" -m, --method transport method: one of unix-listen, virtio-serial,\n"
+" isa-serial, or vsock-listen (virtio-serial is the default)\n"
" -p, --path device/socket path (the default for virtio-serial is:\n"
" %s,\n"
" the default for isa-serial is:\n"
@@ -659,6 +659,8 @@ static gboolean channel_init(GAState *s, const gchar *method, const gchar *path)
channel_method = GA_CHANNEL_ISA_SERIAL;
} else if (strcmp(method, "unix-listen") == 0) {
channel_method = GA_CHANNEL_UNIX_LISTEN;
+ } else if (strcmp(method, "vsock-listen") == 0) {
+ channel_method = GA_CHANNEL_VSOCK_LISTEN;
} else {
g_critical("unsupported channel method/type: %s", method);
return false;
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 31f7fc69c1..fe1d07aaef 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -17,6 +17,10 @@
*/
#include "qemu/osdep.h"
+#ifdef CONFIG_AF_VSOCK
+#include <linux/vm_sockets.h>
+#endif /* CONFIG_AF_VSOCK */
+
#include "monitor/monitor.h"
#include "qapi/error.h"
#include "qemu/sockets.h"
@@ -75,6 +79,9 @@ NetworkAddressFamily inet_netfamily(int family)
case PF_INET6: return NETWORK_ADDRESS_FAMILY_IPV6;
case PF_INET: return NETWORK_ADDRESS_FAMILY_IPV4;
case PF_UNIX: return NETWORK_ADDRESS_FAMILY_UNIX;
+#ifdef CONFIG_AF_VSOCK
+ case PF_VSOCK: return NETWORK_ADDRESS_FAMILY_VSOCK;
+#endif /* CONFIG_AF_VSOCK */
}
return NETWORK_ADDRESS_FAMILY_UNKNOWN;
}
@@ -650,6 +657,181 @@ int inet_connect(const char *str, Error **errp)
return sock;
}
+#ifdef CONFIG_AF_VSOCK
+static bool vsock_parse_vaddr_to_sockaddr(const VsockSocketAddress *vaddr,
+ struct sockaddr_vm *svm,
+ Error **errp)
+{
+ unsigned long long val;
+
+ memset(svm, 0, sizeof(*svm));
+ svm->svm_family = AF_VSOCK;
+
+ if (parse_uint_full(vaddr->cid, &val, 10) < 0 ||
+ val > UINT32_MAX) {
+ error_setg(errp, "Failed to parse cid '%s'", vaddr->cid);
+ return false;
+ }
+ svm->svm_cid = val;
+
+ if (parse_uint_full(vaddr->port, &val, 10) < 0 ||
+ val > UINT32_MAX) {
+ error_setg(errp, "Failed to parse port '%s'", vaddr->port);
+ return false;
+ }
+ svm->svm_port = val;
+
+ return true;
+}
+
+static int vsock_connect_addr(const struct sockaddr_vm *svm, bool *in_progress,
+ ConnectState *connect_state, Error **errp)
+{
+ int sock, rc;
+
+ *in_progress = false;
+
+ sock = qemu_socket(AF_VSOCK, SOCK_STREAM, 0);
+ if (sock < 0) {
+ error_setg_errno(errp, errno, "Failed to create socket");
+ return -1;
+ }
+ if (connect_state != NULL) {
+ qemu_set_nonblock(sock);
+ }
+ /* connect to peer */
+ do {
+ rc = 0;
+ if (connect(sock, (const struct sockaddr *)svm, sizeof(*svm)) < 0) {
+ rc = -errno;
+ }
+ } while (rc == -EINTR);
+
+ if (connect_state != NULL && QEMU_SOCKET_RC_INPROGRESS(rc)) {
+ connect_state->fd = sock;
+ qemu_set_fd_handler(sock, NULL, wait_for_connect, connect_state);
+ *in_progress = true;
+ } else if (rc < 0) {
+ error_setg_errno(errp, errno, "Failed to connect socket");
+ closesocket(sock);
+ return -1;
+ }
+ return sock;
+}
+
+static int vsock_connect_saddr(VsockSocketAddress *vaddr, Error **errp,
+ NonBlockingConnectHandler *callback,
+ void *opaque)
+{
+ struct sockaddr_vm svm;
+ int sock = -1;
+ bool in_progress;
+ ConnectState *connect_state = NULL;
+
+ if (!vsock_parse_vaddr_to_sockaddr(vaddr, &svm, errp)) {
+ return -1;
+ }
+
+ if (callback != NULL) {
+ connect_state = g_malloc0(sizeof(*connect_state));
+ connect_state->callback = callback;
+ connect_state->opaque = opaque;
+ }
+
+ sock = vsock_connect_addr(&svm, &in_progress, connect_state, errp);
+ if (sock < 0) {
+ /* do nothing */
+ } else if (in_progress) {
+ /* wait_for_connect() will do the rest */
+ return sock;
+ } else {
+ if (callback) {
+ callback(sock, NULL, opaque);
+ }
+ }
+ g_free(connect_state);
+ return sock;
+}
+
+static int vsock_listen_saddr(VsockSocketAddress *vaddr,
+ Error **errp)
+{
+ struct sockaddr_vm svm;
+ int slisten;
+
+ if (!vsock_parse_vaddr_to_sockaddr(vaddr, &svm, errp)) {
+ return -1;
+ }
+
+ slisten = qemu_socket(AF_VSOCK, SOCK_STREAM, 0);
+ if (slisten < 0) {
+ error_setg_errno(errp, errno, "Failed to create socket");
+ return -1;
+ }
+
+ if (bind(slisten, (const struct sockaddr *)&svm, sizeof(svm)) != 0) {
+ error_setg_errno(errp, errno, "Failed to bind socket");
+ closesocket(slisten);
+ return -1;
+ }
+
+ if (listen(slisten, 1) != 0) {
+ error_setg_errno(errp, errno, "Failed to listen on socket");
+ closesocket(slisten);
+ return -1;
+ }
+ return slisten;
+}
+
+static VsockSocketAddress *vsock_parse(const char *str, Error **errp)
+{
+ VsockSocketAddress *addr = NULL;
+ char cid[33];
+ char port[33];
+ int n;
+
+ if (sscanf(str, "%32[^:]:%32[^,]%n", cid, port, &n) != 2) {
+ error_setg(errp, "error parsing address '%s'", str);
+ return NULL;
+ }
+ if (str[n] != '\0') {
+ error_setg(errp, "trailing characters in address '%s'", str);
+ return NULL;
+ }
+
+ addr = g_new0(VsockSocketAddress, 1);
+ addr->cid = g_strdup(cid);
+ addr->port = g_strdup(port);
+ return addr;
+}
+#else
+static void vsock_unsupported(Error **errp)
+{
+ error_setg(errp, "socket family AF_VSOCK unsupported");
+}
+
+static int vsock_connect_saddr(VsockSocketAddress *vaddr, Error **errp,
+ NonBlockingConnectHandler *callback,
+ void *opaque)
+{
+ vsock_unsupported(errp);
+ return -1;
+}
+
+static int vsock_listen_saddr(VsockSocketAddress *vaddr,
+ Error **errp)
+{
+ vsock_unsupported(errp);
+ return -1;
+}
+
+static VsockSocketAddress *vsock_parse(const char *str, Error **errp)
+{
+ vsock_unsupported(errp);
+ return NULL;
+}
+#endif /* CONFIG_AF_VSOCK */
+
#ifndef _WIN32
static int unix_listen_saddr(UnixSocketAddress *saddr,
@@ -864,6 +1046,12 @@ SocketAddress *socket_parse(const char *str, Error **errp)
addr->u.fd.data = g_new(String, 1);
addr->u.fd.data->str = g_strdup(str + 3);
}
+ } else if (strstart(str, "vsock:", NULL)) {
+ addr->type = SOCKET_ADDRESS_KIND_VSOCK;
+ addr->u.vsock.data = vsock_parse(str + strlen("vsock:"), errp);
+ if (addr->u.vsock.data == NULL) {
+ goto fail;
+ }
} else {
addr->type = SOCKET_ADDRESS_KIND_INET;
addr->u.inet.data = inet_parse(str, errp);
@@ -900,6 +1088,10 @@ int socket_connect(SocketAddress *addr, Error **errp,
}
break;
+ case SOCKET_ADDRESS_KIND_VSOCK:
+ fd = vsock_connect_saddr(addr->u.vsock.data, errp, callback, opaque);
+ break;
+
default:
abort();
}
@@ -923,6 +1115,10 @@ int socket_listen(SocketAddress *addr, Error **errp)
fd = monitor_get_fd(cur_mon, addr->u.fd.data->str, errp);
break;
+ case SOCKET_ADDRESS_KIND_VSOCK:
+ fd = vsock_listen_saddr(addr->u.vsock.data, errp);
+ break;
+
default:
abort();
}
@@ -1022,6 +1218,26 @@ socket_sockaddr_to_address_unix(struct sockaddr_storage *sa,
}
#endif /* WIN32 */
+#ifdef CONFIG_AF_VSOCK
+static SocketAddress *
+socket_sockaddr_to_address_vsock(struct sockaddr_storage *sa,
+ socklen_t salen,
+ Error **errp)
+{
+ SocketAddress *addr;
+ VsockSocketAddress *vaddr;
+ struct sockaddr_vm *svm = (struct sockaddr_vm *)sa;
+
+ addr = g_new0(SocketAddress, 1);
+ addr->type = SOCKET_ADDRESS_KIND_VSOCK;
+ addr->u.vsock.data = vaddr = g_new0(VsockSocketAddress, 1);
+ vaddr->cid = g_strdup_printf("%u", svm->svm_cid);
+ vaddr->port = g_strdup_printf("%u", svm->svm_port);
+
+ return addr;
+}
+#endif /* CONFIG_AF_VSOCK */
+
SocketAddress *
socket_sockaddr_to_address(struct sockaddr_storage *sa,
socklen_t salen,
@@ -1037,6 +1253,11 @@ socket_sockaddr_to_address(struct sockaddr_storage *sa,
return socket_sockaddr_to_address_unix(sa, salen, errp);
#endif /* WIN32 */
+#ifdef CONFIG_AF_VSOCK
+ case AF_VSOCK:
+ return socket_sockaddr_to_address_vsock(sa, salen, errp);
+#endif
+
default:
error_setg(errp, "socket family %d unsupported",
sa->ss_family);
@@ -1103,6 +1324,12 @@ char *socket_address_to_string(struct SocketAddress *addr, Error **errp)
buf = g_strdup(addr->u.fd.data->str);
break;
+ case SOCKET_ADDRESS_KIND_VSOCK:
+ buf = g_strdup_printf("%s:%s",
+ addr->u.vsock.data->cid,
+ addr->u.vsock.data->port);
+ break;
+
default:
error_setg(errp, "socket family %d unsupported",
addr->type);