/* * QEMU I/O channels external command driver * * Copyright (c) 2015 Red Hat, Inc. * * 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. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * */ #include "qemu/osdep.h" #include "io/channel-command.h" #include "io/channel-watch.h" #include "qapi/error.h" #include "qemu/sockets.h" #include "trace.h" QIOChannelCommand * qio_channel_command_new_pid(int writefd, int readfd, pid_t pid) { QIOChannelCommand *ioc; ioc = QIO_CHANNEL_COMMAND(object_new(TYPE_QIO_CHANNEL_COMMAND)); ioc->readfd = readfd; ioc->writefd = writefd; ioc->pid = pid; trace_qio_channel_command_new_pid(ioc, writefd, readfd, pid); 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; 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; } 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; } #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) { pid_t ret; int status; int step = 0; /* See if intermediate process has exited; if not, try a nice * SIGTERM followed by a more severe SIGKILL. */ rewait: trace_qio_channel_command_abort(ioc, ioc->pid); ret = waitpid(ioc->pid, &status, WNOHANG); trace_qio_channel_command_wait(ioc, ioc->pid, ret, status); if (ret == (pid_t)-1) { if (errno == EINTR) { goto rewait; } else { error_setg_errno(errp, errno, "Cannot wait on pid %llu", (unsigned long long)ioc->pid); return -1; } } else if (ret == 0) { if (step == 0) { kill(ioc->pid, SIGTERM); } else if (step == 1) { kill(ioc->pid, SIGKILL); } else { error_setg(errp, "Process %llu refused to die", (unsigned long long)ioc->pid); return -1; } step++; usleep(10 * 1000); goto rewait; } return 0; } #endif /* ! WIN32 */ static void qio_channel_command_init(Object *obj) { QIOChannelCommand *ioc = QIO_CHANNEL_COMMAND(obj); ioc->readfd = -1; ioc->writefd = -1; ioc->pid = -1; } static void qio_channel_command_finalize(Object *obj) { QIOChannelCommand *ioc = QIO_CHANNEL_COMMAND(obj); if (ioc->readfd != -1) { close(ioc->readfd); } if (ioc->writefd != -1 && ioc->writefd != ioc->readfd) { close(ioc->writefd); } ioc->writefd = ioc->readfd = -1; if (ioc->pid > 0) { #ifndef WIN32 qio_channel_command_abort(ioc, NULL); #endif } } static ssize_t qio_channel_command_readv(QIOChannel *ioc, const struct iovec *iov, size_t niov, int **fds, size_t *nfds, Error **errp) { QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); ssize_t ret; retry: ret = readv(cioc->readfd, iov, niov); if (ret < 0) { if (errno == EAGAIN) { return QIO_CHANNEL_ERR_BLOCK; } if (errno == EINTR) { goto retry; } error_setg_errno(errp, errno, "Unable to read from command"); return -1; } return ret; } static ssize_t qio_channel_command_writev(QIOChannel *ioc, const struct iovec *iov, size_t niov, int *fds, size_t nfds, Error **errp) { QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); ssize_t ret; retry: ret = writev(cioc->writefd, iov, niov); if (ret <= 0) { if (errno == EAGAIN) { return QIO_CHANNEL_ERR_BLOCK; } if (errno == EINTR) { goto retry; } error_setg_errno(errp, errno, "%s", "Unable to write to command"); return -1; } return ret; } static int qio_channel_command_set_blocking(QIOChannel *ioc, bool enabled, Error **errp) { 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); } return 0; } static int qio_channel_command_close(QIOChannel *ioc, Error **errp) { QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); int rv = 0; /* We close FDs before killing, because that * gives a better chance of clean shutdown */ if (cioc->readfd != -1 && close(cioc->readfd) < 0) { rv = -1; } if (cioc->writefd != -1 && cioc->writefd != cioc->readfd && close(cioc->writefd) < 0) { rv = -1; } cioc->writefd = cioc->readfd = -1; #ifndef WIN32 if (qio_channel_command_abort(cioc, errp) < 0) { return -1; } #endif if (rv < 0) { error_setg_errno(errp, errno, "%s", "Unable to close command"); } return rv; } static GSource *qio_channel_command_create_watch(QIOChannel *ioc, GIOCondition condition) { QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); return qio_channel_create_fd_pair_watch(ioc, cioc->readfd, cioc->writefd, condition); } static void qio_channel_command_class_init(ObjectClass *klass, void *class_data G_GNUC_UNUSED) { QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); ioc_klass->io_writev = qio_channel_command_writev; ioc_klass->io_readv = qio_channel_command_readv; ioc_klass->io_set_blocking = qio_channel_command_set_blocking; ioc_klass->io_close = qio_channel_command_close; ioc_klass->io_create_watch = qio_channel_command_create_watch; } static const TypeInfo qio_channel_command_info = { .parent = TYPE_QIO_CHANNEL, .name = TYPE_QIO_CHANNEL_COMMAND, .instance_size = sizeof(QIOChannelCommand), .instance_init = qio_channel_command_init, .instance_finalize = qio_channel_command_finalize, .class_init = qio_channel_command_class_init, }; static void qio_channel_command_register_types(void) { type_register_static(&qio_channel_command_info); } type_init(qio_channel_command_register_types);