io/command: use glib GSpawn, instead of open-coding fork/exec

Simplify qio_channel_command_new_spawn() with GSpawn API. This will
allow to build for WIN32 in the following patches.

As pointed out by Daniel Berrangé: there is a change in semantics here
too. The current code only touches stdin/stdout/stderr. Any other FDs
which do NOT have O_CLOEXEC set will be inherited. With the new code,
all FDs except stdin/out/err will be explicitly closed, because we don't
set the flag G_SPAWN_LEAVE_DESCRIPTORS_OPEN. The only place we use
QIOChannelCommand today is the migration exec: protocol, and that is
only declared to use stdin/stdout.

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20221006113657.2656108-5-marcandre.lureau@redhat.com>
This commit is contained in:
Marc-André Lureau 2022-10-06 15:36:55 +04:00
parent bb06b0143b
commit a95570e3e4
2 changed files with 18 additions and 87 deletions

View File

@ -41,7 +41,7 @@ struct QIOChannelCommand {
QIOChannel parent;
int writefd;
int readfd;
pid_t pid;
GPid pid;
};

View File

@ -31,7 +31,7 @@
* 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 +50,7 @@
static QIOChannelCommand *
qio_channel_command_new_pid(int writefd,
int readfd,
pid_t pid)
GPid pid)
{
QIOChannelCommand *ioc;
@ -69,94 +69,24 @@ 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 (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]);
}
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;
}
return qio_channel_command_new_pid(stdinfd, stdoutfd, pid);
}
#else /* WIN32 */
@ -221,7 +151,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)
@ -239,6 +169,7 @@ static void qio_channel_command_finalize(Object *obj)
#ifndef WIN32
qio_channel_command_abort(ioc, NULL);
#endif
g_spawn_close_pid(ioc->pid);
}
}