Import QEMU upstream snapshot d2e570c

Upstream: https://gitlab.com/qemu-project/qemu.git

Upstream-Commit: d2e570cc0f97b936902a5b1b86b73c0f5998b475
This commit is contained in:
2026-08-31 02:15:30 +02:00
commit cf256aa081
11315 changed files with 3598369 additions and 0 deletions
+253
View File
@@ -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);
+390
View File
@@ -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);
+323
View File
@@ -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);
+239
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+650
View File
@@ -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);
+62
View File
@@ -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);
}
}
}
+347
View File
@@ -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;
}
+1367
View File
File diff suppressed because it is too large Load Diff
+952
View File
@@ -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);
+272
View File
@@ -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);
+16
View File
@@ -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',
))
+483
View File
@@ -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);
+243
View File
@@ -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;
}
+79
View File
@@ -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"
+1
View File
@@ -0,0 +1 @@
#include "trace/trace-io.h"