Browse Source
This is for code which needs a portable equivalent to a QIOChannelFile connected to /dev/null. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Juan Quintela <quintela@redhat.com> Signed-off-by: Juan Quintela <quintela@redhat.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>pull/213/head
committed by
Dr. David Alan Gilbert
6 changed files with 392 additions and 0 deletions
@ -0,0 +1,55 @@ |
|||
/*
|
|||
* 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/>.
|
|||
* |
|||
*/ |
|||
|
|||
#ifndef QIO_CHANNEL_FILE_H |
|||
#define QIO_CHANNEL_FILE_H |
|||
|
|||
#include "io/channel.h" |
|||
#include "qom/object.h" |
|||
|
|||
#define TYPE_QIO_CHANNEL_NULL "qio-channel-null" |
|||
OBJECT_DECLARE_SIMPLE_TYPE(QIOChannelNull, QIO_CHANNEL_NULL) |
|||
|
|||
|
|||
/**
|
|||
* QIOChannelNull: |
|||
* |
|||
* The QIOChannelNull object provides a channel implementation |
|||
* that discards all writes and returns EOF for all reads. |
|||
*/ |
|||
|
|||
struct QIOChannelNull { |
|||
QIOChannel parent; |
|||
bool closed; |
|||
}; |
|||
|
|||
|
|||
/**
|
|||
* qio_channel_null_new: |
|||
* |
|||
* Create a new IO channel object that discards all writes |
|||
* and returns EOF for all reads. |
|||
* |
|||
* Returns: the new channel object |
|||
*/ |
|||
QIOChannelNull * |
|||
qio_channel_null_new(void); |
|||
|
|||
#endif /* QIO_CHANNEL_NULL_H */ |
|||
@ -0,0 +1,237 @@ |
|||
/*
|
|||
* 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, |
|||
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 *ctx G_GNUC_UNUSED, |
|||
IOHandler *io_read 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, |
|||
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); |
|||
@ -0,0 +1,95 @@ |
|||
/*
|
|||
* QEMU I/O channel null test |
|||
* |
|||
* 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 "qapi/error.h" |
|||
|
|||
static gboolean test_io_channel_watch(QIOChannel *ioc, |
|||
GIOCondition condition, |
|||
gpointer opaque) |
|||
{ |
|||
GIOCondition *gotcond = opaque; |
|||
*gotcond = condition; |
|||
return G_SOURCE_REMOVE; |
|||
} |
|||
|
|||
static void test_io_channel_null_io(void) |
|||
{ |
|||
g_autoptr(QIOChannelNull) null = qio_channel_null_new(); |
|||
char buf[1024]; |
|||
GIOCondition gotcond = 0; |
|||
Error *local_err = NULL; |
|||
|
|||
g_assert(qio_channel_write(QIO_CHANNEL(null), |
|||
"Hello World", 11, |
|||
&error_abort) == 11); |
|||
|
|||
g_assert(qio_channel_read(QIO_CHANNEL(null), |
|||
buf, sizeof(buf), |
|||
&error_abort) == 0); |
|||
|
|||
qio_channel_add_watch(QIO_CHANNEL(null), |
|||
G_IO_IN, |
|||
test_io_channel_watch, |
|||
&gotcond, |
|||
NULL); |
|||
|
|||
g_main_context_iteration(NULL, false); |
|||
|
|||
g_assert(gotcond == G_IO_IN); |
|||
|
|||
qio_channel_add_watch(QIO_CHANNEL(null), |
|||
G_IO_IN | G_IO_OUT, |
|||
test_io_channel_watch, |
|||
&gotcond, |
|||
NULL); |
|||
|
|||
g_main_context_iteration(NULL, false); |
|||
|
|||
g_assert(gotcond == (G_IO_IN | G_IO_OUT)); |
|||
|
|||
qio_channel_close(QIO_CHANNEL(null), &error_abort); |
|||
|
|||
g_assert(qio_channel_write(QIO_CHANNEL(null), |
|||
"Hello World", 11, |
|||
&local_err) == -1); |
|||
g_assert_nonnull(local_err); |
|||
|
|||
g_clear_pointer(&local_err, error_free); |
|||
|
|||
g_assert(qio_channel_read(QIO_CHANNEL(null), |
|||
buf, sizeof(buf), |
|||
&local_err) == -1); |
|||
g_assert_nonnull(local_err); |
|||
|
|||
g_clear_pointer(&local_err, error_free); |
|||
} |
|||
|
|||
int main(int argc, char **argv) |
|||
{ |
|||
module_call_init(MODULE_INIT_QOM); |
|||
|
|||
g_test_init(&argc, &argv, NULL); |
|||
|
|||
g_test_add_func("/io/channel/null/io", test_io_channel_null_io); |
|||
|
|||
return g_test_run(); |
|||
} |
|||
Loading…
Reference in new issue