diff options
-rw-r--r-- | chardev/char-socket.c | 24 | ||||
-rw-r--r-- | chardev/char.c | 2 | ||||
-rw-r--r-- | qapi/sockets.json | 14 | ||||
-rw-r--r-- | tests/test-util-sockets.c | 145 | ||||
-rw-r--r-- | util/qemu-sockets.c | 54 |
5 files changed, 152 insertions, 87 deletions
diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 95e45812d5..213a4c8dd0 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -443,10 +443,24 @@ static char *qemu_chr_socket_address(SocketChardev *s, const char *prefix) s->is_listen ? ",server" : ""); break; case SOCKET_ADDRESS_TYPE_UNIX: - return g_strdup_printf("%sunix:%s%s", prefix, - s->addr->u.q_unix.path, + { + const char *tight = "", *abstract = ""; + UnixSocketAddress *sa = &s->addr->u.q_unix; + +#ifdef CONFIG_LINUX + if (sa->has_abstract && sa->abstract) { + abstract = ",abstract"; + if (sa->has_tight && sa->tight) { + tight = ",tight"; + } + } +#endif + + return g_strdup_printf("%sunix:%s%s%s%s", prefix, sa->path, + abstract, tight, s->is_listen ? ",server" : ""); break; + } case SOCKET_ADDRESS_TYPE_FD: return g_strdup_printf("%sfd:%s%s", prefix, s->addr->u.fd.str, s->is_listen ? ",server" : ""); @@ -1386,8 +1400,10 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, const char *host = qemu_opt_get(opts, "host"); const char *port = qemu_opt_get(opts, "port"); const char *fd = qemu_opt_get(opts, "fd"); +#ifdef CONFIG_LINUX bool tight = qemu_opt_get_bool(opts, "tight", true); bool abstract = qemu_opt_get_bool(opts, "abstract", false); +#endif SocketAddressLegacy *addr; ChardevSocket *sock; @@ -1439,8 +1455,12 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, addr->type = SOCKET_ADDRESS_LEGACY_KIND_UNIX; q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1); q_unix->path = g_strdup(path); +#ifdef CONFIG_LINUX + q_unix->has_tight = true; q_unix->tight = tight; + q_unix->has_abstract = true; q_unix->abstract = abstract; +#endif } else if (host) { addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET; addr->u.inet.data = g_new(InetSocketAddress, 1); diff --git a/chardev/char.c b/chardev/char.c index 78553125d3..aa4282164a 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -928,6 +928,7 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "logappend", .type = QEMU_OPT_BOOL, +#ifdef CONFIG_LINUX },{ .name = "tight", .type = QEMU_OPT_BOOL, @@ -935,6 +936,7 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "abstract", .type = QEMU_OPT_BOOL, +#endif }, { /* end of list */ } }, diff --git a/qapi/sockets.json b/qapi/sockets.json index c0c640a5b0..2e83452797 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -74,18 +74,20 @@ # Captures a socket address in the local ("Unix socket") namespace. # # @path: filesystem path to use -# @tight: pass a socket address length confined to the minimum length of the -# abstract string, rather than the full sockaddr_un record length -# (only matters for abstract sockets, default true). (Since 5.1) -# @abstract: whether this is an abstract address, default false. (Since 5.1) +# @abstract: if true, this is a Linux abstract socket address. @path +# will be prefixed by a null byte, and optionally padded +# with null bytes. Defaults to false. (Since 5.1) +# @tight: if false, pad an abstract socket address with enough null +# bytes to make it fill struct sockaddr_un member sun_path. +# Defaults to true. (Since 5.1) # # Since: 1.3 ## { 'struct': 'UnixSocketAddress', 'data': { 'path': 'str', - '*tight': 'bool', - '*abstract': 'bool' } } + '*abstract': { 'type': 'bool', 'if': 'defined(CONFIG_LINUX)' }, + '*tight': { 'type': 'bool', 'if': 'defined(CONFIG_LINUX)' } } } ## # @VsockSocketAddress: diff --git a/tests/test-util-sockets.c b/tests/test-util-sockets.c index f6336e0f91..67486055ed 100644 --- a/tests/test-util-sockets.c +++ b/tests/test-util-sockets.c @@ -229,94 +229,105 @@ static void test_socket_fd_pass_num_nocli(void) } #endif -#ifdef __linux__ -static gchar *abstract_sock_name; +#ifdef CONFIG_LINUX -static gpointer unix_server_thread_func(gpointer user_data) +#define ABSTRACT_SOCKET_VARIANTS 3 + +typedef struct { + SocketAddress *server, *client[ABSTRACT_SOCKET_VARIANTS]; + bool expect_connect[ABSTRACT_SOCKET_VARIANTS]; +} abstract_socket_matrix_row; + +static gpointer unix_client_thread_func(gpointer user_data) { - SocketAddress addr; + abstract_socket_matrix_row *row = user_data; Error *err = NULL; - int fd = -1; - int connfd = -1; + int i, fd; + + for (i = 0; i < ABSTRACT_SOCKET_VARIANTS; i++) { + if (row->expect_connect[i]) { + fd = socket_connect(row->client[i], &error_abort); + g_assert_cmpint(fd, >=, 0); + } else { + fd = socket_connect(row->client[i], &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + } + close(fd); + } + return NULL; +} + +static void test_socket_unix_abstract_row(abstract_socket_matrix_row *test) +{ + int fd, connfd, i; + GThread *cli; struct sockaddr_un un; socklen_t len = sizeof(un); - addr.type = SOCKET_ADDRESS_TYPE_UNIX; - addr.u.q_unix.path = abstract_sock_name; - addr.u.q_unix.tight = user_data != NULL; - addr.u.q_unix.abstract = true; + /* Last one must connect, or else accept() below hangs */ + assert(test->expect_connect[ABSTRACT_SOCKET_VARIANTS - 1]); - fd = socket_listen(&addr, 1, &err); + fd = socket_listen(test->server, 1, &error_abort); g_assert_cmpint(fd, >=, 0); g_assert(fd_is_socket(fd)); - connfd = accept(fd, (struct sockaddr *)&un, &len); - g_assert_cmpint(connfd, !=, -1); + cli = g_thread_new("abstract_unix_client", + unix_client_thread_func, + test); + + for (i = 0; i < ABSTRACT_SOCKET_VARIANTS; i++) { + if (test->expect_connect[i]) { + connfd = accept(fd, (struct sockaddr *)&un, &len); + g_assert_cmpint(connfd, !=, -1); + close(connfd); + } + } close(fd); - - return NULL; + g_thread_join(cli); } -static gpointer unix_client_thread_func(gpointer user_data) +static void test_socket_unix_abstract(void) { - SocketAddress addr; - Error *err = NULL; - int fd = -1; + SocketAddress addr, addr_tight, addr_padded; + abstract_socket_matrix_row matrix[ABSTRACT_SOCKET_VARIANTS] = { + { &addr, + { &addr_tight, &addr_padded, &addr }, + { true, false, true } }, + { &addr_tight, + { &addr_padded, &addr, &addr_tight }, + { false, true, true } }, + { &addr_padded, + { &addr, &addr_tight, &addr_padded }, + { false, false, true } } + }; + int i; addr.type = SOCKET_ADDRESS_TYPE_UNIX; - addr.u.q_unix.path = abstract_sock_name; - addr.u.q_unix.tight = user_data != NULL; + addr.u.q_unix.path = g_strdup_printf("unix-%d-%u", + getpid(), g_random_int()); + addr.u.q_unix.has_abstract = true; addr.u.q_unix.abstract = true; + addr.u.q_unix.has_tight = false; + addr.u.q_unix.tight = false; - fd = socket_connect(&addr, &err); + addr_tight = addr; + addr_tight.u.q_unix.has_tight = true; + addr_tight.u.q_unix.tight = true; - g_assert_cmpint(fd, >=, 0); + addr_padded = addr; + addr_padded.u.q_unix.has_tight = true; + addr_padded.u.q_unix.tight = false; - close(fd); + for (i = 0; i < ABSTRACT_SOCKET_VARIANTS; i++) { + test_socket_unix_abstract_row(&matrix[i]); + } - return NULL; + g_free(addr.u.q_unix.path); } -static void test_socket_unix_abstract_good(void) -{ - GRand *r = g_rand_new(); - - abstract_sock_name = g_strdup_printf("unix-%d-%d", getpid(), - g_rand_int_range(r, 100, 1000)); - - /* non tight socklen serv and cli */ - GThread *serv = g_thread_new("abstract_unix_server", - unix_server_thread_func, - NULL); - - sleep(1); - - GThread *cli = g_thread_new("abstract_unix_client", - unix_client_thread_func, - NULL); - - g_thread_join(cli); - g_thread_join(serv); - - /* tight socklen serv and cli */ - serv = g_thread_new("abstract_unix_server", - unix_server_thread_func, - (gpointer)1); - - sleep(1); - - cli = g_thread_new("abstract_unix_client", - unix_client_thread_func, - (gpointer)1); - - g_thread_join(cli); - g_thread_join(serv); - - g_free(abstract_sock_name); - g_rand_free(r); -} -#endif +#endif /* CONFIG_LINUX */ int main(int argc, char **argv) { @@ -358,9 +369,9 @@ int main(int argc, char **argv) #endif } -#ifdef __linux__ - g_test_add_func("/util/socket/unix-abstract/good", - test_socket_unix_abstract_good); +#ifdef CONFIG_LINUX + g_test_add_func("/util/socket/unix-abstract", + test_socket_unix_abstract); #endif end: diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 38f82179b0..8af0278f15 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -860,10 +860,29 @@ static int vsock_parse(VsockSocketAddress *addr, const char *str, #ifndef _WIN32 +static bool saddr_is_abstract(UnixSocketAddress *saddr) +{ +#ifdef CONFIG_LINUX + return saddr->abstract; +#else + return false; +#endif +} + +static bool saddr_is_tight(UnixSocketAddress *saddr) +{ +#ifdef CONFIG_LINUX + return !saddr->has_tight || saddr->tight; +#else + return false; +#endif +} + static int unix_listen_saddr(UnixSocketAddress *saddr, int num, Error **errp) { + bool abstract = saddr_is_abstract(saddr); struct sockaddr_un un; int sock, fd; char *pathbuf = NULL; @@ -877,7 +896,7 @@ static int unix_listen_saddr(UnixSocketAddress *saddr, return -1; } - if (saddr->path && saddr->path[0]) { + if (saddr->path[0] || abstract) { path = saddr->path; } else { const char *tmpdir = getenv("TMPDIR"); @@ -887,10 +906,10 @@ static int unix_listen_saddr(UnixSocketAddress *saddr, pathlen = strlen(path); if (pathlen > sizeof(un.sun_path) || - (saddr->abstract && pathlen > (sizeof(un.sun_path) - 1))) { + (abstract && pathlen > (sizeof(un.sun_path) - 1))) { error_setg(errp, "UNIX socket path '%s' is too long", path); error_append_hint(errp, "Path must be less than %zu bytes\n", - saddr->abstract ? sizeof(un.sun_path) - 1 : + abstract ? sizeof(un.sun_path) - 1 : sizeof(un.sun_path)); goto err; } @@ -912,7 +931,7 @@ static int unix_listen_saddr(UnixSocketAddress *saddr, close(fd); } - if (!saddr->abstract && unlink(path) < 0 && errno != ENOENT) { + if (!abstract && unlink(path) < 0 && errno != ENOENT) { error_setg_errno(errp, errno, "Failed to unlink socket %s", path); goto err; @@ -922,10 +941,10 @@ static int unix_listen_saddr(UnixSocketAddress *saddr, un.sun_family = AF_UNIX; addrlen = sizeof(un); - if (saddr->abstract) { + if (abstract) { un.sun_path[0] = '\0'; memcpy(&un.sun_path[1], path, pathlen); - if (saddr->tight) { + if (saddr_is_tight(saddr)) { addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + pathlen; } } else { @@ -952,6 +971,7 @@ err: static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp) { + bool abstract = saddr_is_abstract(saddr); struct sockaddr_un un; int sock, rc; size_t pathlen; @@ -970,10 +990,10 @@ static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp) pathlen = strlen(saddr->path); if (pathlen > sizeof(un.sun_path) || - (saddr->abstract && pathlen > (sizeof(un.sun_path) - 1))) { + (abstract && pathlen > (sizeof(un.sun_path) - 1))) { error_setg(errp, "UNIX socket path '%s' is too long", saddr->path); error_append_hint(errp, "Path must be less than %zu bytes\n", - saddr->abstract ? sizeof(un.sun_path) - 1 : + abstract ? sizeof(un.sun_path) - 1 : sizeof(un.sun_path)); goto err; } @@ -982,10 +1002,10 @@ static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp) un.sun_family = AF_UNIX; addrlen = sizeof(un); - if (saddr->abstract) { + if (abstract) { un.sun_path[0] = '\0'; memcpy(&un.sun_path[1], saddr->path, pathlen); - if (saddr->tight) { + if (saddr_is_tight(saddr)) { addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + pathlen; } } else { @@ -1270,10 +1290,20 @@ socket_sockaddr_to_address_unix(struct sockaddr_storage *sa, addr = g_new0(SocketAddress, 1); addr->type = SOCKET_ADDRESS_TYPE_UNIX; - if (su->sun_path[0]) { - addr->u.q_unix.path = g_strndup(su->sun_path, sizeof(su->sun_path)); +#ifdef CONFIG_LINUX + if (!su->sun_path[0]) { + /* Linux abstract socket */ + addr->u.q_unix.path = g_strndup(su->sun_path + 1, + sizeof(su->sun_path) - 1); + addr->u.q_unix.has_abstract = true; + addr->u.q_unix.abstract = true; + addr->u.q_unix.has_tight = true; + addr->u.q_unix.tight = salen < sizeof(*su); + return addr; } +#endif + addr->u.q_unix.path = g_strndup(su->sun_path, sizeof(su->sun_path)); return addr; } #endif /* WIN32 */ |