* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
+ * version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
#include "qemu/osdep.h"
#include "io/channel-command.h"
+#include "io/channel-util.h"
#include "io/channel-watch.h"
#include "qapi/error.h"
#include "qemu/module.h"
#include "qemu/sockets.h"
#include "trace.h"
-
-QIOChannelCommand *
+/**
+ * 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/HANDLE of the running child command
+ * @errp: pointer to a NULL-initialized error object
+ *
+ * Create a channel for performing I/O with the
+ * previously spawned command identified by @pid.
+ * The two file descriptors provide the connection
+ * to command's stdio streams, either one or which
+ * may be -1 to indicate that stream is not open.
+ *
+ * The channel will take ownership of the process
+ * @pid and will kill it when closing the channel.
+ * Similarly it will take responsibility for
+ * closing the file descriptors @writefd and @readfd.
+ *
+ * Returns: the command channel object, or NULL on error
+ */
+static QIOChannelCommand *
qio_channel_command_new_pid(int writefd,
int readfd,
- pid_t pid)
+ GPid pid)
{
QIOChannelCommand *ioc;
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;
}
-
-#ifndef WIN32
QIOChannelCommand *
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;
-
- if (flags == O_RDONLY) {
- stdinnull = true;
- }
- if (flags == O_WRONLY) {
- stdoutnull = true;
- }
-
- 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 && pipe(stdinfd) < 0) ||
- (!stdoutnull && pipe(stdoutfd) < 0)) {
- error_setg_errno(errp, errno,
- "Unable to open pipe");
- goto error;
+ gflags |= flags == O_WRONLY ? G_SPAWN_STDOUT_TO_DEV_NULL : 0;
+
+ 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;
}
- 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)
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 */
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)
}
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,
size_t niov,
int **fds,
size_t *nfds,
+ int flags,
Error **errp)
{
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) {
size_t niov,
int *fds,
size_t nfds,
+ int flags,
Error **errp)
{
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) {
{
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
- if (enabled) {
- qemu_set_block(cioc->writefd);
- qemu_set_block(cioc->readfd);
- } else {
- qemu_set_nonblock(cioc->writefd);
- qemu_set_nonblock(cioc->readfd);
- }
+#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;
+ }
+#endif
return 0;
}
(unsigned long long)cioc->pid);
return -1;
}
+#else
+ WaitForSingleObject(cioc->pid, INFINITE);
#endif
if (rv < 0) {
static void qio_channel_command_set_aio_fd_handler(QIOChannel *ioc,
- AioContext *ctx,
+ AioContext *read_ctx,
IOHandler *io_read,
+ AioContext *write_ctx,
IOHandler *io_write,
void *opaque)
{
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
- aio_set_fd_handler(ctx, cioc->readfd, false, io_read, NULL, NULL, opaque);
- aio_set_fd_handler(ctx, cioc->writefd, false, NULL, io_write, NULL, opaque);
+
+ qio_channel_util_set_aio_fd_handler(cioc->readfd, read_ctx, io_read,
+ cioc->writefd, write_ctx, io_write,
+ opaque);
}