io/command: implement support for win32
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>
This commit is contained in:
parent
a95570e3e4
commit
ec5b6c9c5d
@ -42,6 +42,9 @@ struct QIOChannelCommand {
|
|||||||
int writefd;
|
int writefd;
|
||||||
int readfd;
|
int readfd;
|
||||||
GPid pid;
|
GPid pid;
|
||||||
|
#ifdef WIN32
|
||||||
|
bool blocking;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
#include "qemu/sockets.h"
|
#include "qemu/sockets.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
#ifndef WIN32
|
|
||||||
/**
|
/**
|
||||||
* qio_channel_command_new_pid:
|
* qio_channel_command_new_pid:
|
||||||
* @writefd: the FD connected to the command's stdin
|
* @writefd: the FD connected to the command's stdin
|
||||||
@ -60,7 +59,13 @@ qio_channel_command_new_pid(int writefd,
|
|||||||
ioc->writefd = writefd;
|
ioc->writefd = writefd;
|
||||||
ioc->pid = pid;
|
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;
|
return ioc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,18 +94,6 @@ qio_channel_command_new_spawn(const char *const argv[],
|
|||||||
return qio_channel_command_new_pid(stdinfd, stdoutfd, pid);
|
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
|
#ifndef WIN32
|
||||||
static int qio_channel_command_abort(QIOChannelCommand *ioc,
|
static int qio_channel_command_abort(QIOChannelCommand *ioc,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
@ -143,6 +136,23 @@ static int qio_channel_command_abort(QIOChannelCommand *ioc,
|
|||||||
|
|
||||||
return 0;
|
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 */
|
#endif /* ! WIN32 */
|
||||||
|
|
||||||
|
|
||||||
@ -166,13 +176,27 @@ static void qio_channel_command_finalize(Object *obj)
|
|||||||
}
|
}
|
||||||
ioc->writefd = ioc->readfd = -1;
|
ioc->writefd = ioc->readfd = -1;
|
||||||
if (ioc->pid > 0) {
|
if (ioc->pid > 0) {
|
||||||
#ifndef WIN32
|
|
||||||
qio_channel_command_abort(ioc, NULL);
|
qio_channel_command_abort(ioc, NULL);
|
||||||
#endif
|
|
||||||
g_spawn_close_pid(ioc->pid);
|
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,
|
static ssize_t qio_channel_command_readv(QIOChannel *ioc,
|
||||||
const struct iovec *iov,
|
const struct iovec *iov,
|
||||||
@ -184,6 +208,12 @@ static ssize_t qio_channel_command_readv(QIOChannel *ioc,
|
|||||||
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
|
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
if (!cioc->blocking && !win32_fd_poll(cioc->readfd, G_IO_IN)) {
|
||||||
|
return QIO_CHANNEL_ERR_BLOCK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
ret = readv(cioc->readfd, iov, niov);
|
ret = readv(cioc->readfd, iov, niov);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@ -213,6 +243,12 @@ static ssize_t qio_channel_command_writev(QIOChannel *ioc,
|
|||||||
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
|
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
if (!cioc->blocking && !win32_fd_poll(cioc->writefd, G_IO_OUT)) {
|
||||||
|
return QIO_CHANNEL_ERR_BLOCK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
ret = writev(cioc->writefd, iov, niov);
|
ret = writev(cioc->writefd, iov, niov);
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
@ -233,14 +269,14 @@ static int qio_channel_command_set_blocking(QIOChannel *ioc,
|
|||||||
bool enabled,
|
bool enabled,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
#ifdef WIN32
|
|
||||||
/* command spawn is not supported on win32 */
|
|
||||||
g_assert_not_reached();
|
|
||||||
#else
|
|
||||||
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
|
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
|
||||||
|
|
||||||
if (!g_unix_set_fd_nonblocking(cioc->writefd, !enabled, NULL) ||
|
#ifdef WIN32
|
||||||
!g_unix_set_fd_nonblocking(cioc->readfd, !enabled, NULL)) {
|
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");
|
error_setg_errno(errp, errno, "Failed to set FD nonblocking");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -281,6 +317,8 @@ static int qio_channel_command_close(QIOChannel *ioc,
|
|||||||
(unsigned long long)cioc->pid);
|
(unsigned long long)cioc->pid);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
WaitForSingleObject(cioc->pid, INFINITE);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
|
Loading…
Reference in New Issue
Block a user