Import QEMU upstream snapshot d2e570c
Upstream: https://gitlab.com/qemu-project/qemu.git Upstream-Commit: d2e570cc0f97b936902a5b1b86b73c0f5998b475
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* QEMU I/O channels memory buffer 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.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
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "io/channel-buffer.h"
|
||||
#include "io/channel-watch.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "trace.h"
|
||||
|
||||
QIOChannelBuffer *
|
||||
qio_channel_buffer_new(size_t capacity)
|
||||
{
|
||||
QIOChannelBuffer *ioc;
|
||||
|
||||
ioc = QIO_CHANNEL_BUFFER(object_new(TYPE_QIO_CHANNEL_BUFFER));
|
||||
|
||||
if (capacity) {
|
||||
ioc->data = g_new0(uint8_t, capacity);
|
||||
ioc->capacity = capacity;
|
||||
}
|
||||
|
||||
return ioc;
|
||||
}
|
||||
|
||||
|
||||
static void qio_channel_buffer_finalize(Object *obj)
|
||||
{
|
||||
QIOChannelBuffer *ioc = QIO_CHANNEL_BUFFER(obj);
|
||||
g_free(ioc->data);
|
||||
ioc->capacity = ioc->usage = ioc->offset = 0;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t qio_channel_buffer_readv(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
int **fds,
|
||||
size_t *nfds,
|
||||
int flags,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc);
|
||||
ssize_t ret = 0;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < niov; i++) {
|
||||
size_t want = iov[i].iov_len;
|
||||
if (bioc->offset >= bioc->usage) {
|
||||
break;
|
||||
}
|
||||
if ((bioc->offset + want) > bioc->usage) {
|
||||
want = bioc->usage - bioc->offset;
|
||||
}
|
||||
memcpy(iov[i].iov_base, bioc->data + bioc->offset, want);
|
||||
ret += want;
|
||||
bioc->offset += want;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t qio_channel_buffer_writev(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
int *fds,
|
||||
size_t nfds,
|
||||
int flags,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc);
|
||||
ssize_t ret = 0;
|
||||
size_t i;
|
||||
size_t towrite = 0;
|
||||
|
||||
for (i = 0; i < niov; i++) {
|
||||
towrite += iov[i].iov_len;
|
||||
}
|
||||
|
||||
if ((bioc->offset + towrite) > bioc->capacity) {
|
||||
bioc->capacity = bioc->offset + towrite;
|
||||
bioc->data = g_realloc(bioc->data, bioc->capacity);
|
||||
}
|
||||
|
||||
if (bioc->offset > bioc->usage) {
|
||||
memset(bioc->data, 0, bioc->offset - bioc->usage);
|
||||
bioc->usage = bioc->offset;
|
||||
}
|
||||
|
||||
for (i = 0; i < niov; i++) {
|
||||
memcpy(bioc->data + bioc->usage,
|
||||
iov[i].iov_base,
|
||||
iov[i].iov_len);
|
||||
bioc->usage += iov[i].iov_len;
|
||||
bioc->offset += iov[i].iov_len;
|
||||
ret += iov[i].iov_len;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qio_channel_buffer_set_blocking(QIOChannel *ioc G_GNUC_UNUSED,
|
||||
bool enabled G_GNUC_UNUSED,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static off_t qio_channel_buffer_seek(QIOChannel *ioc,
|
||||
off_t offset,
|
||||
int whence,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc);
|
||||
|
||||
bioc->offset = offset;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
||||
static int qio_channel_buffer_close(QIOChannel *ioc,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc);
|
||||
|
||||
g_free(bioc->data);
|
||||
bioc->data = NULL;
|
||||
bioc->capacity = bioc->usage = bioc->offset = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
typedef struct QIOChannelBufferSource QIOChannelBufferSource;
|
||||
struct QIOChannelBufferSource {
|
||||
GSource parent;
|
||||
QIOChannelBuffer *bioc;
|
||||
GIOCondition condition;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
qio_channel_buffer_source_prepare(GSource *source,
|
||||
gint *timeout)
|
||||
{
|
||||
QIOChannelBufferSource *bsource = (QIOChannelBufferSource *)source;
|
||||
|
||||
*timeout = -1;
|
||||
|
||||
return (G_IO_IN | G_IO_OUT) & bsource->condition;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
qio_channel_buffer_source_check(GSource *source)
|
||||
{
|
||||
QIOChannelBufferSource *bsource = (QIOChannelBufferSource *)source;
|
||||
|
||||
return (G_IO_IN | G_IO_OUT) & bsource->condition;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
qio_channel_buffer_source_dispatch(GSource *source,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
QIOChannelFunc func = (QIOChannelFunc)callback;
|
||||
QIOChannelBufferSource *bsource = (QIOChannelBufferSource *)source;
|
||||
|
||||
return (*func)(QIO_CHANNEL(bsource->bioc),
|
||||
((G_IO_IN | G_IO_OUT) & bsource->condition),
|
||||
user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
qio_channel_buffer_source_finalize(GSource *source)
|
||||
{
|
||||
QIOChannelBufferSource *ssource = (QIOChannelBufferSource *)source;
|
||||
|
||||
object_unref(OBJECT(ssource->bioc));
|
||||
}
|
||||
|
||||
GSourceFuncs qio_channel_buffer_source_funcs = {
|
||||
qio_channel_buffer_source_prepare,
|
||||
qio_channel_buffer_source_check,
|
||||
qio_channel_buffer_source_dispatch,
|
||||
qio_channel_buffer_source_finalize
|
||||
};
|
||||
|
||||
static GSource *qio_channel_buffer_create_watch(QIOChannel *ioc,
|
||||
GIOCondition condition)
|
||||
{
|
||||
QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc);
|
||||
QIOChannelBufferSource *ssource;
|
||||
GSource *source;
|
||||
|
||||
source = g_source_new(&qio_channel_buffer_source_funcs,
|
||||
sizeof(QIOChannelBufferSource));
|
||||
ssource = (QIOChannelBufferSource *)source;
|
||||
|
||||
ssource->bioc = bioc;
|
||||
object_ref(OBJECT(bioc));
|
||||
|
||||
ssource->condition = condition;
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
|
||||
static void qio_channel_buffer_class_init(ObjectClass *klass,
|
||||
const void *class_data G_GNUC_UNUSED)
|
||||
{
|
||||
QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass);
|
||||
|
||||
ioc_klass->io_writev = qio_channel_buffer_writev;
|
||||
ioc_klass->io_readv = qio_channel_buffer_readv;
|
||||
ioc_klass->io_set_blocking = qio_channel_buffer_set_blocking;
|
||||
ioc_klass->io_seek = qio_channel_buffer_seek;
|
||||
ioc_klass->io_close = qio_channel_buffer_close;
|
||||
ioc_klass->io_create_watch = qio_channel_buffer_create_watch;
|
||||
}
|
||||
|
||||
static const TypeInfo qio_channel_buffer_info = {
|
||||
.parent = TYPE_QIO_CHANNEL,
|
||||
.name = TYPE_QIO_CHANNEL_BUFFER,
|
||||
.instance_size = sizeof(QIOChannelBuffer),
|
||||
.instance_finalize = qio_channel_buffer_finalize,
|
||||
.class_init = qio_channel_buffer_class_init,
|
||||
};
|
||||
|
||||
static void qio_channel_buffer_register_types(void)
|
||||
{
|
||||
type_register_static(&qio_channel_buffer_info);
|
||||
}
|
||||
|
||||
type_init(qio_channel_buffer_register_types);
|
||||
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
* 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.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
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#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"
|
||||
|
||||
/**
|
||||
* 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,
|
||||
GPid 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,
|
||||
#ifdef WIN32
|
||||
GetProcessId(pid)
|
||||
#else
|
||||
pid
|
||||
#endif
|
||||
);
|
||||
return ioc;
|
||||
}
|
||||
|
||||
QIOChannelCommand *
|
||||
qio_channel_command_new_spawn(const char *const argv[],
|
||||
int flags,
|
||||
Error **errp)
|
||||
{
|
||||
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 (!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);
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
#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 */
|
||||
|
||||
|
||||
static void qio_channel_command_init(Object *obj)
|
||||
{
|
||||
QIOChannelCommand *ioc = QIO_CHANNEL_COMMAND(obj);
|
||||
ioc->readfd = -1;
|
||||
ioc->writefd = -1;
|
||||
ioc->pid = 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
qio_channel_command_abort(ioc, NULL);
|
||||
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) {
|
||||
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,
|
||||
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) {
|
||||
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);
|
||||
|
||||
#ifdef WIN32
|
||||
cioc->blocking = enabled;
|
||||
#else
|
||||
|
||||
if (cioc->writefd >= 0 &&
|
||||
!qemu_set_blocking(cioc->writefd, enabled, errp)) {
|
||||
return -1;
|
||||
}
|
||||
if (cioc->readfd >= 0 &&
|
||||
!qemu_set_blocking(cioc->readfd, enabled, errp)) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int qio_channel_command_close(QIOChannel *ioc,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
|
||||
int rv = 0;
|
||||
#ifndef WIN32
|
||||
pid_t wp;
|
||||
#endif
|
||||
|
||||
/* 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
|
||||
do {
|
||||
wp = waitpid(cioc->pid, NULL, 0);
|
||||
} while (wp == (pid_t)-1 && errno == EINTR);
|
||||
if (wp == (pid_t)-1) {
|
||||
error_setg_errno(errp, errno, "Failed to wait for pid %llu",
|
||||
(unsigned long long)cioc->pid);
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
WaitForSingleObject(cioc->pid, INFINITE);
|
||||
#endif
|
||||
|
||||
if (rv < 0) {
|
||||
error_setg_errno(errp, errno, "%s",
|
||||
"Unable to close command");
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static void qio_channel_command_set_aio_fd_handler(QIOChannel *ioc,
|
||||
AioContext *read_ctx,
|
||||
IOHandler *io_read,
|
||||
AioContext *write_ctx,
|
||||
IOHandler *io_write,
|
||||
void *opaque)
|
||||
{
|
||||
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
|
||||
|
||||
qio_channel_util_set_aio_fd_handler(cioc->readfd, read_ctx, io_read,
|
||||
cioc->writefd, write_ctx, io_write,
|
||||
opaque);
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
const 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;
|
||||
ioc_klass->io_set_aio_fd_handler = qio_channel_command_set_aio_fd_handler;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
* QEMU I/O channels files 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.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
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "io/channel-file.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"
|
||||
|
||||
QIOChannelFile *
|
||||
qio_channel_file_new_fd(int fd)
|
||||
{
|
||||
QIOChannelFile *ioc;
|
||||
|
||||
ioc = QIO_CHANNEL_FILE(object_new(TYPE_QIO_CHANNEL_FILE));
|
||||
|
||||
ioc->fd = fd;
|
||||
|
||||
if (lseek(fd, 0, SEEK_CUR) != (off_t)-1) {
|
||||
qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_SEEKABLE);
|
||||
}
|
||||
|
||||
trace_qio_channel_file_new_fd(ioc, fd);
|
||||
|
||||
return ioc;
|
||||
}
|
||||
|
||||
QIOChannelFile *
|
||||
qio_channel_file_new_dupfd(int fd, Error **errp)
|
||||
{
|
||||
int newfd = dup(fd);
|
||||
|
||||
if (newfd < 0) {
|
||||
error_setg_errno(errp, errno, "Could not dup FD %d", fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return qio_channel_file_new_fd(newfd);
|
||||
}
|
||||
|
||||
QIOChannelFile *
|
||||
qio_channel_file_new_path(const char *path,
|
||||
int flags,
|
||||
mode_t mode,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelFile *ioc;
|
||||
|
||||
ioc = QIO_CHANNEL_FILE(object_new(TYPE_QIO_CHANNEL_FILE));
|
||||
|
||||
if (flags & O_CREAT) {
|
||||
ioc->fd = qemu_create(path, flags & ~O_CREAT, mode, errp);
|
||||
} else {
|
||||
ioc->fd = qemu_open(path, flags, errp);
|
||||
}
|
||||
if (ioc->fd < 0) {
|
||||
object_unref(OBJECT(ioc));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (lseek(ioc->fd, 0, SEEK_CUR) != (off_t)-1) {
|
||||
qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_SEEKABLE);
|
||||
}
|
||||
|
||||
trace_qio_channel_file_new_path(ioc, path, flags, mode, ioc->fd);
|
||||
|
||||
return ioc;
|
||||
}
|
||||
|
||||
|
||||
static void qio_channel_file_init(Object *obj)
|
||||
{
|
||||
QIOChannelFile *ioc = QIO_CHANNEL_FILE(obj);
|
||||
ioc->fd = -1;
|
||||
}
|
||||
|
||||
static void qio_channel_file_finalize(Object *obj)
|
||||
{
|
||||
QIOChannelFile *ioc = QIO_CHANNEL_FILE(obj);
|
||||
if (ioc->fd != -1) {
|
||||
qemu_close(ioc->fd);
|
||||
ioc->fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ssize_t qio_channel_file_readv(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
int **fds,
|
||||
size_t *nfds,
|
||||
int flags,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
|
||||
ssize_t ret;
|
||||
|
||||
retry:
|
||||
ret = readv(fioc->fd, 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 file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t qio_channel_file_writev(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
int *fds,
|
||||
size_t nfds,
|
||||
int flags,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
|
||||
ssize_t ret;
|
||||
|
||||
retry:
|
||||
ret = writev(fioc->fd, 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 write to file");
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PREADV
|
||||
static ssize_t qio_channel_file_preadv(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
off_t offset,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
|
||||
ssize_t ret;
|
||||
|
||||
retry:
|
||||
ret = preadv(fioc->fd, iov, niov, offset);
|
||||
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 file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t qio_channel_file_pwritev(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
off_t offset,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
|
||||
ssize_t ret;
|
||||
|
||||
retry:
|
||||
ret = pwritev(fioc->fd, iov, niov, offset);
|
||||
if (ret <= 0) {
|
||||
if (errno == EAGAIN) {
|
||||
return QIO_CHANNEL_ERR_BLOCK;
|
||||
}
|
||||
if (errno == EINTR) {
|
||||
goto retry;
|
||||
}
|
||||
error_setg_errno(errp, errno, "Unable to write to file");
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PREADV */
|
||||
|
||||
static int qio_channel_file_set_blocking(QIOChannel *ioc,
|
||||
bool enabled,
|
||||
Error **errp)
|
||||
{
|
||||
#ifdef WIN32
|
||||
/* not implemented */
|
||||
error_setg_errno(errp, errno, "Failed to set FD nonblocking");
|
||||
return -1;
|
||||
#else
|
||||
QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
|
||||
|
||||
if (!qemu_set_blocking(fioc->fd, enabled, errp)) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static off_t qio_channel_file_seek(QIOChannel *ioc,
|
||||
off_t offset,
|
||||
int whence,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
|
||||
off_t ret;
|
||||
|
||||
ret = lseek(fioc->fd, offset, whence);
|
||||
if (ret == (off_t)-1) {
|
||||
error_setg_errno(errp, errno,
|
||||
"Unable to seek to offset %lld whence %d in file",
|
||||
(long long int)offset, whence);
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int qio_channel_file_close(QIOChannel *ioc,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
|
||||
|
||||
if (qemu_close(fioc->fd) < 0) {
|
||||
error_setg_errno(errp, errno,
|
||||
"Unable to close file");
|
||||
return -1;
|
||||
}
|
||||
fioc->fd = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void qio_channel_file_set_aio_fd_handler(QIOChannel *ioc,
|
||||
AioContext *read_ctx,
|
||||
IOHandler *io_read,
|
||||
AioContext *write_ctx,
|
||||
IOHandler *io_write,
|
||||
void *opaque)
|
||||
{
|
||||
QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
|
||||
|
||||
qio_channel_util_set_aio_fd_handler(fioc->fd, read_ctx, io_read,
|
||||
fioc->fd, write_ctx, io_write,
|
||||
opaque);
|
||||
}
|
||||
|
||||
static GSource *qio_channel_file_create_watch(QIOChannel *ioc,
|
||||
GIOCondition condition)
|
||||
{
|
||||
QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
|
||||
return qio_channel_create_fd_watch(ioc,
|
||||
fioc->fd,
|
||||
condition);
|
||||
}
|
||||
|
||||
static void qio_channel_file_class_init(ObjectClass *klass,
|
||||
const void *class_data G_GNUC_UNUSED)
|
||||
{
|
||||
QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass);
|
||||
|
||||
ioc_klass->io_writev = qio_channel_file_writev;
|
||||
ioc_klass->io_readv = qio_channel_file_readv;
|
||||
ioc_klass->io_set_blocking = qio_channel_file_set_blocking;
|
||||
#ifdef CONFIG_PREADV
|
||||
ioc_klass->io_pwritev = qio_channel_file_pwritev;
|
||||
ioc_klass->io_preadv = qio_channel_file_preadv;
|
||||
#endif
|
||||
ioc_klass->io_seek = qio_channel_file_seek;
|
||||
ioc_klass->io_close = qio_channel_file_close;
|
||||
ioc_klass->io_create_watch = qio_channel_file_create_watch;
|
||||
ioc_klass->io_set_aio_fd_handler = qio_channel_file_set_aio_fd_handler;
|
||||
}
|
||||
|
||||
static const TypeInfo qio_channel_file_info = {
|
||||
.parent = TYPE_QIO_CHANNEL,
|
||||
.name = TYPE_QIO_CHANNEL_FILE,
|
||||
.instance_size = sizeof(QIOChannelFile),
|
||||
.instance_init = qio_channel_file_init,
|
||||
.instance_finalize = qio_channel_file_finalize,
|
||||
.class_init = qio_channel_file_class_init,
|
||||
};
|
||||
|
||||
static void qio_channel_file_register_types(void)
|
||||
{
|
||||
type_register_static(&qio_channel_file_info);
|
||||
}
|
||||
|
||||
type_init(qio_channel_file_register_types);
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* QEMU I/O channels null driver
|
||||
*
|
||||
* Copyright (c) 2022 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.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
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "io/channel-null.h"
|
||||
#include "io/channel-watch.h"
|
||||
#include "qapi/error.h"
|
||||
#include "trace.h"
|
||||
#include "qemu/iov.h"
|
||||
|
||||
typedef struct QIOChannelNullSource QIOChannelNullSource;
|
||||
struct QIOChannelNullSource {
|
||||
GSource parent;
|
||||
QIOChannel *ioc;
|
||||
GIOCondition condition;
|
||||
};
|
||||
|
||||
|
||||
QIOChannelNull *
|
||||
qio_channel_null_new(void)
|
||||
{
|
||||
QIOChannelNull *ioc;
|
||||
|
||||
ioc = QIO_CHANNEL_NULL(object_new(TYPE_QIO_CHANNEL_NULL));
|
||||
|
||||
trace_qio_channel_null_new(ioc);
|
||||
|
||||
return ioc;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qio_channel_null_init(Object *obj)
|
||||
{
|
||||
QIOChannelNull *ioc = QIO_CHANNEL_NULL(obj);
|
||||
ioc->closed = false;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t
|
||||
qio_channel_null_readv(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
int **fds G_GNUC_UNUSED,
|
||||
size_t *nfds G_GNUC_UNUSED,
|
||||
int flags,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelNull *nioc = QIO_CHANNEL_NULL(ioc);
|
||||
|
||||
if (nioc->closed) {
|
||||
error_setg_errno(errp, EINVAL,
|
||||
"Channel is closed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t
|
||||
qio_channel_null_writev(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
int *fds G_GNUC_UNUSED,
|
||||
size_t nfds G_GNUC_UNUSED,
|
||||
int flags G_GNUC_UNUSED,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelNull *nioc = QIO_CHANNEL_NULL(ioc);
|
||||
|
||||
if (nioc->closed) {
|
||||
error_setg_errno(errp, EINVAL,
|
||||
"Channel is closed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return iov_size(iov, niov);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qio_channel_null_set_blocking(QIOChannel *ioc G_GNUC_UNUSED,
|
||||
bool enabled G_GNUC_UNUSED,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static off_t
|
||||
qio_channel_null_seek(QIOChannel *ioc G_GNUC_UNUSED,
|
||||
off_t offset G_GNUC_UNUSED,
|
||||
int whence G_GNUC_UNUSED,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qio_channel_null_close(QIOChannel *ioc,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QIOChannelNull *nioc = QIO_CHANNEL_NULL(ioc);
|
||||
|
||||
nioc->closed = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qio_channel_null_set_aio_fd_handler(QIOChannel *ioc G_GNUC_UNUSED,
|
||||
AioContext *read_ctx G_GNUC_UNUSED,
|
||||
IOHandler *io_read G_GNUC_UNUSED,
|
||||
AioContext *write_ctx G_GNUC_UNUSED,
|
||||
IOHandler *io_write G_GNUC_UNUSED,
|
||||
void *opaque G_GNUC_UNUSED)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
qio_channel_null_source_prepare(GSource *source G_GNUC_UNUSED,
|
||||
gint *timeout)
|
||||
{
|
||||
*timeout = -1;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
qio_channel_null_source_check(GSource *source G_GNUC_UNUSED)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
qio_channel_null_source_dispatch(GSource *source,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
QIOChannelFunc func = (QIOChannelFunc)callback;
|
||||
QIOChannelNullSource *ssource = (QIOChannelNullSource *)source;
|
||||
|
||||
return (*func)(ssource->ioc,
|
||||
ssource->condition,
|
||||
user_data);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qio_channel_null_source_finalize(GSource *source)
|
||||
{
|
||||
QIOChannelNullSource *ssource = (QIOChannelNullSource *)source;
|
||||
|
||||
object_unref(OBJECT(ssource->ioc));
|
||||
}
|
||||
|
||||
|
||||
GSourceFuncs qio_channel_null_source_funcs = {
|
||||
qio_channel_null_source_prepare,
|
||||
qio_channel_null_source_check,
|
||||
qio_channel_null_source_dispatch,
|
||||
qio_channel_null_source_finalize
|
||||
};
|
||||
|
||||
|
||||
static GSource *
|
||||
qio_channel_null_create_watch(QIOChannel *ioc,
|
||||
GIOCondition condition)
|
||||
{
|
||||
GSource *source;
|
||||
QIOChannelNullSource *ssource;
|
||||
|
||||
source = g_source_new(&qio_channel_null_source_funcs,
|
||||
sizeof(QIOChannelNullSource));
|
||||
ssource = (QIOChannelNullSource *)source;
|
||||
|
||||
ssource->ioc = ioc;
|
||||
object_ref(OBJECT(ioc));
|
||||
|
||||
ssource->condition = condition;
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qio_channel_null_class_init(ObjectClass *klass,
|
||||
const void *class_data G_GNUC_UNUSED)
|
||||
{
|
||||
QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass);
|
||||
|
||||
ioc_klass->io_writev = qio_channel_null_writev;
|
||||
ioc_klass->io_readv = qio_channel_null_readv;
|
||||
ioc_klass->io_set_blocking = qio_channel_null_set_blocking;
|
||||
ioc_klass->io_seek = qio_channel_null_seek;
|
||||
ioc_klass->io_close = qio_channel_null_close;
|
||||
ioc_klass->io_create_watch = qio_channel_null_create_watch;
|
||||
ioc_klass->io_set_aio_fd_handler = qio_channel_null_set_aio_fd_handler;
|
||||
}
|
||||
|
||||
|
||||
static const TypeInfo qio_channel_null_info = {
|
||||
.parent = TYPE_QIO_CHANNEL,
|
||||
.name = TYPE_QIO_CHANNEL_NULL,
|
||||
.instance_size = sizeof(QIOChannelNull),
|
||||
.instance_init = qio_channel_null_init,
|
||||
.class_init = qio_channel_null_class_init,
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
qio_channel_null_register_types(void)
|
||||
{
|
||||
type_register_static(&qio_channel_null_info);
|
||||
}
|
||||
|
||||
type_init(qio_channel_null_register_types);
|
||||
+1099
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,650 @@
|
||||
/*
|
||||
* QEMU I/O channels TLS 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.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
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/module.h"
|
||||
#include "io/channel-tls.h"
|
||||
#include "trace.h"
|
||||
#include "qemu/atomic.h"
|
||||
|
||||
|
||||
static ssize_t qio_channel_tls_write_handler(const void *buf,
|
||||
size_t len,
|
||||
void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque);
|
||||
ssize_t ret;
|
||||
|
||||
ret = qio_channel_write(tioc->master, buf, len, errp);
|
||||
if (ret == QIO_CHANNEL_ERR_BLOCK) {
|
||||
return QCRYPTO_TLS_SESSION_ERR_BLOCK;
|
||||
} else if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t qio_channel_tls_read_handler(void *buf,
|
||||
size_t len,
|
||||
void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque);
|
||||
ssize_t ret;
|
||||
|
||||
ret = qio_channel_read(tioc->master, buf, len, errp);
|
||||
if (ret == QIO_CHANNEL_ERR_BLOCK) {
|
||||
return QCRYPTO_TLS_SESSION_ERR_BLOCK;
|
||||
} else if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
QIOChannelTLS *
|
||||
qio_channel_tls_new_server(QIOChannel *master,
|
||||
QCryptoTLSCreds *creds,
|
||||
const char *aclname,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelTLS *tioc;
|
||||
QIOChannel *ioc;
|
||||
|
||||
tioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS));
|
||||
ioc = QIO_CHANNEL(tioc);
|
||||
|
||||
tioc->master = master;
|
||||
ioc->follow_coroutine_ctx = master->follow_coroutine_ctx;
|
||||
if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) {
|
||||
qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN);
|
||||
}
|
||||
object_ref(OBJECT(master));
|
||||
|
||||
tioc->session = qcrypto_tls_session_new(
|
||||
creds,
|
||||
NULL,
|
||||
aclname,
|
||||
QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
|
||||
errp);
|
||||
if (!tioc->session) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
qcrypto_tls_session_set_callbacks(
|
||||
tioc->session,
|
||||
qio_channel_tls_write_handler,
|
||||
qio_channel_tls_read_handler,
|
||||
tioc);
|
||||
|
||||
trace_qio_channel_tls_new_server(tioc, master, creds, aclname);
|
||||
return tioc;
|
||||
|
||||
error:
|
||||
object_unref(OBJECT(tioc));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QIOChannelTLS *
|
||||
qio_channel_tls_new_client(QIOChannel *master,
|
||||
QCryptoTLSCreds *creds,
|
||||
const char *hostname,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelTLS *tioc;
|
||||
QIOChannel *ioc;
|
||||
|
||||
tioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS));
|
||||
ioc = QIO_CHANNEL(tioc);
|
||||
|
||||
tioc->master = master;
|
||||
ioc->follow_coroutine_ctx = master->follow_coroutine_ctx;
|
||||
if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) {
|
||||
qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN);
|
||||
}
|
||||
object_ref(OBJECT(master));
|
||||
|
||||
tioc->session = qcrypto_tls_session_new(
|
||||
creds,
|
||||
hostname,
|
||||
NULL,
|
||||
QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT,
|
||||
errp);
|
||||
if (!tioc->session) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
qcrypto_tls_session_set_callbacks(
|
||||
tioc->session,
|
||||
qio_channel_tls_write_handler,
|
||||
qio_channel_tls_read_handler,
|
||||
tioc);
|
||||
|
||||
trace_qio_channel_tls_new_client(tioc, master, creds, hostname);
|
||||
return tioc;
|
||||
|
||||
error:
|
||||
object_unref(OBJECT(tioc));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct QIOChannelTLSData {
|
||||
QIOTask *task;
|
||||
GMainContext *context;
|
||||
};
|
||||
typedef struct QIOChannelTLSData QIOChannelTLSData;
|
||||
|
||||
static void qio_channel_tls_io_data_free(gpointer user_data)
|
||||
{
|
||||
QIOChannelTLSData *data = user_data;
|
||||
/*
|
||||
* Usually 'task' will be NULL since the GSource
|
||||
* callback will either complete the task or pass
|
||||
* it on to a new GSource. We'll see a non-NULL
|
||||
* task here only if the GSource was released before
|
||||
* its callback triggers
|
||||
*/
|
||||
if (data->task) {
|
||||
qio_task_free(data->task);
|
||||
}
|
||||
if (data->context) {
|
||||
g_main_context_unref(data->context);
|
||||
}
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc,
|
||||
GIOCondition condition,
|
||||
gpointer user_data);
|
||||
|
||||
static gboolean qio_channel_tls_handshake_task(QIOChannelTLS *ioc,
|
||||
QIOTask *task,
|
||||
GMainContext *context)
|
||||
{
|
||||
Error *err = NULL;
|
||||
int status;
|
||||
|
||||
status = qcrypto_tls_session_handshake(ioc->session, &err);
|
||||
|
||||
if (status < 0) {
|
||||
trace_qio_channel_tls_handshake_fail(ioc);
|
||||
qio_task_set_error(task, err);
|
||||
qio_task_complete(task);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (status == QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
|
||||
trace_qio_channel_tls_handshake_complete(ioc);
|
||||
if (qcrypto_tls_session_check_credentials(ioc->session,
|
||||
&err) < 0) {
|
||||
trace_qio_channel_tls_credentials_deny(ioc);
|
||||
qio_task_set_error(task, err);
|
||||
} else {
|
||||
trace_qio_channel_tls_credentials_allow(ioc);
|
||||
}
|
||||
qio_task_complete(task);
|
||||
return TRUE;
|
||||
} else {
|
||||
GIOCondition condition;
|
||||
QIOChannelTLSData *data = g_new0(typeof(*data), 1);
|
||||
|
||||
data->task = task;
|
||||
data->context = context;
|
||||
|
||||
if (context) {
|
||||
g_main_context_ref(context);
|
||||
}
|
||||
|
||||
if (status == QCRYPTO_TLS_HANDSHAKE_SENDING) {
|
||||
condition = G_IO_OUT;
|
||||
} else {
|
||||
condition = G_IO_IN;
|
||||
}
|
||||
|
||||
trace_qio_channel_tls_handshake_pending(ioc, status);
|
||||
ioc->hs_ioc_tag =
|
||||
qio_channel_add_watch_full(ioc->master,
|
||||
condition,
|
||||
qio_channel_tls_handshake_io,
|
||||
data,
|
||||
qio_channel_tls_io_data_free,
|
||||
context);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc,
|
||||
GIOCondition condition,
|
||||
gpointer user_data)
|
||||
{
|
||||
QIOChannelTLSData *data = user_data;
|
||||
QIOTask *task = data->task;
|
||||
GMainContext *context = data->context;
|
||||
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(
|
||||
qio_task_get_source(task));
|
||||
|
||||
tioc->hs_ioc_tag = 0;
|
||||
if (!qio_channel_tls_handshake_task(tioc, task, context)) {
|
||||
/* task is kept by new GSource so must not be released yet */
|
||||
data->task = NULL;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void qio_channel_tls_handshake(QIOChannelTLS *ioc,
|
||||
QIOTaskFunc func,
|
||||
gpointer opaque,
|
||||
GDestroyNotify destroy,
|
||||
GMainContext *context)
|
||||
{
|
||||
QIOTask *task;
|
||||
|
||||
if (qio_channel_has_feature(QIO_CHANNEL(ioc),
|
||||
QIO_CHANNEL_FEATURE_CONCURRENT_IO)) {
|
||||
qcrypto_tls_session_require_thread_safety(ioc->session);
|
||||
}
|
||||
|
||||
task = qio_task_new(OBJECT(ioc),
|
||||
func, opaque, destroy);
|
||||
|
||||
trace_qio_channel_tls_handshake_start(ioc);
|
||||
if (qio_channel_tls_handshake_task(ioc, task, context)) {
|
||||
qio_task_free(task);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean qio_channel_tls_bye_io(QIOChannel *ioc, GIOCondition condition,
|
||||
gpointer user_data);
|
||||
|
||||
static gboolean qio_channel_tls_bye_task(QIOChannelTLS *ioc, QIOTask *task,
|
||||
GMainContext *context)
|
||||
{
|
||||
GIOCondition condition;
|
||||
QIOChannelTLSData *data;
|
||||
int status;
|
||||
Error *err = NULL;
|
||||
|
||||
status = qcrypto_tls_session_bye(ioc->session, &err);
|
||||
|
||||
if (status < 0) {
|
||||
trace_qio_channel_tls_bye_fail(ioc);
|
||||
qio_task_set_error(task, err);
|
||||
qio_task_complete(task);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (status == QCRYPTO_TLS_BYE_COMPLETE) {
|
||||
qio_task_complete(task);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
data = g_new0(typeof(*data), 1);
|
||||
data->task = task;
|
||||
data->context = context;
|
||||
|
||||
if (context) {
|
||||
g_main_context_ref(context);
|
||||
}
|
||||
|
||||
if (status == QCRYPTO_TLS_BYE_SENDING) {
|
||||
condition = G_IO_OUT;
|
||||
} else {
|
||||
condition = G_IO_IN;
|
||||
}
|
||||
|
||||
trace_qio_channel_tls_bye_pending(ioc, status);
|
||||
ioc->bye_ioc_tag = qio_channel_add_watch_full(ioc->master, condition,
|
||||
qio_channel_tls_bye_io,
|
||||
data,
|
||||
qio_channel_tls_io_data_free,
|
||||
context);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
static gboolean qio_channel_tls_bye_io(QIOChannel *ioc, GIOCondition condition,
|
||||
gpointer user_data)
|
||||
{
|
||||
QIOChannelTLSData *data = user_data;
|
||||
QIOTask *task = data->task;
|
||||
GMainContext *context = data->context;
|
||||
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(qio_task_get_source(task));
|
||||
|
||||
tioc->bye_ioc_tag = 0;
|
||||
if (!qio_channel_tls_bye_task(tioc, task, context)) {
|
||||
/* task is kept by new GSource so must not be released yet */
|
||||
data->task = NULL;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void propagate_error(QIOTask *task, gpointer opaque)
|
||||
{
|
||||
qio_task_propagate_error(task, opaque);
|
||||
}
|
||||
|
||||
void qio_channel_tls_bye(QIOChannelTLS *ioc, Error **errp)
|
||||
{
|
||||
QIOTask *task;
|
||||
|
||||
task = qio_task_new(OBJECT(ioc), propagate_error, errp, NULL);
|
||||
|
||||
trace_qio_channel_tls_bye_start(ioc);
|
||||
if (qio_channel_tls_bye_task(ioc, task, NULL)) {
|
||||
qio_task_free(task);
|
||||
}
|
||||
}
|
||||
|
||||
static void qio_channel_tls_init(Object *obj G_GNUC_UNUSED)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static void qio_channel_tls_finalize(Object *obj)
|
||||
{
|
||||
QIOChannelTLS *ioc = QIO_CHANNEL_TLS(obj);
|
||||
|
||||
if (ioc->hs_ioc_tag) {
|
||||
trace_qio_channel_tls_handshake_cancel(ioc);
|
||||
g_clear_handle_id(&ioc->hs_ioc_tag, g_source_remove);
|
||||
}
|
||||
|
||||
if (ioc->bye_ioc_tag) {
|
||||
trace_qio_channel_tls_bye_cancel(ioc);
|
||||
g_clear_handle_id(&ioc->bye_ioc_tag, g_source_remove);
|
||||
}
|
||||
|
||||
object_unref(OBJECT(ioc->master));
|
||||
qcrypto_tls_session_free(ioc->session);
|
||||
}
|
||||
|
||||
static bool
|
||||
qio_channel_tls_allow_premature_termination(QIOChannelTLS *tioc, int flags)
|
||||
{
|
||||
if (flags & QIO_CHANNEL_READ_FLAG_RELAXED_EOF) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (qatomic_read(&tioc->shutdown) & QIO_CHANNEL_SHUTDOWN_READ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static ssize_t qio_channel_tls_readv(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
int **fds,
|
||||
size_t *nfds,
|
||||
int flags,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
|
||||
size_t i;
|
||||
ssize_t got = 0;
|
||||
|
||||
for (i = 0 ; i < niov ; i++) {
|
||||
ssize_t ret = qcrypto_tls_session_read(
|
||||
tioc->session,
|
||||
iov[i].iov_base,
|
||||
iov[i].iov_len,
|
||||
errp);
|
||||
if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) {
|
||||
if (got) {
|
||||
return got;
|
||||
} else {
|
||||
return QIO_CHANNEL_ERR_BLOCK;
|
||||
}
|
||||
} else if (ret < 0) {
|
||||
if (ret == QCRYPTO_TLS_SESSION_PREMATURE_TERMINATION &&
|
||||
qio_channel_tls_allow_premature_termination(tioc, flags)) {
|
||||
error_free(*errp);
|
||||
*errp = NULL;
|
||||
return got;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
got += ret;
|
||||
if (ret < iov[i].iov_len) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return got;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t qio_channel_tls_writev(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
int *fds,
|
||||
size_t nfds,
|
||||
int flags,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
|
||||
size_t i;
|
||||
ssize_t done = 0;
|
||||
|
||||
for (i = 0 ; i < niov ; i++) {
|
||||
ssize_t ret = qcrypto_tls_session_write(tioc->session,
|
||||
iov[i].iov_base,
|
||||
iov[i].iov_len,
|
||||
errp);
|
||||
if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) {
|
||||
if (done) {
|
||||
return done;
|
||||
} else {
|
||||
return QIO_CHANNEL_ERR_BLOCK;
|
||||
}
|
||||
} else if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
done += ret;
|
||||
if (ret < iov[i].iov_len) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
static int qio_channel_tls_set_blocking(QIOChannel *ioc,
|
||||
bool enabled,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
|
||||
|
||||
return qio_channel_set_blocking(tioc->master, enabled, errp) ? 0 : -1;
|
||||
}
|
||||
|
||||
static void qio_channel_tls_set_delay(QIOChannel *ioc,
|
||||
bool enabled)
|
||||
{
|
||||
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
|
||||
|
||||
qio_channel_set_delay(tioc->master, enabled);
|
||||
}
|
||||
|
||||
static void qio_channel_tls_set_cork(QIOChannel *ioc,
|
||||
bool enabled)
|
||||
{
|
||||
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
|
||||
|
||||
qio_channel_set_cork(tioc->master, enabled);
|
||||
}
|
||||
|
||||
static int qio_channel_tls_shutdown(QIOChannel *ioc,
|
||||
QIOChannelShutdown how,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
|
||||
|
||||
qatomic_or(&tioc->shutdown, how);
|
||||
|
||||
return qio_channel_shutdown(tioc->master, how, errp);
|
||||
}
|
||||
|
||||
static int qio_channel_tls_close(QIOChannel *ioc,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
|
||||
|
||||
if (tioc->hs_ioc_tag) {
|
||||
trace_qio_channel_tls_handshake_cancel(ioc);
|
||||
g_clear_handle_id(&tioc->hs_ioc_tag, g_source_remove);
|
||||
}
|
||||
|
||||
if (tioc->bye_ioc_tag) {
|
||||
trace_qio_channel_tls_bye_cancel(ioc);
|
||||
g_clear_handle_id(&tioc->bye_ioc_tag, g_source_remove);
|
||||
}
|
||||
|
||||
return qio_channel_close(tioc->master, errp);
|
||||
}
|
||||
|
||||
static void qio_channel_tls_set_aio_fd_handler(QIOChannel *ioc,
|
||||
AioContext *read_ctx,
|
||||
IOHandler *io_read,
|
||||
AioContext *write_ctx,
|
||||
IOHandler *io_write,
|
||||
void *opaque)
|
||||
{
|
||||
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
|
||||
|
||||
qio_channel_set_aio_fd_handler(tioc->master, read_ctx, io_read,
|
||||
write_ctx, io_write, opaque);
|
||||
}
|
||||
|
||||
typedef struct QIOChannelTLSSource QIOChannelTLSSource;
|
||||
struct QIOChannelTLSSource {
|
||||
GSource parent;
|
||||
QIOChannelTLS *tioc;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
qio_channel_tls_source_check(GSource *source)
|
||||
{
|
||||
QIOChannelTLSSource *tsource = (QIOChannelTLSSource *)source;
|
||||
|
||||
return qcrypto_tls_session_check_pending(tsource->tioc->session) > 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
qio_channel_tls_source_prepare(GSource *source, gint *timeout)
|
||||
{
|
||||
*timeout = -1;
|
||||
return qio_channel_tls_source_check(source);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
qio_channel_tls_source_dispatch(GSource *source, GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
qio_channel_tls_source_finalize(GSource *source)
|
||||
{
|
||||
QIOChannelTLSSource *tsource = (QIOChannelTLSSource *)source;
|
||||
|
||||
object_unref(OBJECT(tsource->tioc));
|
||||
}
|
||||
|
||||
static GSourceFuncs qio_channel_tls_source_funcs = {
|
||||
qio_channel_tls_source_prepare,
|
||||
qio_channel_tls_source_check,
|
||||
qio_channel_tls_source_dispatch,
|
||||
qio_channel_tls_source_finalize
|
||||
};
|
||||
|
||||
static void
|
||||
qio_channel_tls_read_watch(QIOChannelTLS *tioc, GSource *source)
|
||||
{
|
||||
GSource *child;
|
||||
QIOChannelTLSSource *tlssource;
|
||||
|
||||
child = g_source_new(&qio_channel_tls_source_funcs,
|
||||
sizeof(QIOChannelTLSSource));
|
||||
tlssource = (QIOChannelTLSSource *)child;
|
||||
|
||||
tlssource->tioc = tioc;
|
||||
object_ref(OBJECT(tioc));
|
||||
|
||||
g_source_add_child_source(source, child);
|
||||
g_source_unref(child);
|
||||
}
|
||||
|
||||
static GSource *qio_channel_tls_create_watch(QIOChannel *ioc,
|
||||
GIOCondition condition)
|
||||
{
|
||||
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
|
||||
GSource *source = qio_channel_create_watch(tioc->master, condition);
|
||||
|
||||
if (condition & G_IO_IN) {
|
||||
qio_channel_tls_read_watch(tioc, source);
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
QCryptoTLSSession *
|
||||
qio_channel_tls_get_session(QIOChannelTLS *ioc)
|
||||
{
|
||||
return ioc->session;
|
||||
}
|
||||
|
||||
static void qio_channel_tls_class_init(ObjectClass *klass,
|
||||
const void *class_data G_GNUC_UNUSED)
|
||||
{
|
||||
QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass);
|
||||
|
||||
ioc_klass->io_writev = qio_channel_tls_writev;
|
||||
ioc_klass->io_readv = qio_channel_tls_readv;
|
||||
ioc_klass->io_set_blocking = qio_channel_tls_set_blocking;
|
||||
ioc_klass->io_set_delay = qio_channel_tls_set_delay;
|
||||
ioc_klass->io_set_cork = qio_channel_tls_set_cork;
|
||||
ioc_klass->io_close = qio_channel_tls_close;
|
||||
ioc_klass->io_shutdown = qio_channel_tls_shutdown;
|
||||
ioc_klass->io_create_watch = qio_channel_tls_create_watch;
|
||||
ioc_klass->io_set_aio_fd_handler = qio_channel_tls_set_aio_fd_handler;
|
||||
}
|
||||
|
||||
static const TypeInfo qio_channel_tls_info = {
|
||||
.parent = TYPE_QIO_CHANNEL,
|
||||
.name = TYPE_QIO_CHANNEL_TLS,
|
||||
.instance_size = sizeof(QIOChannelTLS),
|
||||
.instance_init = qio_channel_tls_init,
|
||||
.instance_finalize = qio_channel_tls_finalize,
|
||||
.class_init = qio_channel_tls_class_init,
|
||||
};
|
||||
|
||||
static void qio_channel_tls_register_types(void)
|
||||
{
|
||||
type_register_static(&qio_channel_tls_info);
|
||||
}
|
||||
|
||||
type_init(qio_channel_tls_register_types);
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* QEMU I/O channels utility APIs
|
||||
*
|
||||
* Copyright (c) 2016 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.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
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "io/channel-util.h"
|
||||
#include "io/channel-file.h"
|
||||
#include "io/channel-socket.h"
|
||||
|
||||
|
||||
QIOChannel *qio_channel_new_fd(int fd,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannel *ioc;
|
||||
|
||||
if (fd_is_socket(fd)) {
|
||||
ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fd, errp));
|
||||
} else {
|
||||
ioc = QIO_CHANNEL(qio_channel_file_new_fd(fd));
|
||||
}
|
||||
return ioc;
|
||||
}
|
||||
|
||||
|
||||
void qio_channel_util_set_aio_fd_handler(int read_fd,
|
||||
AioContext *read_ctx,
|
||||
IOHandler *io_read,
|
||||
int write_fd,
|
||||
AioContext *write_ctx,
|
||||
IOHandler *io_write,
|
||||
void *opaque)
|
||||
{
|
||||
if (read_fd == write_fd && read_ctx == write_ctx) {
|
||||
aio_set_fd_handler(read_ctx, read_fd, io_read, io_write,
|
||||
NULL, NULL, opaque);
|
||||
} else {
|
||||
if (read_ctx) {
|
||||
aio_set_fd_handler(read_ctx, read_fd, io_read, NULL,
|
||||
NULL, NULL, opaque);
|
||||
}
|
||||
if (write_ctx) {
|
||||
aio_set_fd_handler(write_ctx, write_fd, NULL, io_write,
|
||||
NULL, NULL, opaque);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* QEMU I/O channels watch helper APIs
|
||||
*
|
||||
* 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.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
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "io/channel-watch.h"
|
||||
|
||||
typedef struct QIOChannelFDSource QIOChannelFDSource;
|
||||
struct QIOChannelFDSource {
|
||||
GSource parent;
|
||||
GPollFD fd;
|
||||
QIOChannel *ioc;
|
||||
GIOCondition condition;
|
||||
};
|
||||
|
||||
|
||||
#ifdef CONFIG_WIN32
|
||||
typedef struct QIOChannelSocketSource QIOChannelSocketSource;
|
||||
struct QIOChannelSocketSource {
|
||||
GSource parent;
|
||||
GPollFD fd;
|
||||
QIOChannel *ioc;
|
||||
SOCKET socket;
|
||||
int revents;
|
||||
GIOCondition condition;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct QIOChannelFDPairSource QIOChannelFDPairSource;
|
||||
struct QIOChannelFDPairSource {
|
||||
GSource parent;
|
||||
GPollFD fdread;
|
||||
GPollFD fdwrite;
|
||||
QIOChannel *ioc;
|
||||
GIOCondition condition;
|
||||
};
|
||||
|
||||
|
||||
static gboolean
|
||||
qio_channel_fd_source_prepare(GSource *source G_GNUC_UNUSED,
|
||||
gint *timeout)
|
||||
{
|
||||
*timeout = -1;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
qio_channel_fd_source_check(GSource *source)
|
||||
{
|
||||
QIOChannelFDSource *ssource = (QIOChannelFDSource *)source;
|
||||
|
||||
return ssource->fd.revents & ssource->condition;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
qio_channel_fd_source_dispatch(GSource *source,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
QIOChannelFunc func = (QIOChannelFunc)callback;
|
||||
QIOChannelFDSource *ssource = (QIOChannelFDSource *)source;
|
||||
|
||||
return (*func)(ssource->ioc,
|
||||
ssource->fd.revents & ssource->condition,
|
||||
user_data);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qio_channel_fd_source_finalize(GSource *source)
|
||||
{
|
||||
QIOChannelFDSource *ssource = (QIOChannelFDSource *)source;
|
||||
|
||||
object_unref(OBJECT(ssource->ioc));
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_WIN32
|
||||
static gboolean
|
||||
qio_channel_socket_source_prepare(GSource *source G_GNUC_UNUSED,
|
||||
gint *timeout)
|
||||
{
|
||||
*timeout = -1;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* NB, this impl only works when the socket is in non-blocking
|
||||
* mode on Win32
|
||||
*/
|
||||
static gboolean
|
||||
qio_channel_socket_source_check(GSource *source)
|
||||
{
|
||||
static struct timeval tv0;
|
||||
QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source;
|
||||
fd_set rfds, wfds, xfds;
|
||||
|
||||
if (!ssource->condition) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_ZERO(&wfds);
|
||||
FD_ZERO(&xfds);
|
||||
if (ssource->condition & G_IO_IN) {
|
||||
FD_SET(ssource->socket, &rfds);
|
||||
}
|
||||
if (ssource->condition & G_IO_OUT) {
|
||||
FD_SET(ssource->socket, &wfds);
|
||||
}
|
||||
if (ssource->condition & G_IO_PRI) {
|
||||
FD_SET(ssource->socket, &xfds);
|
||||
}
|
||||
ssource->revents = 0;
|
||||
if (select(0, &rfds, &wfds, &xfds, &tv0) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (FD_ISSET(ssource->socket, &rfds)) {
|
||||
ssource->revents |= G_IO_IN;
|
||||
}
|
||||
if (FD_ISSET(ssource->socket, &wfds)) {
|
||||
ssource->revents |= G_IO_OUT;
|
||||
}
|
||||
if (FD_ISSET(ssource->socket, &xfds)) {
|
||||
ssource->revents |= G_IO_PRI;
|
||||
}
|
||||
|
||||
return ssource->revents;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
qio_channel_socket_source_dispatch(GSource *source,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
QIOChannelFunc func = (QIOChannelFunc)callback;
|
||||
QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source;
|
||||
|
||||
return (*func)(ssource->ioc, ssource->revents, user_data);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qio_channel_socket_source_finalize(GSource *source)
|
||||
{
|
||||
QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source;
|
||||
|
||||
object_unref(OBJECT(ssource->ioc));
|
||||
}
|
||||
|
||||
|
||||
GSourceFuncs qio_channel_socket_source_funcs = {
|
||||
qio_channel_socket_source_prepare,
|
||||
qio_channel_socket_source_check,
|
||||
qio_channel_socket_source_dispatch,
|
||||
qio_channel_socket_source_finalize
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
static gboolean
|
||||
qio_channel_fd_pair_source_prepare(GSource *source G_GNUC_UNUSED,
|
||||
gint *timeout)
|
||||
{
|
||||
*timeout = -1;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
qio_channel_fd_pair_source_check(GSource *source)
|
||||
{
|
||||
QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source;
|
||||
GIOCondition poll_condition = ssource->fdread.revents |
|
||||
ssource->fdwrite.revents;
|
||||
|
||||
return poll_condition & ssource->condition;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
qio_channel_fd_pair_source_dispatch(GSource *source,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
QIOChannelFunc func = (QIOChannelFunc)callback;
|
||||
QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source;
|
||||
GIOCondition poll_condition = ssource->fdread.revents |
|
||||
ssource->fdwrite.revents;
|
||||
|
||||
return (*func)(ssource->ioc,
|
||||
poll_condition & ssource->condition,
|
||||
user_data);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qio_channel_fd_pair_source_finalize(GSource *source)
|
||||
{
|
||||
QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source;
|
||||
|
||||
object_unref(OBJECT(ssource->ioc));
|
||||
}
|
||||
|
||||
|
||||
GSourceFuncs qio_channel_fd_source_funcs = {
|
||||
qio_channel_fd_source_prepare,
|
||||
qio_channel_fd_source_check,
|
||||
qio_channel_fd_source_dispatch,
|
||||
qio_channel_fd_source_finalize
|
||||
};
|
||||
|
||||
|
||||
GSourceFuncs qio_channel_fd_pair_source_funcs = {
|
||||
qio_channel_fd_pair_source_prepare,
|
||||
qio_channel_fd_pair_source_check,
|
||||
qio_channel_fd_pair_source_dispatch,
|
||||
qio_channel_fd_pair_source_finalize
|
||||
};
|
||||
|
||||
|
||||
GSource *qio_channel_create_fd_watch(QIOChannel *ioc,
|
||||
int fd,
|
||||
GIOCondition condition)
|
||||
{
|
||||
GSource *source;
|
||||
QIOChannelFDSource *ssource;
|
||||
|
||||
source = g_source_new(&qio_channel_fd_source_funcs,
|
||||
sizeof(QIOChannelFDSource));
|
||||
ssource = (QIOChannelFDSource *)source;
|
||||
|
||||
ssource->ioc = ioc;
|
||||
object_ref(OBJECT(ioc));
|
||||
|
||||
ssource->condition = condition;
|
||||
|
||||
#ifdef CONFIG_WIN32
|
||||
ssource->fd.fd = (gint64)_get_osfhandle(fd);
|
||||
#else
|
||||
ssource->fd.fd = fd;
|
||||
#endif
|
||||
ssource->fd.events = condition;
|
||||
|
||||
g_source_add_poll(source, &ssource->fd);
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_WIN32
|
||||
GSource *qio_channel_create_socket_watch(QIOChannel *ioc,
|
||||
int sockfd,
|
||||
GIOCondition condition)
|
||||
{
|
||||
GSource *source;
|
||||
QIOChannelSocketSource *ssource;
|
||||
|
||||
qemu_socket_select_nofail(sockfd, ioc->event,
|
||||
FD_READ | FD_ACCEPT | FD_CLOSE |
|
||||
FD_CONNECT | FD_WRITE | FD_OOB);
|
||||
|
||||
source = g_source_new(&qio_channel_socket_source_funcs,
|
||||
sizeof(QIOChannelSocketSource));
|
||||
ssource = (QIOChannelSocketSource *)source;
|
||||
|
||||
ssource->ioc = ioc;
|
||||
object_ref(OBJECT(ioc));
|
||||
|
||||
ssource->condition = condition;
|
||||
ssource->socket = _get_osfhandle(sockfd);
|
||||
ssource->revents = 0;
|
||||
|
||||
ssource->fd.fd = (gintptr)ioc->event;
|
||||
ssource->fd.events = G_IO_IN;
|
||||
|
||||
g_source_add_poll(source, &ssource->fd);
|
||||
|
||||
return source;
|
||||
}
|
||||
#else
|
||||
GSource *qio_channel_create_socket_watch(QIOChannel *ioc,
|
||||
int socket,
|
||||
GIOCondition condition)
|
||||
{
|
||||
return qio_channel_create_fd_watch(ioc, socket, condition);
|
||||
}
|
||||
#endif
|
||||
|
||||
GSource *qio_channel_create_fd_pair_watch(QIOChannel *ioc,
|
||||
int fdread,
|
||||
int fdwrite,
|
||||
GIOCondition condition)
|
||||
{
|
||||
GSource *source;
|
||||
QIOChannelFDPairSource *ssource;
|
||||
|
||||
source = g_source_new(&qio_channel_fd_pair_source_funcs,
|
||||
sizeof(QIOChannelFDPairSource));
|
||||
ssource = (QIOChannelFDPairSource *)source;
|
||||
|
||||
ssource->ioc = ioc;
|
||||
object_ref(OBJECT(ioc));
|
||||
|
||||
ssource->condition = condition;
|
||||
|
||||
#ifdef CONFIG_WIN32
|
||||
ssource->fdread.fd = (gint64)_get_osfhandle(fdread);
|
||||
ssource->fdwrite.fd = (gint64)_get_osfhandle(fdwrite);
|
||||
#else
|
||||
ssource->fdread.fd = fdread;
|
||||
ssource->fdwrite.fd = fdwrite;
|
||||
#endif
|
||||
|
||||
ssource->fdread.events = condition & G_IO_IN;
|
||||
ssource->fdwrite.events = condition & G_IO_OUT;
|
||||
|
||||
g_source_add_poll(source, &ssource->fdread);
|
||||
g_source_add_poll(source, &ssource->fdwrite);
|
||||
|
||||
return source;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+952
@@ -0,0 +1,952 @@
|
||||
/*
|
||||
* QEMU I/O channels
|
||||
*
|
||||
* 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.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
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/aio-wait.h"
|
||||
#include "io/channel.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/iov.h"
|
||||
|
||||
bool qio_channel_has_feature(QIOChannel *ioc,
|
||||
QIOChannelFeature feature)
|
||||
{
|
||||
return ioc->features & (1 << feature);
|
||||
}
|
||||
|
||||
|
||||
void qio_channel_set_feature(QIOChannel *ioc,
|
||||
QIOChannelFeature feature)
|
||||
{
|
||||
ioc->features |= (1 << feature);
|
||||
}
|
||||
|
||||
|
||||
void qio_channel_set_name(QIOChannel *ioc,
|
||||
const char *name)
|
||||
{
|
||||
g_free(ioc->name);
|
||||
ioc->name = g_strdup(name);
|
||||
}
|
||||
|
||||
|
||||
ssize_t qio_channel_readv_full(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
int **fds,
|
||||
size_t *nfds,
|
||||
int flags,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
|
||||
|
||||
if ((fds || nfds) &&
|
||||
!qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS)) {
|
||||
error_setg_errno(errp, EINVAL,
|
||||
"Channel does not support file descriptor passing");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((flags & QIO_CHANNEL_READ_FLAG_MSG_PEEK) &&
|
||||
!qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_READ_MSG_PEEK)) {
|
||||
error_setg_errno(errp, EINVAL,
|
||||
"Channel does not support peek read");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return klass->io_readv(ioc, iov, niov, fds, nfds, flags, errp);
|
||||
}
|
||||
|
||||
|
||||
ssize_t qio_channel_writev_full(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
int *fds,
|
||||
size_t nfds,
|
||||
int flags,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
|
||||
|
||||
if (fds || nfds) {
|
||||
if (!qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS)) {
|
||||
error_setg_errno(errp, EINVAL,
|
||||
"Channel does not support file descriptor passing");
|
||||
return -1;
|
||||
}
|
||||
if (flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) {
|
||||
error_setg_errno(errp, EINVAL,
|
||||
"Zero Copy does not support file descriptor passing");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) &&
|
||||
!qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY)) {
|
||||
error_setg_errno(errp, EINVAL,
|
||||
"Requested Zero Copy feature is not available");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return klass->io_writev(ioc, iov, niov, fds, nfds, flags, errp);
|
||||
}
|
||||
|
||||
|
||||
int coroutine_mixed_fn qio_channel_readv_all_eof(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
Error **errp)
|
||||
{
|
||||
return qio_channel_readv_full_all_eof(ioc, iov, niov, NULL, NULL, 0,
|
||||
errp);
|
||||
}
|
||||
|
||||
int coroutine_mixed_fn qio_channel_readv_all(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
Error **errp)
|
||||
{
|
||||
return qio_channel_readv_full_all(ioc, iov, niov, NULL, NULL, errp);
|
||||
}
|
||||
|
||||
int coroutine_mixed_fn qio_channel_readv_full_all_eof(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
int **fds, size_t *nfds,
|
||||
int flags,
|
||||
Error **errp)
|
||||
{
|
||||
int ret = -1;
|
||||
struct iovec *local_iov = g_new(struct iovec, niov);
|
||||
struct iovec *local_iov_head = local_iov;
|
||||
unsigned int nlocal_iov = niov;
|
||||
int **local_fds = fds;
|
||||
size_t *local_nfds = nfds;
|
||||
bool partial = false;
|
||||
|
||||
if (nfds) {
|
||||
*nfds = 0;
|
||||
}
|
||||
|
||||
if (fds) {
|
||||
*fds = NULL;
|
||||
}
|
||||
|
||||
nlocal_iov = iov_copy(local_iov, nlocal_iov,
|
||||
iov, niov,
|
||||
0, iov_size(iov, niov));
|
||||
|
||||
while ((nlocal_iov > 0) || local_fds) {
|
||||
ssize_t len;
|
||||
len = qio_channel_readv_full(ioc, local_iov, nlocal_iov, local_fds,
|
||||
local_nfds, flags, errp);
|
||||
if (len == QIO_CHANNEL_ERR_BLOCK) {
|
||||
qio_channel_wait_cond(ioc, G_IO_IN);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
if (local_nfds && *local_nfds) {
|
||||
/*
|
||||
* Got some FDs, but no data yet. This isn't an EOF
|
||||
* scenario (yet), so carry on to try to read data
|
||||
* on next loop iteration
|
||||
*/
|
||||
goto next_iter;
|
||||
} else if (!partial) {
|
||||
/* No fds and no data - EOF before any data read */
|
||||
ret = 0;
|
||||
goto cleanup;
|
||||
} else {
|
||||
len = -1;
|
||||
error_setg(errp,
|
||||
"Unexpected end-of-file before all data were read");
|
||||
/* Fallthrough into len < 0 handling */
|
||||
}
|
||||
}
|
||||
|
||||
if (len < 0) {
|
||||
/* Close any FDs we previously received */
|
||||
if (nfds && fds) {
|
||||
size_t i;
|
||||
for (i = 0; i < (*nfds); i++) {
|
||||
close((*fds)[i]);
|
||||
}
|
||||
g_free(*fds);
|
||||
*fds = NULL;
|
||||
*nfds = 0;
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (nlocal_iov) {
|
||||
iov_discard_front(&local_iov, &nlocal_iov, len);
|
||||
}
|
||||
|
||||
next_iter:
|
||||
partial = true;
|
||||
local_fds = NULL;
|
||||
local_nfds = NULL;
|
||||
}
|
||||
|
||||
ret = 1;
|
||||
|
||||
cleanup:
|
||||
g_free(local_iov_head);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int coroutine_mixed_fn qio_channel_readv_full_all(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
int **fds, size_t *nfds,
|
||||
Error **errp)
|
||||
{
|
||||
int ret = qio_channel_readv_full_all_eof(ioc, iov, niov, fds, nfds, 0,
|
||||
errp);
|
||||
|
||||
if (ret == 0) {
|
||||
error_setg(errp, "Unexpected end-of-file before all data were read");
|
||||
return -1;
|
||||
}
|
||||
if (ret == 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int coroutine_mixed_fn qio_channel_writev_all(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
Error **errp)
|
||||
{
|
||||
return qio_channel_writev_full_all(ioc, iov, niov, NULL, 0, 0, errp);
|
||||
}
|
||||
|
||||
int coroutine_mixed_fn qio_channel_writev_full_all(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
int *fds, size_t nfds,
|
||||
int flags, Error **errp)
|
||||
{
|
||||
int ret = -1;
|
||||
struct iovec *local_iov = g_new(struct iovec, niov);
|
||||
struct iovec *local_iov_head = local_iov;
|
||||
unsigned int nlocal_iov = niov;
|
||||
|
||||
nlocal_iov = iov_copy(local_iov, nlocal_iov,
|
||||
iov, niov,
|
||||
0, iov_size(iov, niov));
|
||||
|
||||
while (nlocal_iov > 0) {
|
||||
ssize_t len;
|
||||
|
||||
len = qio_channel_writev_full(ioc, local_iov, nlocal_iov, fds,
|
||||
nfds, flags, errp);
|
||||
|
||||
if (len == QIO_CHANNEL_ERR_BLOCK) {
|
||||
qio_channel_wait_cond(ioc, G_IO_OUT);
|
||||
continue;
|
||||
}
|
||||
if (len < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
iov_discard_front(&local_iov, &nlocal_iov, len);
|
||||
|
||||
fds = NULL;
|
||||
nfds = 0;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
cleanup:
|
||||
g_free(local_iov_head);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t qio_channel_readv(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
Error **errp)
|
||||
{
|
||||
return qio_channel_readv_full(ioc, iov, niov, NULL, NULL, 0, errp);
|
||||
}
|
||||
|
||||
|
||||
ssize_t qio_channel_writev(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
Error **errp)
|
||||
{
|
||||
return qio_channel_writev_full(ioc, iov, niov, NULL, 0, 0, errp);
|
||||
}
|
||||
|
||||
|
||||
ssize_t qio_channel_read(QIOChannel *ioc,
|
||||
void *buf,
|
||||
size_t buflen,
|
||||
Error **errp)
|
||||
{
|
||||
struct iovec iov = { .iov_base = buf, .iov_len = buflen };
|
||||
return qio_channel_readv_full(ioc, &iov, 1, NULL, NULL, 0, errp);
|
||||
}
|
||||
|
||||
|
||||
ssize_t qio_channel_write(QIOChannel *ioc,
|
||||
const void *buf,
|
||||
size_t buflen,
|
||||
Error **errp)
|
||||
{
|
||||
struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen };
|
||||
return qio_channel_writev_full(ioc, &iov, 1, NULL, 0, 0, errp);
|
||||
}
|
||||
|
||||
|
||||
int coroutine_mixed_fn qio_channel_read_all_eof(QIOChannel *ioc,
|
||||
void *buf,
|
||||
size_t buflen,
|
||||
Error **errp)
|
||||
{
|
||||
struct iovec iov = { .iov_base = buf, .iov_len = buflen };
|
||||
return qio_channel_readv_all_eof(ioc, &iov, 1, errp);
|
||||
}
|
||||
|
||||
|
||||
int coroutine_mixed_fn qio_channel_read_all(QIOChannel *ioc,
|
||||
void *buf,
|
||||
size_t buflen,
|
||||
Error **errp)
|
||||
{
|
||||
struct iovec iov = { .iov_base = buf, .iov_len = buflen };
|
||||
return qio_channel_readv_all(ioc, &iov, 1, errp);
|
||||
}
|
||||
|
||||
|
||||
int coroutine_mixed_fn qio_channel_write_all(QIOChannel *ioc,
|
||||
const void *buf,
|
||||
size_t buflen,
|
||||
Error **errp)
|
||||
{
|
||||
struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen };
|
||||
return qio_channel_writev_all(ioc, &iov, 1, errp);
|
||||
}
|
||||
|
||||
|
||||
bool qio_channel_set_blocking(QIOChannel *ioc,
|
||||
bool enabled,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
|
||||
return klass->io_set_blocking(ioc, enabled, errp) == 0;
|
||||
}
|
||||
|
||||
|
||||
void qio_channel_set_follow_coroutine_ctx(QIOChannel *ioc, bool enabled)
|
||||
{
|
||||
ioc->follow_coroutine_ctx = enabled;
|
||||
}
|
||||
|
||||
|
||||
int qio_channel_close(QIOChannel *ioc,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
|
||||
return klass->io_close(ioc, errp);
|
||||
}
|
||||
|
||||
|
||||
GSource *qio_channel_create_watch(QIOChannel *ioc,
|
||||
GIOCondition condition)
|
||||
{
|
||||
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
|
||||
GSource *ret = klass->io_create_watch(ioc, condition);
|
||||
|
||||
if (ioc->name) {
|
||||
g_source_set_name(ret, ioc->name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void qio_channel_set_aio_fd_handler(QIOChannel *ioc,
|
||||
AioContext *read_ctx,
|
||||
IOHandler *io_read,
|
||||
AioContext *write_ctx,
|
||||
IOHandler *io_write,
|
||||
void *opaque)
|
||||
{
|
||||
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
|
||||
|
||||
klass->io_set_aio_fd_handler(ioc, read_ctx, io_read, write_ctx, io_write,
|
||||
opaque);
|
||||
}
|
||||
|
||||
guint qio_channel_add_watch_full(QIOChannel *ioc,
|
||||
GIOCondition condition,
|
||||
QIOChannelFunc func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify,
|
||||
GMainContext *context)
|
||||
{
|
||||
GSource *source;
|
||||
guint id;
|
||||
|
||||
source = qio_channel_create_watch(ioc, condition);
|
||||
|
||||
g_source_set_callback(source, (GSourceFunc)func, user_data, notify);
|
||||
|
||||
id = g_source_attach(source, context);
|
||||
g_source_unref(source);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
guint qio_channel_add_watch(QIOChannel *ioc,
|
||||
GIOCondition condition,
|
||||
QIOChannelFunc func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify)
|
||||
{
|
||||
return qio_channel_add_watch_full(ioc, condition, func,
|
||||
user_data, notify, NULL);
|
||||
}
|
||||
|
||||
GSource *qio_channel_add_watch_source(QIOChannel *ioc,
|
||||
GIOCondition condition,
|
||||
QIOChannelFunc func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify,
|
||||
GMainContext *context)
|
||||
{
|
||||
GSource *source;
|
||||
guint id;
|
||||
|
||||
id = qio_channel_add_watch_full(ioc, condition, func,
|
||||
user_data, notify, context);
|
||||
source = g_main_context_find_source_by_id(context, id);
|
||||
g_source_ref(source);
|
||||
return source;
|
||||
}
|
||||
|
||||
|
||||
ssize_t qio_channel_pwritev(QIOChannel *ioc, const struct iovec *iov,
|
||||
size_t niov, off_t offset, Error **errp)
|
||||
{
|
||||
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
|
||||
|
||||
if (!klass->io_pwritev) {
|
||||
error_setg(errp, "Channel does not support pwritev");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_SEEKABLE)) {
|
||||
error_setg_errno(errp, EINVAL, "Requested channel is not seekable");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return klass->io_pwritev(ioc, iov, niov, offset, errp);
|
||||
}
|
||||
|
||||
ssize_t qio_channel_pwrite(QIOChannel *ioc, void *buf, size_t buflen,
|
||||
off_t offset, Error **errp)
|
||||
{
|
||||
struct iovec iov = {
|
||||
.iov_base = buf,
|
||||
.iov_len = buflen
|
||||
};
|
||||
|
||||
return qio_channel_pwritev(ioc, &iov, 1, offset, errp);
|
||||
}
|
||||
|
||||
int coroutine_mixed_fn qio_channel_pwritev_all(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
off_t offset,
|
||||
Error **errp)
|
||||
{
|
||||
int ret = -1;
|
||||
struct iovec *local_iov = g_new(struct iovec, niov);
|
||||
struct iovec *local_iov_head = local_iov;
|
||||
unsigned int nlocal_iov = niov;
|
||||
|
||||
nlocal_iov = iov_copy(local_iov, nlocal_iov,
|
||||
iov, niov,
|
||||
0, iov_size(iov, niov));
|
||||
|
||||
while (nlocal_iov > 0) {
|
||||
ssize_t len;
|
||||
|
||||
len = qio_channel_pwritev(ioc, local_iov, nlocal_iov, offset, errp);
|
||||
|
||||
if (len == QIO_CHANNEL_ERR_BLOCK) {
|
||||
qio_channel_wait_cond(ioc, G_IO_OUT);
|
||||
continue;
|
||||
}
|
||||
if (len < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
offset += len;
|
||||
iov_discard_front(&local_iov, &nlocal_iov, len);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
cleanup:
|
||||
g_free(local_iov_head);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int coroutine_mixed_fn qio_channel_pwrite_all(QIOChannel *ioc,
|
||||
const void *buf,
|
||||
size_t buflen,
|
||||
off_t offset,
|
||||
Error **errp)
|
||||
{
|
||||
struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen };
|
||||
return qio_channel_pwritev_all(ioc, &iov, 1, offset, errp);
|
||||
}
|
||||
|
||||
ssize_t qio_channel_preadv(QIOChannel *ioc, const struct iovec *iov,
|
||||
size_t niov, off_t offset, Error **errp)
|
||||
{
|
||||
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
|
||||
|
||||
if (!klass->io_preadv) {
|
||||
error_setg(errp, "Channel does not support preadv");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_SEEKABLE)) {
|
||||
error_setg_errno(errp, EINVAL, "Requested channel is not seekable");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return klass->io_preadv(ioc, iov, niov, offset, errp);
|
||||
}
|
||||
|
||||
ssize_t qio_channel_pread(QIOChannel *ioc, void *buf, size_t buflen,
|
||||
off_t offset, Error **errp)
|
||||
{
|
||||
struct iovec iov = {
|
||||
.iov_base = buf,
|
||||
.iov_len = buflen
|
||||
};
|
||||
|
||||
return qio_channel_preadv(ioc, &iov, 1, offset, errp);
|
||||
}
|
||||
|
||||
int coroutine_mixed_fn qio_channel_preadv_all_eof(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
off_t offset,
|
||||
Error **errp)
|
||||
{
|
||||
int ret = -1;
|
||||
struct iovec *local_iov = g_new(struct iovec, niov);
|
||||
struct iovec *local_iov_head = local_iov;
|
||||
unsigned int nlocal_iov = niov;
|
||||
bool partial = false;
|
||||
|
||||
nlocal_iov = iov_copy(local_iov, nlocal_iov,
|
||||
iov, niov,
|
||||
0, iov_size(iov, niov));
|
||||
|
||||
while (nlocal_iov > 0) {
|
||||
ssize_t len;
|
||||
len = qio_channel_preadv(ioc, local_iov, nlocal_iov, offset, errp);
|
||||
|
||||
if (len == QIO_CHANNEL_ERR_BLOCK) {
|
||||
qio_channel_wait_cond(ioc, G_IO_IN);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
if (!partial) {
|
||||
ret = 0;
|
||||
goto cleanup;
|
||||
}
|
||||
error_setg(errp,
|
||||
"Unexpected end-of-file before all data were read");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (len < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
partial = true;
|
||||
offset += len;
|
||||
iov_discard_front(&local_iov, &nlocal_iov, len);
|
||||
}
|
||||
|
||||
ret = 1;
|
||||
|
||||
cleanup:
|
||||
g_free(local_iov_head);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int coroutine_mixed_fn qio_channel_preadv_all(QIOChannel *ioc,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
off_t offset,
|
||||
Error **errp)
|
||||
{
|
||||
int ret = qio_channel_preadv_all_eof(ioc, iov, niov, offset, errp);
|
||||
|
||||
if (ret == 0) {
|
||||
error_setg(errp,
|
||||
"Unexpected end-of-file before all data were read");
|
||||
return -1;
|
||||
}
|
||||
if (ret == 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int coroutine_mixed_fn qio_channel_pread_all_eof(QIOChannel *ioc,
|
||||
void *buf,
|
||||
size_t buflen,
|
||||
off_t offset,
|
||||
Error **errp)
|
||||
{
|
||||
struct iovec iov = { .iov_base = buf, .iov_len = buflen };
|
||||
return qio_channel_preadv_all_eof(ioc, &iov, 1, offset, errp);
|
||||
}
|
||||
|
||||
int coroutine_mixed_fn qio_channel_pread_all(QIOChannel *ioc,
|
||||
void *buf,
|
||||
size_t buflen,
|
||||
off_t offset,
|
||||
Error **errp)
|
||||
{
|
||||
struct iovec iov = { .iov_base = buf, .iov_len = buflen };
|
||||
return qio_channel_preadv_all(ioc, &iov, 1, offset, errp);
|
||||
}
|
||||
|
||||
int qio_channel_shutdown(QIOChannel *ioc,
|
||||
QIOChannelShutdown how,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
|
||||
|
||||
if (!klass->io_shutdown) {
|
||||
error_setg(errp, "Data path shutdown not supported");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return klass->io_shutdown(ioc, how, errp);
|
||||
}
|
||||
|
||||
|
||||
void qio_channel_set_delay(QIOChannel *ioc,
|
||||
bool enabled)
|
||||
{
|
||||
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
|
||||
|
||||
if (klass->io_set_delay) {
|
||||
klass->io_set_delay(ioc, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void qio_channel_set_cork(QIOChannel *ioc,
|
||||
bool enabled)
|
||||
{
|
||||
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
|
||||
|
||||
if (klass->io_set_cork) {
|
||||
klass->io_set_cork(ioc, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
int qio_channel_get_peerpid(QIOChannel *ioc,
|
||||
unsigned int *pid,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
|
||||
|
||||
if (!klass->io_peerpid) {
|
||||
error_setg(errp, "Channel does not support peer pid");
|
||||
return -1;
|
||||
}
|
||||
klass->io_peerpid(ioc, pid, errp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
off_t qio_channel_io_seek(QIOChannel *ioc,
|
||||
off_t offset,
|
||||
int whence,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
|
||||
|
||||
if (!klass->io_seek) {
|
||||
error_setg(errp, "Channel does not support random access");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return klass->io_seek(ioc, offset, whence, errp);
|
||||
}
|
||||
|
||||
int qio_channel_flush(QIOChannel *ioc,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
|
||||
|
||||
if (!klass->io_flush ||
|
||||
!qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return klass->io_flush(ioc, errp);
|
||||
}
|
||||
|
||||
|
||||
static void qio_channel_restart_read(void *opaque)
|
||||
{
|
||||
QIOChannel *ioc = opaque;
|
||||
Coroutine *co = qatomic_xchg(&ioc->read_coroutine, NULL);
|
||||
|
||||
if (!co) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Assert that aio_co_wake() reenters the coroutine directly */
|
||||
assert(qemu_get_current_aio_context() ==
|
||||
qemu_coroutine_get_aio_context(co));
|
||||
aio_co_wake(co);
|
||||
}
|
||||
|
||||
static void qio_channel_restart_write(void *opaque)
|
||||
{
|
||||
QIOChannel *ioc = opaque;
|
||||
Coroutine *co = qatomic_xchg(&ioc->write_coroutine, NULL);
|
||||
|
||||
if (!co) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Assert that aio_co_wake() reenters the coroutine directly */
|
||||
assert(qemu_get_current_aio_context() ==
|
||||
qemu_coroutine_get_aio_context(co));
|
||||
aio_co_wake(co);
|
||||
}
|
||||
|
||||
static void coroutine_fn
|
||||
qio_channel_set_fd_handlers(QIOChannel *ioc, GIOCondition condition)
|
||||
{
|
||||
AioContext *ctx = ioc->follow_coroutine_ctx ?
|
||||
qemu_coroutine_get_aio_context(qemu_coroutine_self()) :
|
||||
iohandler_get_aio_context();
|
||||
AioContext *read_ctx = NULL;
|
||||
IOHandler *io_read = NULL;
|
||||
AioContext *write_ctx = NULL;
|
||||
IOHandler *io_write = NULL;
|
||||
|
||||
if (condition == G_IO_IN) {
|
||||
ioc->read_coroutine = qemu_coroutine_self();
|
||||
ioc->read_ctx = ctx;
|
||||
read_ctx = ctx;
|
||||
io_read = qio_channel_restart_read;
|
||||
|
||||
/*
|
||||
* Thread safety: if the other coroutine is set and its AioContext
|
||||
* matches ours, then there is mutual exclusion between read and write
|
||||
* because they share a single thread and it's safe to set both read
|
||||
* and write fd handlers here. If the AioContext does not match ours,
|
||||
* then both threads may run in parallel but there is no shared state
|
||||
* to worry about.
|
||||
*/
|
||||
if (ioc->write_coroutine && ioc->write_ctx == ctx) {
|
||||
write_ctx = ctx;
|
||||
io_write = qio_channel_restart_write;
|
||||
}
|
||||
} else if (condition == G_IO_OUT) {
|
||||
ioc->write_coroutine = qemu_coroutine_self();
|
||||
ioc->write_ctx = ctx;
|
||||
write_ctx = ctx;
|
||||
io_write = qio_channel_restart_write;
|
||||
if (ioc->read_coroutine && ioc->read_ctx == ctx) {
|
||||
read_ctx = ctx;
|
||||
io_read = qio_channel_restart_read;
|
||||
}
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
|
||||
qio_channel_set_aio_fd_handler(ioc, read_ctx, io_read,
|
||||
write_ctx, io_write, ioc);
|
||||
}
|
||||
|
||||
static void coroutine_fn
|
||||
qio_channel_clear_fd_handlers(QIOChannel *ioc, GIOCondition condition)
|
||||
{
|
||||
AioContext *read_ctx = NULL;
|
||||
IOHandler *io_read = NULL;
|
||||
AioContext *write_ctx = NULL;
|
||||
IOHandler *io_write = NULL;
|
||||
AioContext *ctx;
|
||||
|
||||
if (condition == G_IO_IN) {
|
||||
ctx = ioc->read_ctx;
|
||||
read_ctx = ctx;
|
||||
io_read = NULL;
|
||||
if (ioc->write_coroutine && ioc->write_ctx == ctx) {
|
||||
write_ctx = ctx;
|
||||
io_write = qio_channel_restart_write;
|
||||
}
|
||||
} else if (condition == G_IO_OUT) {
|
||||
ctx = ioc->write_ctx;
|
||||
write_ctx = ctx;
|
||||
io_write = NULL;
|
||||
if (ioc->read_coroutine && ioc->read_ctx == ctx) {
|
||||
read_ctx = ctx;
|
||||
io_read = qio_channel_restart_read;
|
||||
}
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
|
||||
qio_channel_set_aio_fd_handler(ioc, read_ctx, io_read,
|
||||
write_ctx, io_write, ioc);
|
||||
}
|
||||
|
||||
void coroutine_fn qio_channel_yield(QIOChannel *ioc,
|
||||
GIOCondition condition)
|
||||
{
|
||||
AioContext *ioc_ctx;
|
||||
|
||||
assert(qemu_in_coroutine());
|
||||
ioc_ctx = qemu_coroutine_get_aio_context(qemu_coroutine_self());
|
||||
|
||||
if (condition == G_IO_IN) {
|
||||
assert(!ioc->read_coroutine);
|
||||
} else if (condition == G_IO_OUT) {
|
||||
assert(!ioc->write_coroutine);
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
qio_channel_set_fd_handlers(ioc, condition);
|
||||
qemu_coroutine_yield();
|
||||
assert(in_aio_context_home_thread(ioc_ctx));
|
||||
|
||||
/* Allow interrupting the operation by reentering the coroutine other than
|
||||
* through the aio_fd_handlers. */
|
||||
if (condition == G_IO_IN) {
|
||||
assert(ioc->read_coroutine == NULL);
|
||||
} else if (condition == G_IO_OUT) {
|
||||
assert(ioc->write_coroutine == NULL);
|
||||
}
|
||||
qio_channel_clear_fd_handlers(ioc, condition);
|
||||
}
|
||||
|
||||
void qio_channel_wake_read(QIOChannel *ioc)
|
||||
{
|
||||
Coroutine *co = qatomic_xchg(&ioc->read_coroutine, NULL);
|
||||
if (co) {
|
||||
aio_co_wake(co);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean qio_channel_wait_complete(QIOChannel *ioc,
|
||||
GIOCondition condition,
|
||||
gpointer opaque)
|
||||
{
|
||||
GMainLoop *loop = opaque;
|
||||
|
||||
g_main_loop_quit(loop);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
void qio_channel_wait(QIOChannel *ioc,
|
||||
GIOCondition condition)
|
||||
{
|
||||
GMainContext *ctxt = g_main_context_new();
|
||||
GMainLoop *loop = g_main_loop_new(ctxt, TRUE);
|
||||
GSource *source;
|
||||
|
||||
source = qio_channel_create_watch(ioc, condition);
|
||||
|
||||
g_source_set_callback(source,
|
||||
(GSourceFunc)qio_channel_wait_complete,
|
||||
loop,
|
||||
NULL);
|
||||
|
||||
g_source_attach(source, ctxt);
|
||||
|
||||
g_main_loop_run(loop);
|
||||
|
||||
g_source_unref(source);
|
||||
g_main_loop_unref(loop);
|
||||
g_main_context_unref(ctxt);
|
||||
}
|
||||
|
||||
void coroutine_mixed_fn
|
||||
qio_channel_wait_cond(QIOChannel *ioc,
|
||||
GIOCondition condition)
|
||||
{
|
||||
if (qemu_in_coroutine()) {
|
||||
qio_channel_yield(ioc, condition);
|
||||
} else {
|
||||
qio_channel_wait(ioc, condition);
|
||||
}
|
||||
}
|
||||
|
||||
static void qio_channel_finalize(Object *obj)
|
||||
{
|
||||
QIOChannel *ioc = QIO_CHANNEL(obj);
|
||||
|
||||
/* Must not have coroutines in qio_channel_yield() */
|
||||
assert(!ioc->read_coroutine);
|
||||
assert(!ioc->write_coroutine);
|
||||
|
||||
g_free(ioc->name);
|
||||
|
||||
#ifdef _WIN32
|
||||
if (ioc->event) {
|
||||
CloseHandle(ioc->event);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static const TypeInfo qio_channel_info = {
|
||||
.parent = TYPE_OBJECT,
|
||||
.name = TYPE_QIO_CHANNEL,
|
||||
.instance_size = sizeof(QIOChannel),
|
||||
.instance_finalize = qio_channel_finalize,
|
||||
.abstract = true,
|
||||
.class_size = sizeof(QIOChannelClass),
|
||||
};
|
||||
|
||||
|
||||
static void qio_channel_register_types(void)
|
||||
{
|
||||
type_register_static(&qio_channel_info);
|
||||
}
|
||||
|
||||
|
||||
type_init(qio_channel_register_types);
|
||||
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* QEMU DNS resolver
|
||||
*
|
||||
* Copyright (c) 2016 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.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
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "io/dns-resolver.h"
|
||||
#include "qapi/clone-visitor.h"
|
||||
#include "qapi/qapi-visit-sockets.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
#ifndef AI_NUMERICSERV
|
||||
# define AI_NUMERICSERV 0
|
||||
#endif
|
||||
|
||||
static QIODNSResolver *instance;
|
||||
static GOnce instance_init = G_ONCE_INIT;
|
||||
|
||||
static gpointer qio_dns_resolve_init_instance(gpointer unused G_GNUC_UNUSED)
|
||||
{
|
||||
instance = QIO_DNS_RESOLVER(object_new(TYPE_QIO_DNS_RESOLVER));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QIODNSResolver *qio_dns_resolver_get_instance(void)
|
||||
{
|
||||
g_once(&instance_init, qio_dns_resolve_init_instance, NULL);
|
||||
return instance;
|
||||
}
|
||||
|
||||
static int qio_dns_resolver_lookup_sync_inet(QIODNSResolver *resolver,
|
||||
SocketAddress *addr,
|
||||
size_t *naddrs,
|
||||
SocketAddress ***addrs,
|
||||
Error **errp)
|
||||
{
|
||||
struct addrinfo ai, *res, *e;
|
||||
InetSocketAddress *iaddr = &addr->u.inet;
|
||||
char port[33];
|
||||
char uaddr[INET6_ADDRSTRLEN + 1];
|
||||
char uport[33];
|
||||
int rc;
|
||||
Error *err = NULL;
|
||||
size_t i;
|
||||
|
||||
*naddrs = 0;
|
||||
*addrs = NULL;
|
||||
|
||||
memset(&ai, 0, sizeof(ai));
|
||||
ai.ai_flags = AI_PASSIVE;
|
||||
if (iaddr->has_numeric && iaddr->numeric) {
|
||||
ai.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV;
|
||||
}
|
||||
ai.ai_family = inet_ai_family_from_address(iaddr, &err);
|
||||
ai.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (iaddr->host == NULL) {
|
||||
error_setg(errp, "host not specified");
|
||||
return -1;
|
||||
}
|
||||
if (iaddr->port != NULL) {
|
||||
pstrcpy(port, sizeof(port), iaddr->port);
|
||||
} else {
|
||||
port[0] = '\0';
|
||||
}
|
||||
|
||||
rc = getaddrinfo(strlen(iaddr->host) ? iaddr->host : NULL,
|
||||
strlen(port) ? port : NULL, &ai, &res);
|
||||
if (rc != 0) {
|
||||
error_setg(errp, "address resolution failed for %s:%s: %s",
|
||||
iaddr->host, port, gai_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (e = res; e != NULL; e = e->ai_next) {
|
||||
(*naddrs)++;
|
||||
}
|
||||
|
||||
*addrs = g_new0(SocketAddress *, *naddrs);
|
||||
|
||||
/* create socket + bind */
|
||||
for (i = 0, e = res; e != NULL; i++, e = e->ai_next) {
|
||||
SocketAddress *newaddr = g_new0(SocketAddress, 1);
|
||||
|
||||
newaddr->type = SOCKET_ADDRESS_TYPE_INET;
|
||||
|
||||
getnameinfo((struct sockaddr *)e->ai_addr, e->ai_addrlen,
|
||||
uaddr, INET6_ADDRSTRLEN, uport, 32,
|
||||
NI_NUMERICHOST | NI_NUMERICSERV);
|
||||
|
||||
newaddr->u.inet = *iaddr;
|
||||
newaddr->u.inet.host = g_strdup(uaddr),
|
||||
newaddr->u.inet.port = g_strdup(uport),
|
||||
newaddr->u.inet.has_numeric = true,
|
||||
newaddr->u.inet.numeric = true,
|
||||
|
||||
(*addrs)[i] = newaddr;
|
||||
}
|
||||
freeaddrinfo(res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int qio_dns_resolver_lookup_sync_nop(QIODNSResolver *resolver,
|
||||
SocketAddress *addr,
|
||||
size_t *naddrs,
|
||||
SocketAddress ***addrs,
|
||||
Error **errp)
|
||||
{
|
||||
*naddrs = 1;
|
||||
*addrs = g_new0(SocketAddress *, 1);
|
||||
(*addrs)[0] = QAPI_CLONE(SocketAddress, addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int qio_dns_resolver_lookup_sync(QIODNSResolver *resolver,
|
||||
SocketAddress *addr,
|
||||
size_t *naddrs,
|
||||
SocketAddress ***addrs,
|
||||
Error **errp)
|
||||
{
|
||||
switch (addr->type) {
|
||||
case SOCKET_ADDRESS_TYPE_INET:
|
||||
return qio_dns_resolver_lookup_sync_inet(resolver,
|
||||
addr,
|
||||
naddrs,
|
||||
addrs,
|
||||
errp);
|
||||
|
||||
case SOCKET_ADDRESS_TYPE_UNIX:
|
||||
case SOCKET_ADDRESS_TYPE_VSOCK:
|
||||
case SOCKET_ADDRESS_TYPE_FD:
|
||||
return qio_dns_resolver_lookup_sync_nop(resolver,
|
||||
addr,
|
||||
naddrs,
|
||||
addrs,
|
||||
errp);
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct QIODNSResolverLookupData {
|
||||
SocketAddress *addr;
|
||||
SocketAddress **addrs;
|
||||
size_t naddrs;
|
||||
};
|
||||
|
||||
|
||||
static void qio_dns_resolver_lookup_data_free(gpointer opaque)
|
||||
{
|
||||
struct QIODNSResolverLookupData *data = opaque;
|
||||
size_t i;
|
||||
|
||||
qapi_free_SocketAddress(data->addr);
|
||||
for (i = 0; i < data->naddrs; i++) {
|
||||
qapi_free_SocketAddress(data->addrs[i]);
|
||||
}
|
||||
|
||||
g_free(data->addrs);
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
|
||||
static void qio_dns_resolver_lookup_worker(QIOTask *task,
|
||||
gpointer opaque)
|
||||
{
|
||||
QIODNSResolver *resolver = QIO_DNS_RESOLVER(qio_task_get_source(task));
|
||||
struct QIODNSResolverLookupData *data = opaque;
|
||||
Error *err = NULL;
|
||||
|
||||
qio_dns_resolver_lookup_sync(resolver,
|
||||
data->addr,
|
||||
&data->naddrs,
|
||||
&data->addrs,
|
||||
&err);
|
||||
if (err) {
|
||||
qio_task_set_error(task, err);
|
||||
} else {
|
||||
qio_task_set_result_pointer(task, opaque, NULL);
|
||||
}
|
||||
|
||||
object_unref(OBJECT(resolver));
|
||||
}
|
||||
|
||||
|
||||
void qio_dns_resolver_lookup_async(QIODNSResolver *resolver,
|
||||
SocketAddress *addr,
|
||||
QIOTaskFunc func,
|
||||
gpointer opaque,
|
||||
GDestroyNotify notify)
|
||||
{
|
||||
QIOTask *task;
|
||||
struct QIODNSResolverLookupData *data =
|
||||
g_new0(struct QIODNSResolverLookupData, 1);
|
||||
|
||||
data->addr = QAPI_CLONE(SocketAddress, addr);
|
||||
|
||||
task = qio_task_new(OBJECT(resolver), func, opaque, notify);
|
||||
|
||||
qio_task_run_in_thread(task,
|
||||
qio_dns_resolver_lookup_worker,
|
||||
data,
|
||||
qio_dns_resolver_lookup_data_free,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
void qio_dns_resolver_lookup_result(QIODNSResolver *resolver,
|
||||
QIOTask *task,
|
||||
size_t *naddrs,
|
||||
SocketAddress ***addrs)
|
||||
{
|
||||
struct QIODNSResolverLookupData *data =
|
||||
qio_task_get_result_pointer(task);
|
||||
size_t i;
|
||||
|
||||
*naddrs = 0;
|
||||
*addrs = NULL;
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
*naddrs = data->naddrs;
|
||||
*addrs = g_new0(SocketAddress *, data->naddrs);
|
||||
for (i = 0; i < data->naddrs; i++) {
|
||||
(*addrs)[i] = QAPI_CLONE(SocketAddress, data->addrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const TypeInfo qio_dns_resolver_info = {
|
||||
.parent = TYPE_OBJECT,
|
||||
.name = TYPE_QIO_DNS_RESOLVER,
|
||||
.instance_size = sizeof(QIODNSResolver),
|
||||
};
|
||||
|
||||
|
||||
static void qio_dns_resolver_register_types(void)
|
||||
{
|
||||
type_register_static(&qio_dns_resolver_info);
|
||||
}
|
||||
|
||||
|
||||
type_init(qio_dns_resolver_register_types);
|
||||
@@ -0,0 +1,16 @@
|
||||
io_ss.add(genh)
|
||||
io_ss.add(files(
|
||||
'channel-buffer.c',
|
||||
'channel-command.c',
|
||||
'channel-file.c',
|
||||
'channel-null.c',
|
||||
'channel-socket.c',
|
||||
'channel-tls.c',
|
||||
'channel-util.c',
|
||||
'channel-watch.c',
|
||||
'channel-websock.c',
|
||||
'channel.c',
|
||||
'dns-resolver.c',
|
||||
'net-listener.c',
|
||||
'task.c',
|
||||
))
|
||||
@@ -0,0 +1,483 @@
|
||||
/*
|
||||
* QEMU network listener
|
||||
*
|
||||
* Copyright (c) 2016-2017 Red Hat, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "io/net-listener.h"
|
||||
#include "io/dns-resolver.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/lockable.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "trace.h"
|
||||
|
||||
struct QIONetListenerSource {
|
||||
QIOChannelSocket *sioc;
|
||||
GSource *io_source;
|
||||
QIONetListener *listener;
|
||||
};
|
||||
|
||||
QIONetListener *qio_net_listener_new(void)
|
||||
{
|
||||
QIONetListener *listener;
|
||||
|
||||
listener = QIO_NET_LISTENER(object_new(TYPE_QIO_NET_LISTENER));
|
||||
return listener;
|
||||
}
|
||||
|
||||
void qio_net_listener_set_name(QIONetListener *listener,
|
||||
const char *name)
|
||||
{
|
||||
g_free(listener->name);
|
||||
listener->name = g_strdup(name);
|
||||
}
|
||||
|
||||
|
||||
static gboolean qio_net_listener_channel_func(QIOChannel *ioc,
|
||||
GIOCondition condition,
|
||||
gpointer opaque)
|
||||
{
|
||||
QIONetListener *listener = QIO_NET_LISTENER(opaque);
|
||||
QIOChannelSocket *sioc;
|
||||
QIONetListenerClientFunc io_func;
|
||||
gpointer io_data;
|
||||
GMainContext *context;
|
||||
AioContext *aio_context;
|
||||
|
||||
sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
|
||||
NULL);
|
||||
if (!sioc) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WITH_QEMU_LOCK_GUARD(&listener->lock) {
|
||||
io_func = listener->io_func;
|
||||
io_data = listener->io_data;
|
||||
context = listener->context;
|
||||
aio_context = listener->aio_context;
|
||||
}
|
||||
|
||||
trace_qio_net_listener_callback(listener, io_func, context, aio_context);
|
||||
if (io_func) {
|
||||
io_func(listener, sioc, io_data);
|
||||
}
|
||||
|
||||
object_unref(OBJECT(sioc));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void qio_net_listener_aio_func(void *opaque)
|
||||
{
|
||||
QIONetListenerSource *data = opaque;
|
||||
|
||||
assert(data->io_source == NULL);
|
||||
assert(data->listener->aio_context != NULL);
|
||||
qio_net_listener_channel_func(QIO_CHANNEL(data->sioc), G_IO_IN,
|
||||
data->listener);
|
||||
}
|
||||
|
||||
|
||||
int qio_net_listener_open_sync(QIONetListener *listener,
|
||||
SocketAddress *addr,
|
||||
int num,
|
||||
Error **errp)
|
||||
{
|
||||
QIODNSResolver *resolver = qio_dns_resolver_get_instance();
|
||||
SocketAddress **resaddrs;
|
||||
size_t nresaddrs;
|
||||
size_t i;
|
||||
Error *err = NULL;
|
||||
bool success = false;
|
||||
|
||||
if (qio_dns_resolver_lookup_sync(resolver,
|
||||
addr,
|
||||
&nresaddrs,
|
||||
&resaddrs,
|
||||
errp) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < nresaddrs; i++) {
|
||||
QIOChannelSocket *sioc = qio_channel_socket_new();
|
||||
|
||||
if (qio_channel_socket_listen_sync(sioc, resaddrs[i], num,
|
||||
err ? NULL : &err) == 0) {
|
||||
success = true;
|
||||
|
||||
qio_net_listener_add(listener, sioc);
|
||||
}
|
||||
|
||||
qapi_free_SocketAddress(resaddrs[i]);
|
||||
object_unref(OBJECT(sioc));
|
||||
}
|
||||
g_free(resaddrs);
|
||||
|
||||
if (success) {
|
||||
error_free(err);
|
||||
return 0;
|
||||
} else {
|
||||
error_propagate(errp, err);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* i == 0 to set watch on entire array, non-zero to only set watch on
|
||||
* recent additions when earlier entries are already watched.
|
||||
*
|
||||
* listener->lock must be held by caller.
|
||||
*/
|
||||
static void
|
||||
qio_net_listener_watch(QIONetListener *listener, size_t i, const char *caller)
|
||||
{
|
||||
if (!listener->io_func) {
|
||||
return;
|
||||
}
|
||||
|
||||
trace_qio_net_listener_watch(listener, listener->io_func,
|
||||
listener->context, listener->aio_context,
|
||||
caller);
|
||||
for ( ; i < listener->nsioc; i++) {
|
||||
if (!listener->aio_context) {
|
||||
/*
|
||||
* The user passed a GMainContext with the async callback;
|
||||
* they plan on running the default or their own g_main_loop.
|
||||
*/
|
||||
object_ref(OBJECT(listener));
|
||||
listener->source[i]->io_source = qio_channel_add_watch_source(
|
||||
QIO_CHANNEL(listener->source[i]->sioc), G_IO_IN,
|
||||
qio_net_listener_channel_func,
|
||||
listener, (GDestroyNotify)object_unref, listener->context);
|
||||
} else {
|
||||
/*
|
||||
* The user passed an AioContext. At this point,
|
||||
* AioContext lacks a clean way to call a notify function
|
||||
* to release a final reference after any callback is
|
||||
* complete. But we asserted earlier that the async
|
||||
* callback is changed only from the thread associated
|
||||
* with aio_context, which means no other thread is in the
|
||||
* middle of running the callback when we are changing the
|
||||
* refcount on listener here. Therefore, a single
|
||||
* reference here is sufficient to ensure listener is not
|
||||
* finalized during the callback.
|
||||
*/
|
||||
assert(listener->context == NULL);
|
||||
if (i == 0) {
|
||||
object_ref(OBJECT(listener));
|
||||
}
|
||||
qio_channel_set_aio_fd_handler(
|
||||
QIO_CHANNEL(listener->source[i]->sioc),
|
||||
listener->aio_context, qio_net_listener_aio_func,
|
||||
NULL, NULL, listener->source[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* listener->lock must be held by caller. */
|
||||
static void
|
||||
qio_net_listener_unwatch(QIONetListener *listener, const char *caller)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!listener->io_func) {
|
||||
return;
|
||||
}
|
||||
|
||||
trace_qio_net_listener_unwatch(listener, listener->io_func,
|
||||
listener->context, listener->aio_context,
|
||||
caller);
|
||||
for (i = 0; i < listener->nsioc; i++) {
|
||||
if (!listener->aio_context) {
|
||||
if (listener->source[i]->io_source) {
|
||||
g_source_destroy(listener->source[i]->io_source);
|
||||
g_source_unref(listener->source[i]->io_source);
|
||||
listener->source[i]->io_source = NULL;
|
||||
}
|
||||
} else {
|
||||
assert(listener->context == NULL);
|
||||
qio_channel_set_aio_fd_handler(
|
||||
QIO_CHANNEL(listener->source[i]->sioc),
|
||||
listener->aio_context, NULL, NULL, NULL, NULL);
|
||||
if (i == listener->nsioc - 1) {
|
||||
object_unref(OBJECT(listener));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void qio_net_listener_add(QIONetListener *listener,
|
||||
QIOChannelSocket *sioc)
|
||||
{
|
||||
if (listener->name) {
|
||||
qio_channel_set_name(QIO_CHANNEL(sioc), listener->name);
|
||||
}
|
||||
|
||||
listener->source = g_renew(typeof(listener->source[0]),
|
||||
listener->source,
|
||||
listener->nsioc + 1);
|
||||
listener->source[listener->nsioc] = g_new0(QIONetListenerSource, 1);
|
||||
listener->source[listener->nsioc]->sioc = sioc;
|
||||
listener->source[listener->nsioc]->listener = listener;
|
||||
|
||||
object_ref(OBJECT(sioc));
|
||||
listener->connected = true;
|
||||
|
||||
QEMU_LOCK_GUARD(&listener->lock);
|
||||
listener->nsioc++;
|
||||
qio_net_listener_watch(listener, listener->nsioc - 1, "add");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qio_net_listener_set_client_func_internal(QIONetListener *listener,
|
||||
QIONetListenerClientFunc func,
|
||||
gpointer data,
|
||||
GDestroyNotify notify,
|
||||
GMainContext *context,
|
||||
AioContext *aio_context)
|
||||
{
|
||||
QEMU_LOCK_GUARD(&listener->lock);
|
||||
if (listener->io_func == func && listener->io_data == data &&
|
||||
listener->io_notify == notify && listener->context == context &&
|
||||
listener->aio_context == aio_context) {
|
||||
return;
|
||||
}
|
||||
|
||||
qio_net_listener_unwatch(listener, "set_client_func");
|
||||
if (listener->io_notify) {
|
||||
listener->io_notify(listener->io_data);
|
||||
}
|
||||
listener->io_func = func;
|
||||
listener->io_data = data;
|
||||
listener->io_notify = notify;
|
||||
listener->context = context;
|
||||
listener->aio_context = aio_context;
|
||||
|
||||
qio_net_listener_watch(listener, 0, "set_client_func");
|
||||
}
|
||||
|
||||
void qio_net_listener_set_client_func_full(QIONetListener *listener,
|
||||
QIONetListenerClientFunc func,
|
||||
gpointer data,
|
||||
GDestroyNotify notify,
|
||||
GMainContext *context)
|
||||
{
|
||||
qio_net_listener_set_client_func_internal(listener, func, data,
|
||||
notify, context, NULL);
|
||||
}
|
||||
|
||||
void qio_net_listener_set_client_func(QIONetListener *listener,
|
||||
QIONetListenerClientFunc func,
|
||||
gpointer data,
|
||||
GDestroyNotify notify)
|
||||
{
|
||||
qio_net_listener_set_client_func_internal(listener, func, data,
|
||||
notify, NULL, NULL);
|
||||
}
|
||||
|
||||
void qio_net_listener_set_client_aio_func(QIONetListener *listener,
|
||||
QIONetListenerClientFunc func,
|
||||
void *data,
|
||||
AioContext *context)
|
||||
{
|
||||
if (!context) {
|
||||
assert(qemu_in_main_thread());
|
||||
context = qemu_get_aio_context();
|
||||
} else {
|
||||
/*
|
||||
* TODO: The API was intentionally designed to allow a caller
|
||||
* to pass an alternative AioContext for future expansion;
|
||||
* however, actually implementating that is not possible
|
||||
* without notify callbacks wired into AioContext similar to
|
||||
* how they work in GSource. So for now, this code hard-codes
|
||||
* the knowledge that the only client needing AioContext is
|
||||
* the NBD server, which uses the global context and does not
|
||||
* suffer from cross-thread safety issues.
|
||||
*/
|
||||
g_assert_not_reached();
|
||||
}
|
||||
qio_net_listener_set_client_func_internal(listener, func, data,
|
||||
NULL, NULL, context);
|
||||
}
|
||||
|
||||
struct QIONetListenerClientWaitData {
|
||||
QIOChannelSocket *sioc;
|
||||
GMainLoop *loop;
|
||||
};
|
||||
|
||||
|
||||
static gboolean qio_net_listener_wait_client_func(QIOChannel *ioc,
|
||||
GIOCondition condition,
|
||||
gpointer opaque)
|
||||
{
|
||||
struct QIONetListenerClientWaitData *data = opaque;
|
||||
QIOChannelSocket *sioc;
|
||||
|
||||
sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
|
||||
NULL);
|
||||
if (!sioc) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (data->sioc) {
|
||||
object_unref(OBJECT(sioc));
|
||||
} else {
|
||||
data->sioc = sioc;
|
||||
g_main_loop_quit(data->loop);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
QIOChannelSocket *qio_net_listener_wait_client(QIONetListener *listener)
|
||||
{
|
||||
GMainContext *ctxt = g_main_context_new();
|
||||
GMainLoop *loop = g_main_loop_new(ctxt, TRUE);
|
||||
GSource **sources;
|
||||
struct QIONetListenerClientWaitData data = {
|
||||
.sioc = NULL,
|
||||
.loop = loop
|
||||
};
|
||||
size_t i;
|
||||
|
||||
WITH_QEMU_LOCK_GUARD(&listener->lock) {
|
||||
qio_net_listener_unwatch(listener, "wait_client");
|
||||
}
|
||||
|
||||
sources = g_new0(GSource *, listener->nsioc);
|
||||
for (i = 0; i < listener->nsioc; i++) {
|
||||
sources[i] = qio_channel_create_watch(
|
||||
QIO_CHANNEL(listener->source[i]->sioc), G_IO_IN);
|
||||
|
||||
g_source_set_callback(sources[i],
|
||||
(GSourceFunc)qio_net_listener_wait_client_func,
|
||||
&data,
|
||||
NULL);
|
||||
g_source_attach(sources[i], ctxt);
|
||||
}
|
||||
|
||||
g_main_loop_run(loop);
|
||||
|
||||
for (i = 0; i < listener->nsioc; i++) {
|
||||
g_source_unref(sources[i]);
|
||||
}
|
||||
g_free(sources);
|
||||
g_main_loop_unref(loop);
|
||||
g_main_context_unref(ctxt);
|
||||
|
||||
WITH_QEMU_LOCK_GUARD(&listener->lock) {
|
||||
qio_net_listener_watch(listener, 0, "wait_client");
|
||||
}
|
||||
|
||||
return data.sioc;
|
||||
}
|
||||
|
||||
void qio_net_listener_disconnect(QIONetListener *listener)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!listener->connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
QEMU_LOCK_GUARD(&listener->lock);
|
||||
qio_net_listener_unwatch(listener, "disconnect");
|
||||
for (i = 0; i < listener->nsioc; i++) {
|
||||
qio_channel_close(QIO_CHANNEL(listener->source[i]->sioc), NULL);
|
||||
}
|
||||
listener->connected = false;
|
||||
}
|
||||
|
||||
|
||||
bool qio_net_listener_is_connected(QIONetListener *listener)
|
||||
{
|
||||
return listener->connected;
|
||||
}
|
||||
|
||||
size_t qio_net_listener_nsioc(QIONetListener *listener)
|
||||
{
|
||||
return listener->nsioc;
|
||||
}
|
||||
|
||||
QIOChannelSocket *qio_net_listener_sioc(QIONetListener *listener, size_t n)
|
||||
{
|
||||
if (n >= listener->nsioc) {
|
||||
return NULL;
|
||||
}
|
||||
return listener->source[n]->sioc;
|
||||
}
|
||||
|
||||
SocketAddress *
|
||||
qio_net_listener_get_local_address(QIONetListener *listener, size_t n,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelSocket *sioc = qio_net_listener_sioc(listener, n);
|
||||
|
||||
if (!sioc) {
|
||||
error_setg(errp, "Listener index out of range");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return qio_channel_socket_get_local_address(sioc, errp);
|
||||
}
|
||||
|
||||
static void qio_net_listener_instance_init(Object *obj)
|
||||
{
|
||||
QIONetListener *listener = QIO_NET_LISTENER(obj);
|
||||
|
||||
qemu_mutex_init(&listener->lock);
|
||||
}
|
||||
|
||||
static void qio_net_listener_finalize(Object *obj)
|
||||
{
|
||||
QIONetListener *listener = QIO_NET_LISTENER(obj);
|
||||
size_t i;
|
||||
|
||||
qio_net_listener_disconnect(listener);
|
||||
if (listener->io_notify) {
|
||||
listener->io_notify(listener->io_data);
|
||||
}
|
||||
|
||||
for (i = 0; i < listener->nsioc; i++) {
|
||||
object_unref(OBJECT(listener->source[i]->sioc));
|
||||
g_free(listener->source[i]);
|
||||
}
|
||||
g_free(listener->source);
|
||||
g_free(listener->name);
|
||||
qemu_mutex_destroy(&listener->lock);
|
||||
}
|
||||
|
||||
static const TypeInfo qio_net_listener_info = {
|
||||
.parent = TYPE_OBJECT,
|
||||
.name = TYPE_QIO_NET_LISTENER,
|
||||
.instance_size = sizeof(QIONetListener),
|
||||
.instance_init = qio_net_listener_instance_init,
|
||||
.instance_finalize = qio_net_listener_finalize,
|
||||
};
|
||||
|
||||
|
||||
static void qio_net_listener_register_types(void)
|
||||
{
|
||||
type_register_static(&qio_net_listener_info);
|
||||
}
|
||||
|
||||
|
||||
type_init(qio_net_listener_register_types);
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* QEMU I/O task
|
||||
*
|
||||
* 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.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
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "io/task.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qom/object.h"
|
||||
#include "trace.h"
|
||||
|
||||
struct QIOTaskThreadData {
|
||||
QIOTaskWorker worker;
|
||||
gpointer opaque;
|
||||
GDestroyNotify destroy;
|
||||
GMainContext *context;
|
||||
GSource *completion;
|
||||
};
|
||||
|
||||
|
||||
struct QIOTask {
|
||||
Object *source;
|
||||
QIOTaskFunc func;
|
||||
gpointer opaque;
|
||||
GDestroyNotify destroy;
|
||||
Error *err;
|
||||
gpointer result;
|
||||
GDestroyNotify destroyResult;
|
||||
QemuMutex thread_lock;
|
||||
QemuCond thread_cond;
|
||||
struct QIOTaskThreadData *thread;
|
||||
};
|
||||
|
||||
|
||||
QIOTask *qio_task_new(Object *source,
|
||||
QIOTaskFunc func,
|
||||
gpointer opaque,
|
||||
GDestroyNotify destroy)
|
||||
{
|
||||
QIOTask *task;
|
||||
|
||||
task = g_new0(QIOTask, 1);
|
||||
|
||||
task->source = source;
|
||||
object_ref(source);
|
||||
task->func = func;
|
||||
task->opaque = opaque;
|
||||
task->destroy = destroy;
|
||||
qemu_mutex_init(&task->thread_lock);
|
||||
qemu_cond_init(&task->thread_cond);
|
||||
|
||||
trace_qio_task_new(task, source, func, opaque);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
void qio_task_free(QIOTask *task)
|
||||
{
|
||||
if (!task) {
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_mutex_lock(&task->thread_lock);
|
||||
if (task->thread) {
|
||||
if (task->thread->destroy) {
|
||||
task->thread->destroy(task->thread->opaque);
|
||||
}
|
||||
|
||||
if (task->thread->context) {
|
||||
g_main_context_unref(task->thread->context);
|
||||
}
|
||||
|
||||
g_free(task->thread);
|
||||
}
|
||||
|
||||
if (task->destroy) {
|
||||
task->destroy(task->opaque);
|
||||
}
|
||||
if (task->destroyResult) {
|
||||
task->destroyResult(task->result);
|
||||
}
|
||||
error_free(task->err);
|
||||
object_unref(task->source);
|
||||
|
||||
qemu_mutex_unlock(&task->thread_lock);
|
||||
qemu_mutex_destroy(&task->thread_lock);
|
||||
qemu_cond_destroy(&task->thread_cond);
|
||||
|
||||
g_free(task);
|
||||
}
|
||||
|
||||
|
||||
static gboolean qio_task_thread_result(gpointer opaque)
|
||||
{
|
||||
QIOTask *task = opaque;
|
||||
|
||||
trace_qio_task_thread_result(task);
|
||||
qio_task_complete(task);
|
||||
qio_task_free(task);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
static gpointer qio_task_thread_worker(gpointer opaque)
|
||||
{
|
||||
QIOTask *task = opaque;
|
||||
|
||||
trace_qio_task_thread_run(task);
|
||||
|
||||
task->thread->worker(task, task->thread->opaque);
|
||||
|
||||
/* We're running in the background thread, and must only
|
||||
* ever report the task results in the main event loop
|
||||
* thread. So we schedule an idle callback to report
|
||||
* the worker results
|
||||
*/
|
||||
trace_qio_task_thread_exit(task);
|
||||
|
||||
qemu_mutex_lock(&task->thread_lock);
|
||||
|
||||
task->thread->completion = g_idle_source_new();
|
||||
g_source_set_callback(task->thread->completion,
|
||||
qio_task_thread_result, task, NULL);
|
||||
g_source_attach(task->thread->completion,
|
||||
task->thread->context);
|
||||
g_source_unref(task->thread->completion);
|
||||
trace_qio_task_thread_source_attach(task, task->thread->completion);
|
||||
|
||||
qemu_cond_signal(&task->thread_cond);
|
||||
qemu_mutex_unlock(&task->thread_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void qio_task_run_in_thread(QIOTask *task,
|
||||
QIOTaskWorker worker,
|
||||
gpointer opaque,
|
||||
GDestroyNotify destroy,
|
||||
GMainContext *context)
|
||||
{
|
||||
struct QIOTaskThreadData *data = g_new0(struct QIOTaskThreadData, 1);
|
||||
QemuThread thread;
|
||||
|
||||
if (context) {
|
||||
g_main_context_ref(context);
|
||||
}
|
||||
|
||||
data->worker = worker;
|
||||
data->opaque = opaque;
|
||||
data->destroy = destroy;
|
||||
data->context = context;
|
||||
|
||||
task->thread = data;
|
||||
|
||||
trace_qio_task_thread_start(task, worker, opaque);
|
||||
qemu_thread_create(&thread,
|
||||
"io-task-worker",
|
||||
qio_task_thread_worker,
|
||||
task,
|
||||
QEMU_THREAD_DETACHED);
|
||||
}
|
||||
|
||||
|
||||
void qio_task_wait_thread(QIOTask *task)
|
||||
{
|
||||
qemu_mutex_lock(&task->thread_lock);
|
||||
g_assert(task->thread != NULL);
|
||||
while (task->thread->completion == NULL) {
|
||||
qemu_cond_wait(&task->thread_cond, &task->thread_lock);
|
||||
}
|
||||
|
||||
trace_qio_task_thread_source_cancel(task, task->thread->completion);
|
||||
g_source_destroy(task->thread->completion);
|
||||
qemu_mutex_unlock(&task->thread_lock);
|
||||
|
||||
qio_task_thread_result(task);
|
||||
}
|
||||
|
||||
|
||||
void qio_task_complete(QIOTask *task)
|
||||
{
|
||||
task->func(task, task->opaque);
|
||||
trace_qio_task_complete(task);
|
||||
}
|
||||
|
||||
|
||||
void qio_task_set_error(QIOTask *task,
|
||||
Error *err)
|
||||
{
|
||||
error_propagate(&task->err, err);
|
||||
}
|
||||
|
||||
|
||||
bool qio_task_propagate_error(QIOTask *task,
|
||||
Error **errp)
|
||||
{
|
||||
if (task->err) {
|
||||
error_propagate(errp, task->err);
|
||||
task->err = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void qio_task_set_result_pointer(QIOTask *task,
|
||||
gpointer result,
|
||||
GDestroyNotify destroy)
|
||||
{
|
||||
task->result = result;
|
||||
task->destroyResult = destroy;
|
||||
}
|
||||
|
||||
|
||||
gpointer qio_task_get_result_pointer(QIOTask *task)
|
||||
{
|
||||
return task->result;
|
||||
}
|
||||
|
||||
|
||||
Object *qio_task_get_source(QIOTask *task)
|
||||
{
|
||||
return task->source;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
# See docs/devel/tracing.rst for syntax documentation.
|
||||
|
||||
# task.c
|
||||
qio_task_new(void *task, void *source, void *func, void *opaque) "Task new task=%p source=%p func=%p opaque=%p"
|
||||
qio_task_complete(void *task) "Task complete task=%p"
|
||||
qio_task_thread_start(void *task, void *worker, void *opaque) "Task thread start task=%p worker=%p opaque=%p"
|
||||
qio_task_thread_run(void *task) "Task thread run task=%p"
|
||||
qio_task_thread_exit(void *task) "Task thread exit task=%p"
|
||||
qio_task_thread_result(void *task) "Task thread result task=%p"
|
||||
qio_task_thread_source_attach(void *task, void *source) "Task thread source attach task=%p source=%p"
|
||||
qio_task_thread_source_cancel(void *task, void *source) "Task thread source cancel task=%p source=%p"
|
||||
|
||||
# channel-null.c
|
||||
qio_channel_null_new(void *ioc) "Null new ioc=%p"
|
||||
|
||||
# channel-socket.c
|
||||
qio_channel_socket_new(void *ioc) "Socket new ioc=%p"
|
||||
qio_channel_socket_new_fd(void *ioc, int fd) "Socket new ioc=%p fd=%d"
|
||||
qio_channel_socket_connect_sync(void *ioc, void *addr) "Socket connect sync ioc=%p addr=%p"
|
||||
qio_channel_socket_connect_async(void *ioc, void *addr) "Socket connect async ioc=%p addr=%p"
|
||||
qio_channel_socket_connect_fail(void *ioc) "Socket connect fail ioc=%p"
|
||||
qio_channel_socket_connect_complete(void *ioc, int fd) "Socket connect complete ioc=%p fd=%d"
|
||||
qio_channel_socket_listen_sync(void *ioc, void *addr, int num) "Socket listen sync ioc=%p addr=%p num=%d"
|
||||
qio_channel_socket_listen_async(void *ioc, void *addr, int num) "Socket listen async ioc=%p addr=%p num=%d"
|
||||
qio_channel_socket_listen_fail(void *ioc) "Socket listen fail ioc=%p"
|
||||
qio_channel_socket_listen_complete(void *ioc, int fd) "Socket listen complete ioc=%p fd=%d"
|
||||
qio_channel_socket_dgram_sync(void *ioc, void *localAddr, void *remoteAddr) "Socket dgram sync ioc=%p localAddr=%p remoteAddr=%p"
|
||||
qio_channel_socket_dgram_async(void *ioc, void *localAddr, void *remoteAddr) "Socket dgram async ioc=%p localAddr=%p remoteAddr=%p"
|
||||
qio_channel_socket_dgram_fail(void *ioc) "Socket dgram fail ioc=%p"
|
||||
qio_channel_socket_dgram_complete(void *ioc, int fd) "Socket dgram complete ioc=%p fd=%d"
|
||||
qio_channel_socket_accept(void *ioc) "Socket accept start ioc=%p"
|
||||
qio_channel_socket_accept_fail(void *ioc) "Socket accept fail ioc=%p"
|
||||
qio_channel_socket_accept_complete(void *ioc, void *cioc, int fd) "Socket accept complete ioc=%p cioc=%p fd=%d"
|
||||
|
||||
# channel-file.c
|
||||
qio_channel_file_new_fd(void *ioc, int fd) "File new fd ioc=%p fd=%d"
|
||||
qio_channel_file_new_path(void *ioc, const char *path, int flags, int mode, int fd) "File new fd ioc=%p path=%s flags=%d mode=%d fd=%d"
|
||||
|
||||
# channel-tls.c
|
||||
qio_channel_tls_new_client(void *ioc, void *master, void *creds, const char *hostname) "TLS new client ioc=%p master=%p creds=%p hostname=%s"
|
||||
qio_channel_tls_new_server(void *ioc, void *master, void *creds, const char *aclname) "TLS new client ioc=%p master=%p creds=%p acltname=%s"
|
||||
qio_channel_tls_handshake_start(void *ioc) "TLS handshake start ioc=%p"
|
||||
qio_channel_tls_handshake_pending(void *ioc, int status) "TLS handshake pending ioc=%p status=%d"
|
||||
qio_channel_tls_handshake_fail(void *ioc) "TLS handshake fail ioc=%p"
|
||||
qio_channel_tls_handshake_complete(void *ioc) "TLS handshake complete ioc=%p"
|
||||
qio_channel_tls_handshake_cancel(void *ioc) "TLS handshake cancel ioc=%p"
|
||||
qio_channel_tls_bye_start(void *ioc) "TLS termination start ioc=%p"
|
||||
qio_channel_tls_bye_pending(void *ioc, int status) "TLS termination pending ioc=%p status=%d"
|
||||
qio_channel_tls_bye_fail(void *ioc) "TLS termination fail ioc=%p"
|
||||
qio_channel_tls_bye_complete(void *ioc) "TLS termination complete ioc=%p"
|
||||
qio_channel_tls_bye_cancel(void *ioc) "TLS termination cancel ioc=%p"
|
||||
qio_channel_tls_credentials_allow(void *ioc) "TLS credentials allow ioc=%p"
|
||||
qio_channel_tls_credentials_deny(void *ioc) "TLS credentials deny ioc=%p"
|
||||
|
||||
# channel-websock.c
|
||||
qio_channel_websock_new_server(void *ioc, void *master) "Websock new client ioc=%p master=%p"
|
||||
qio_channel_websock_handshake_start(void *ioc) "Websock handshake start ioc=%p"
|
||||
qio_channel_websock_handshake_pending(void *ioc, int status) "Websock handshake pending ioc=%p status=%d"
|
||||
qio_channel_websock_handshake_reply(void *ioc) "Websock handshake reply ioc=%p"
|
||||
qio_channel_websock_handshake_fail(void *ioc, const char *msg) "Websock handshake fail ioc=%p err=%s"
|
||||
qio_channel_websock_handshake_complete(void *ioc) "Websock handshake complete ioc=%p"
|
||||
qio_channel_websock_http_greeting(void *ioc, const char *greeting) "Websocket HTTP request ioc=%p greeting='%s'"
|
||||
qio_channel_websock_http_request(void *ioc, const char *protocols, const char *version, const char *host, const char *connection, const char *upgrade, const char *key) "Websocket HTTP request ioc=%p protocols='%s' version='%s' host='%s' connection='%s' upgrade='%s' key='%s'"
|
||||
qio_channel_websock_header_partial_decode(void *ioc, size_t payloadlen, unsigned char fin, unsigned char opcode, unsigned char has_mask) "Websocket header decoded ioc=%p payload-len=%zu fin=0x%x opcode=0x%x has_mask=0x%x"
|
||||
qio_channel_websock_header_full_decode(void *ioc, size_t headerlen, size_t payloadlen, uint32_t mask) "Websocket header decoded ioc=%p header-len=%zu payload-len=%zu mask=0x%x"
|
||||
qio_channel_websock_payload_decode(void *ioc, uint8_t opcode, size_t payload_remain) "Websocket header decoded ioc=%p opcode=0x%x payload-remain=%zu"
|
||||
qio_channel_websock_encode(void *ioc, uint8_t opcode, size_t payloadlen, size_t headerlen) "Websocket encoded ioc=%p opcode=0x%x header-len=%zu payload-len=%zu"
|
||||
qio_channel_websock_close(void *ioc) "Websocket close ioc=%p"
|
||||
|
||||
# channel-command.c
|
||||
qio_channel_command_new_pid(void *ioc, int writefd, int readfd, int pid) "Command new pid ioc=%p writefd=%d readfd=%d pid=%d"
|
||||
qio_channel_command_new_spawn(void *ioc, const char *binary, int flags) "Command new spawn ioc=%p binary=%s flags=%d"
|
||||
qio_channel_command_abort(void *ioc, int pid) "Command abort ioc=%p pid=%d"
|
||||
qio_channel_command_wait(void *ioc, int pid, int ret, int status) "Command abort ioc=%p pid=%d ret=%d status=%d"
|
||||
|
||||
# net-listener.c
|
||||
qio_net_listener_watch(void *listener, void *func, void *gctx, void *actx, const char *extra) "Net listener=%p watch enabled func=%p ctx=%p/%p by %s"
|
||||
qio_net_listener_unwatch(void *listener, void *func, void *gctx, void *actx, const char *extra) "Net listener=%p watch disabled func=%p ctx=%p/%p by %s"
|
||||
qio_net_listener_callback(void *listener, void *func, void *gctx, void *actx) "Net listener=%p callback forwarding to func=%p ctx=%p/%p"
|
||||
@@ -0,0 +1 @@
|
||||
#include "trace/trace-io.h"
|
||||
Reference in New Issue
Block a user