qemu/io/channel-buffer.c
manish.mishra 84615a19dd io: Add support for MSG_PEEK for socket channel
MSG_PEEK peeks at the channel, The data is treated as unread and
the next read shall still return this data. This support is
currently added only for socket class. Extra parameter 'flags'
is added to io_readv calls to pass extra read flags like MSG_PEEK.

Reviewed-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
Suggested-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: manish.mishra <manish.mishra@nutanix.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
2023-02-06 19:22:56 +01:00

253 lines
7.1 KiB
C

/*
* 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,
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);