Skip to content

Commit

Permalink
io/command: implement support for win32
Browse files Browse the repository at this point in the history
The initial implementation was changing the pipe state created by GLib
to PIPE_NOWAIT, but it turns out it doesn't work (read/write returns an
error). Since reading may return less than the requested amount, it
seems to be non-blocking already. However, the IO operation may block
until the FD is ready, I can't find good sources of information, to be
safe we can just poll for readiness before.

Alternatively, we could setup the FDs ourself, and use UNIX sockets on
Windows, which can be used in blocking/non-blocking mode. I haven't
tried it, as I am not sure it is necessary.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Message-Id: <20221006113657.2656108-6-marcandre.lureau@redhat.com>
  • Loading branch information
elmarco committed Oct 12, 2022
1 parent a95570e commit ec5b6c9
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 21 deletions.
3 changes: 3 additions & 0 deletions include/io/channel-command.h
Expand Up @@ -42,6 +42,9 @@ struct QIOChannelCommand {
int writefd;
int readfd;
GPid pid;
#ifdef WIN32
bool blocking;
#endif
};


Expand Down
80 changes: 59 additions & 21 deletions io/channel-command.c
Expand Up @@ -26,7 +26,6 @@
#include "qemu/sockets.h"
#include "trace.h"

#ifndef WIN32
/**
* qio_channel_command_new_pid:
* @writefd: the FD connected to the command's stdin
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -89,18 +94,6 @@ qio_channel_command_new_spawn(const char *const argv[],
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)
Expand Down Expand Up @@ -143,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 */


Expand All @@ -166,13 +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,
Expand All @@ -184,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) {
Expand Down Expand Up @@ -213,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) {
Expand All @@ -233,14 +269,14 @@ static int qio_channel_command_set_blocking(QIOChannel *ioc,
bool enabled,
Error **errp)
{
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);

#ifdef WIN32
/* command spawn is not supported on win32 */
g_assert_not_reached();
cioc->blocking = enabled;
#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)) {
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;
}
Expand Down Expand Up @@ -281,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) {
Expand Down

0 comments on commit ec5b6c9

Please sign in to comment.