提交 67a70840 编写于 作者: P Peter Maydell

Merge remote-tracking branch 'remotes/berrange/tags/pull-io-channel-base-2015-12-18-1' into staging

Merge I/O channels base classes

# gpg: Signature made Fri 18 Dec 2015 12:18:38 GMT using RSA key ID 15104FDF
# gpg: Good signature from "Daniel P. Berrange <dan@berrange.com>"
# gpg:                 aka "Daniel P. Berrange <berrange@redhat.com>"

* remotes/berrange/tags/pull-io-channel-base-2015-12-18-1:
  io: add QIOChannelBuffer class
  io: add QIOChannelCommand class
  io: add QIOChannelWebsock class
  io: add QIOChannelTLS class
  io: add QIOChannelFile class
  io: add QIOChannelSocket class
  io: add QIOTask class for async operations
  io: add helper module for creating watches on FDs
  io: add abstract QIOChannel classes
Signed-off-by: NPeter Maydell <peter.maydell@linaro.org>
......@@ -1243,6 +1243,13 @@ S: Odd fixes
F: util/buffer.c
F: include/qemu/buffer.h
I/O Channels
M: Daniel P. Berrange <berrange@redhat.com>
S: Maintained
F: io/
F: include/io/
F: tests/test-io-*
Usermode Emulation
------------------
Overall
......
......@@ -159,6 +159,7 @@ dummy := $(call unnest-vars,, \
crypto-obj-y \
crypto-aes-obj-y \
qom-obj-y \
io-obj-y \
common-obj-y \
common-obj-m)
......@@ -178,6 +179,7 @@ SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
$(SOFTMMU_SUBDIR_RULES): $(block-obj-y)
$(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y)
$(SOFTMMU_SUBDIR_RULES): $(io-obj-y)
$(SOFTMMU_SUBDIR_RULES): config-all-devices.mak
subdir-%:
......
......@@ -28,6 +28,11 @@ crypto-aes-obj-y = crypto/
qom-obj-y = qom/
#######################################################################
# io-obj-y is code used by both qemu system emulation and qemu-img
io-obj-y = io/
######################################################################
# Target independent part of system emulation. The long term path is to
# suppress *all* target specific code in case of system emulation, i.e. a
......
......@@ -176,6 +176,7 @@ dummy := $(call unnest-vars,.., \
crypto-obj-y \
crypto-aes-obj-y \
qom-obj-y \
io-obj-y \
common-obj-y \
common-obj-m)
target-obj-y := $(target-obj-y-save)
......@@ -185,6 +186,7 @@ all-obj-y += $(qom-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y)
all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
$(QEMU_PROG_BUILD): config-devices.mak
......
......@@ -2426,6 +2426,14 @@ else
fi
##########################################
# getifaddrs (for tests/test-io-channel-socket )
have_ifaddrs_h=yes
if ! check_include "ifaddrs.h" ; then
have_ifaddrs_h=no
fi
##########################################
# VTE probe
......@@ -5137,6 +5145,9 @@ fi
if test "$tasn1" = "yes" ; then
echo "CONFIG_TASN1=y" >> $config_host_mak
fi
if test "$have_ifaddrs_h" = "yes" ; then
echo "HAVE_IFADDRS_H=y" >> $config_host_mak
fi
if test "$vte" = "yes" ; then
echo "CONFIG_VTE=y" >> $config_host_mak
echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak
......
/*
* 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 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_BUFFER_H__
#define QIO_CHANNEL_BUFFER_H__
#include "io/channel.h"
#define TYPE_QIO_CHANNEL_BUFFER "qio-channel-buffer"
#define QIO_CHANNEL_BUFFER(obj) \
OBJECT_CHECK(QIOChannelBuffer, (obj), TYPE_QIO_CHANNEL_BUFFER)
typedef struct QIOChannelBuffer QIOChannelBuffer;
/**
* QIOChannelBuffer:
*
* The QIOChannelBuffer object provides a channel implementation
* that is able to perform I/O to/from a memory buffer.
*
*/
struct QIOChannelBuffer {
QIOChannel parent;
size_t capacity; /* Total allocated memory */
size_t usage; /* Current size of data */
size_t offset; /* Offset for future I/O ops */
char *data;
};
/**
* qio_channel_buffer_new:
* @capacity: the initial buffer capacity to allocate
*
* Allocate a new buffer which is initially empty
*
* Returns: the new channel object
*/
QIOChannelBuffer *
qio_channel_buffer_new(size_t capacity);
#endif /* QIO_CHANNEL_BUFFER_H__ */
/*
* QEMU I/O channels external command driver
*
* Copyright (c) 2015 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef QIO_CHANNEL_COMMAND_H__
#define QIO_CHANNEL_COMMAND_H__
#include "io/channel.h"
#define TYPE_QIO_CHANNEL_COMMAND "qio-channel-command"
#define QIO_CHANNEL_COMMAND(obj) \
OBJECT_CHECK(QIOChannelCommand, (obj), TYPE_QIO_CHANNEL_COMMAND)
typedef struct QIOChannelCommand QIOChannelCommand;
/**
* QIOChannelCommand:
*
* The QIOChannelCommand class provides a channel implementation
* that can transport data with an externally running command
* via its stdio streams.
*/
struct QIOChannelCommand {
QIOChannel parent;
int writefd;
int readfd;
pid_t pid;
};
/**
* 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 of the running child command
* @errp: pointer to an uninitialized 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
*/
QIOChannelCommand *
qio_channel_command_new_pid(int writefd,
int readfd,
pid_t pid);
/**
* qio_channel_command_new_spawn:
* @argv: the NULL terminated list of command arguments
* @flags: the I/O mode, one of O_RDONLY, O_WRONLY, O_RDWR
* @errp: pointer to an uninitialized error object
*
* Create a channel for performing I/O with the
* command to be spawned with arguments @argv.
*
* Returns: the command channel object, or NULL on error
*/
QIOChannelCommand *
qio_channel_command_new_spawn(const char *const argv[],
int flags,
Error **errp);
#endif /* QIO_CHANNEL_COMMAND_H__ */
/*
* 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 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"
#define TYPE_QIO_CHANNEL_FILE "qio-channel-file"
#define QIO_CHANNEL_FILE(obj) \
OBJECT_CHECK(QIOChannelFile, (obj), TYPE_QIO_CHANNEL_FILE)
typedef struct QIOChannelFile QIOChannelFile;
/**
* QIOChannelFile:
*
* The QIOChannelFile object provides a channel implementation
* that is able to perform I/O on block devices, character
* devices, FIFOs, pipes and plain files. While it is technically
* able to work on sockets too on the UNIX platform, this is not
* portable to Windows and lacks some extra sockets specific
* functionality. So the QIOChannelSocket object is recommended
* for that use case.
*
*/
struct QIOChannelFile {
QIOChannel parent;
int fd;
};
/**
* qio_channel_file_new_fd:
* @fd: the file descriptor
*
* Create a new IO channel object for a file represented
* by the @fd parameter. @fd can be associated with a
* block device, character device, fifo, pipe, or a
* regular file. For sockets, the QIOChannelSocket class
* should be used instead, as this provides greater
* functionality and cross platform portability.
*
* The channel will own the passed in file descriptor
* and will take responsibility for closing it, so the
* caller must not close it. If appropriate the caller
* should dup() its FD before opening the channel.
*
* Returns: the new channel object
*/
QIOChannelFile *
qio_channel_file_new_fd(int fd);
/**
* qio_channel_file_new_path:
* @fd: the file descriptor
* @flags: the open flags (O_RDONLY|O_WRONLY|O_RDWR, etc)
* @mode: the file creation mode if O_WRONLY is set in @flags
* @errp: pointer to initialized error object
*
* Create a new IO channel object for a file represented
* by the @path parameter. @path can point to any
* type of file on which sequential I/O can be
* performed, whether it be a plain file, character
* device or block device.
*
* Returns: the new channel object
*/
QIOChannelFile *
qio_channel_file_new_path(const char *path,
int flags,
mode_t mode,
Error **errp);
#endif /* QIO_CHANNEL_FILE_H__ */
/*
* QEMU I/O channels sockets driver
*
* Copyright (c) 2015 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef QIO_CHANNEL_SOCKET_H__
#define QIO_CHANNEL_SOCKET_H__
#include "io/channel.h"
#include "io/task.h"
#include "qemu/sockets.h"
#define TYPE_QIO_CHANNEL_SOCKET "qio-channel-socket"
#define QIO_CHANNEL_SOCKET(obj) \
OBJECT_CHECK(QIOChannelSocket, (obj), TYPE_QIO_CHANNEL_SOCKET)
typedef struct QIOChannelSocket QIOChannelSocket;
/**
* QIOChannelSocket:
*
* The QIOChannelSocket class provides a channel implementation
* that can transport data over a UNIX socket or TCP socket.
* Beyond the core channel API, it also provides functionality
* for accepting client connections, tuning some socket
* parameters and getting socket address strings.
*/
struct QIOChannelSocket {
QIOChannel parent;
int fd;
struct sockaddr_storage localAddr;
socklen_t localAddrLen;
struct sockaddr_storage remoteAddr;
socklen_t remoteAddrLen;
};
/**
* qio_channel_socket_new:
*
* Create a channel for performing I/O on a socket
* connection, that is initially closed. After
* creating the socket, it must be setup as a client
* connection or server.
*
* Returns: the socket channel object
*/
QIOChannelSocket *
qio_channel_socket_new(void);
/**
* qio_channel_socket_new_fd:
* @fd: the socket file descriptor
* @errp: pointer to an uninitialized error object
*
* Create a channel for performing I/O on the socket
* connection represented by the file descriptor @fd.
*
* Returns: the socket channel object, or NULL on error
*/
QIOChannelSocket *
qio_channel_socket_new_fd(int fd,
Error **errp);
/**
* qio_channel_socket_connect_sync:
* @ioc: the socket channel object
* @addr: the address to connect to
* @errp: pointer to an uninitialized error object
*
* Attempt to connect to the address @addr. This method
* will run in the foreground so the caller will not regain
* execution control until the connection is established or
* an error occurs.
*/
int qio_channel_socket_connect_sync(QIOChannelSocket *ioc,
SocketAddress *addr,
Error **errp);
/**
* qio_channel_socket_connect_async:
* @ioc: the socket channel object
* @addr: the address to connect to
* @callback: the function to invoke on completion
* @opaque: user data to pass to @callback
* @destroy: the function to free @opaque
*
* Attempt to connect to the address @addr. This method
* will run in the background so the caller will regain
* execution control immediately. The function @callback
* will be invoked on completion or failure.
*/
void qio_channel_socket_connect_async(QIOChannelSocket *ioc,
SocketAddress *addr,
QIOTaskFunc callback,
gpointer opaque,
GDestroyNotify destroy);
/**
* qio_channel_socket_listen_sync:
* @ioc: the socket channel object
* @addr: the address to listen to
* @errp: pointer to an uninitialized error object
*
* Attempt to listen to the address @addr. This method
* will run in the foreground so the caller will not regain
* execution control until the connection is established or
* an error occurs.
*/
int qio_channel_socket_listen_sync(QIOChannelSocket *ioc,
SocketAddress *addr,
Error **errp);
/**
* qio_channel_socket_listen_async:
* @ioc: the socket channel object
* @addr: the address to listen to
* @callback: the function to invoke on completion
* @opaque: user data to pass to @callback
* @destroy: the function to free @opaque
*
* Attempt to listen to the address @addr. This method
* will run in the background so the caller will regain
* execution control immediately. The function @callback
* will be invoked on completion or failure.
*/
void qio_channel_socket_listen_async(QIOChannelSocket *ioc,
SocketAddress *addr,
QIOTaskFunc callback,
gpointer opaque,
GDestroyNotify destroy);
/**
* qio_channel_socket_dgram_sync:
* @ioc: the socket channel object
* @localAddr: the address to local bind address
* @remoteAddr: the address to remote peer address
* @errp: pointer to an uninitialized error object
*
* Attempt to initialize a datagram socket bound to
* @localAddr and communicating with peer @remoteAddr.
* This method will run in the foreground so the caller
* will not regain execution control until the socket
* is established or an error occurs.
*/
int qio_channel_socket_dgram_sync(QIOChannelSocket *ioc,
SocketAddress *localAddr,
SocketAddress *remoteAddr,
Error **errp);
/**
* qio_channel_socket_dgram_async:
* @ioc: the socket channel object
* @localAddr: the address to local bind address
* @remoteAddr: the address to remote peer address
* @callback: the function to invoke on completion
* @opaque: user data to pass to @callback
* @destroy: the function to free @opaque
*
* Attempt to initialize a datagram socket bound to
* @localAddr and communicating with peer @remoteAddr.
* This method will run in the background so the caller
* will regain execution control immediately. The function
* @callback will be invoked on completion or failure.
*/
void qio_channel_socket_dgram_async(QIOChannelSocket *ioc,
SocketAddress *localAddr,
SocketAddress *remoteAddr,
QIOTaskFunc callback,
gpointer opaque,
GDestroyNotify destroy);
/**
* qio_channel_socket_get_local_address:
* @ioc: the socket channel object
* @errp: pointer to an uninitialized error object
*
* Get the string representation of the local socket
* address. A pointer to the allocated address information
* struct will be returned, which the caller is required to
* release with a call qapi_free_SocketAddress when no
* longer required.
*
* Returns: 0 on success, -1 on error
*/
SocketAddress *
qio_channel_socket_get_local_address(QIOChannelSocket *ioc,
Error **errp);
/**
* qio_channel_socket_get_remote_address:
* @ioc: the socket channel object
* @errp: pointer to an uninitialized error object
*
* Get the string representation of the local socket
* address. A pointer to the allocated address information
* struct will be returned, which the caller is required to
* release with a call qapi_free_SocketAddress when no
* longer required.
*
* Returns: the socket address struct, or NULL on error
*/
SocketAddress *
qio_channel_socket_get_remote_address(QIOChannelSocket *ioc,
Error **errp);
/**
* qio_channel_socket_accept:
* @ioc: the socket channel object
* @errp: pointer to an uninitialized error object
*
* If the socket represents a server, then this accepts
* a new client connection. The returned channel will
* represent the connected client socket.
*
* Returns: the new client channel, or NULL on error
*/
QIOChannelSocket *
qio_channel_socket_accept(QIOChannelSocket *ioc,
Error **errp);
#endif /* QIO_CHANNEL_SOCKET_H__ */
/*
* 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 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_TLS_H__
#define QIO_CHANNEL_TLS_H__
#include "io/channel.h"
#include "io/task.h"
#include "crypto/tlssession.h"
#define TYPE_QIO_CHANNEL_TLS "qio-channel-tls"
#define QIO_CHANNEL_TLS(obj) \
OBJECT_CHECK(QIOChannelTLS, (obj), TYPE_QIO_CHANNEL_TLS)
typedef struct QIOChannelTLS QIOChannelTLS;
/**
* QIOChannelTLS
*
* The QIOChannelTLS class provides a channel wrapper which
* can transparently run the TLS encryption protocol. It is
* usually used over a TCP socket, but there is actually no
* technical restriction on which type of master channel is
* used as the transport.
*
* This channel object is capable of running as either a
* TLS server or TLS client.
*/
struct QIOChannelTLS {
QIOChannel parent;
QIOChannel *master;
QCryptoTLSSession *session;
};
/**
* qio_channel_tls_new_server:
* @master: the underlying channel object
* @creds: the credentials to use for TLS handshake
* @aclname: the access control list for validating clients
* @errp: pointer to an uninitialized error object
*
* Create a new TLS channel that runs the server side of
* a TLS session. The TLS session handshake will use the
* credentials provided in @creds. If the @aclname parameter
* is non-NULL, then the client will have to provide
* credentials (ie a x509 client certificate) which will
* then be validated against the ACL.
*
* After creating the channel, it is mandatory to call
* the qio_channel_tls_handshake() method before attempting
* todo any I/O on the channel.
*
* Once the handshake has completed, all I/O should be done
* via the new TLS channel object and not the original
* master channel
*
* Returns: the new TLS channel object, or NULL
*/
QIOChannelTLS *
qio_channel_tls_new_server(QIOChannel *master,
QCryptoTLSCreds *creds,
const char *aclname,
Error **errp);
/**
* qio_channel_tls_new_client:
* @master: the underlying channel object
* @creds: the credentials to use for TLS handshake
* @hostname: the user specified server hostname
* @errp: pointer to an uninitialized error object
*
* Create a new TLS channel that runs the client side of
* a TLS session. The TLS session handshake will use the
* credentials provided in @creds. The @hostname parameter
* should provide the user specified hostname of the server
* and will be validated against the server's credentials
* (ie CommonName of the x509 certificate)
*
* After creating the channel, it is mandatory to call
* the qio_channel_tls_handshake() method before attempting
* todo any I/O on the channel.
*
* Once the handshake has completed, all I/O should be done
* via the new TLS channel object and not the original
* master channel
*
* Returns: the new TLS channel object, or NULL
*/
QIOChannelTLS *
qio_channel_tls_new_client(QIOChannel *master,
QCryptoTLSCreds *creds,
const char *hostname,
Error **errp);
/**
* qio_channel_tls_handshake:
* @ioc: the TLS channel object
* @func: the callback to invoke when completed
* @opaque: opaque data to pass to @func
* @destroy: optional callback to free @opaque
*
* Perform the TLS session handshake. This method
* will return immediately and the handshake will
* continue in the background, provided the main
* loop is running. When the handshake is complete,
* or fails, the @func callback will be invoked.
*/
void qio_channel_tls_handshake(QIOChannelTLS *ioc,
QIOTaskFunc func,
gpointer opaque,
GDestroyNotify destroy);
/**
* qio_channel_tls_get_session:
* @ioc: the TLS channel object
*
* Get the TLS session used by the channel.
*
* Returns: the TLS session
*/
QCryptoTLSSession *
qio_channel_tls_get_session(QIOChannelTLS *ioc);
#endif /* QIO_CHANNEL_TLS_H__ */
/*
* 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 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_WATCH_H__
#define QIO_CHANNEL_WATCH_H__
#include "io/channel.h"
/*
* This module provides helper functions that will be needed by
* the various QIOChannel implementations, for creating watches
* on file descriptors / sockets
*/
/**
* qio_channel_create_fd_watch:
* @ioc: the channel object
* @fd: the file descriptor
* @condition: the I/O condition
*
* Create a new main loop source that is able to
* monitor the file descriptor @fd for the
* I/O conditions in @condition. This is able
* monitor block devices, character devices,
* sockets, pipes but not plain files.
*
* Returns: the new main loop source
*/
GSource *qio_channel_create_fd_watch(QIOChannel *ioc,
int fd,
GIOCondition condition);
/**
* qio_channel_create_fd_pair_watch:
* @ioc: the channel object
* @fdread: the file descriptor for reading
* @fdwrite: the file descriptor for writing
* @condition: the I/O condition
*
* Create a new main loop source that is able to
* monitor the pair of file descriptors @fdread
* and @fdwrite for the I/O conditions in @condition.
* This is intended for monitoring unidirectional
* file descriptors such as pipes, where a pair
* of descriptors is required for bidirectional
* I/O
*
* Returns: the new main loop source
*/
GSource *qio_channel_create_fd_pair_watch(QIOChannel *ioc,
int fdread,
int fdwrite,
GIOCondition condition);
#endif /* QIO_CHANNEL_WATCH_H__ */
/*
* QEMU I/O channels driver websockets
*
* Copyright (c) 2015 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef QIO_CHANNEL_WEBSOCK_H__
#define QIO_CHANNEL_WEBSOCK_H__
#include "io/channel.h"
#include "qemu/buffer.h"
#include "io/task.h"
#define TYPE_QIO_CHANNEL_WEBSOCK "qio-channel-websock"
#define QIO_CHANNEL_WEBSOCK(obj) \
OBJECT_CHECK(QIOChannelWebsock, (obj), TYPE_QIO_CHANNEL_WEBSOCK)
typedef struct QIOChannelWebsock QIOChannelWebsock;
typedef union QIOChannelWebsockMask QIOChannelWebsockMask;
union QIOChannelWebsockMask {
char c[4];
uint32_t u;
};
/**
* QIOChannelWebsock
*
* The QIOChannelWebsock class provides a channel wrapper which
* can transparently run the HTTP websockets protocol. This is
* usually used over a TCP socket, but there is actually no
* technical restriction on which type of master channel is
* used as the transport.
*
* This channel object is currently only capable of running as
* a websocket server and is a pretty crude implementation
* of it, not supporting the full websockets protocol feature
* set. It is sufficient to use with a simple websockets
* client for encapsulating VNC for noVNC in-browser client.
*/
struct QIOChannelWebsock {
QIOChannel parent;
QIOChannel *master;
Buffer encinput;
Buffer encoutput;
Buffer rawinput;
Buffer rawoutput;
size_t payload_remain;
QIOChannelWebsockMask mask;
guint io_tag;
Error *io_err;
gboolean io_eof;
};
/**
* qio_channel_websock_new_server:
* @master: the underlying channel object
*
* Create a new websockets channel that runs the server
* side of the protocol.
*
* After creating the channel, it is mandatory to call
* the qio_channel_websock_handshake() method before attempting
* todo any I/O on the channel.
*
* Once the handshake has completed, all I/O should be done
* via the new websocket channel object and not the original
* master channel
*
* Returns: the new websockets channel object
*/
QIOChannelWebsock *
qio_channel_websock_new_server(QIOChannel *master);
/**
* qio_channel_websock_handshake:
* @ioc: the websocket channel object
* @func: the callback to invoke when completed
* @opaque: opaque data to pass to @func
* @destroy: optional callback to free @opaque
*
* Perform the websocket handshake. This method
* will return immediately and the handshake will
* continue in the background, provided the main
* loop is running. When the handshake is complete,
* or fails, the @func callback will be invoked.
*/
void qio_channel_websock_handshake(QIOChannelWebsock *ioc,
QIOTaskFunc func,
gpointer opaque,
GDestroyNotify destroy);
#endif /* QIO_CHANNEL_WEBSOCK_H__ */
/*
* 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 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_H__
#define QIO_CHANNEL_H__
#include "qemu-common.h"
#include "qapi/error.h"
#include "qom/object.h"
#define TYPE_QIO_CHANNEL "qio-channel"
#define QIO_CHANNEL(obj) \
OBJECT_CHECK(QIOChannel, (obj), TYPE_QIO_CHANNEL)
#define QIO_CHANNEL_CLASS(klass) \
OBJECT_CLASS_CHECK(QIOChannelClass, klass, TYPE_QIO_CHANNEL)
#define QIO_CHANNEL_GET_CLASS(obj) \
OBJECT_GET_CLASS(QIOChannelClass, obj, TYPE_QIO_CHANNEL)
typedef struct QIOChannel QIOChannel;
typedef struct QIOChannelClass QIOChannelClass;
#define QIO_CHANNEL_ERR_BLOCK -2
typedef enum QIOChannelFeature QIOChannelFeature;
enum QIOChannelFeature {
QIO_CHANNEL_FEATURE_FD_PASS = (1 << 0),
QIO_CHANNEL_FEATURE_SHUTDOWN = (1 << 1),
};
typedef enum QIOChannelShutdown QIOChannelShutdown;
enum QIOChannelShutdown {
QIO_CHANNEL_SHUTDOWN_BOTH,
QIO_CHANNEL_SHUTDOWN_READ,
QIO_CHANNEL_SHUTDOWN_WRITE,
};
typedef gboolean (*QIOChannelFunc)(QIOChannel *ioc,
GIOCondition condition,
gpointer data);
/**
* QIOChannel:
*
* The QIOChannel defines the core API for a generic I/O channel
* class hierarchy. It is inspired by GIOChannel, but has the
* following differences
*
* - Use QOM to properly support arbitrary subclassing
* - Support use of iovecs for efficient I/O with multiple blocks
* - None of the character set translation, binary data exclusively
* - Direct support for QEMU Error object reporting
* - File descriptor passing
*
* This base class is abstract so cannot be instantiated. There
* will be subclasses for dealing with sockets, files, and higher
* level protocols such as TLS, WebSocket, etc.
*/
struct QIOChannel {
Object parent;
unsigned int features; /* bitmask of QIOChannelFeatures */
};
/**
* QIOChannelClass:
*
* This class defines the contract that all subclasses
* must follow to provide specific channel implementations.
* The first five callbacks are mandatory to support, others
* provide additional optional features.
*
* Consult the corresponding public API docs for a description
* of the semantics of each callback
*/
struct QIOChannelClass {
ObjectClass parent;
/* Mandatory callbacks */
ssize_t (*io_writev)(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
int *fds,
size_t nfds,
Error **errp);
ssize_t (*io_readv)(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
int **fds,
size_t *nfds,
Error **errp);
int (*io_close)(QIOChannel *ioc,
Error **errp);
GSource * (*io_create_watch)(QIOChannel *ioc,
GIOCondition condition);
int (*io_set_blocking)(QIOChannel *ioc,
bool enabled,
Error **errp);
/* Optional callbacks */
int (*io_shutdown)(QIOChannel *ioc,
QIOChannelShutdown how,
Error **errp);
void (*io_set_cork)(QIOChannel *ioc,
bool enabled);
void (*io_set_delay)(QIOChannel *ioc,
bool enabled);
off_t (*io_seek)(QIOChannel *ioc,
off_t offset,
int whence,
Error **errp);
};
/* General I/O handling functions */
/**
* qio_channel_has_feature:
* @ioc: the channel object
* @feature: the feature to check support of
*
* Determine whether the channel implementation supports
* the optional feature named in @feature.
*
* Returns: true if supported, false otherwise.
*/
bool qio_channel_has_feature(QIOChannel *ioc,
QIOChannelFeature feature);
/**
* qio_channel_readv_full:
* @ioc: the channel object
* @iov: the array of memory regions to read data into
* @niov: the length of the @iov array
* @fds: pointer to an array that will received file handles
* @nfds: pointer filled with number of elements in @fds on return
* @errp: pointer to an uninitialized error object
*
* Read data from the IO channel, storing it in the
* memory regions referenced by @iov. Each element
* in the @iov will be fully populated with data
* before the next one is used. The @niov parameter
* specifies the total number of elements in @iov.
*
* It is not required for all @iov to be filled with
* data. If the channel is in blocking mode, at least
* one byte of data will be read, but no more is
* guaranteed. If the channel is non-blocking and no
* data is available, it will return QIO_CHANNEL_ERR_BLOCK
*
* If the channel has passed any file descriptors,
* the @fds array pointer will be allocated and
* the elements filled with the received file
* descriptors. The @nfds pointer will be updated
* to indicate the size of the @fds array that
* was allocated. It is the callers responsibility
* to call close() on each file descriptor and to
* call g_free() on the array pointer in @fds.
*
* It is an error to pass a non-NULL @fds parameter
* unless qio_channel_has_feature() returns a true
* value for the QIO_CHANNEL_FEATURE_FD_PASS constant.
*
* Returns: the number of bytes read, or -1 on error,
* or QIO_CHANNEL_ERR_BLOCK if no data is available
* and the channel is non-blocking
*/
ssize_t qio_channel_readv_full(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
int **fds,
size_t *nfds,
Error **errp);
/**
* qio_channel_writev_full:
* @ioc: the channel object
* @iov: the array of memory regions to write data from
* @niov: the length of the @iov array
* @fds: an array of file handles to send
* @nfds: number of file handles in @fds
* @errp: pointer to an uninitialized error object
*
* Write data to the IO channel, reading it from the
* memory regions referenced by @iov. Each element
* in the @iov will be fully sent, before the next
* one is used. The @niov parameter specifies the
* total number of elements in @iov.
*
* It is not required for all @iov data to be fully
* sent. If the channel is in blocking mode, at least
* one byte of data will be sent, but no more is
* guaranteed. If the channel is non-blocking and no
* data can be sent, it will return QIO_CHANNEL_ERR_BLOCK
*
* If there are file descriptors to send, the @fds
* array should be non-NULL and provide the handles.
* All file descriptors will be sent if at least one
* byte of data was sent.
*
* It is an error to pass a non-NULL @fds parameter
* unless qio_channel_has_feature() returns a true
* value for the QIO_CHANNEL_FEATURE_FD_PASS constant.
*
* Returns: the number of bytes sent, or -1 on error,
* or QIO_CHANNEL_ERR_BLOCK if no data is can be sent
* and the channel is non-blocking
*/
ssize_t qio_channel_writev_full(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
int *fds,
size_t nfds,
Error **errp);
/**
* qio_channel_readv:
* @ioc: the channel object
* @iov: the array of memory regions to read data into
* @niov: the length of the @iov array
* @errp: pointer to an uninitialized error object
*
* Behaves as qio_channel_readv_full() but does not support
* receiving of file handles.
*/
ssize_t qio_channel_readv(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
Error **errp);
/**
* qio_channel_writev:
* @ioc: the channel object
* @iov: the array of memory regions to write data from
* @niov: the length of the @iov array
* @errp: pointer to an uninitialized error object
*
* Behaves as qio_channel_writev_full() but does not support
* sending of file handles.
*/
ssize_t qio_channel_writev(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
Error **errp);
/**
* qio_channel_readv:
* @ioc: the channel object
* @buf: the memory region to read data into
* @buflen: the length of @buf
* @errp: pointer to an uninitialized error object
*
* Behaves as qio_channel_readv_full() but does not support
* receiving of file handles, and only supports reading into
* a single memory region.
*/
ssize_t qio_channel_read(QIOChannel *ioc,
char *buf,
size_t buflen,
Error **errp);
/**
* qio_channel_writev:
* @ioc: the channel object
* @buf: the memory regions to send data from
* @buflen: the length of @buf
* @errp: pointer to an uninitialized error object
*
* Behaves as qio_channel_writev_full() but does not support
* sending of file handles, and only supports writing from a
* single memory region.
*/
ssize_t qio_channel_write(QIOChannel *ioc,
const char *buf,
size_t buflen,
Error **errp);
/**
* qio_channel_set_blocking:
* @ioc: the channel object
* @enabled: the blocking flag state
* @errp: pointer to an uninitialized error object
*
* If @enabled is true, then the channel is put into
* blocking mode, otherwise it will be non-blocking.
*
* In non-blocking mode, read/write operations may
* return QIO_CHANNEL_ERR_BLOCK if they would otherwise
* block on I/O
*/
int qio_channel_set_blocking(QIOChannel *ioc,
bool enabled,
Error **errp);
/**
* qio_channel_close:
* @ioc: the channel object
* @errp: pointer to an uninitialized error object
*
* Close the channel, flushing any pending I/O
*
* Returns: 0 on success, -1 on error
*/
int qio_channel_close(QIOChannel *ioc,
Error **errp);
/**
* qio_channel_shutdown:
* @ioc: the channel object
* @how: the direction to shutdown
* @errp: pointer to an uninitialized error object
*
* Shutdowns transmission and/or receiving of data
* without closing the underlying transport.
*
* Not all implementations will support this facility,
* so may report an error. To avoid errors, the
* caller may check for the feature flag
* QIO_CHANNEL_FEATURE_SHUTDOWN prior to calling
* this method.
*
* Returns: 0 on success, -1 on error
*/
int qio_channel_shutdown(QIOChannel *ioc,
QIOChannelShutdown how,
Error **errp);
/**
* qio_channel_set_delay:
* @ioc: the channel object
* @enabled: the new flag state
*
* Controls whether the underlying transport is
* permitted to delay writes in order to merge
* small packets. If @enabled is true, then the
* writes may be delayed in order to opportunistically
* merge small packets into larger ones. If @enabled
* is false, writes are dispatched immediately with
* no delay.
*
* When @enabled is false, applications may wish to
* use the qio_channel_set_cork() method to explicitly
* control write merging.
*
* On channels which are backed by a socket, this
* API corresponds to the inverse of TCP_NODELAY flag,
* controlling whether the Nagle algorithm is active.
*
* This setting is merely a hint, so implementations are
* free to ignore this without it being considered an
* error.
*/
void qio_channel_set_delay(QIOChannel *ioc,
bool enabled);
/**
* qio_channel_set_cork:
* @ioc: the channel object
* @enabled: the new flag state
*
* Controls whether the underlying transport is
* permitted to dispatch data that is written.
* If @enabled is true, then any data written will
* be queued in local buffers until @enabled is
* set to false once again.
*
* This feature is typically used when the automatic
* write coalescing facility is disabled via the
* qio_channel_set_delay() method.
*
* On channels which are backed by a socket, this
* API corresponds to the TCP_CORK flag.
*
* This setting is merely a hint, so implementations are
* free to ignore this without it being considered an
* error.
*/
void qio_channel_set_cork(QIOChannel *ioc,
bool enabled);
/**
* qio_channel_seek:
* @ioc: the channel object
* @offset: the position to seek to, relative to @whence
* @whence: one of the (POSIX) SEEK_* constants listed below
* @errp: pointer to an uninitialized error object
*
* Moves the current I/O position within the channel
* @ioc, to be @offset. The value of @offset is
* interpreted relative to @whence:
*
* SEEK_SET - the position is set to @offset bytes
* SEEK_CUR - the position is moved by @offset bytes
* SEEK_END - the position is set to end of the file plus @offset bytes
*
* Not all implementations will support this facility,
* so may report an error.
*
* Returns: the new position on success, (off_t)-1 on failure
*/
off_t qio_channel_io_seek(QIOChannel *ioc,
off_t offset,
int whence,
Error **errp);
/**
* qio_channel_create_watch:
* @ioc: the channel object
* @condition: the I/O condition to monitor
*
* Create a new main loop source that is used to watch
* for the I/O condition @condition. Typically the
* qio_channel_add_watch() method would be used instead
* of this, since it directly attaches a callback to
* the source
*
* Returns: the new main loop source.
*/
GSource *qio_channel_create_watch(QIOChannel *ioc,
GIOCondition condition);
/**
* qio_channel_add_watch:
* @ioc: the channel object
* @condition: the I/O condition to monitor
* @func: callback to invoke when the source becomes ready
* @user_data: opaque data to pass to @func
* @notify: callback to free @user_data
*
* Create a new main loop source that is used to watch
* for the I/O condition @condition. The callback @func
* will be registered against the source, to be invoked
* when the source becomes ready. The optional @user_data
* will be passed to @func when it is invoked. The @notify
* callback will be used to free @user_data when the
* watch is deleted
*
* The returned source ID can be used with g_source_remove()
* to remove and free the source when no longer required.
* Alternatively the @func callback can return a FALSE
* value.
*
* Returns: the source ID
*/
guint qio_channel_add_watch(QIOChannel *ioc,
GIOCondition condition,
QIOChannelFunc func,
gpointer user_data,
GDestroyNotify notify);
/**
* qio_channel_yield:
* @ioc: the channel object
* @condition: the I/O condition to wait for
*
* Yields execution from the current coroutine until
* the condition indicated by @condition becomes
* available.
*
* This must only be called from coroutine context
*/
void qio_channel_yield(QIOChannel *ioc,
GIOCondition condition);
/**
* qio_channel_wait:
* @ioc: the channel object
* @condition: the I/O condition to wait for
*
* Block execution from the current thread until
* the condition indicated by @condition becomes
* available.
*
* This will enter a nested event loop to perform
* the wait.
*/
void qio_channel_wait(QIOChannel *ioc,
GIOCondition condition);
#endif /* QIO_CHANNEL_H__ */
/*
* 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 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_TASK_H__
#define QIO_TASK_H__
#include "qemu-common.h"
#include "qapi/error.h"
#include "qom/object.h"
typedef struct QIOTask QIOTask;
typedef void (*QIOTaskFunc)(Object *source,
Error *err,
gpointer opaque);
typedef int (*QIOTaskWorker)(QIOTask *task,
Error **errp,
gpointer opaque);
/**
* QIOTask:
*
* The QIOTask object provides a simple mechanism for reporting
* success / failure of long running background operations.
*
* A object on which the operation is to be performed could have
* a public API which accepts a task callback:
*
* <example>
* <title>Task callback function signature</title>
* <programlisting>
* void myobject_operation(QMyObject *obj,
* QIOTaskFunc *func,
* gpointer opaque,
* GDestroyNotify *notify);
* </programlisting>
* </example>
*
* The 'func' parameter is the callback to be invoked, and 'opaque'
* is data to pass to it. The optional 'notify' function is used
* to free 'opaque' when no longer needed.
*
* Now, lets say the implementation of this method wants to set
* a timer to run once a second checking for completion of some
* activity. It would do something like
*
* <example>
* <title>Task callback function implementation</title>
* <programlisting>
* void myobject_operation(QMyObject *obj,
* QIOTaskFunc *func,
* gpointer opaque,
* GDestroyNotify *notify)
* {
* QIOTask *task;
*
* task = qio_task_new(OBJECT(obj), func, opaque, notify);
*
* g_timeout_add_full(G_PRIORITY_DEFAULT,
* 1000,
* myobject_operation_timer,
* task,
* NULL);
* }
* </programlisting>
* </example>
*
* It could equally have setup a watch on a file descriptor or
* created a background thread, or something else entirely.
* Notice that the source object is passed to the task, and
* QIOTask will hold a reference on that. This ensure that
* the QMyObject instance cannot be garbage collected while
* the async task is still in progress.
*
* In this case, myobject_operation_timer will fire after
* 3 secs and do
*
* <example>
* <title>Task timer function</title>
* <programlisting>
* gboolean myobject_operation_timer(gpointer opaque)
* {
* QIOTask *task = QIO_TASK(opaque);
* Error *err;*
*
* ...check something important...
* if (err) {
* qio_task_abort(task, err);
* error_free(task);
* return FALSE;
* } else if (...work is completed ...) {
* qio_task_complete(task);
* return FALSE;
* }
* ...carry on polling ...
* return TRUE;
* }
* </programlisting>
* </example>
*
* Once this function returns false, object_unref will be called
* automatically on the task causing it to be released and the
* ref on QMyObject dropped too.
*
* The QIOTask module can also be used to perform operations
* in a background thread context, while still reporting the
* results in the main event thread. This allows code which
* cannot easily be rewritten to be asychronous (such as DNS
* lookups) to be easily run non-blocking. Reporting the
* results in the main thread context means that the caller
* typically does not need to be concerned about thread
* safety wrt the QEMU global mutex.
*
* For example, the socket_listen() method will block the caller
* while DNS lookups take place if given a name, instead of IP
* address. The C library often do not provide a practical async
* DNS API, so the to get non-blocking DNS lookups in a portable
* manner requires use of a thread. So achieve a non-blocking
* socket listen using QIOTask would require:
*
* <example>
* static int myobject_listen_worker(QIOTask *task,
* Error **errp,
* gpointer opaque)
* {
* QMyObject obj = QMY_OBJECT(qio_task_get_source(task));
* SocketAddress *addr = opaque;
*
* obj->fd = socket_listen(addr, errp);
* if (obj->fd < 0) {
* return -1;
* }
* return 0;
* }
*
* void myobject_listen_async(QMyObject *obj,
* SocketAddress *addr,
* QIOTaskFunc *func,
* gpointer opaque,
* GDestroyNotify *notify)
* {
* QIOTask *task;
* SocketAddress *addrCopy;
*
* qapi_copy_SocketAddress(&addrCopy, addr);
* task = qio_task_new(OBJECT(obj), func, opaque, notify);
*
* qio_task_run_in_thread(task, myobject_listen_worker,
* addrCopy,
* qapi_free_SocketAddress);
* }
* </example>
*
* NB, The 'func' callback passed into myobject_listen_async
* will be invoked from the main event thread, despite the
* actual operation being performed in a different thread.
*/
/**
* qio_task_new:
* @source: the object on which the operation is invoked
* @func: the callback to invoke when the task completes
* @opaque: opaque data to pass to @func when invoked
* @destroy: optional callback to free @opaque
*
* Creates a new task struct to track completion of a
* background operation running on the object @source.
* When the operation completes or fails, the callback
* @func will be invoked. The callback can access the
* 'err' attribute in the task object to determine if
* the operation was successful or not.
*
* The returned task will be released when one of
* qio_task_abort() or qio_task_complete() are invoked.
*
* Returns: the task struct
*/
QIOTask *qio_task_new(Object *source,
QIOTaskFunc func,
gpointer opaque,
GDestroyNotify destroy);
/**
* qio_task_run_in_thread:
* @task: the task struct
* @worker: the function to invoke in a thread
* @opaque: opaque data to pass to @worker
* @destroy: function to free @opaque
*
* Run a task in a background thread. If @worker
* returns 0 it will call qio_task_complete() in
* the main event thread context. If @worker
* returns -1 it will call qio_task_abort() in
* the main event thread context.
*/
void qio_task_run_in_thread(QIOTask *task,
QIOTaskWorker worker,
gpointer opaque,
GDestroyNotify destroy);
/**
* qio_task_complete:
* @task: the task struct
*
* Mark the operation as succesfully completed
* and free the memory for @task.
*/
void qio_task_complete(QIOTask *task);
/**
* qio_task_abort:
* @task: the task struct
* @err: the error to record for the operation
*
* Mark the operation as failed, with @err providing
* details about the failure. The @err may be freed
* afer the function returns, as the notification
* callback is invoked synchronously. The @task will
* be freed when this call completes.
*/
void qio_task_abort(QIOTask *task,
Error *err);
/**
* qio_task_get_source:
* @task: the task struct
*
* Get the source object associated with the background
* task. This returns a new reference to the object,
* which the caller must released with object_unref()
* when no longer required.
*
* Returns: the source object
*/
Object *qio_task_get_source(QIOTask *task);
#endif /* QIO_TASK_H__ */
......@@ -88,6 +88,25 @@ int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp);
int parse_host_port(struct sockaddr_in *saddr, const char *str);
int socket_init(void);
/**
* socket_sockaddr_to_address:
* @sa: socket address struct
* @salen: size of @sa struct
* @errp: pointer to uninitialized error object
*
* Get the string representation of the socket
* address. A pointer to the allocated address information
* struct will be returned, which the caller is required to
* release with a call qapi_free_SocketAddress when no
* longer required.
*
* Returns: the socket address struct, or NULL on error
*/
SocketAddress *
socket_sockaddr_to_address(struct sockaddr_storage *sa,
socklen_t salen,
Error **errp);
/**
* socket_local_address:
* @fd: the socket file handle
......
io-obj-y = channel.o
io-obj-y += channel-buffer.o
io-obj-y += channel-command.o
io-obj-y += channel-file.o
io-obj-y += channel-socket.o
io-obj-y += channel-tls.o
io-obj-y += channel-watch.o
io-obj-y += channel-websock.o
io-obj-y += task.o
/*
* 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 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 "io/channel-buffer.h"
#include "io/channel-watch.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(char, 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,
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,
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->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);
/*
* QEMU I/O channels external command driver
*
* Copyright (c) 2015 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include "io/channel-command.h"
#include "io/channel-watch.h"
#include "qemu/sockets.h"
#include "trace.h"
QIOChannelCommand *
qio_channel_command_new_pid(int writefd,
int readfd,
pid_t pid)
{
QIOChannelCommand *ioc;
ioc = QIO_CHANNEL_COMMAND(object_new(TYPE_QIO_CHANNEL_COMMAND));
ioc->readfd = readfd;
ioc->writefd = writefd;
ioc->pid = pid;
trace_qio_channel_command_new_pid(ioc, writefd, readfd, pid);
return ioc;
}
#ifndef WIN32
QIOChannelCommand *
qio_channel_command_new_spawn(const char *const argv[],
int flags,
Error **errp)
{
pid_t pid = -1;
int stdinfd[2] = { -1, -1 };
int stdoutfd[2] = { -1, -1 };
int devnull = -1;
bool stdinnull = false, stdoutnull = false;
QIOChannelCommand *ioc;
flags = flags & O_ACCMODE;
if (flags == O_RDONLY) {
stdinnull = true;
}
if (flags == O_WRONLY) {
stdoutnull = true;
}
if (stdinnull || stdoutnull) {
devnull = open("/dev/null", O_RDWR);
if (!devnull) {
error_setg_errno(errp, errno,
"Unable to open /dev/null");
goto error;
}
}
if ((!stdinnull && pipe(stdinfd) < 0) ||
(!stdoutnull && pipe(stdoutfd) < 0)) {
error_setg_errno(errp, errno,
"Unable to open pipe");
goto error;
}
pid = qemu_fork(errp);
if (pid < 0) {
goto error;
}
if (pid == 0) { /* child */
dup2(stdinnull ? devnull : stdinfd[0], STDIN_FILENO);
dup2(stdoutnull ? devnull : stdoutfd[1], STDOUT_FILENO);
/* Leave stderr connected to qemu's stderr */
if (!stdinnull) {
close(stdinfd[0]);
close(stdinfd[1]);
}
if (!stdoutnull) {
close(stdoutfd[0]);
close(stdoutfd[1]);
}
execv(argv[0], (char * const *)argv);
_exit(1);
}
if (!stdinnull) {
close(stdinfd[0]);
}
if (!stdoutnull) {
close(stdoutfd[1]);
}
ioc = qio_channel_command_new_pid(stdinnull ? devnull : stdinfd[1],
stdoutnull ? devnull : stdoutfd[0],
pid);
trace_qio_channel_command_new_spawn(ioc, argv[0], flags);
return ioc;
error:
if (stdinfd[0] != -1) {
close(stdinfd[0]);
}
if (stdinfd[1] != -1) {
close(stdinfd[1]);
}
if (stdoutfd[0] != -1) {
close(stdoutfd[0]);
}
if (stdoutfd[1] != -1) {
close(stdoutfd[1]);
}
return NULL;
}
#else /* WIN32 */
QIOChannelCommand *
qio_channel_command_new_spawn(const char *const argv[],
int flags,
Error **errp)
{
error_setg_errno(errp, ENOSYS,
"Command spawn not supported on this platform");
return NULL;
}
#endif /* WIN32 */
#ifndef WIN32
static int qio_channel_command_abort(QIOChannelCommand *ioc,
Error **errp)
{
pid_t ret;
int status;
int step = 0;
/* See if intermediate process has exited; if not, try a nice
* SIGTERM followed by a more severe SIGKILL.
*/
rewait:
trace_qio_channel_command_abort(ioc, ioc->pid);
ret = waitpid(ioc->pid, &status, WNOHANG);
trace_qio_channel_command_wait(ioc, ioc->pid, ret, status);
if (ret == (pid_t)-1) {
if (errno == EINTR) {
goto rewait;
} else {
error_setg_errno(errp, errno,
"Cannot wait on pid %llu",
(unsigned long long)ioc->pid);
return -1;
}
} else if (ret == 0) {
if (step == 0) {
kill(ioc->pid, SIGTERM);
} else if (step == 1) {
kill(ioc->pid, SIGKILL);
} else {
error_setg(errp,
"Process %llu refused to die",
(unsigned long long)ioc->pid);
return -1;
}
usleep(10 * 1000);
goto rewait;
}
return 0;
}
#endif /* ! WIN32 */
static void qio_channel_command_init(Object *obj)
{
QIOChannelCommand *ioc = QIO_CHANNEL_COMMAND(obj);
ioc->readfd = -1;
ioc->writefd = -1;
ioc->pid = -1;
}
static void qio_channel_command_finalize(Object *obj)
{
QIOChannelCommand *ioc = QIO_CHANNEL_COMMAND(obj);
if (ioc->readfd != -1) {
close(ioc->readfd);
ioc->readfd = -1;
}
if (ioc->writefd != -1) {
close(ioc->writefd);
ioc->writefd = -1;
}
if (ioc->pid > 0) {
#ifndef WIN32
qio_channel_command_abort(ioc, NULL);
#endif
}
}
static ssize_t qio_channel_command_readv(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
int **fds,
size_t *nfds,
Error **errp)
{
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
ssize_t ret;
retry:
ret = readv(cioc->readfd, iov, niov);
if (ret < 0) {
if (errno == EAGAIN ||
errno == EWOULDBLOCK) {
return QIO_CHANNEL_ERR_BLOCK;
}
if (errno == EINTR) {
goto retry;
}
error_setg_errno(errp, errno,
"Unable to read from command");
return -1;
}
return ret;
}
static ssize_t qio_channel_command_writev(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
int *fds,
size_t nfds,
Error **errp)
{
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
ssize_t ret;
retry:
ret = writev(cioc->writefd, iov, niov);
if (ret <= 0) {
if (errno == EAGAIN ||
errno == EWOULDBLOCK) {
return QIO_CHANNEL_ERR_BLOCK;
}
if (errno == EINTR) {
goto retry;
}
error_setg_errno(errp, errno, "%s",
"Unable to write to command");
return -1;
}
return ret;
}
static int qio_channel_command_set_blocking(QIOChannel *ioc,
bool enabled,
Error **errp)
{
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
if (enabled) {
qemu_set_block(cioc->writefd);
qemu_set_block(cioc->readfd);
} else {
qemu_set_nonblock(cioc->writefd);
qemu_set_nonblock(cioc->readfd);
}
return 0;
}
static int qio_channel_command_close(QIOChannel *ioc,
Error **errp)
{
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
int rv = 0;
/* We close FDs before killing, because that
* gives a better chance of clean shutdown
*/
if (close(cioc->writefd) < 0) {
rv = -1;
}
if (close(cioc->readfd) < 0) {
rv = -1;
}
#ifndef WIN32
if (qio_channel_command_abort(cioc, errp) < 0) {
return -1;
}
#endif
if (rv < 0) {
error_setg_errno(errp, errno, "%s",
"Unable to close command");
}
return rv;
}
static GSource *qio_channel_command_create_watch(QIOChannel *ioc,
GIOCondition condition)
{
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
return qio_channel_create_fd_pair_watch(ioc,
cioc->readfd,
cioc->writefd,
condition);
}
static void qio_channel_command_class_init(ObjectClass *klass,
void *class_data G_GNUC_UNUSED)
{
QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass);
ioc_klass->io_writev = qio_channel_command_writev;
ioc_klass->io_readv = qio_channel_command_readv;
ioc_klass->io_set_blocking = qio_channel_command_set_blocking;
ioc_klass->io_close = qio_channel_command_close;
ioc_klass->io_create_watch = qio_channel_command_create_watch;
}
static const TypeInfo qio_channel_command_info = {
.parent = TYPE_QIO_CHANNEL,
.name = TYPE_QIO_CHANNEL_COMMAND,
.instance_size = sizeof(QIOChannelCommand),
.instance_init = qio_channel_command_init,
.instance_finalize = qio_channel_command_finalize,
.class_init = qio_channel_command_class_init,
};
static void qio_channel_command_register_types(void)
{
type_register_static(&qio_channel_command_info);
}
type_init(qio_channel_command_register_types);
/*
* 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 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 "io/channel-file.h"
#include "io/channel-watch.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;
trace_qio_channel_file_new_fd(ioc, fd);
return ioc;
}
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_WRONLY) {
ioc->fd = open(path, flags, mode);
} else {
ioc->fd = open(path, flags);
}
if (ioc->fd < 0) {
object_unref(OBJECT(ioc));
error_setg_errno(errp, errno,
"Unable to open %s", path);
return NULL;
}
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) {
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,
Error **errp)
{
QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
ssize_t ret;
retry:
ret = readv(fioc->fd, iov, niov);
if (ret < 0) {
if (errno == EAGAIN ||
errno == EWOULDBLOCK) {
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,
Error **errp)
{
QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
ssize_t ret;
retry:
ret = writev(fioc->fd, iov, niov);
if (ret <= 0) {
if (errno == EAGAIN ||
errno == EWOULDBLOCK) {
return QIO_CHANNEL_ERR_BLOCK;
}
if (errno == EINTR) {
goto retry;
}
error_setg_errno(errp, errno,
"Unable to write to file");
return -1;
}
return ret;
}
static int qio_channel_file_set_blocking(QIOChannel *ioc,
bool enabled,
Error **errp)
{
QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
if (enabled) {
qemu_set_block(fioc->fd);
} else {
qemu_set_nonblock(fioc->fd);
}
return 0;
}
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 (close(fioc->fd) < 0) {
error_setg_errno(errp, errno,
"Unable to close file");
return -1;
}
return 0;
}
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,
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;
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;
}
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);
此差异已折叠。
/*
* 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 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 "io/channel-tls.h"
#include "trace.h"
static ssize_t qio_channel_tls_write_handler(const char *buf,
size_t len,
void *opaque)
{
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque);
ssize_t ret;
ret = qio_channel_write(tioc->master, buf, len, NULL);
if (ret == QIO_CHANNEL_ERR_BLOCK) {
errno = EAGAIN;
return -1;
} else if (ret < 0) {
errno = EIO;
return -1;
}
return ret;
}
static ssize_t qio_channel_tls_read_handler(char *buf,
size_t len,
void *opaque)
{
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque);
ssize_t ret;
ret = qio_channel_read(tioc->master, buf, len, NULL);
if (ret == QIO_CHANNEL_ERR_BLOCK) {
errno = EAGAIN;
return -1;
} else if (ret < 0) {
errno = EIO;
return -1;
}
return ret;
}
QIOChannelTLS *
qio_channel_tls_new_server(QIOChannel *master,
QCryptoTLSCreds *creds,
const char *aclname,
Error **errp)
{
QIOChannelTLS *ioc;
ioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS));
ioc->master = master;
object_ref(OBJECT(master));
ioc->session = qcrypto_tls_session_new(
creds,
NULL,
aclname,
QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
errp);
if (!ioc->session) {
goto error;
}
qcrypto_tls_session_set_callbacks(
ioc->session,
qio_channel_tls_write_handler,
qio_channel_tls_read_handler,
ioc);
trace_qio_channel_tls_new_server(ioc, master, creds, aclname);
return ioc;
error:
object_unref(OBJECT(ioc));
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;
if (master->features & (1 << QIO_CHANNEL_FEATURE_SHUTDOWN)) {
ioc->features |= (1 << 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;
}
static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc,
GIOCondition condition,
gpointer user_data);
static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc,
QIOTask *task)
{
Error *err = NULL;
QCryptoTLSSessionHandshakeStatus status;
if (qcrypto_tls_session_handshake(ioc->session, &err) < 0) {
trace_qio_channel_tls_handshake_fail(ioc);
qio_task_abort(task, err);
goto cleanup;
}
status = qcrypto_tls_session_get_handshake_status(ioc->session);
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_abort(task, err);
goto cleanup;
}
trace_qio_channel_tls_credentials_allow(ioc);
qio_task_complete(task);
} else {
GIOCondition condition;
if (status == QCRYPTO_TLS_HANDSHAKE_SENDING) {
condition = G_IO_OUT;
} else {
condition = G_IO_IN;
}
trace_qio_channel_tls_handshake_pending(ioc, status);
qio_channel_add_watch(ioc->master,
condition,
qio_channel_tls_handshake_io,
task,
NULL);
}
cleanup:
error_free(err);
}
static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc,
GIOCondition condition,
gpointer user_data)
{
QIOTask *task = user_data;
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(
qio_task_get_source(task));
qio_channel_tls_handshake_task(
tioc, task);
object_unref(OBJECT(tioc));
return FALSE;
}
void qio_channel_tls_handshake(QIOChannelTLS *ioc,
QIOTaskFunc func,
gpointer opaque,
GDestroyNotify destroy)
{
QIOTask *task;
task = qio_task_new(OBJECT(ioc),
func, opaque, destroy);
trace_qio_channel_tls_handshake_start(ioc);
qio_channel_tls_handshake_task(ioc, 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);
object_unref(OBJECT(ioc->master));
qcrypto_tls_session_free(ioc->session);
}
static ssize_t qio_channel_tls_readv(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
int **fds,
size_t *nfds,
Error **errp)
{
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);
if (ret < 0) {
if (errno == EAGAIN) {
if (got) {
return got;
} else {
return QIO_CHANNEL_ERR_BLOCK;
}
}
error_setg_errno(errp, errno,
"Cannot read from TLS channel");
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,
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);
if (ret <= 0) {
if (errno == EAGAIN) {
if (done) {
return done;
} else {
return QIO_CHANNEL_ERR_BLOCK;
}
}
error_setg_errno(errp, errno,
"Cannot write to TLS channel");
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);
}
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);
return qio_channel_shutdown(tioc->master, how, errp);
}
static int qio_channel_tls_close(QIOChannel *ioc,
Error **errp)
{
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
return qio_channel_close(tioc->master, errp);
}
static GSource *qio_channel_tls_create_watch(QIOChannel *ioc,
GIOCondition condition)
{
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
return qio_channel_create_watch(tioc->master, condition);
}
QCryptoTLSSession *
qio_channel_tls_get_session(QIOChannelTLS *ioc)
{
return ioc->session;
}
static void qio_channel_tls_class_init(ObjectClass *klass,
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;
}
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);
/*
* 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 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 "io/channel-watch.h"
typedef struct QIOChannelFDSource QIOChannelFDSource;
struct QIOChannelFDSource {
GSource parent;
GPollFD fd;
QIOChannel *ioc;
GIOCondition condition;
};
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));
}
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;
ssource->fd.fd = fd;
ssource->fd.events = condition;
g_source_add_poll(source, &ssource->fd);
return source;
}
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;
ssource->fdread.fd = fdread;
ssource->fdread.events = condition & G_IO_IN;
ssource->fdwrite.fd = fdwrite;
ssource->fdwrite.events = condition & G_IO_OUT;
g_source_add_poll(source, &ssource->fdread);
g_source_add_poll(source, &ssource->fdwrite);
return source;
}
此差异已折叠。
此差异已折叠。
/*
* 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 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 "io/task.h"
#include "qemu/thread.h"
#include "trace.h"
struct QIOTask {
Object *source;
QIOTaskFunc func;
gpointer opaque;
GDestroyNotify destroy;
};
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;
trace_qio_task_new(task, source, func, opaque);
return task;
}
static void qio_task_free(QIOTask *task)
{
if (task->destroy) {
task->destroy(task->opaque);
}
object_unref(task->source);
g_free(task);
}
struct QIOTaskThreadData {
QIOTask *task;
QIOTaskWorker worker;
gpointer opaque;
GDestroyNotify destroy;
Error *err;
int ret;
};
static gboolean gio_task_thread_result(gpointer opaque)
{
struct QIOTaskThreadData *data = opaque;
trace_qio_task_thread_result(data->task);
if (data->ret == 0) {
qio_task_complete(data->task);
} else {
qio_task_abort(data->task, data->err);
}
error_free(data->err);
if (data->destroy) {
data->destroy(data->opaque);
}
g_free(data);
return FALSE;
}
static gpointer qio_task_thread_worker(gpointer opaque)
{
struct QIOTaskThreadData *data = opaque;
trace_qio_task_thread_run(data->task);
data->ret = data->worker(data->task, &data->err, data->opaque);
if (data->ret < 0 && data->err == NULL) {
error_setg(&data->err, "Task worker failed but did not set an error");
}
/* 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(data->task);
g_idle_add(gio_task_thread_result, data);
return NULL;
}
void qio_task_run_in_thread(QIOTask *task,
QIOTaskWorker worker,
gpointer opaque,
GDestroyNotify destroy)
{
struct QIOTaskThreadData *data = g_new0(struct QIOTaskThreadData, 1);
QemuThread thread;
data->task = task;
data->worker = worker;
data->opaque = opaque;
data->destroy = destroy;
trace_qio_task_thread_start(task, worker, opaque);
qemu_thread_create(&thread,
"io-task-worker",
qio_task_thread_worker,
data,
QEMU_THREAD_DETACHED);
}
void qio_task_complete(QIOTask *task)
{
task->func(task->source, NULL, task->opaque);
trace_qio_task_complete(task);
qio_task_free(task);
}
void qio_task_abort(QIOTask *task,
Error *err)
{
task->func(task->source, err, task->opaque);
trace_qio_task_abort(task);
qio_task_free(task);
}
Object *qio_task_get_source(QIOTask *task)
{
object_ref(task->source);
return task->source;
}
......@@ -61,6 +61,15 @@ case $line in
value=${line#*=}
echo "#define $name $value"
;;
HAVE_*=y) # configuration
name=${line%=*}
echo "#define $name 1"
;;
HAVE_*=*) # configuration
name=${line%=*}
value=${line#*=}
echo "#define $name $value"
;;
ARCH=*) # configuration
arch=${line#*=}
arch_name=`echo $arch | LC_ALL=C tr '[a-z]' '[A-Z]'`
......
......@@ -24,6 +24,14 @@ test-cutils
test-hbitmap
test-int128
test-iov
test-io-channel-buffer
test-io-channel-command
test-io-channel-command.fifo
test-io-channel-file
test-io-channel-file.txt
test-io-channel-socket
test-io-channel-tls
test-io-task
test-mul64
test-opts-visitor
test-qapi-event.[ch]
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -1086,7 +1086,7 @@ socket_sockaddr_to_address_unix(struct sockaddr_storage *sa,
}
#endif /* WIN32 */
static SocketAddress *
SocketAddress *
socket_sockaddr_to_address(struct sockaddr_storage *sa,
socklen_t salen,
Error **errp)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册