win32-related misc patches

-----BEGIN PGP SIGNATURE-----
 
 iQJPBAABCAA6FiEEh6m9kz+HxgbSdvYt2ujhCXWWnOUFAmNG488cHG1hcmNhbmRy
 ZS5sdXJlYXVAcmVkaGF0LmNvbQAKCRDa6OEJdZac5eQTD/j/rEcONwL4gZn/Rcp8
 aJlr39GEHo0JxBAF3eoxCLJlebPcdaUQ4pu/FTegS1A4abPaajDH7rdtcA58ciAG
 rCQjUOrobHzxmI9XaTIPT4PQh3DA4HB58rTpAvb/6P/UDRc0MpkcvaOkGlJVhi+7
 WB63+gnQOBEjcieNcQtmRwYRkx7K5/9G4qEESl0i2E+SE4DM+/vcVa7lfqEZ+6HS
 bsDy2BslxtPFmHj1UElwXjTbCs4Y7pfTFd+9z8ySsGL1Komf45MZs0iS4FmZLqL/
 7Cuj+xRWibnPN9jnAc+Sdua3FAFZbqmfPQaH6DN6SICZ6Txf2hxFkAgTahagcxYX
 9EiKGHZzI4L3l/YAxFg9RfK+AsF44ZLPId58AVvUnG1jWwxl3nRaTmvtvHaEwJuZ
 PgnbAdsNzQAJjLnk8ndpTq4mQFM+9/mrQo+iaOCwmB5s07woyEq+L+KJHMUgyk2D
 lECn3vlqVGGb6GA6MS5gSXh0TDRxPxLyr9ofIG5i5YaTo4nH56S80tHrzZMUYNKD
 xe2yUrEZ7UjeV4/6M19xdw3haPOdrG3BoBshb61vI1bF/4iQxYNo8AxptCRhzNNM
 5Jrn/gyt47SEgMYpGIvHa/qo1lQiLsQAVKAK3O2QWd5T58V6J1a804zhTuT7T45O
 kZS2c8XEdAiBtUAkYNgFxwGM
 =Lpqm
 -----END PGP SIGNATURE-----

Merge tag 'win32-pull-request' of https://gitlab.com/marcandre.lureau/qemu into staging

win32-related misc patches

# -----BEGIN PGP SIGNATURE-----
#
# iQJPBAABCAA6FiEEh6m9kz+HxgbSdvYt2ujhCXWWnOUFAmNG488cHG1hcmNhbmRy
# ZS5sdXJlYXVAcmVkaGF0LmNvbQAKCRDa6OEJdZac5eQTD/j/rEcONwL4gZn/Rcp8
# aJlr39GEHo0JxBAF3eoxCLJlebPcdaUQ4pu/FTegS1A4abPaajDH7rdtcA58ciAG
# rCQjUOrobHzxmI9XaTIPT4PQh3DA4HB58rTpAvb/6P/UDRc0MpkcvaOkGlJVhi+7
# WB63+gnQOBEjcieNcQtmRwYRkx7K5/9G4qEESl0i2E+SE4DM+/vcVa7lfqEZ+6HS
# bsDy2BslxtPFmHj1UElwXjTbCs4Y7pfTFd+9z8ySsGL1Komf45MZs0iS4FmZLqL/
# 7Cuj+xRWibnPN9jnAc+Sdua3FAFZbqmfPQaH6DN6SICZ6Txf2hxFkAgTahagcxYX
# 9EiKGHZzI4L3l/YAxFg9RfK+AsF44ZLPId58AVvUnG1jWwxl3nRaTmvtvHaEwJuZ
# PgnbAdsNzQAJjLnk8ndpTq4mQFM+9/mrQo+iaOCwmB5s07woyEq+L+KJHMUgyk2D
# lECn3vlqVGGb6GA6MS5gSXh0TDRxPxLyr9ofIG5i5YaTo4nH56S80tHrzZMUYNKD
# xe2yUrEZ7UjeV4/6M19xdw3haPOdrG3BoBshb61vI1bF/4iQxYNo8AxptCRhzNNM
# 5Jrn/gyt47SEgMYpGIvHa/qo1lQiLsQAVKAK3O2QWd5T58V6J1a804zhTuT7T45O
# kZS2c8XEdAiBtUAkYNgFxwGM
# =Lpqm
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 12 Oct 2022 11:57:03 EDT
# gpg:                using RSA key 87A9BD933F87C606D276F62DDAE8E10975969CE5
# gpg:                issuer "marcandre.lureau@redhat.com"
# gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" [full]
# gpg:                 aka "Marc-André Lureau <marcandre.lureau@gmail.com>" [full]
# Primary key fingerprint: 87A9 BD93 3F87 C606 D276  F62D DAE8 E109 7596 9CE5

* tag 'win32-pull-request' of https://gitlab.com/marcandre.lureau/qemu:
  tests/unit: make test-io-channel-command work on win32
  io/command: implement support for win32
  io/command: use glib GSpawn, instead of open-coding fork/exec
  tests/channel-helper: set blocking in main thread
  util: make do_send_recv work with partial send/recv
  osdep: make readv_writev() work with partial read/write
  win32: set threads name

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2022-10-13 13:55:38 -04:00
commit 644eb9ceb4
7 changed files with 171 additions and 140 deletions

View File

@ -41,7 +41,10 @@ struct QIOChannelCommand {
QIOChannel parent;
int writefd;
int readfd;
pid_t pid;
GPid pid;
#ifdef WIN32
bool blocking;
#endif
};

View File

@ -26,12 +26,11 @@
#include "qemu/sockets.h"
#include "trace.h"
#ifndef WIN32
/**
* qio_channel_command_new_pid:
* @writefd: the FD connected to the command's stdin
* @readfd: the FD connected to the command's stdout
* @pid: the PID of the running child command
* @pid: the PID/HANDLE of the running child command
* @errp: pointer to a NULL-initialized error object
*
* Create a channel for performing I/O with the
@ -50,7 +49,7 @@
static QIOChannelCommand *
qio_channel_command_new_pid(int writefd,
int readfd,
pid_t pid)
GPid pid)
{
QIOChannelCommand *ioc;
@ -60,7 +59,13 @@ qio_channel_command_new_pid(int writefd,
ioc->writefd = writefd;
ioc->pid = pid;
trace_qio_channel_command_new_pid(ioc, writefd, readfd, pid);
trace_qio_channel_command_new_pid(ioc, writefd, readfd,
#ifdef WIN32
GetProcessId(pid)
#else
pid
#endif
);
return ioc;
}
@ -69,108 +74,26 @@ qio_channel_command_new_spawn(const char *const argv[],
int flags,
Error **errp)
{
pid_t pid = -1;
int stdinfd[2] = { -1, -1 };
int stdoutfd[2] = { -1, -1 };
int devnull = -1;
bool stdinnull = false, stdoutnull = false;
QIOChannelCommand *ioc;
g_autoptr(GError) err = NULL;
GPid pid = 0;
GSpawnFlags gflags = G_SPAWN_CLOEXEC_PIPES | G_SPAWN_DO_NOT_REAP_CHILD;
int stdinfd = -1, stdoutfd = -1;
flags = flags & O_ACCMODE;
gflags |= flags == O_WRONLY ? G_SPAWN_STDOUT_TO_DEV_NULL : 0;
if (flags == O_RDONLY) {
stdinnull = true;
}
if (flags == O_WRONLY) {
stdoutnull = true;
if (!g_spawn_async_with_pipes(NULL, (char **)argv, NULL, gflags, NULL, NULL,
&pid,
flags == O_RDONLY ? NULL : &stdinfd,
flags == O_WRONLY ? NULL : &stdoutfd,
NULL, &err)) {
error_setg(errp, "%s", err->message);
return NULL;
}
if (stdinnull || stdoutnull) {
devnull = open("/dev/null", O_RDWR);
if (devnull < 0) {
error_setg_errno(errp, errno,
"Unable to open /dev/null");
goto error;
}
}
if ((!stdinnull && !g_unix_open_pipe(stdinfd, FD_CLOEXEC, NULL)) ||
(!stdoutnull && !g_unix_open_pipe(stdoutfd, FD_CLOEXEC, NULL))) {
error_setg_errno(errp, errno,
"Unable to open pipe");
goto error;
}
pid = qemu_fork(errp);
if (pid < 0) {
goto error;
}
if (pid == 0) { /* child */
dup2(stdinnull ? devnull : stdinfd[0], STDIN_FILENO);
dup2(stdoutnull ? devnull : stdoutfd[1], STDOUT_FILENO);
/* Leave stderr connected to qemu's stderr */
if (!stdinnull) {
close(stdinfd[0]);
close(stdinfd[1]);
}
if (!stdoutnull) {
close(stdoutfd[0]);
close(stdoutfd[1]);
}
if (devnull != -1) {
close(devnull);
}
execv(argv[0], (char * const *)argv);
_exit(1);
}
if (!stdinnull) {
close(stdinfd[0]);
}
if (!stdoutnull) {
close(stdoutfd[1]);
}
ioc = qio_channel_command_new_pid(stdinnull ? devnull : stdinfd[1],
stdoutnull ? devnull : stdoutfd[0],
pid);
trace_qio_channel_command_new_spawn(ioc, argv[0], flags);
return ioc;
error:
if (devnull != -1) {
close(devnull);
}
if (stdinfd[0] != -1) {
close(stdinfd[0]);
}
if (stdinfd[1] != -1) {
close(stdinfd[1]);
}
if (stdoutfd[0] != -1) {
close(stdoutfd[0]);
}
if (stdoutfd[1] != -1) {
close(stdoutfd[1]);
}
return NULL;
return qio_channel_command_new_pid(stdinfd, stdoutfd, pid);
}
#else /* WIN32 */
QIOChannelCommand *
qio_channel_command_new_spawn(const char *const argv[],
int flags,
Error **errp)
{
error_setg_errno(errp, ENOSYS,
"Command spawn not supported on this platform");
return NULL;
}
#endif /* WIN32 */
#ifndef WIN32
static int qio_channel_command_abort(QIOChannelCommand *ioc,
Error **errp)
@ -213,6 +136,23 @@ static int qio_channel_command_abort(QIOChannelCommand *ioc,
return 0;
}
#else
static int qio_channel_command_abort(QIOChannelCommand *ioc,
Error **errp)
{
DWORD ret;
TerminateProcess(ioc->pid, 0);
ret = WaitForSingleObject(ioc->pid, 1000);
if (ret != WAIT_OBJECT_0) {
error_setg(errp,
"Process %llu refused to die",
(unsigned long long)GetProcessId(ioc->pid));
return -1;
}
return 0;
}
#endif /* ! WIN32 */
@ -221,7 +161,7 @@ static void qio_channel_command_init(Object *obj)
QIOChannelCommand *ioc = QIO_CHANNEL_COMMAND(obj);
ioc->readfd = -1;
ioc->writefd = -1;
ioc->pid = -1;
ioc->pid = 0;
}
static void qio_channel_command_finalize(Object *obj)
@ -236,12 +176,27 @@ static void qio_channel_command_finalize(Object *obj)
}
ioc->writefd = ioc->readfd = -1;
if (ioc->pid > 0) {
#ifndef WIN32
qio_channel_command_abort(ioc, NULL);
#endif
g_spawn_close_pid(ioc->pid);
}
}
#ifdef WIN32
static bool win32_fd_poll(int fd, gushort events)
{
GPollFD pfd = { .fd = _get_osfhandle(fd), .events = events };
int res;
do {
res = g_poll(&pfd, 1, 0);
} while (res < 0 && errno == EINTR);
if (res == 0) {
return false;
}
return true;
}
#endif
static ssize_t qio_channel_command_readv(QIOChannel *ioc,
const struct iovec *iov,
@ -253,6 +208,12 @@ static ssize_t qio_channel_command_readv(QIOChannel *ioc,
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
ssize_t ret;
#ifdef WIN32
if (!cioc->blocking && !win32_fd_poll(cioc->readfd, G_IO_IN)) {
return QIO_CHANNEL_ERR_BLOCK;
}
#endif
retry:
ret = readv(cioc->readfd, iov, niov);
if (ret < 0) {
@ -282,6 +243,12 @@ static ssize_t qio_channel_command_writev(QIOChannel *ioc,
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
ssize_t ret;
#ifdef WIN32
if (!cioc->blocking && !win32_fd_poll(cioc->writefd, G_IO_OUT)) {
return QIO_CHANNEL_ERR_BLOCK;
}
#endif
retry:
ret = writev(cioc->writefd, iov, niov);
if (ret <= 0) {
@ -302,14 +269,14 @@ static int qio_channel_command_set_blocking(QIOChannel *ioc,
bool enabled,
Error **errp)
{
#ifdef WIN32
/* command spawn is not supported on win32 */
g_assert_not_reached();
#else
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
if (!g_unix_set_fd_nonblocking(cioc->writefd, !enabled, NULL) ||
!g_unix_set_fd_nonblocking(cioc->readfd, !enabled, NULL)) {
#ifdef WIN32
cioc->blocking = enabled;
#else
if ((cioc->writefd >= 0 && !g_unix_set_fd_nonblocking(cioc->writefd, !enabled, NULL)) ||
(cioc->readfd >= 0 && !g_unix_set_fd_nonblocking(cioc->readfd, !enabled, NULL))) {
error_setg_errno(errp, errno, "Failed to set FD nonblocking");
return -1;
}
@ -350,6 +317,8 @@ static int qio_channel_command_close(QIOChannel *ioc,
(unsigned long long)cioc->pid);
return -1;
}
#else
WaitForSingleObject(cioc->pid, INFINITE);
#endif
if (rv < 0) {

View File

@ -25,7 +25,6 @@
struct QIOChannelTest {
QIOChannel *src;
QIOChannel *dst;
bool blocking;
size_t len;
size_t niov;
char *input;
@ -42,8 +41,6 @@ static gpointer test_io_thread_writer(gpointer opaque)
{
QIOChannelTest *data = opaque;
qio_channel_set_blocking(data->src, data->blocking, NULL);
qio_channel_writev_all(data->src,
data->inputv,
data->niov,
@ -58,8 +55,6 @@ static gpointer test_io_thread_reader(gpointer opaque)
{
QIOChannelTest *data = opaque;
qio_channel_set_blocking(data->dst, data->blocking, NULL);
qio_channel_readv_all(data->dst,
data->outputv,
data->niov,
@ -113,7 +108,9 @@ void qio_channel_test_run_threads(QIOChannelTest *test,
test->src = src;
test->dst = dst;
test->blocking = blocking;
qio_channel_set_blocking(test->dst, blocking, NULL);
qio_channel_set_blocking(test->src, blocking, NULL);
reader = g_thread_new("reader",
test_io_thread_reader,

View File

@ -24,29 +24,30 @@
#include "qapi/error.h"
#include "qemu/module.h"
#ifndef WIN32
#define TEST_FIFO "test-io-channel-command.fifo"
#define SOCAT_SRC "PIPE:" TEST_FIFO ",wronly"
#define SOCAT_DST "PIPE:" TEST_FIFO ",rdonly"
static char *socat = NULL;
static void test_io_channel_command_fifo(bool async)
{
#define TEST_FIFO "tests/test-io-channel-command.fifo"
QIOChannel *src, *dst;
QIOChannelTest *test;
const char *srcfifo = "PIPE:" TEST_FIFO ",wronly";
const char *dstfifo = "PIPE:" TEST_FIFO ",rdonly";
const char *srcargv[] = {
"/bin/socat", "-", srcfifo, NULL,
socat, "-", SOCAT_SRC, NULL,
};
const char *dstargv[] = {
"/bin/socat", dstfifo, "-", NULL,
socat, SOCAT_DST, "-", NULL,
};
unlink(TEST_FIFO);
if (access("/bin/socat", X_OK) < 0) {
g_test_skip("socat is missing");
if (!socat) {
g_test_skip("socat is not found in PATH");
return;
}
if (mkfifo(TEST_FIFO, 0600) < 0) {
abort();
}
unlink(TEST_FIFO);
src = QIO_CHANNEL(qio_channel_command_new_spawn(srcargv,
O_WRONLY,
&error_abort));
@ -81,11 +82,12 @@ static void test_io_channel_command_echo(bool async)
QIOChannel *ioc;
QIOChannelTest *test;
const char *socatargv[] = {
"/bin/socat", "-", "-", NULL,
socat, "-", "-", NULL,
};
if (access("/bin/socat", X_OK) < 0) {
return; /* Pretend success if socat is not present */
if (!socat) {
g_test_skip("socat is not found in PATH");
return;
}
ioc = QIO_CHANNEL(qio_channel_command_new_spawn(socatargv,
@ -108,7 +110,6 @@ static void test_io_channel_command_echo_sync(void)
{
test_io_channel_command_echo(false);
}
#endif
int main(int argc, char **argv)
{
@ -116,7 +117,8 @@ int main(int argc, char **argv)
g_test_init(&argc, &argv, NULL);
#ifndef WIN32
socat = g_find_program_in_path("socat");
g_test_add_func("/io/channel/command/fifo/sync",
test_io_channel_command_fifo_sync);
g_test_add_func("/io/channel/command/fifo/async",
@ -125,7 +127,6 @@ int main(int argc, char **argv)
test_io_channel_command_echo_sync);
g_test_add_func("/io/channel/command/echo/async",
test_io_channel_command_echo_async);
#endif
return g_test_run();
}

View File

@ -111,12 +111,17 @@ do_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt, bool do_send)
/*XXX Note: windows has WSASend() and WSARecv() */
unsigned i = 0;
ssize_t ret = 0;
ssize_t off = 0;
while (i < iov_cnt) {
ssize_t r = do_send
? send(sockfd, iov[i].iov_base, iov[i].iov_len, 0)
: recv(sockfd, iov[i].iov_base, iov[i].iov_len, 0);
? send(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, 0)
: recv(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, 0);
if (r > 0) {
ret += r;
off += r;
if (off < iov[i].iov_len) {
continue;
}
} else if (!r) {
break;
} else if (errno == EINTR) {
@ -129,6 +134,7 @@ do_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt, bool do_send)
}
break;
}
off = 0;
i++;
}
return ret;

View File

@ -538,18 +538,22 @@ int socket_init(void)
#ifndef CONFIG_IOVEC
/* helper function for iov_send_recv() */
static ssize_t
readv_writev(int fd, const struct iovec *iov, int iov_cnt, bool do_write)
{
unsigned i = 0;
ssize_t ret = 0;
ssize_t off = 0;
while (i < iov_cnt) {
ssize_t r = do_write
? write(fd, iov[i].iov_base, iov[i].iov_len)
: read(fd, iov[i].iov_base, iov[i].iov_len);
? write(fd, iov[i].iov_base + off, iov[i].iov_len - off)
: read(fd, iov[i].iov_base + off, iov[i].iov_len - off);
if (r > 0) {
ret += r;
off += r;
if (off < iov[i].iov_len) {
continue;
}
} else if (!r) {
break;
} else if (errno == EINTR) {
@ -562,6 +566,7 @@ readv_writev(int fd, const struct iovec *iov, int iov_cnt, bool do_write)
}
break;
}
off = 0;
i++;
}
return ret;

View File

@ -19,12 +19,39 @@
static bool name_threads;
typedef HRESULT (WINAPI *pSetThreadDescription) (HANDLE hThread,
PCWSTR lpThreadDescription);
static pSetThreadDescription SetThreadDescriptionFunc;
static HMODULE kernel32_module;
static bool load_set_thread_description(void)
{
static gsize _init_once = 0;
if (g_once_init_enter(&_init_once)) {
kernel32_module = LoadLibrary("kernel32.dll");
if (kernel32_module) {
SetThreadDescriptionFunc =
(pSetThreadDescription)GetProcAddress(kernel32_module,
"SetThreadDescription");
if (!SetThreadDescriptionFunc) {
FreeLibrary(kernel32_module);
}
}
g_once_init_leave(&_init_once, 1);
}
return !!SetThreadDescriptionFunc;
}
void qemu_thread_naming(bool enable)
{
/* But note we don't actually name them on Windows yet */
name_threads = enable;
fprintf(stderr, "qemu: thread naming not supported on this host\n");
if (enable && !load_set_thread_description()) {
fprintf(stderr, "qemu: thread naming not supported on this host\n");
name_threads = false;
}
}
static void error_exit(int err, const char *msg)
@ -400,6 +427,25 @@ void *qemu_thread_join(QemuThread *thread)
return ret;
}
static bool set_thread_description(HANDLE h, const char *name)
{
HRESULT hr;
g_autofree wchar_t *namew = NULL;
if (!load_set_thread_description()) {
return false;
}
namew = g_utf8_to_utf16(name, -1, NULL, NULL, NULL);
if (!namew) {
return false;
}
hr = SetThreadDescriptionFunc(h, namew);
return SUCCEEDED(hr);
}
void qemu_thread_create(QemuThread *thread, const char *name,
void *(*start_routine)(void *),
void *arg, int mode)
@ -423,7 +469,11 @@ void qemu_thread_create(QemuThread *thread, const char *name,
if (!hThread) {
error_exit(GetLastError(), __func__);
}
if (name_threads && name && !set_thread_description(hThread, name)) {
fprintf(stderr, "qemu: failed to set thread description: %s\n", name);
}
CloseHandle(hThread);
thread->data = data;
}