diff --git a/gio/Makefile.am b/gio/Makefile.am index 12413c14d..3f8f18055 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -130,6 +130,7 @@ appinfo_sources += gdesktopappinfo.c gdesktopappinfo.h platform_libadd += libasyncns/libasyncns.la xdgmime/libxdgmime.la platform_deps += libasyncns/libasyncns.la xdgmime/libxdgmime.la unix_sources = \ + gunixconnection.c \ gunixfdmessage.c \ gunixmount.c \ gunixmount.h \ @@ -150,6 +151,7 @@ unix_sources = \ giounixincludedir=$(includedir)/gio-unix-2.0/gio giounixinclude_HEADERS = \ gdesktopappinfo.h \ + gunixconnection.h \ gunixmounts.h \ gunixfdmessage.h \ gunixinputstream.h \ @@ -235,11 +237,21 @@ libgio_2_0_la_SOURCES = \ gseekable.c \ gsimpleasyncresult.c \ gsocket.c \ - gsocketcontrolmessage.c \ gsocketaddress.c \ gsocketaddressenumerator.c \ + gsocketclient.c \ gsocketconnectable.c \ + gsocketconnection.c \ + gsocketcontrolmessage.c \ + gsocketinputstream.c \ + gsocketinputstream.h \ + gsocketlistener.c \ + gsocketoutputstream.c \ + gsocketoutputstream.h \ + gsocketservice.c \ gsrvtarget.c \ + gtcpconnection.c \ + gthreadedsocketservice.c\ gthemedicon.c \ gthreadedresolver.c \ gthreadedresolver.h \ @@ -353,11 +365,17 @@ gio_headers = \ gseekable.h \ gsimpleasyncresult.h \ gsocket.h \ - gsocketcontrolmessage.h \ gsocketaddress.h \ gsocketaddressenumerator.h \ + gsocketclient.h \ gsocketconnectable.h \ + gsocketconnection.h \ + gsocketcontrolmessage.h \ + gsocketlistener.h \ + gsocketservice.h \ gsrvtarget.h \ + gtcpconnection.h \ + gthreadedsocketservice.h\ gthemedicon.h \ gvfs.h \ gvolume.h \ diff --git a/gio/gio-marshal.list b/gio/gio-marshal.list index ab176d6d4..ee0d2e38b 100644 --- a/gio/gio-marshal.list +++ b/gio/gio-marshal.list @@ -2,3 +2,4 @@ VOID:STRING,STRING,STRING,FLAGS VOID:STRING,BOXED VOID:BOOLEAN,POINTER VOID:OBJECT,OBJECT,ENUM +BOOLEAN:OBJECT,OBJECT diff --git a/gio/gio.h b/gio/gio.h index 0160b8266..ad1750305 100644 --- a/gio/gio.h +++ b/gio/gio.h @@ -74,10 +74,16 @@ #include #include #include -#include #include #include +#include #include +#include +#include +#include +#include +#include +#include #include #include #include diff --git a/gio/gio.symbols b/gio/gio.symbols index 3078b3b7c..48561b661 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -1110,6 +1110,88 @@ g_socket_control_message_serialize #endif #endif +#if IN_HEADER(__G_SOCKET_CLIENT_H__) +#if IN_FILE(__G_SOCKET_CLIENT_C__) +g_socket_client_get_type G_GNUC_CONST +g_socket_client_connect +g_socket_client_connect_async +g_socket_client_connect_finish +g_socket_client_connect_to_host +g_socket_client_connect_to_host_async +g_socket_client_connect_to_host_finish +g_socket_client_get_family +g_socket_client_get_local_address +g_socket_client_get_protocol +g_socket_client_get_socket_type +g_socket_client_new +g_socket_client_set_family +g_socket_client_set_local_address +g_socket_client_set_protocol +g_socket_client_set_socket_type +#endif +#endif + +#if IN_HEADER(__G_SOCKET_CONNECTION_H__) +#if IN_FILE(__G_SOCKET_CONNECTION_C__) +g_socket_connection_get_type G_GNUC_CONST +g_socket_connection_factory_create_connection +g_socket_connection_factory_lookup_type +g_socket_connection_factory_register_type +g_socket_connection_get_local_address +g_socket_connection_get_remote_address +g_socket_connection_get_socket +#endif +#endif + +#if IN_HEADER(__G_SOCKET_LISTENER_H__) +#if IN_FILE(__G_SOCKET_LISTENER_C__) +g_socket_listener_get_type G_GNUC_CONST +g_socket_listener_accept +g_socket_listener_accept_async +g_socket_listener_accept_finish +g_socket_listener_accept_socket +g_socket_listener_accept_socket_async +g_socket_listener_accept_socket_finish +g_socket_listener_add_address +g_socket_listener_add_inet_port +g_socket_listener_add_socket +g_socket_listener_close +g_socket_listener_new +g_socket_listener_set_backlog +#endif +#endif + +#if IN_HEADER(__G_SOCKET_SERVICE_H__) +#if IN_FILE(__G_SOCKET_SERVICE_C__) +g_socket_service_get_type G_GNUC_CONST +g_socket_service_is_active +g_socket_service_new +g_socket_service_start +g_socket_service_stop +#endif +#endif + +#if IN_HEADER(__G_THREADED_SOCKET_SERVICE_H__) +#if IN_FILE(__G_THREADED_SOCKET_SERVICE_C__) +g_threaded_socket_service_get_type G_GNUC_CONST +g_threaded_socket_service_new +#endif +#endif + +#if IN_HEADER(__G_TCP_CONNECTION_H__) +#if IN_FILE(__G_TCP_CONNECTION_C__) +g_tcp_connection_get_type G_GNUC_CONST +#endif +#endif + +#if IN_HEADER(__G_UNIX_CONNECTION_H__) +#if IN_FILE(__G_UNIX_CONNECTION_C__) +g_unix_connection_get_type G_GNUC_CONST +g_unix_connection_receive_fd +g_unix_connection_send_fd +#endif +#endif + #if IN_HEADER(__G_UNIX_FD_MESSAGE_H__) #if IN_FILE(__G_UNIX_FD_MESSAGE_C__) g_unix_fd_message_get_type G_GNUC_CONST diff --git a/gio/giotypes.h b/gio/giotypes.h index 70bc0549a..2a7fbac2b 100644 --- a/gio/giotypes.h +++ b/gio/giotypes.h @@ -126,10 +126,60 @@ typedef struct _GSocket GSocket; * received over #GSocket. **/ typedef struct _GSocketControlMessage GSocketControlMessage; +/** + * GSocketClient: + * + * A helper class for network clients to make connections. + * + * Since: 2.22 + **/ +typedef struct _GSocketClient GSocketClient; +/** + * GSocketConnection: + * + * A socket connection GIOStream object for connection-oriented sockets. + * + * Since: 2.22 + **/ +typedef struct _GSocketConnection GSocketConnection; +/** + * GSocketClient: + * + * A helper class for network servers to listen for and accept connections. + * + * Since: 2.22 + **/ +typedef struct _GSocketListener GSocketListener; +/** + * GSocketService: + * + * A helper class for handling accepting incomming connections in the + * glib mainloop. + * + * Since: 2.22 + **/ +typedef struct _GSocketService GSocketService; typedef struct _GSocketAddress GSocketAddress; typedef struct _GSocketAddressEnumerator GSocketAddressEnumerator; typedef struct _GSocketConnectable GSocketConnectable; typedef struct _GSrvTarget GSrvTarget; +/** + * GTcpConnection: + * + * A #GSocketConnection for TCP/IP connections. + * + * Since: 2.22 + **/ +typedef struct _GTcpConnection GTcpConnection; +/** + * GThreadedSocketService: + * + * A helper class for handling accepting incomming connections in the + * glib mainloop and handling them in a thread. + * + * Since: 2.22 + **/ +typedef struct _GThreadedSocketService GThreadedSocketService; typedef struct _GThemedIcon GThemedIcon; typedef struct _GVfs GVfs; /* Dummy typedef */ diff --git a/gio/gsocketclient.c b/gio/gsocketclient.c new file mode 100644 index 000000000..84b9b32a6 --- /dev/null +++ b/gio/gsocketclient.c @@ -0,0 +1,912 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2008, 2009 codethink + * Copyright © 2009 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, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Ryan Lortie + * Alexander Larsson + */ + +#include "config.h" +#include "gsocketclient.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "glibintl.h" + +#include "gioalias.h" + +/** + * SECTION:gsocketclient + * @short_description: High-level client network helper + * @include: gio/gio.h + * @see_also: #GSocketConnection, #GSocketListener + * + * #GSocketClient is a high-level utility class for connecting to a + * network host using a connection oriented socket type. + * + * You create a #GSocketClient object, set any options you want, then + * call a sync or async connect operation, which returns a #GSocketConnection + * subclass on success. + * + * The type of the #GSocketConnection object returned depends on the type of + * the underlying socket that is in use. For instance, for a TCP/IP connection + * it will be a #GTcpConnection. + * + * Since: 2.22 + **/ + + +G_DEFINE_TYPE (GSocketClient, g_socket_client, G_TYPE_OBJECT); + +enum +{ + PROP_NONE, + PROP_FAMILY, + PROP_TYPE, + PROP_PROTOCOL, + PROP_LOCAL_ADDRESS +}; + +struct _GSocketClientPrivate +{ + GSocketFamily family; + GSocketType type; + char *protocol; + GSocketAddress *local_address; +}; + +static GSocket * +create_socket (GSocketClient *client, + GSocketAddress *dest_address, + GError **error) +{ + GSocketFamily family; + GSocket *socket; + int proto; + + family = client->priv->family; + if (family == G_SOCKET_FAMILY_INVALID && + client->priv->local_address != NULL) + family = g_socket_address_get_family (client->priv->local_address); + if (family == G_SOCKET_FAMILY_INVALID) + family = g_socket_address_get_family (dest_address); + + proto = g_socket_protocol_id_lookup_by_name (client->priv->protocol); + socket = g_socket_new (family, + client->priv->type, + proto, + error); + if (socket == NULL) + return NULL; + + if (client->priv->local_address) + { + if (!g_socket_bind (socket, + client->priv->local_address, + FALSE, + error)) + { + g_object_unref (socket); + return NULL; + } + } + + return socket; +} + +static void +g_socket_client_init (GSocketClient *client) +{ + client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client, + G_TYPE_SOCKET_CLIENT, + GSocketClientPrivate); + client->priv->type = G_SOCKET_TYPE_STREAM; +} + +/** + * g_socket_client_new: + * + * Creates a new #GSocketClient with the default options. + * + * Returns: a #GSocketClient. + * Free the returned object with g_object_unref(). + * + * Since: 2.22 + **/ +GSocketClient * +g_socket_client_new (void) +{ + return g_object_new (G_TYPE_SOCKET_CLIENT, NULL); +} + +static void +g_socket_client_finalize (GObject *object) +{ + GSocketClient *client = G_SOCKET_CLIENT (object); + + if (client->priv->local_address) + g_object_unref (client->priv->local_address); + + g_free (client->priv->protocol); + + if (G_OBJECT_CLASS (g_socket_client_parent_class)->finalize) + (*G_OBJECT_CLASS (g_socket_client_parent_class)->finalize) (object); +} + +static void +g_socket_client_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GSocketClient *client = G_SOCKET_CLIENT (object); + + switch (prop_id) + { + case PROP_FAMILY: + g_value_set_enum (value, client->priv->family); + break; + + case PROP_TYPE: + g_value_set_enum (value, client->priv->type); + break; + + case PROP_PROTOCOL: + g_value_set_string (value, client->priv->protocol); + break; + + case PROP_LOCAL_ADDRESS: + g_value_set_object (value, client->priv->local_address); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_socket_client_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GSocketClient *client = G_SOCKET_CLIENT (object); + + switch (prop_id) + { + case PROP_FAMILY: + g_socket_client_set_family (client, g_value_get_enum (value)); + break; + + case PROP_TYPE: + g_socket_client_set_socket_type (client, g_value_get_enum (value)); + break; + + case PROP_PROTOCOL: + g_socket_client_set_protocol (client, g_value_get_string (value)); + break; + + case PROP_LOCAL_ADDRESS: + g_socket_client_set_local_address (client, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +/** + * g_socket_client_get_family: + * @socket: a #GSocket. + * + * Gets the socket family of the socket client. + * + * See g_socket_client_set_family() for details. + * + * Returns: a #GSocketFamily + * + * Since: 2.22 + **/ +GSocketFamily +g_socket_client_get_family (GSocketClient *client) +{ + return client->priv->family; +} + +/** + * g_socket_client_set_family: + * @socket: a #GSocket. + * @family: a #GSocketFamily + * + * Sets the socket family of the socket client. + * If this is set to something other than %G_SOCKET_FAMILY_INVALID + * then the sockets created by this object will be of the specified + * family. + * + * This might be useful for instance if you want to force the local + * connection to be an ipv4 socket, even though the address might + * be an ipv6 mapped to ipv4 address. + * + * Since: 2.22 + **/ +void +g_socket_client_set_family (GSocketClient *client, + GSocketFamily family) +{ + if (client->priv->family == family) + return; + + client->priv->family = family; + g_object_notify (G_OBJECT (client), "family"); +} + +/** + * g_socket_client_get_socket_type: + * @socket: a #GSocket. + * + * Gets the socket type of the socket client. + * + * See g_socket_client_set_socket_type() for details. + * + * Returns: a #GSocketFamily + * + * Since: 2.22 + **/ +GSocketType +g_socket_client_get_socket_type (GSocketClient *client) +{ + return client->priv->type; +} + +/** + * g_socket_client_set_socket_type: + * @socket: a #GSocket. + * @type: a #GSocketType + * + * Sets the socket type of the socket client. + * The sockets created by this object will be of the specified + * type. + * + * It doesn't make sense to specify a type of %G_SOCKET_TYPE_DATAGRAM, + * as GSocketClient is used for connection oriented services. + * + * Since: 2.22 + **/ +void +g_socket_client_set_socket_type (GSocketClient *client, + GSocketType type) +{ + if (client->priv->type == type) + return; + + client->priv->type = type; + g_object_notify (G_OBJECT (client), "type"); +} + +/** + * g_socket_client_get_protocol: + * @socket: a #GSocket. + * + * Gets the protocol name type of the socket client. + * + * See g_socket_client_set_protocol() for details. + * + * Returns: a string or %NULL. don't free + * + * Since: 2.22 + **/ +const char * +g_socket_client_get_protocol (GSocketClient *client) +{ + return client->priv->protocol; +} + +/** + * g_socket_client_set_protocol: + * @socket: a #GSocket. + * @protocol: a string, or %NULL + * + * Sets the protocol of the socket client. + * The sockets created by this object will use of the specified + * protocol. + * + * If @protocol is %NULL that means to use the default + * protocol for the socket family and type. + * + * Since: 2.22 + **/ +void +g_socket_client_set_protocol (GSocketClient *client, + const char *protocol) +{ + if (g_strcmp0 (client->priv->protocol, protocol) == 0) + return; + + g_free (client->priv->protocol); + client->priv->protocol = g_strdup (protocol); + g_object_notify (G_OBJECT (client), "protocol"); +} + +/** + * g_socket_client_get_local_address: + * @socket: a #GSocket. + * + * Gets the local address of the socket client. + * + * See g_socket_client_set_local_address() for details. + * + * Returns: a #GSocketAddres or %NULL. don't free + * + * Since: 2.22 + **/ +GSocketAddress * +g_socket_client_get_local_address (GSocketClient *client) +{ + return client->priv->local_address; +} + +/** + * g_socket_client_set_local_address: + * @socket: a #GSocket. + * @addres: a #GSocketAddress, or %NULL + * + * Sets the local address of the socket client. + * The sockets created by this object will bound to the + * specified address (if not %NULL) before connecting. + * + * This is useful if you want to ensure the the local + * side of the connection is on a specific port, or on + * a specific interface. + * + * Since: 2.22 + **/ +void +g_socket_client_set_local_address (GSocketClient *client, + GSocketAddress *address) +{ + if (address) + g_object_ref (address); + + if (client->priv->local_address) + { + g_object_unref (client->priv->local_address); + } + client->priv->local_address = address; + g_object_notify (G_OBJECT (client), "local-address"); +} + +static void +g_socket_client_class_init (GSocketClientClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + + g_type_class_add_private (class, sizeof (GSocketClientPrivate)); + + gobject_class->finalize = g_socket_client_finalize; + gobject_class->set_property = g_socket_client_set_property; + gobject_class->get_property = g_socket_client_get_property; + + g_object_class_install_property (gobject_class, PROP_FAMILY, + g_param_spec_enum ("family", + P_("Socket family"), + P_("The sockets address family to use for socket construction"), + G_TYPE_SOCKET_FAMILY, + G_SOCKET_FAMILY_INVALID, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_TYPE, + g_param_spec_enum ("type", + P_("Socket type"), + P_("The sockets type to use for socket construction"), + G_TYPE_SOCKET_TYPE, + G_SOCKET_TYPE_STREAM, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_PROTOCOL, + g_param_spec_string ("protocol", + P_("Socket protocol"), + P_("The protocol to use for socket construction, or %NULL for default"), + NULL, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_LOCAL_ADDRESS, + g_param_spec_object ("local-address", + P_("Local address"), + P_("The local address constructed sockets will be bound to"), + G_TYPE_SOCKET_ADDRESS, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +/** + * g_socket_client_connect: + * @client: a #GSocketClient. + * @connectable: a #GSocketConnectable specifying the remote address. + * @cancellable: optional #GCancellable object, %NULL to ignore. + * @error: #GError for error reporting, or %NULL to ignore. + * + * Tries to resolve the @connectable and make a network connection to it.. + * + * Upon a successful connection, a new #GSocketConnection is constructed + * and returned. The caller owns this new object and must drop their + * reference to it when finished with it. + * + * The type of the #GSocketConnection object returned depends on the type of + * the underlying socket that is used. For instance, for a TCP/IP connection + * it will be a #GTcpConnection. + * + * The socket created will be the same family as the the address that the + * @connectable resolves to, unless family is set with g_socket_client_set_family() + * or indirectly via g_socket_client_set_local_address(). The socket type + * defaults to %G_SOCKET_TYPE_STREAM but can be set with + * g_socket_client_set_socket_type(). + * + * If a local address is specified with g_socket_client_set_local_address() the + * socket will be bound to this address before connecting. + * + * Returns: a #GSocketConnection on success, %NULL on error. + * + * Since: 2.22 + **/ +GSocketConnection * +g_socket_client_connect (GSocketClient *client, + GSocketConnectable *connectable, + GCancellable *cancellable, + GError **error) +{ + GSocketConnection *connection = NULL; + GSocketAddressEnumerator *enumerator; + GError *last_error, *tmp_error; + + last_error = NULL; + enumerator = g_socket_connectable_enumerate (connectable); + while (connection == NULL) + { + GSocketAddress *address; + GSocket *socket; + + if (g_cancellable_is_cancelled (cancellable)) + { + g_clear_error (error); + g_cancellable_set_error_if_cancelled (cancellable, error); + break; + } + + tmp_error = NULL; + address = g_socket_address_enumerator_next (enumerator, cancellable, + &tmp_error); + if (address == NULL) + { + if (tmp_error) + { + g_clear_error (&last_error); + g_propagate_error (error, tmp_error); + } + else if (last_error) + { + g_propagate_error (error, tmp_error); + } + else + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Unknown error on connect")); + break; + } + + /* clear error from previous attempt */ + g_clear_error (&last_error); + + socket = create_socket (client, address, &last_error); + if (socket != NULL) + { + if (g_socket_connect (socket, address, &last_error)) + connection = g_socket_connection_factory_create_connection (socket); + + g_object_unref (socket); + } + + g_object_unref (address); + } + g_object_unref (enumerator); + + return connection; +} + +/** + * g_socket_client_connect_to_host: + * @client: a #GTcpClient + * @host_and_port: the name and optionally port of the host to connect to + * @default_port: the default port to connect to + * @cancellable: a #GCancellable, or %NULL + * @error: a pointer to a #GError, or %NULL + * @returns: a #GSocketConnection if successful, or %NULL on error + * + * This is a helper function for g_socket_client_connect(). + * + * Attempts to create a TCP connection to the named host. + * + * @host_and_port may be in any of a number of recognised formats: an IPv6 + * address, an IPv4 address, or a domain name (in which case a DNS + * lookup is performed). Quoting with [] is supported for all address + * types. A port override may be specified in the usual way with a + * colon. Ports may be given as decimal numbers or symbolic names (in + * which case an /etc/services lookup is performed). + * + * If no port override is given in @host_and_port then @default_port will be + * used as the port number to connect to. + * + * In general, @host_and_port is expected to be provided by the user (allowing + * them to give the hostname, and a port overide if necessary) and + * @default_port is expected to be provided by the application. + + * In the case that an IP address is given, a single connection + * attempt is made. In the case that a name is given, multiple + * connection attempts may be made, in turn and according to the + * number of address records in DNS, until a connection succeeds. + * + * Upon a successful connection, a new #GSocketConnection is constructed + * and returned. The caller owns this new object and must drop their + * reference to it when finished with it. + * + * In the event of any failure (DNS error, service not found, no hosts + * connectable) %NULL is returned and @error (if non-%NULL) is set + * accordingly. + * + Returns: a #GSocketConnection on success, %NULL on error. + * + * Since: 2.22 + **/ +GSocketConnection * +g_socket_client_connect_to_host (GSocketClient *client, + const char *host_and_port, + int default_port, + GCancellable *cancellable, + GError **error) +{ + GSocketConnectable *connectable; + GSocketConnection *connection; + + connectable = g_network_address_parse (host_and_port, default_port, error); + if (connectable == NULL) + return NULL; + + connection = g_socket_client_connect (client, connectable, + cancellable, error); + g_object_unref (connectable); + + return connection; +} + +typedef struct +{ + GSimpleAsyncResult *result; + GCancellable *cancellable; + GSocketClient *client; + + GSocketAddressEnumerator *enumerator; + GSocket *current_socket; + + GError *last_error; +} GSocketClientAsyncConnectData; + +static void +g_socket_client_async_connect_complete (GSocketClientAsyncConnectData *data) +{ + GSocketConnection *connection; + + if (data->last_error) + { + g_simple_async_result_set_from_error (data->result, data->last_error); + g_error_free (data->last_error); + } + else + { + g_assert (data->current_socket); + + g_socket_set_blocking (data->current_socket, TRUE); + + connection = g_socket_connection_factory_create_connection (data->current_socket); + g_simple_async_result_set_op_res_gpointer (data->result, + connection, + g_object_unref); + } + + g_simple_async_result_complete (data->result); + g_object_unref (data->result); +} + + +static void +g_socket_client_enumerator_callback (GObject *object, + GAsyncResult *result, + gpointer user_data); + +static void +set_last_error (GSocketClientAsyncConnectData *data, + GError *error) +{ + g_clear_error (&data->last_error); + data->last_error = error; +} + +static gboolean +g_socket_client_socket_callback (GSocket *socket, + GIOCondition condition, + GSocketClientAsyncConnectData *data) +{ + GError *error = NULL; + + if (g_cancellable_is_cancelled (data->cancellable)) + { + /* Cancelled, return done with last error being cancelled */ + g_clear_error (&data->last_error); + g_object_unref (data->current_socket); + data->current_socket = NULL; + g_cancellable_set_error_if_cancelled (data->cancellable, + &data->last_error); + } + else + { + /* socket is ready for writing means connect done, did it succeed? */ + if (!g_socket_check_pending_error (data->current_socket, &error)) + { + set_last_error (data, error); + + /* try next one */ + g_socket_address_enumerator_next_async (data->enumerator, + data->cancellable, + g_socket_client_enumerator_callback, + data); + + return FALSE; + } + } + + g_socket_client_async_connect_complete (data); + + return FALSE; +} + +static void +g_socket_client_enumerator_callback (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GSocketClientAsyncConnectData *data = user_data; + GSocketAddress *address; + GSocket *socket; + GError *tmp_error = NULL; + + if (g_cancellable_is_cancelled (data->cancellable)) + { + g_clear_error (&data->last_error); + g_cancellable_set_error_if_cancelled (data->cancellable, &data->last_error); + g_socket_client_async_connect_complete (data); + return; + } + + address = g_socket_address_enumerator_next_finish (data->enumerator, + result, &tmp_error); + + if (address == NULL) + { + if (tmp_error) + set_last_error (data, tmp_error); + else if (data->last_error == NULL) + g_set_error (&data->last_error, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Unknown error on connect")); + + g_socket_client_async_connect_complete (data); + return; + } + + g_clear_error (&data->last_error); + + socket = create_socket (data->client, address, &data->last_error); + if (socket != NULL) + { + g_socket_set_blocking (socket, FALSE); + if (g_socket_connect (socket, address, &tmp_error)) + { + data->current_socket = socket; + g_socket_client_async_connect_complete (data); + + g_object_unref (address); + return; + } + else if (g_error_matches (tmp_error, G_IO_ERROR, G_IO_ERROR_PENDING)) + { + GSource *source; + + data->current_socket = socket; + g_error_free (tmp_error); + + source = g_socket_create_source (socket, G_IO_OUT, + data->cancellable); + g_source_set_callback (source, + (GSourceFunc) g_socket_client_socket_callback, + data, NULL); + g_source_attach (source, NULL); + g_source_unref (source); + + g_object_unref (address); + return; + } + else + { + data->last_error = tmp_error; + g_object_unref (socket); + } + g_object_unref (address); + } + + g_socket_address_enumerator_next_async (data->enumerator, + data->cancellable, + g_socket_client_enumerator_callback, + data); +} + +/** + * g_socket_client_connect_to_host_async: + * @client: a #GTcpClient + * @connectable: a #GSocketConnectable specifying the remote address. + * @cancellable: a #GCancellable, or %NULL + * @callback: a #GAsyncReadyCallback + * @user_data: user data for the callback + * + * This is the asynchronous version of g_socket_client_connect(). + * + * When the operation is finished @callback will be + * called. You can then call g_socket_client_connect_finish() to get + * the result of the operation. + * + * Since: 2.22 + **/ +void +g_socket_client_connect_async (GSocketClient *client, + GSocketConnectable *connectable, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSocketClientAsyncConnectData *data; + + g_return_if_fail (G_IS_SOCKET_CLIENT (client)); + + data = g_slice_new (GSocketClientAsyncConnectData); + + data->result = g_simple_async_result_new (G_OBJECT (client), + callback, user_data, + g_socket_client_connect_async); + data->client = client; + if (cancellable) + data->cancellable = g_object_ref (cancellable); + else + data->cancellable = NULL; + data->last_error = NULL; + data->enumerator = g_socket_connectable_enumerate (connectable); + + g_socket_address_enumerator_next_async (data->enumerator, cancellable, + g_socket_client_enumerator_callback, + data); +} + +/** + * g_socket_client_connect_to_host_async: + * @client: a #GTcpClient + * @host_and_port: the name and optionally the port of the host to connect to + * @default_port: the default port to connect to + * @cancellable: a #GCancellable, or %NULL + * @callback: a #GAsyncReadyCallback + * @user_data: user data for the callback + * + * This is the asynchronous version of g_socket_client_connect_to_host(). + * + * When the operation is finished @callback will be + * called. You can then call g_socket_client_connect_to_host_finish() to get + * the result of the operation. + * + * Since: 2.22 + **/ +void +g_socket_client_connect_to_host_async (GSocketClient *client, + const char *host_and_port, + int default_port, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSocketConnectable *connectable; + GError *error; + + error = NULL; + connectable = g_network_address_parse (host_and_port, default_port, + &error); + if (connectable == NULL) + { + g_simple_async_report_gerror_in_idle (G_OBJECT (client), + callback, user_data, error); + g_error_free (error); + } + else + { + g_socket_client_connect_async (client, + connectable, cancellable, + callback, user_data); + g_object_unref (connectable); + } +} + +/** + * g_socket_client_connect_finish: + * @client: a #GSocketClient. + * @result: a #GAsyncResult. + * @error: a #GError location to store the error occuring, or %NULL to + * ignore. + * + * Finishes an async connect operation. See g_socket_client_connect_async() + * + * Returns: a #GSocketConnection on success, %NULL on error. + * + * Since: 2.22 + **/ +GSocketConnection * +g_socket_client_connect_finish (GSocketClient *client, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return NULL; + + return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple)); +} + +/** + * g_socket_client_connect_to_host_finish: + * @client: a #GSocketClient. + * @result: a #GAsyncResult. + * @error: a #GError location to store the error occuring, or %NULL to + * ignore. + * + * Finishes an async connect operation. See g_socket_client_connect_to_host_async() + * + * Returns: a #GSocketConnection on success, %NULL on error. + * + * Since: 2.22 + **/ +GSocketConnection * +g_socket_client_connect_to_host_finish (GSocketClient *client, + GAsyncResult *result, + GError **error) +{ + return g_socket_client_connect_finish (client, result, error); +} + +#define __G_SOCKET_CLIENT_C__ +#include "gioaliasdef.c" diff --git a/gio/gsocketclient.h b/gio/gsocketclient.h new file mode 100644 index 000000000..d8259b3a6 --- /dev/null +++ b/gio/gsocketclient.h @@ -0,0 +1,115 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2008, 2009 Codethink Limited + * Copyright © 2009 Red Hat, Inc + * + * This program 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 licence 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, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Ryan Lortie + * Alexander Larsson + */ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __G_SOCKET_CLIENT_H__ +#define __G_SOCKET_CLIENT_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_SOCKET_CLIENT (g_socket_client_get_type ()) +#define G_SOCKET_CLIENT(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_SOCKET_CLIENT, GSocketClient)) +#define G_SOCKET_CLIENT_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \ + G_TYPE_SOCKET_CLIENT, GSocketClientClass)) +#define G_IS_SOCKET_CLIENT(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_SOCKET_CLIENT)) +#define G_IS_SOCKET_CLIENT_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \ + G_TYPE_SOCKET_CLIENT)) +#define G_SOCKET_CLIENT_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \ + G_TYPE_SOCKET_CLIENT, GSocketClientClass)) + +typedef struct _GSocketClientPrivate GSocketClientPrivate; +typedef struct _GSocketClientClass GSocketClientClass; + +struct _GSocketClientClass +{ + GObjectClass parent_class; + + /* Padding for future expansion */ + void (*_g_reserved1) (void); + void (*_g_reserved2) (void); + void (*_g_reserved3) (void); + void (*_g_reserved4) (void); + void (*_g_reserved5) (void); +}; + +struct _GSocketClient +{ + GObject parent_instance; + GSocketClientPrivate *priv; +}; + +GType g_socket_client_get_type (void) G_GNUC_CONST; + +GSocketClient *g_socket_client_new (void); + +GSocketFamily g_socket_client_get_family (GSocketClient *client); +void g_socket_client_set_family (GSocketClient *client, + GSocketFamily family); +GSocketType g_socket_client_get_socket_type (GSocketClient *client); +void g_socket_client_set_socket_type (GSocketClient *client, + GSocketType type); +const char *g_socket_client_get_protocol (GSocketClient *client); +void g_socket_client_set_protocol (GSocketClient *client, + const char *protocol); +GSocketAddress *g_socket_client_get_local_address (GSocketClient *client); +void g_socket_client_set_local_address (GSocketClient *client, + GSocketAddress *address); + +GSocketConnection * g_socket_client_connect (GSocketClient *client, + GSocketConnectable *connectable, + GCancellable *cancellable, + GError **error); +GSocketConnection * g_socket_client_connect_to_host (GSocketClient *client, + const char *hostname, + int port, + GCancellable *cancellable, + GError **error); +void g_socket_client_connect_async (GSocketClient *client, + GSocketConnectable *connectable, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GSocketConnection * g_socket_client_connect_finish (GSocketClient *client, + GAsyncResult *result, + GError **error); +void g_socket_client_connect_to_host_async (GSocketClient *client, + const char *hostname, + int port, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GSocketConnection * g_socket_client_connect_to_host_finish (GSocketClient *client, + GAsyncResult *result, + GError **error); + +G_END_DECLS + +#endif /* __G_SOCKET_CLIENT_H___ */ diff --git a/gio/gsocketconnection.c b/gio/gsocketconnection.c new file mode 100644 index 000000000..878a282b4 --- /dev/null +++ b/gio/gsocketconnection.c @@ -0,0 +1,474 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2008 Christian Kellner, Samuel Cormier-Iijima + * © 2008 codethink + * Copyright © 2009 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, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Christian Kellner + * Samuel Cormier-Iijima + * Ryan Lortie + * Alexander Larsson + */ + +#include "config.h" + +#include "gsocketconnection.h" + +#include "gsocketoutputstream.h" +#include "gsocketinputstream.h" +#include +#include +#include "gunixconnection.h" +#include "gtcpconnection.h" +#include "glibintl.h" + +#include "gioalias.h" + +/** + * SECTION:gsocketconnection + * @short_description: High-level socket connection stream + * @include: gio/gio.h + * @see_also: #GIOStream, #GSocketClient, #GSocketListener + * + * #GSocketConnection is a #GIOStream for a connected socket. They + * can be created either by #GSocketClient when connecting to a host, + * or by #GSocketListener when accepting a new client. + * + * The type of the #GSocketConnection object returned from these calls depends + * on the type of the underlying socket that is in use. For instance, for a + * TCP/IP connection it will be a #GTcpConnection. + * + * Chosing what type of object to construct is done with the socket connection + * factory, and it is possible for 3rd parties to register custom socket connection + * types for specific combination of socket family/type/protocol using + * g_socket_connection_factory_register_type(). + * + * Since: 2.22 + **/ + +G_DEFINE_TYPE (GSocketConnection, + g_socket_connection, G_TYPE_IO_STREAM); + +enum +{ + PROP_NONE, + PROP_SOCKET, +}; + +struct _GSocketConnectionPrivate +{ + GSocket *socket; + GInputStream *input_stream; + GOutputStream *output_stream; +}; + +static gboolean g_socket_connection_close (GIOStream *stream, + GCancellable *cancellable, + GError **error); +static void g_socket_connection_close_async (GIOStream *stream, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +static gboolean g_socket_connection_close_finish (GIOStream *stream, + GAsyncResult *result, + GError **error); + +static GInputStream * +g_socket_connection_get_input_stream (GIOStream *io_stream) +{ + GSocketConnection *connection = G_SOCKET_CONNECTION (io_stream); + + if (connection->priv->input_stream == NULL) + connection->priv->input_stream = (GInputStream *) + _g_socket_input_stream_new (connection->priv->socket); + + return connection->priv->input_stream; +} + +static GOutputStream * +g_socket_connection_get_output_stream (GIOStream *io_stream) +{ + GSocketConnection *connection = G_SOCKET_CONNECTION (io_stream); + + if (connection->priv->output_stream == NULL) + connection->priv->output_stream = (GOutputStream *) + _g_socket_output_stream_new (connection->priv->socket); + + return connection->priv->output_stream; +} + +GSocket * +g_socket_connection_get_socket (GSocketConnection *connection) +{ + g_return_val_if_fail (G_IS_SOCKET_CONNECTION (connection), NULL); + + return connection->priv->socket; +} + +/** + * g_socket_connection_get_local_address: + * @connection: a #GSocketConnection. + * @error: #GError for error reporting, or %NULL to ignore. + * + * Try to get the local address of a socket connection. + * + * Returns: a #GSocketAddress or %NULL on error. + * + * Since: 2.22 + **/ +GSocketAddress * +g_socket_connection_get_local_address (GSocketConnection *connection, + GError **error) +{ + return g_socket_get_local_address (connection->priv->socket, error); +} + +/** + * g_socket_connection_get_remote_address: + * @connection: a #GSocketConnection. + * @error: #GError for error reporting, or %NULL to ignore. + * + * Try to get the remove address of a socket connection. + * + * Returns: a #GSocketAddress or %NULL on error. + * + * Since: 2.22 + **/ +GSocketAddress * +g_socket_connection_get_remote_address (GSocketConnection *connection, + GError **error) +{ + return g_socket_get_remote_address (connection->priv->socket, error); +} + +static void +g_socket_connection_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + GSocketConnection *connection = G_SOCKET_CONNECTION (object); + + switch (prop_id) + { + case PROP_SOCKET: + g_value_set_object (value, connection->priv->socket); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_socket_connection_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + GSocketConnection *connection = G_SOCKET_CONNECTION (object); + + switch (prop_id) + { + case PROP_SOCKET: + connection->priv->socket = G_SOCKET (g_value_dup_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_socket_connection_constructed (GObject *object) +{ + GSocketConnection *connection = G_SOCKET_CONNECTION (object); + + g_assert (connection->priv->socket != NULL); +} + +static void +g_socket_connection_finalize (GObject *object) +{ + GSocketConnection *connection = G_SOCKET_CONNECTION (object); + + if (connection->priv->input_stream) + g_object_unref (connection->priv->input_stream); + + if (connection->priv->output_stream) + g_object_unref (connection->priv->output_stream); + + g_object_unref (connection->priv->socket); + + G_OBJECT_CLASS (g_socket_connection_parent_class) + ->finalize (object); +} + +static void +g_socket_connection_class_init (GSocketConnectionClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GIOStreamClass *stream_class = G_IO_STREAM_CLASS (klass); + + g_type_class_add_private (klass, sizeof (GSocketConnectionPrivate)); + + gobject_class->set_property = g_socket_connection_set_property; + gobject_class->get_property = g_socket_connection_get_property; + gobject_class->constructed = g_socket_connection_constructed; + gobject_class->finalize = g_socket_connection_finalize; + + stream_class->get_input_stream = g_socket_connection_get_input_stream; + stream_class->get_output_stream = g_socket_connection_get_output_stream; + stream_class->close_fn = g_socket_connection_close; + stream_class->close_async = g_socket_connection_close_async; + stream_class->close_finish = g_socket_connection_close_finish; + + g_object_class_install_property (gobject_class, PROP_SOCKET, + g_param_spec_object ("socket", + P_("Socket"), + P_("The underlying GSocket"), + G_TYPE_SOCKET, G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +static void +g_socket_connection_init (GSocketConnection *connection) +{ + connection->priv = G_TYPE_INSTANCE_GET_PRIVATE (connection, + G_TYPE_SOCKET_CONNECTION, + GSocketConnectionPrivate); +} + +static gboolean +g_socket_connection_close (GIOStream *stream, + GCancellable *cancellable, + GError **error) +{ + GSocketConnection *connection = G_SOCKET_CONNECTION (stream); + + if (connection->priv->output_stream) + g_output_stream_close (connection->priv->output_stream, + cancellable, NULL); + if (connection->priv->input_stream) + g_input_stream_close (connection->priv->input_stream, + cancellable, NULL); + + return g_socket_close (connection->priv->socket, error); +} + + +static void +g_socket_connection_close_async (GIOStream *stream, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + GError *error; + + /* socket close is not blocked, just do it! */ + error = NULL; + if (!g_io_stream_close (stream, cancellable, &error)) + { + g_simple_async_report_gerror_in_idle (G_OBJECT (stream), + callback, user_data, + error); + g_error_free (error); + return; + } + + res = g_simple_async_result_new (G_OBJECT (stream), + callback, + user_data, + g_socket_connection_close_async); + g_simple_async_result_complete_in_idle (res); + g_object_unref (res); +} + +static gboolean +g_socket_connection_close_finish (GIOStream *stream, + GAsyncResult *result, + GError **error) +{ + return TRUE; +} + +typedef struct { + GSocketFamily socket_family; + GSocketType socket_type; + int protocol; + GType implementation; +} ConnectionFactory; + +static guint +connection_factory_hash (gconstpointer key) +{ + const ConnectionFactory *factory = key; + guint h; + + h = factory->socket_family ^ (factory->socket_type << 4) ^ (factory->protocol << 8); + /* This is likely to be small, so spread over whole + hash space to get some distribution */ + h = h ^ (h << 8) ^ (h << 16) ^ (h << 24); + + return h; +} + +static gboolean +connection_factory_equal (gconstpointer _a, + gconstpointer _b) +{ + const ConnectionFactory *a = _a; + const ConnectionFactory *b = _b; + + if (a->socket_family != b->socket_family) + return FALSE; + + if (a->socket_type != b->socket_type) + return FALSE; + + if (a->protocol != b->protocol) + return FALSE; + + return TRUE; +} + +static GHashTable *connection_factories = NULL; +G_LOCK_DEFINE_STATIC(connection_factories); + +/** + * g_socket_connection_factory_register_type: + * @g_type: a #GType, inheriting from G_SOCKET_CONNECTION + * @family: a #GSocketFamily. + * @type: a #GSocketType + * @protocol: a protocol id + * + * Looks up the #GType to be used when creating socket connections on + * sockets with the specified @family,@type and @protocol_id. + * + * If no type is registered, the #GSocketConnection base type is returned. + * + * Returns: a #GType + * Since: 2.22 + **/ +void +g_socket_connection_factory_register_type (GType g_type, + GSocketFamily family, + GSocketType type, + gint protocol) +{ + ConnectionFactory *factory; + + g_return_if_fail (g_type_is_a (g_type, G_TYPE_SOCKET_CONNECTION)); + + G_LOCK (connection_factories); + + if (connection_factories == NULL) + connection_factories = g_hash_table_new_full (connection_factory_hash, + connection_factory_equal, + (GDestroyNotify)g_free, + NULL); + + factory = g_new0 (ConnectionFactory, 1); + factory->socket_family = family; + factory->socket_type = type; + factory->protocol = protocol; + factory->implementation = g_type; + + g_hash_table_insert (connection_factories, + factory, factory); + + G_UNLOCK (connection_factories); +} + +static void +init_builtin_types (void) +{ + volatile GType a_type; +#ifndef G_OS_WIN32 + a_type = g_unix_connection_get_type (); +#endif + a_type = g_tcp_connection_get_type (); +} + +/** + * g_socket_connection_factory_lookup_type: + * @family: a #GSocketFamily. + * @type: a #GSocketType + * @protocol_id: a protocol id + * + * Looks up the #GType to be used when creating socket connections on + * sockets with the specified @family,@type and @protocol_id. + * + * If no type is registered, the #GSocketConnection base type is returned. + * + * Returns: a #GType + * Since: 2.22 + **/ +GType +g_socket_connection_factory_lookup_type (GSocketFamily family, + GSocketType type, + gint protocol_id) +{ + ConnectionFactory *factory, key; + GType g_type; + + init_builtin_types (); + + G_LOCK (connection_factories); + + g_type = G_TYPE_SOCKET_CONNECTION; + + if (connection_factories) + { + key.socket_family = family; + key.socket_type = type; + key.protocol = protocol_id; + + factory = g_hash_table_lookup (connection_factories, &key); + if (factory) + g_type = factory->implementation; + } + + G_UNLOCK (connection_factories); + + return g_type; +} + +/** + * g_socket_connection_factory_create_connection: + * @socket: a #GSocket. + * + * Creates a #GSocketConnection subclass of the right type for + * @socket. + * + * Returns: a #GSocketConnection + * + * Since: 2.22 + **/ +GSocketConnection * +g_socket_connection_factory_create_connection (GSocket *socket) +{ + GType type; + + type = g_socket_connection_factory_lookup_type (g_socket_get_family (socket), + g_socket_get_socket_type (socket), + g_socket_get_protocol_id (socket)); + return g_object_new (type, "socket", socket, NULL); +} + +#define __G_SOCKET_CONNECTION_C__ +#include "gioaliasdef.c" diff --git a/gio/gsocketconnection.h b/gio/gsocketconnection.h new file mode 100644 index 000000000..df37c9143 --- /dev/null +++ b/gio/gsocketconnection.h @@ -0,0 +1,91 @@ +/* GIO - GLib Input, Output and Streaming Library + * Copyright © 2008 Christian Kellner, Samuel Cormier-Iijima + * Copyright © 2009 Codethink Limited + * + * This program 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 licence 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, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Christian Kellner + * Samuel Cormier-Iijima + * Ryan Lortie + * Alexander Larsson + */ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __G_SOCKET_CONNECTION_H__ +#define __G_SOCKET_CONNECTION_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#define G_TYPE_SOCKET_CONNECTION (g_socket_connection_get_type ()) +#define G_SOCKET_CONNECTION(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_SOCKET_CONNECTION, GSocketConnection)) +#define G_SOCKET_CONNECTION_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \ + G_TYPE_SOCKET_CONNECTION, GSocketConnectionClass)) +#define G_IS_SOCKET_CONNECTION(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_SOCKET_CONNECTION)) +#define G_IS_SOCKET_CONNECTION_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \ + G_TYPE_SOCKET_CONNECTION)) +#define G_SOCKET_CONNECTION_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \ + G_TYPE_SOCKET_CONNECTION, GSocketConnectionClass)) + +typedef struct _GSocketConnectionPrivate GSocketConnectionPrivate; +typedef struct _GSocketConnectionClass GSocketConnectionClass; + +struct _GSocketConnectionClass +{ + GIOStreamClass parent_class; + + /* Padding for future expansion */ + void (*_g_reserved1) (void); + void (*_g_reserved2) (void); + void (*_g_reserved3) (void); + void (*_g_reserved4) (void); + void (*_g_reserved5) (void); + void (*_g_reserved6) (void); +}; + +struct _GSocketConnection +{ + GIOStream parent_instance; + GSocketConnectionPrivate *priv; +}; + +GType g_socket_connection_get_type (void) G_GNUC_CONST; + +GSocket *g_socket_connection_get_socket (GSocketConnection *connection); +GSocketAddress *g_socket_connection_get_local_address (GSocketConnection *connection, + GError **error); +GSocketAddress *g_socket_connection_get_remote_address (GSocketConnection *connection, + GError **error); +void g_socket_connection_factory_register_type (GType g_type, + GSocketFamily family, + GSocketType type, + gint protocol); +GType g_socket_connection_factory_lookup_type (GSocketFamily family, + GSocketType type, + gint protocol); +GSocketConnection *g_socket_connection_factory_create_connection (GSocket *socket); + +G_END_DECLS + +#endif /* __G_SOCKET_CONNECTION_H__ */ diff --git a/gio/gsocketinputstream.c b/gio/gsocketinputstream.c new file mode 100644 index 000000000..e24ef02f2 --- /dev/null +++ b/gio/gsocketinputstream.c @@ -0,0 +1,259 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2008 Christian Kellner, Samuel Cormier-Iijima + * © 2009 codethink + * + * 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, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Christian Kellner + * Samuel Cormier-Iijima + * Ryan Lortie + */ + +#include "config.h" +#include "gsocketinputstream.h" +#include "glibintl.h" + +#include +#include + +#define g_socket_input_stream_get_type _g_socket_input_stream_get_type +G_DEFINE_TYPE (GSocketInputStream, g_socket_input_stream, G_TYPE_INPUT_STREAM); + +enum +{ + PROP_0, + PROP_SOCKET +}; + +struct _GSocketInputStreamPrivate +{ + GSocket *socket; + + /* pending operation metadata */ + GSimpleAsyncResult *result; + GCancellable *cancellable; + gboolean from_mainloop; + gpointer buffer; + gsize count; +}; + +static void +g_socket_input_stream_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GSocketInputStream *stream = G_SOCKET_INPUT_STREAM (object); + + switch (prop_id) + { + case PROP_SOCKET: + g_value_set_object (value, stream->priv->socket); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_socket_input_stream_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GSocketInputStream *stream = G_SOCKET_INPUT_STREAM (object); + + switch (prop_id) + { + case PROP_SOCKET: + stream->priv->socket = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_socket_input_stream_finalize (GObject *object) +{ + GSocketInputStream *stream = G_SOCKET_INPUT_STREAM (object); + + if (stream->priv->socket) + g_object_unref (stream->priv->socket); + + if (G_OBJECT_CLASS (g_socket_input_stream_parent_class)->finalize) + (*G_OBJECT_CLASS (g_socket_input_stream_parent_class)->finalize) (object); +} + +static gssize +g_socket_input_stream_read (GInputStream *stream, + void *buffer, + gsize count, + GCancellable *cancellable, + GError **error) +{ + GSocketInputStream *input_stream = G_SOCKET_INPUT_STREAM (stream); + + if (!g_socket_condition_wait (input_stream->priv->socket, + G_IO_IN, cancellable, error)) + return -1; + + return g_socket_receive (input_stream->priv->socket, buffer, count, error); +} + +static gboolean +g_socket_input_stream_read_ready (GSocket *socket, + GIOCondition condition, + GSocketInputStream *stream) +{ + GSimpleAsyncResult *simple; + GError *error = NULL; + + simple = stream->priv->result; + stream->priv->result = NULL; + + if (!g_cancellable_set_error_if_cancelled (stream->priv->cancellable, + &error)) + { + gssize result; + + result = g_socket_receive (stream->priv->socket, + stream->priv->buffer, + stream->priv->count, + &error); + if (result >= 0) + g_simple_async_result_set_op_res_gssize (simple, result); + } + + if (error) + { + g_simple_async_result_set_from_error (simple, error); + g_error_free (error); + } + + if (stream->priv->cancellable) + g_object_unref (stream->priv->cancellable); + + if (stream->priv->from_mainloop) + g_simple_async_result_complete (simple); + else + g_simple_async_result_complete_in_idle (simple); + + g_object_unref (simple); + + return FALSE; +} + +static void +g_socket_input_stream_read_async (GInputStream *stream, + void *buffer, + gsize count, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSocketInputStream *input_stream = G_SOCKET_INPUT_STREAM (stream); + + g_assert (input_stream->priv->result == NULL); + + input_stream->priv->result = + g_simple_async_result_new (G_OBJECT (stream), callback, user_data, + g_socket_input_stream_read_async); + if (cancellable) + g_object_ref (cancellable); + input_stream->priv->cancellable = cancellable; + input_stream->priv->buffer = buffer; + input_stream->priv->count = count; + + if (!g_socket_condition_check (input_stream->priv->socket, G_IO_IN)) + { + GSource *source; + + input_stream->priv->from_mainloop = TRUE; + source = g_socket_create_source (input_stream->priv->socket, + G_IO_IN | G_IO_HUP | G_IO_ERR, + cancellable); + g_source_set_callback (source, + (GSourceFunc) g_socket_input_stream_read_ready, + g_object_ref (input_stream), g_object_unref); + g_source_attach (source, NULL); + g_source_unref (source); + } + else + { + input_stream->priv->from_mainloop = FALSE; + g_socket_input_stream_read_ready (input_stream->priv->socket, G_IO_IN, input_stream); + } +} + +static gssize +g_socket_input_stream_read_finish (GInputStream *stream, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + gssize count; + + g_return_val_if_fail (G_IS_SOCKET_INPUT_STREAM (stream), -1); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_socket_input_stream_read_async); + + count = g_simple_async_result_get_op_res_gssize (simple); + + return count; +} + +static void +g_socket_input_stream_class_init (GSocketInputStreamClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GInputStreamClass *ginputstream_class = G_INPUT_STREAM_CLASS (klass); + + g_type_class_add_private (klass, sizeof (GSocketInputStreamPrivate)); + + gobject_class->finalize = g_socket_input_stream_finalize; + gobject_class->get_property = g_socket_input_stream_get_property; + gobject_class->set_property = g_socket_input_stream_set_property; + + ginputstream_class->read_fn = g_socket_input_stream_read; + ginputstream_class->read_async = g_socket_input_stream_read_async; + ginputstream_class->read_finish = g_socket_input_stream_read_finish; + + g_object_class_install_property (gobject_class, PROP_SOCKET, + g_param_spec_object ("socket", + P_("socket"), + P_("The socket that this stream wraps"), + G_TYPE_SOCKET, G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +static void +g_socket_input_stream_init (GSocketInputStream *stream) +{ + stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, G_TYPE_SOCKET_INPUT_STREAM, GSocketInputStreamPrivate); +} + +GSocketInputStream * +_g_socket_input_stream_new (GSocket *socket) +{ + return G_SOCKET_INPUT_STREAM (g_object_new (G_TYPE_SOCKET_INPUT_STREAM, "socket", socket, NULL)); +} diff --git a/gio/gsocketinputstream.h b/gio/gsocketinputstream.h new file mode 100644 index 000000000..8e5776fef --- /dev/null +++ b/gio/gsocketinputstream.h @@ -0,0 +1,58 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2008 Christian Kellner, Samuel Cormier-Iijima + * Copyright © 2009 Codethink Limited + * + * This program 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 licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Authors: Christian Kellner + * Samuel Cormier-Iijima + * Ryan Lortie + */ + +#ifndef __G_SOCKET_INPUT_STREAM_H__ +#define __G_SOCKET_INPUT_STREAM_H__ + +#include +#include + +G_BEGIN_DECLS + +#define G_TYPE_SOCKET_INPUT_STREAM (_g_socket_input_stream_get_type ()) +#define G_SOCKET_INPUT_STREAM(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_SOCKET_INPUT_STREAM, GSocketInputStream)) +#define G_SOCKET_INPUT_STREAM_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \ + G_TYPE_SOCKET_INPUT_STREAM, GSocketInputStreamClass)) +#define G_IS_SOCKET_INPUT_STREAM(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_SOCKET_INPUT_STREAM)) +#define G_IS_SOCKET_INPUT_STREAM_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \ + G_TYPE_SOCKET_INPUT_STREAM)) +#define G_SOCKET_INPUT_STREAM_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \ + G_TYPE_SOCKET_INPUT_STREAM, GSocketInputStreamClass)) + +typedef struct _GSocketInputStreamPrivate GSocketInputStreamPrivate; +typedef struct _GSocketInputStreamClass GSocketInputStreamClass; +typedef struct _GSocketInputStream GSocketInputStream; + +struct _GSocketInputStreamClass +{ + GInputStreamClass parent_class; +}; + +struct _GSocketInputStream +{ + GInputStream parent_instance; + GSocketInputStreamPrivate *priv; +}; + +GType _g_socket_input_stream_get_type (void) G_GNUC_CONST; +GSocketInputStream * _g_socket_input_stream_new (GSocket *socket); + +G_END_DECLS + +#endif /* __G_SOCKET_INPUT_STREAM_H___ */ diff --git a/gio/gsocketlistener.c b/gio/gsocketlistener.c new file mode 100644 index 000000000..228d9142f --- /dev/null +++ b/gio/gsocketlistener.c @@ -0,0 +1,815 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2008 Christian Kellner, Samuel Cormier-Iijima + * Copyright © 2009 codethink + * Copyright © 2009 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, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Christian Kellner + * Samuel Cormier-Iijima + * Ryan Lortie + * Alexander Larsson + */ + +#include "config.h" +#include "gsocketlistener.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "glibintl.h" + +#include "gioalias.h" + +/** + * SECTION: gsocketlistener + * @title: GSocketListener + * @short_description: a high-level helper object for server sockets + * @see_also: #GThreadedSocketService, #GSocketService. + * + * A #GSocketListener is an object that keeps track of a set + * of server sockets and helps you accept sockets from any of the + * socket, either sync or async. + * + * If you want to implement a network server, also look at #GSocketService + * and #GThreadedSocketService which are subclass of #GSocketListener + * that makes this even easier. + * + * Since: 2.22 + */ + +G_DEFINE_TYPE (GSocketListener, g_socket_listener, G_TYPE_OBJECT); + +enum +{ + PROP_0, + PROP_LISTEN_BACKLOG +}; + + +static GQuark source_quark = 0; + +struct _GSocketListenerPrivate +{ + GPtrArray *sockets; + GMainContext *main_context; + int listen_backlog; + guint closed : 1; +}; + +static void +g_socket_listener_finalize (GObject *object) +{ + GSocketListener *listener = G_SOCKET_LISTENER (object); + + if (listener->priv->main_context) + g_main_context_unref (listener->priv->main_context); + + if (!listener->priv->closed) + g_socket_listener_close (listener); + + g_ptr_array_free (listener->priv->sockets, TRUE); + + G_OBJECT_CLASS (g_socket_listener_parent_class) + ->finalize (object); +} + +static void +g_socket_listener_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GSocketListener *listener = G_SOCKET_LISTENER (object); + + switch (prop_id) + { + case PROP_LISTEN_BACKLOG: + g_value_set_int (value, listener->priv->listen_backlog); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_socket_listener_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GSocketListener *listener = G_SOCKET_LISTENER (object); + + switch (prop_id) + { + case PROP_LISTEN_BACKLOG: + g_socket_listener_set_backlog (listener, g_value_get_int (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + + +static void +g_socket_listener_class_init (GSocketListenerClass *klass) +{ + GObjectClass *gobject_class G_GNUC_UNUSED = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (GSocketListenerPrivate)); + + gobject_class->finalize = g_socket_listener_finalize; + gobject_class->set_property = g_socket_listener_set_property; + gobject_class->get_property = g_socket_listener_get_property; + g_object_class_install_property (gobject_class, PROP_LISTEN_BACKLOG, + g_param_spec_int ("listen-backlog", + P_("Listen backlog"), + P_("outstanding connections in the listen queue"), + 0, + 2000, + 10, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + source_quark = g_quark_from_static_string ("g-socket-listener-source"); +} + +static void +g_socket_listener_init (GSocketListener *listener) +{ + listener->priv = G_TYPE_INSTANCE_GET_PRIVATE (listener, + G_TYPE_SOCKET_LISTENER, + GSocketListenerPrivate); + listener->priv->sockets = + g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + listener->priv->listen_backlog = 10; +} + +/** + * g_socket_service_new: + * + * Creates a new #GSocketListener with no sockets to listen for. + * New listeners can be added with e.g. g_socket_listener_add_address() + * or g_socket_listener_add_inet_port(). + * + * Returns: a new #GSocketListener. + * + * Since: 2.22 + **/ +GSocketListener * +g_socket_listener_new (void) +{ + return g_object_new (G_TYPE_SOCKET_LISTENER, NULL); +} + +static gboolean +check_listener (GSocketListener *listener, + GError **error) +{ + if (listener->priv->closed) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED, + _("Listener is already closed")); + return FALSE; + } + + return TRUE; +} + +/** + * g_socket_listener_add_socket: + * @listener: a #GSocketListener + * @socket: a listening #GSocket + * @source_object: Optional #GObject identifying this source + * @error: #GError for error reporting, or %NULL to ignore. + * + * Adds @socket to the set of sockets that we try to accept + * new clients from. The socket must be bound to a local + * address and listened to. + * + * @source_object will be passed out in the various calls + * to accept to identify this particular source, which is + * useful if you're listening on multiple addresses and do + * different things depending on what address is connected to. + * + * Returns: %TRUE on success, %FALSE on error. + * + * Since: 2.22 + **/ +gboolean +g_socket_listener_add_socket (GSocketListener *listener, + GSocket *socket, + GObject *source_object, + GError **error) +{ + if (!check_listener (listener, error)) + return FALSE; + + /* TODO: Check that socket it is bound & not closed? */ + + if (g_socket_is_closed (socket)) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Added socket is closed")); + return FALSE; + } + + g_ptr_array_add (listener->priv->sockets, socket); + g_socket_set_listen_backlog (socket, listener->priv->listen_backlog); + + if (source_object) + g_object_set_qdata_full (G_OBJECT (socket), source_quark, + g_object_ref (source_object), g_object_unref); + + return TRUE; +} + +/** + * g_socket_listener_add_socket: + * @listener: a #GSocketListener + * @address: a #GSocketAddres + * @type: a #GSocketType + * @protocol: a protocol name, or %NULL + * @source_object: Optional #GObject identifying this source + * @error: #GError for error reporting, or %NULL to ignore. + * + * Creates a socket of type @type and protocol @protocol, binds + * it to @address and adds it to the set of sockets we're accepting + * sockets from. + * + * @source_object will be passed out in the various calls + * to accept to identify this particular source, which is + * useful if you're listening on multiple addresses and do + * different things depending on what address is connected to. + * + * Returns: %TRUE on success, %FALSE on error. + * + * Since: 2.22 + **/ +gboolean +g_socket_listener_add_address (GSocketListener *listener, + GSocketAddress *address, + GSocketType type, + const char *protocol, + GObject *source_object, + GError **error) +{ + GSocketFamily family; + GSocket *socket; + + if (!check_listener (listener, error)) + return FALSE; + + family = g_socket_address_get_family (address); + socket = g_socket_new (family, type, + g_socket_protocol_id_lookup_by_name (protocol), error); + if (socket == NULL) + return FALSE; + + if (!g_socket_bind (socket, address, TRUE, error) || + !g_socket_listen (socket, error) || + !g_socket_listener_add_socket (listener, socket, + source_object, + error)) + { + g_object_unref (socket); + return FALSE; + } + + if (G_SOCKET_LISTENER_GET_CLASS (listener)->changed) + G_SOCKET_LISTENER_GET_CLASS (listener)->changed (listener); + + return TRUE; +} + +/** + * g_socket_listener_add_inet_port: + * @listener: a #GSocketListener + * @port: an ip port number + * @source_object: Optional #GObject identifying this source + * @error: #GError for error reporting, or %NULL to ignore. + * + * Helper function for g_socket_listener_add_address() that + * creates a TCP/IP socket listening on IPv4 and IPv6 (if + * supported) on the specified port on all interfaces. + * + * @source_object will be passed out in the various calls + * to accept to identify this particular source, which is + * useful if you're listening on multiple addresses and do + * different things depending on what address is connected to. + * + * Returns: %TRUE on success, %FALSE on error. + * + * Since: 2.22 + **/ +gboolean +g_socket_listener_add_inet_port (GSocketListener *listener, + int port, + GObject *source_object, + GError **error) +{ + GSocketAddress *address4, *address6; + GInetAddress *inet_address; + gboolean res; + + if (!check_listener (listener, error)) + return FALSE; + + inet_address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4); + address4 = g_inet_socket_address_new (inet_address, port); + g_object_unref (inet_address); + + inet_address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV6); + address6 = g_inet_socket_address_new (inet_address, port); + g_object_unref (inet_address); + + if (!g_socket_listener_add_address (listener, + address6, + G_SOCKET_TYPE_STREAM, + NULL, + source_object, + NULL)) + { + /* Failed, to create ipv6, socket, just use ipv4, + return any error */ + res = g_socket_listener_add_address (listener, + address4, + G_SOCKET_TYPE_STREAM, + NULL, + source_object, + error); + } + else + { + /* Succeeded with ipv6, also try ipv4 in case its ipv6 only, + but ignore errors here */ + res = TRUE; + g_socket_listener_add_address (listener, + address4, + G_SOCKET_TYPE_STREAM, + NULL, + source_object, + NULL); + } + + g_object_unref (address4); + g_object_unref (address6); + + return res; +} + +static GList * +add_sources (GSocketListener *listener, + GSocketSourceFunc callback, + gpointer callback_data, + GCancellable *cancellable, + GMainContext *context) +{ + GSocket *socket; + GSource *source; + GList *sources; + int i; + + sources = NULL; + for (i = 0; i < listener->priv->sockets->len; i++) + { + socket = listener->priv->sockets->pdata[i]; + + source = g_socket_create_source (socket, G_IO_IN, cancellable); + g_source_set_callback (source, + (GSourceFunc) callback, + callback_data, NULL); + g_source_attach (source, context); + + sources = g_list_prepend (sources, source); + } + + return sources; +} + +static void +free_sources (GList *sources) +{ + GSource *source; + while (sources != NULL) + { + source = sources->data; + sources = g_list_delete_link (sources, sources); + g_source_destroy (source); + g_source_unref (source); + } +} + +struct AcceptData { + GMainLoop *loop; + GSocket *socket; +}; + +static gboolean +accept_callback (GSocket *socket, + GIOCondition condition, + gpointer user_data) +{ + struct AcceptData *data = user_data; + + data->socket = socket; + g_main_loop_quit (data->loop); + + return TRUE; +} + +/** + * g_socket_listener_accept_socket: + * @listener: a #GSocketListener + * @source_object: location where #GObject pointer will be stored, or %NULL + * @cancellable: optional #GCancellable object, %NULL to ignore. + * @error: #GError for error reporting, or %NULL to ignore. + * + * Blocks waiting for a client to connect to any of the sockets added + * to the listener. Returns the #GSocket that was accepted. + * + * If you want to accept the high-level #GSocketConnection, not a #GSocket, + * which is often the case, then you should use g_socket_listener_accept() + * instead. + * + * If @source_object is not %NULL it will be filled out with the source + * object specified when the corresponding socket or address was added + * to the listener. + * + * If @cancellable is not NULL, then the operation can be cancelled by + * triggering the cancellable object from another thread. If the operation + * was cancelled, the error G_IO_ERROR_CANCELLED will be returned. + * + * Returns: a #GSocket on success, %NULL on error. + * + * Since: 2.22 + **/ +GSocket * +g_socket_listener_accept_socket (GSocketListener *listener, + GObject **source_object, + GCancellable *cancellable, + GError **error) +{ + GSocket *accept_socket, *socket; + + g_return_val_if_fail (G_IS_SOCKET_LISTENER (listener), NULL); + + if (!check_listener (listener, error)) + return NULL; + + if (listener->priv->sockets->len == 1) + { + accept_socket = listener->priv->sockets->pdata[0]; + if (!g_socket_condition_wait (accept_socket, G_IO_IN, + cancellable, error)) + return NULL; + } + else + { + GList *sources; + struct AcceptData data; + GMainLoop *loop; + + if (listener->priv->main_context == NULL) + listener->priv->main_context = g_main_context_new (); + + loop = g_main_loop_new (listener->priv->main_context, FALSE); + data.loop = loop; + sources = add_sources (listener, + accept_callback, + &data, + cancellable, + listener->priv->main_context); + g_main_loop_run (loop); + accept_socket = data.socket; + free_sources (sources); + g_main_loop_unref (loop); + } + + if (!(socket = g_socket_accept (accept_socket, error))) + return NULL; + + if (source_object) + *source_object = g_object_get_qdata (G_OBJECT (accept_socket), source_quark); + + return socket; +} + +/** + * g_socket_listener_accept: + * @listener: a #GSocketListener + * @source_object: location where #GObject pointer will be stored, or %NULL + * @cancellable: optional #GCancellable object, %NULL to ignore. + * @error: #GError for error reporting, or %NULL to ignore. + * + * Blocks waiting for a client to connect to any of the sockets added + * to the listener. Returns a #GSocketConnection for the socket that was + * accepted. + * + * If @source_object is not %NULL it will be filled out with the source + * object specified when the corresponding socket or address was added + * to the listener. + * + * If @cancellable is not NULL, then the operation can be cancelled by + * triggering the cancellable object from another thread. If the operation + * was cancelled, the error G_IO_ERROR_CANCELLED will be returned. + * + * Returns: a #GSocketConnection on success, %NULL on error. + * + * Since: 2.22 + **/ +GSocketConnection * +g_socket_listener_accept (GSocketListener *listener, + GObject **source_object, + GCancellable *cancellable, + GError **error) +{ + GSocketConnection *connection; + GSocket *socket; + + socket = g_socket_listener_accept_socket (listener, + source_object, + cancellable, + error); + if (socket == NULL) + return NULL; + + connection = g_socket_connection_factory_create_connection (socket); + g_object_unref (socket); + + return connection; +} + +struct AcceptAsyncData { + GSimpleAsyncResult *simple; + GCancellable *cancellable; + GList *sources; +}; + +static gboolean +accept_ready (GSocket *accept_socket, + GIOCondition condition, + gpointer _data) +{ + struct AcceptAsyncData *data = _data; + GError *error = NULL; + + if (!g_cancellable_set_error_if_cancelled (data->cancellable, + &error)) + { + GSocket *socket; + GObject *source_object; + + socket = g_socket_accept (accept_socket, &error); + if (socket) + { + g_simple_async_result_set_op_res_gpointer (data->simple, socket, + g_object_unref); + source_object = g_object_get_qdata (G_OBJECT (accept_socket), source_quark); + if (source_object) + g_object_set_qdata_full (G_OBJECT (data->simple), + source_quark, + g_object_ref (source_object), g_object_unref); + } + } + + if (error) + { + g_simple_async_result_set_from_error (data->simple, error); + g_error_free (error); + } + + g_simple_async_result_complete_in_idle (data->simple); + g_object_unref (data->simple); + free_sources (data->sources); + g_free (data); + + return FALSE; +} + +/** + * g_socket_listener_accept_socket_async: + * @listener: a #GSocketListener + * @cancellable: a #GCancellable, or %NULL + * @callback: a #GAsyncReadyCallback + * @user_data: user data for the callback + * + * This is the asynchronous version of g_socket_listener_accept_socket(). + * + * When the operation is finished @callback will be + * called. You can then call g_socket_listener_accept_socket_finish() to get + * the result of the operation. + * + * Since: 2.22 + **/ +void +g_socket_listener_accept_socket_async (GSocketListener *listener, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + struct AcceptAsyncData *data; + GError *error = NULL; + + if (!check_listener (listener, &error)) + { + g_simple_async_report_gerror_in_idle (G_OBJECT (listener), + callback, user_data, + error); + g_error_free (error); + return; + } + + data = g_new0 (struct AcceptAsyncData, 1); + data->simple = g_simple_async_result_new (G_OBJECT (listener), + callback, user_data, + g_socket_listener_accept_socket_async); + data->cancellable = cancellable; + data->sources = add_sources (listener, + accept_ready, + data, + cancellable, + NULL); +} + +/** + * g_socket_listener_accept_socket_finish: + * @listener: a #GSocketListener + * @result: a #GAsyncResult. + * @source_object: Optional #GObject identifying this source + * @error: a #GError location to store the error occuring, or %NULL to + * ignore. + * + * Finishes an async accept operation. See g_socket_listener_accept_socket_async() + * + * Returns: a #GSocket on success, %NULL on error. + * + * Since: 2.22 + **/ +GSocket * +g_socket_listener_accept_socket_finish (GSocketListener *listener, + GAsyncResult *result, + GObject **source_object, + GError **error) +{ + GSocket *socket; + GSimpleAsyncResult *simple; + + g_return_val_if_fail (G_IS_SOCKET_LISTENER (listener), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return NULL; + + g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_socket_listener_accept_socket_async); + + socket = g_simple_async_result_get_op_res_gpointer (simple); + + if (source_object) + *source_object = g_object_get_qdata (G_OBJECT (result), source_quark); + + return g_object_ref (socket); +} + +/** + * g_socket_listener_accept_socket_async: + * @listener: a #GSocketListener + * @cancellable: a #GCancellable, or %NULL + * @callback: a #GAsyncReadyCallback + * @user_data: user data for the callback + * + * This is the asynchronous version of g_socket_listener_accept(). + * + * When the operation is finished @callback will be + * called. You can then call g_socket_listener_accept_socket() to get + * the result of the operation. + * + * Since: 2.22 + **/ +void +g_socket_listener_accept_async (GSocketListener *listener, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_socket_listener_accept_socket_async (listener, + cancellable, + callback, + user_data); +} + +/** + * g_socket_listener_accept_finish: + * @listener: a #GSocketListener + * @result: a #GAsyncResult. + * @source_object: Optional #GObject identifying this source + * @error: a #GError location to store the error occuring, or %NULL to + * ignore. + * + * Finishes an async accept operation. See g_socket_listener_accept_async() + * + * Returns: a #GSocketConnection on success, %NULL on error. + * + * Since: 2.22 + **/ +GSocketConnection * +g_socket_listener_accept_finish (GSocketListener *listener, + GAsyncResult *result, + GObject **source_object, + GError **error) +{ + GSocket *socket; + GSocketConnection *connection; + + socket = g_socket_listener_accept_socket_finish (listener, + result, + source_object, + error); + if (socket == NULL) + return NULL; + + connection = g_socket_connection_factory_create_connection (socket); + g_object_unref (socket); + return connection; +} + +/** + * g_socket_listener_accept_finish: + * @listener: a #GSocketListener + * @listen_backlog: an integer + * + * Sets the listen backlog on the sockets in the listener. + * + * See g_socket_set_listen_backlog() for details + * + * Since: 2.22 + **/ +void +g_socket_listener_set_backlog (GSocketListener *listener, + int listen_backlog) +{ + GSocket *socket; + int i; + + if (listener->priv->closed) + return; + + listener->priv->listen_backlog = listen_backlog; + + for (i = 0; i < listener->priv->sockets->len; i++) + { + socket = listener->priv->sockets->pdata[i]; + g_socket_set_listen_backlog (socket, listen_backlog); + } +} + +/** + * g_socket_listener_close: + * @listener: a #GSocketListener + * + * Closes all the sockets in the listener. + * + * Since: 2.22 + **/ +void +g_socket_listener_close (GSocketListener *listener) +{ + GSocket *socket; + int i; + + g_return_if_fail (G_IS_SOCKET_LISTENER (listener)); + + if (listener->priv->closed) + return; + + for (i = 0; i < listener->priv->sockets->len; i++) + { + socket = listener->priv->sockets->pdata[i]; + g_socket_close (socket, NULL); + } + listener->priv->closed = TRUE; +} + +#define __G_SOCKET_LISTENER_C__ +#include "gioaliasdef.c" diff --git a/gio/gsocketlistener.h b/gio/gsocketlistener.h new file mode 100644 index 000000000..9a13209a2 --- /dev/null +++ b/gio/gsocketlistener.h @@ -0,0 +1,134 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2008 Christian Kellner, Samuel Cormier-Iijima + * Copyright © 2009 Codethink Limited + * Copyright © 2009 Red Hat, Inc + * + * This program 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 licence 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, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Christian Kellner + * Samuel Cormier-Iijima + * Ryan Lortie + * Alexander Larsson + */ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __G_SOCKET_LISTENER_H__ +#define __G_SOCKET_LISTENER_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_SOCKET_LISTENER (g_socket_listener_get_type ()) +#define G_SOCKET_LISTENER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_SOCKET_LISTENER, GSocketListener)) +#define G_SOCKET_LISTENER_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \ + G_TYPE_SOCKET_LISTENER, GSocketListenerClass)) +#define G_IS_SOCKET_LISTENER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_SOCKET_LISTENER)) +#define G_IS_SOCKET_LISTENER_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \ + G_TYPE_SOCKET_LISTENER)) +#define G_SOCKET_LISTENER_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \ + G_TYPE_SOCKET_LISTENER, GSocketListenerClass)) + +typedef struct _GSocketListenerPrivate GSocketListenerPrivate; +typedef struct _GSocketListenerClass GSocketListenerClass; + +/** + * GSocketListenerClass: + * @changed: virtual method called when the set of socket listened to changes + **/ +struct _GSocketListenerClass +{ + GObjectClass parent_class; + + void (* changed) (GSocketListener *listener); + + /* Padding for future expansion */ + void (*_g_reserved1) (void); + void (*_g_reserved2) (void); + void (*_g_reserved3) (void); + void (*_g_reserved4) (void); + void (*_g_reserved5) (void); + void (*_g_reserved6) (void); +}; + +struct _GSocketListener +{ + GObject parent_instance; + GSocketListenerPrivate *priv; +}; + +GType g_socket_listener_get_type (void) G_GNUC_CONST; + +GSocketListener * g_socket_listener_new (void); + +void g_socket_listener_set_backlog (GSocketListener *listener, + int listen_backlog); + +gboolean g_socket_listener_add_socket (GSocketListener *listener, + GSocket *socket, + GObject *source_object, + GError **error); +gboolean g_socket_listener_add_address (GSocketListener *listener, + GSocketAddress *address, + GSocketType type, + const char *protocol, + GObject *source_object, + GError **error); +gboolean g_socket_listener_add_inet_port (GSocketListener *listener, + int port, + GObject *source_object, + GError **error); + +GSocket * g_socket_listener_accept_socket (GSocketListener *listener, + GObject **source_object, + GCancellable *cancellable, + GError **error); +void g_socket_listener_accept_socket_async (GSocketListener *listener, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GSocket * g_socket_listener_accept_socket_finish (GSocketListener *listener, + GAsyncResult *result, + GObject **source_object, + GError **error); + + +GSocketConnection * g_socket_listener_accept (GSocketListener *listener, + GObject **source_object, + GCancellable *cancellable, + GError **error); + +void g_socket_listener_accept_async (GSocketListener *listener, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GSocketConnection * g_socket_listener_accept_finish (GSocketListener *listener, + GAsyncResult *result, + GObject **source_object, + GError **error); + +void g_socket_listener_close (GSocketListener *listener); + +G_END_DECLS + +#endif /* __G_SOCKET_LISTENER_H__ */ diff --git a/gio/gsocketoutputstream.c b/gio/gsocketoutputstream.c new file mode 100644 index 000000000..0e53a5788 --- /dev/null +++ b/gio/gsocketoutputstream.c @@ -0,0 +1,259 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2008 Christian Kellner, Samuel Cormier-Iijima + * © 2009 codethink + * + * 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, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Christian Kellner + * Samuel Cormier-Iijima + * Ryan Lortie + */ + +#include "config.h" +#include "gsocketoutputstream.h" + +#include +#include +#include "glibintl.h" + +#define g_socket_output_stream_get_type _g_socket_output_stream_get_type +G_DEFINE_TYPE (GSocketOutputStream, g_socket_output_stream, G_TYPE_OUTPUT_STREAM); + +enum +{ + PROP_0, + PROP_SOCKET +}; + +struct _GSocketOutputStreamPrivate +{ + GSocket *socket; + + /* pending operation metadata */ + GSimpleAsyncResult *result; + GCancellable *cancellable; + gboolean from_mainloop; + gconstpointer buffer; + gsize count; +}; + +static void +g_socket_output_stream_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GSocketOutputStream *stream = G_SOCKET_OUTPUT_STREAM (object); + + switch (prop_id) + { + case PROP_SOCKET: + g_value_set_object (value, stream->priv->socket); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_socket_output_stream_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GSocketOutputStream *stream = G_SOCKET_OUTPUT_STREAM (object); + + switch (prop_id) + { + case PROP_SOCKET: + stream->priv->socket = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_socket_output_stream_finalize (GObject *object) +{ + GSocketOutputStream *stream = G_SOCKET_OUTPUT_STREAM (object); + + if (stream->priv->socket) + g_object_unref (stream->priv->socket); + + if (G_OBJECT_CLASS (g_socket_output_stream_parent_class)->finalize) + (*G_OBJECT_CLASS (g_socket_output_stream_parent_class)->finalize) (object); +} + +static gssize +g_socket_output_stream_write (GOutputStream *stream, + const void *buffer, + gsize count, + GCancellable *cancellable, + GError **error) +{ + GSocketOutputStream *onput_stream = G_SOCKET_OUTPUT_STREAM (stream); + + if (!g_socket_condition_wait (onput_stream->priv->socket, + G_IO_OUT, cancellable, error)) + return -1; + + return g_socket_send (onput_stream->priv->socket, buffer, count, error); +} + +static gboolean +g_socket_output_stream_write_ready (GSocket *socket, + GIOCondition condition, + GSocketOutputStream *stream) +{ + GSimpleAsyncResult *simple; + GError *error = NULL; + + simple = stream->priv->result; + stream->priv->result = NULL; + + if (!g_cancellable_set_error_if_cancelled (stream->priv->cancellable, + &error)) + { + gssize result; + + result = g_socket_send (stream->priv->socket, + stream->priv->buffer, + stream->priv->count, + &error); + if (result >= 0) + g_simple_async_result_set_op_res_gssize (simple, result); + } + + if (error) + { + g_simple_async_result_set_from_error (simple, error); + g_error_free (error); + } + + if (stream->priv->cancellable) + g_object_unref (stream->priv->cancellable); + + if (stream->priv->from_mainloop) + g_simple_async_result_complete (simple); + else + g_simple_async_result_complete_in_idle (simple); + + g_object_unref (simple); + + return FALSE; +} + +static void +g_socket_output_stream_write_async (GOutputStream *stream, + const void *buffer, + gsize count, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSocketOutputStream *output_stream = G_SOCKET_OUTPUT_STREAM (stream); + + g_assert (output_stream->priv->result == NULL); + + output_stream->priv->result = + g_simple_async_result_new (G_OBJECT (stream), callback, user_data, + g_socket_output_stream_write_async); + if (cancellable) + g_object_ref (cancellable); + output_stream->priv->cancellable = cancellable; + output_stream->priv->buffer = buffer; + output_stream->priv->count = count; + + if (!g_socket_condition_check (output_stream->priv->socket, G_IO_OUT)) + { + GSource *source; + + output_stream->priv->from_mainloop = TRUE; + source = g_socket_create_source (output_stream->priv->socket, + G_IO_OUT | G_IO_HUP | G_IO_ERR, + cancellable); + g_source_set_callback (source, + (GSourceFunc) g_socket_output_stream_write_ready, + g_object_ref (output_stream), g_object_unref); + g_source_attach (source, NULL); + g_source_unref (source); + } + else + { + output_stream->priv->from_mainloop = FALSE; + g_socket_output_stream_write_ready (output_stream->priv->socket, G_IO_OUT, output_stream); + } +} + +static gssize +g_socket_output_stream_write_finish (GOutputStream *stream, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + gssize count; + + g_return_val_if_fail (G_IS_SOCKET_OUTPUT_STREAM (stream), -1); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_socket_output_stream_write_async); + + count = g_simple_async_result_get_op_res_gssize (simple); + + return count; +} + +static void +g_socket_output_stream_class_init (GSocketOutputStreamClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GOutputStreamClass *goutputstream_class = G_OUTPUT_STREAM_CLASS (klass); + + g_type_class_add_private (klass, sizeof (GSocketOutputStreamPrivate)); + + gobject_class->finalize = g_socket_output_stream_finalize; + gobject_class->get_property = g_socket_output_stream_get_property; + gobject_class->set_property = g_socket_output_stream_set_property; + + goutputstream_class->write_fn = g_socket_output_stream_write; + goutputstream_class->write_async = g_socket_output_stream_write_async; + goutputstream_class->write_finish = g_socket_output_stream_write_finish; + + g_object_class_install_property (gobject_class, PROP_SOCKET, + g_param_spec_object ("socket", + P_("socket"), + P_("The socket that this stream wraps"), + G_TYPE_SOCKET, G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +static void +g_socket_output_stream_init (GSocketOutputStream *stream) +{ + stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, G_TYPE_SOCKET_OUTPUT_STREAM, GSocketOutputStreamPrivate); +} + +GSocketOutputStream * +_g_socket_output_stream_new (GSocket *socket) +{ + return G_SOCKET_OUTPUT_STREAM (g_object_new (G_TYPE_SOCKET_OUTPUT_STREAM, "socket", socket, NULL)); +} diff --git a/gio/gsocketoutputstream.h b/gio/gsocketoutputstream.h new file mode 100644 index 000000000..e3514d1b2 --- /dev/null +++ b/gio/gsocketoutputstream.h @@ -0,0 +1,58 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2008 Christian Kellner, Samuel Cormier-Iijima + * Copyright © 2009 Codethink Limited + * + * This program 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 licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Authors: Christian Kellner + * Samuel Cormier-Iijima + * Ryan Lortie + */ + +#ifndef __G_SOCKET_OUTPUT_STREAM_H__ +#define __G_SOCKET_OUTPUT_STREAM_H__ + +#include +#include + +G_BEGIN_DECLS + +#define G_TYPE_SOCKET_OUTPUT_STREAM (_g_socket_output_stream_get_type ()) +#define G_SOCKET_OUTPUT_STREAM(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_SOCKET_OUTPUT_STREAM, GSocketOutputStream)) +#define G_SOCKET_OUTPUT_STREAM_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \ + G_TYPE_SOCKET_OUTPUT_STREAM, GSocketOutputStreamClass)) +#define G_IS_SOCKET_OUTPUT_STREAM(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_SOCKET_OUTPUT_STREAM)) +#define G_IS_SOCKET_OUTPUT_STREAM_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \ + G_TYPE_SOCKET_OUTPUT_STREAM)) +#define G_SOCKET_OUTPUT_STREAM_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \ + G_TYPE_SOCKET_OUTPUT_STREAM, GSocketOutputStreamClass)) + +typedef struct _GSocketOutputStreamPrivate GSocketOutputStreamPrivate; +typedef struct _GSocketOutputStreamClass GSocketOutputStreamClass; +typedef struct _GSocketOutputStream GSocketOutputStream; + +struct _GSocketOutputStreamClass +{ + GOutputStreamClass parent_class; +}; + +struct _GSocketOutputStream +{ + GOutputStream parent_instance; + GSocketOutputStreamPrivate *priv; +}; + +GType _g_socket_output_stream_get_type (void) G_GNUC_CONST; +GSocketOutputStream * _g_socket_output_stream_new (GSocket *socket); + +G_END_DECLS + +#endif /* __G_SOCKET_OUTPUT_STREAM_H__ */ diff --git a/gio/gsocketservice.c b/gio/gsocketservice.c new file mode 100644 index 000000000..9c56a696e --- /dev/null +++ b/gio/gsocketservice.c @@ -0,0 +1,330 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2009 Codethink Limited + * Copyright © 2009 Red Hat, Inc + * + * This program 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 licence 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, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Ryan Lortie + * Alexander Larsson + */ + +/** + * SECTION: gsocketservice + * @title: GSocketService + * @short_description: a high-level object representing a service + * @see_also: #GThreadedSocketService, #GSocketListener. + * + * A #GSocketService is an object that represents a service that is + * provided to the network or over local sockets. When a new + * connection is made to the service the ::incoming signal is emitted. + * + * A #GSocketService is a subclass of #GSocketListener and you need + * to add the addresses you want to accept connections on to the + * with the #GSocketListener APIs. + * + * There are two options for implementing a network service based on + * #GSocketService. The first is to create the service using + * g_socket_service_new() and to connect to the ::incoming signal. + * The second is to subclass #GSocketService and override the default + * signal handler implementation. + * + * In either case, the handler must immediately return, or else it + * will block additional incoming connections from being serviced. If + * you are interested in writing connection handlers that contain + * blocking code then see #GThreadedSocketService. + * + * The socket service runs on the main loop in the main thread, and is + * not threadsafe in general. However, the calls to start and stop + * the service are threadsafe so these can be used from threads that + * handle incomming clients. + * + * Since: 2.22 + */ + +#include "config.h" +#include "gsocketservice.h" + +#include "gio-marshal.h" +#include +#include "gsocketlistener.h" +#include "gsocketconnection.h" + +#include "gioalias.h" + +static guint g_socket_service_incoming_signal; + +G_DEFINE_TYPE (GSocketService, g_socket_service, G_TYPE_SOCKET_LISTENER); + +G_LOCK_DEFINE_STATIC(active); + +struct _GSocketServicePrivate +{ + GCancellable *cancellable; + guint active : 1; + guint outstanding_accept : 1; +}; + +static void g_socket_service_ready (GObject *object, + GAsyncResult *result, + gpointer user_data); + +static gboolean +g_socket_service_real_incoming (GSocketService *service, + GSocketConnection *connection, + GObject *source_object) +{ + return FALSE; +} + +static void +g_socket_service_init (GSocketService *service) +{ + service->priv = G_TYPE_INSTANCE_GET_PRIVATE (service, + G_TYPE_SOCKET_SERVICE, + GSocketServicePrivate); + service->priv->cancellable = g_cancellable_new (); + service->priv->active = TRUE; +} + +static void +g_socket_service_finalize (GObject *object) +{ + GSocketService *service = G_SOCKET_SERVICE (object); + + g_object_unref (service->priv->cancellable); + + G_OBJECT_CLASS (g_socket_service_parent_class) + ->finalize (object); +} + +static void +do_accept (GSocketService *service) +{ + g_socket_listener_accept_async (G_SOCKET_LISTENER (service), + service->priv->cancellable, + g_socket_service_ready, NULL); + service->priv->outstanding_accept = TRUE; +} + +static void +g_socket_service_changed (GSocketListener *listener) +{ + GSocketService *service = G_SOCKET_SERVICE (listener); + + G_LOCK (active); + + if (service->priv->active) + { + if (service->priv->outstanding_accept) + g_cancellable_cancel (service->priv->cancellable); + else + { + g_socket_listener_accept_async (listener, service->priv->cancellable, + g_socket_service_ready, NULL); + service->priv->outstanding_accept = TRUE; + } + } + + G_UNLOCK (active); +} + +/** + * g_socket_service_is_active: + * @service: a #GSocketService + * + * Check whether the service is active or not. An active + * service will accept new clients that connect, while + * a non-active service will let connecting clients queue + * up until the service is started. + * + * Returns: %TRUE if the service is active, %FALSE otherwise + * + * Since: 2.22 + **/ +gboolean +g_socket_service_is_active (GSocketService *service) +{ + gboolean active; + + G_LOCK (active); + active = service->priv->active; + G_UNLOCK (active); + return active; +} + +/** + * g_socket_service_start: + * @service: a #GSocketService + * + * Starts the service, i.e. start accepting connections + * from the added sockets when the mainloop runs. + * + * This call is threadsafe, so it may be called from a thread + * handling an incomming client request. + * + * Since: 2.22 + **/ +void +g_socket_service_start (GSocketService *service) +{ + G_LOCK (active); + + if (!service->priv->active) + { + service->priv->active = TRUE; + + if (service->priv->outstanding_accept) + g_cancellable_cancel (service->priv->cancellable); + else + do_accept (service); + } + + G_UNLOCK (active); +} + +/** + * g_socket_service_stop: + * @service: a #GSocketService + * + * Stops the service, i.e. stops accepting connections + * from the added sockets when the mainloop runs. + * + * This call is threadsafe, so it may be called from a thread + * handling an incomming client request. + * + * Since: 2.22 + **/ +void +g_socket_service_stop (GSocketService *service) +{ + G_LOCK (active); + + if (service->priv->active) + { + service->priv->active = FALSE; + + if (service->priv->outstanding_accept) + g_cancellable_cancel (service->priv->cancellable); + } + + G_UNLOCK (active); +} + + +static gboolean +g_socket_service_incoming (GSocketService *service, + GSocketConnection *connection, + GObject *source_object) +{ + gboolean result; + + g_signal_emit (service, g_socket_service_incoming_signal, + 0, connection, source_object, &result); + return result; +} + +static void +g_socket_service_class_init (GSocketServiceClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + GSocketListenerClass *listener_class = G_SOCKET_LISTENER_CLASS (class); + + g_type_class_add_private (class, sizeof (GSocketServicePrivate)); + + gobject_class->finalize = g_socket_service_finalize; + listener_class->changed = g_socket_service_changed; + class->incoming = g_socket_service_real_incoming; + + /** + * GSocketService::incoming: + * @service: the #GSocketService. + * @connection: a new #GSocketConnection object. + * @source_object: the source_object passed to g_socket_listener_add_address(). + * @returns: %TRUE if @connection has been handled. + * + * The ::incoming signal is emitted when a new incoming connection + * to @service needs to be handled. The handler must initiate the + * handling of @connection, but may not block; in essence, + * asynchronous operations must be used. + * + * If %TRUE is returned then no other handlers are called. + **/ + g_socket_service_incoming_signal = + g_signal_new ("incoming", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GSocketServiceClass, incoming), + g_signal_accumulator_true_handled, NULL, + _gio_marshal_BOOLEAN__OBJECT_OBJECT, G_TYPE_BOOLEAN, + 2, G_TYPE_SOCKET_CONNECTION, G_TYPE_OBJECT); +} + +static void +g_socket_service_ready (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GSocketListener *listener = G_SOCKET_LISTENER (object); + GSocketService *service = G_SOCKET_SERVICE (object); + GSocketConnection *connection; + GObject *source_object; + GError *error = NULL; + + connection = g_socket_listener_accept_finish (listener, result, &source_object, &error); + if (error) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("fail: %s", error->message); + g_error_free (error); + } + else + { + g_socket_service_incoming (service, connection, source_object); + g_object_unref (connection); + } + + G_LOCK (active); + + g_cancellable_reset (service->priv->cancellable); + + /* requeue */ + service->priv->outstanding_accept = FALSE; + if (service->priv->active) + do_accept (service); + + G_UNLOCK (active); +} + + +/** + * g_socket_service_new: + * @returns: a new #GSocketService. + * + * Creates a new #GSocketService with no sockets to listen for. + * New listeners can be added with e.g. g_socket_listener_add_address() + * or g_socket_listener_add_inet_port(). + * + * Returns: a new #GSocketService. + * + * Since: 2.22 + **/ +GSocketService * +g_socket_service_new (void) +{ + return g_object_new (G_TYPE_SOCKET_SERVICE, NULL); +} + +#define __G_SOCKET_SERVICE_C__ +#include "gioaliasdef.c" diff --git a/gio/gsocketservice.h b/gio/gsocketservice.h new file mode 100644 index 000000000..fe77984bb --- /dev/null +++ b/gio/gsocketservice.h @@ -0,0 +1,88 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2009 Codethink Limited + * Copyright © 2009 Red Hat, Inc + * + * This program 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 licence 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, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Ryan Lortie + * Alexander Larsson + */ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __G_SOCKET_SERVICE_H__ +#define __G_SOCKET_SERVICE_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_SOCKET_SERVICE (g_socket_service_get_type ()) +#define G_SOCKET_SERVICE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_SOCKET_SERVICE, GSocketService)) +#define G_SOCKET_SERVICE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \ + G_TYPE_SOCKET_SERVICE, GSocketServiceClass)) +#define G_IS_SOCKET_SERVICE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_SOCKET_SERVICE)) +#define G_IS_SOCKET_SERVICE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \ + G_TYPE_SOCKET_SERVICE)) +#define G_SOCKET_SERVICE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \ + G_TYPE_SOCKET_SERVICE, GSocketServiceClass)) + +typedef struct _GSocketServicePrivate GSocketServicePrivate; +typedef struct _GSocketServiceClass GSocketServiceClass; + +/** + * GSocketServiceClass: + * @incomming: signal emitted when new connections are accepted + **/ +struct _GSocketServiceClass +{ + GSocketListenerClass parent_class; + + gboolean (* incoming) (GSocketService *service, + GSocketConnection *connection, + GObject *source_object); + + /* Padding for future expansion */ + void (*_g_reserved1) (void); + void (*_g_reserved2) (void); + void (*_g_reserved3) (void); + void (*_g_reserved4) (void); + void (*_g_reserved5) (void); + void (*_g_reserved6) (void); +}; + +struct _GSocketService +{ + GSocketListener parent_instance; + GSocketServicePrivate *priv; +}; + +GType g_socket_service_get_type (void); + +GSocketService *g_socket_service_new (void); +void g_socket_service_start (GSocketService *service); +void g_socket_service_stop (GSocketService *service); +gboolean g_socket_service_is_active (GSocketService *service); + + +G_END_DECLS + +#endif /* __G_SOCKET_SERVICE_H__ */ diff --git a/gio/gtcpconnection.c b/gio/gtcpconnection.c new file mode 100644 index 000000000..bd1c4110c --- /dev/null +++ b/gio/gtcpconnection.c @@ -0,0 +1,67 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2008, 2009 Codethink Limited + * + * This program 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 licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + */ + +/** + * SECTION: gtcpconnection + * @title: GTcpConnection + * @short_description: a TCP #GSocketConnection + * @see_also: #GSocketConnection. + * + * This is the subclass of #GSocketConnection that is created + * for TCP/IP sockets. + * + * It is currently empty; it offers no additional functionality + * over its base class. + * + * Eventually, some TCP-specific socket stuff will be added. + * + * Since: 2.22 + **/ + +#include "config.h" +#include "gtcpconnection.h" +#include "glibintl.h" + +#include "gioalias.h" + +G_DEFINE_TYPE_WITH_CODE (GTcpConnection, g_tcp_connection, + G_TYPE_SOCKET_CONNECTION, + g_socket_connection_factory_register_type (g_define_type_id, + G_SOCKET_FAMILY_IPV4, + G_SOCKET_TYPE_STREAM, + 0); + g_socket_connection_factory_register_type (g_define_type_id, + G_SOCKET_FAMILY_IPV6, + G_SOCKET_TYPE_STREAM, + 0); + g_socket_connection_factory_register_type (g_define_type_id, + G_SOCKET_FAMILY_IPV4, + G_SOCKET_TYPE_STREAM, + g_socket_protocol_id_lookup_by_name ("tcp")); + g_socket_connection_factory_register_type (g_define_type_id, + G_SOCKET_FAMILY_IPV6, + G_SOCKET_TYPE_STREAM, + g_socket_protocol_id_lookup_by_name ("tcp")); + ); + +static void +g_tcp_connection_init (GTcpConnection *connection) +{ +} + +static void +g_tcp_connection_class_init (GTcpConnectionClass *class) +{ +} + +#define __G_TCP_CONNECTION_C__ +#include "gioaliasdef.c" diff --git a/gio/gtcpconnection.h b/gio/gtcpconnection.h new file mode 100644 index 000000000..a9eed5d17 --- /dev/null +++ b/gio/gtcpconnection.h @@ -0,0 +1,64 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2008, 2009 Codethink Limited + * + * This program 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 licence 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, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Ryan Lortie + */ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __G_TCP_CONNECTION_H__ +#define __G_TCP_CONNECTION_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_TCP_CONNECTION (g_tcp_connection_get_type ()) +#define G_TCP_CONNECTION(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_TCP_CONNECTION, GTcpConnection)) +#define G_TCP_CONNECTION_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \ + G_TYPE_TCP_CONNECTION, GTcpConnectionClass)) +#define G_IS_TCP_CONNECTION(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_TCP_CONNECTION)) +#define G_IS_TCP_CONNECTION_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \ + G_TYPE_TCP_CONNECTION)) +#define G_TCP_CONNECTION_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \ + G_TYPE_TCP_CONNECTION, GTcpConnectionClass)) + +typedef struct _GTcpConnectionPrivate GTcpConnectionPrivate; +typedef struct _GTcpConnectionClass GTcpConnectionClass; + +struct _GTcpConnectionClass +{ + GSocketConnectionClass parent_class; +}; + +struct _GTcpConnection +{ + GSocketConnection parent_instance; + GTcpConnectionPrivate *priv; +}; + +GType g_tcp_connection_get_type (void); + +G_END_DECLS + +#endif /* __G_TCP_CONNECTION_H__ */ diff --git a/gio/gthreadedsocketservice.c b/gio/gthreadedsocketservice.c new file mode 100644 index 000000000..182d1b164 --- /dev/null +++ b/gio/gthreadedsocketservice.c @@ -0,0 +1,215 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2009 Codethink Limited + * Copyright © 2009 Red Hat, Inc + * + * This program 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 licence 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, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Ryan Lortie + * Alexander Larsson + */ + +/** + * SECTION: gthreadedsocketservice + * @title: GThreadedSocketService + * @short_description: a threaded #GSocketService + * @see_also: #GSocketService. + * + * A #GThreadedSocketService is a simple subclass of #GSocketService + * that handles incoming connections by creating a worker thread and + * dispatching the connection to it by emitting the ::run signal in + * the new thread. + * + * The signal handler may perform blocking IO and need not return + * until the connection is closed. + * + * The service is implemented using a thread pool, so there is a + * limited amount of threads availible to serve incomming requests. + * The service automatically stops the #GSocketService from accepting + * new connections when all threads are busy. + * + * As with #GSocketService, you may connect to ::run, or subclass and + * override the default handler. + */ + +#include "config.h" +#include "gsocketconnection.h" +#include "gthreadedsocketservice.h" + +#include "gio-marshal.h" + +#include "gioalias.h" + +static guint g_threaded_socket_service_run_signal; + +G_DEFINE_TYPE (GThreadedSocketService, + g_threaded_socket_service, + G_TYPE_SOCKET_SERVICE); + +G_LOCK_DEFINE_STATIC(job_count); + +struct _GThreadedSocketServicePrivate +{ + GThreadPool *thread_pool; + int max_threads; + gint job_count; +}; + +typedef struct +{ + GThreadedSocketService *service; + GSocketConnection *connection; + GObject *source_object; +} GThreadedSocketServiceData; + +static void +g_threaded_socket_service_func (gpointer _data, + gpointer user_data) +{ + GThreadedSocketService *threaded = user_data; + GThreadedSocketServiceData *data = _data; + gboolean result; + + g_signal_emit (data->service, g_threaded_socket_service_run_signal, + 0, data->connection, data->source_object, &result); + + g_object_unref (data->service); + g_object_unref (data->connection); + if (data->source_object) + g_object_unref (data->source_object); + g_slice_free (GThreadedSocketServiceData, data); + + G_LOCK (job_count); + if (threaded->priv->job_count-- == threaded->priv->max_threads) + g_socket_service_start (G_SOCKET_SERVICE (threaded)); + G_UNLOCK (job_count); +} + +static gboolean +g_threaded_socket_service_incoming (GSocketService *service, + GSocketConnection *connection, + GObject *source_object) +{ + GThreadedSocketService *threaded; + GThreadedSocketServiceData *data; + + threaded = G_THREADED_SOCKET_SERVICE (service); + + data = g_slice_new (GThreadedSocketServiceData); + data->service = g_object_ref (service); + data->connection = g_object_ref (connection); + if (source_object) + data->source_object = g_object_ref (source_object); + else + data->source_object = NULL; + + G_LOCK (job_count); + if (++threaded->priv->job_count == threaded->priv->max_threads) + g_socket_service_stop (service); + G_UNLOCK (job_count); + + g_thread_pool_push (threaded->priv->thread_pool, data, NULL); + + + + return FALSE; +} + +static void +g_threaded_socket_service_init (GThreadedSocketService *service) +{ + service->priv = G_TYPE_INSTANCE_GET_PRIVATE (service, + G_TYPE_THREADED_SOCKET_SERVICE, + GThreadedSocketServicePrivate); + service->priv->max_threads = 10; +} + +static void +g_threaded_socket_service_constructed (GObject *object) +{ + GThreadedSocketService *service = G_THREADED_SOCKET_SERVICE (object); + + service->priv->thread_pool = + g_thread_pool_new (g_threaded_socket_service_func, + service, + service->priv->max_threads, + FALSE, + NULL); +} + + +static void +g_threaded_socket_service_finalize (GObject *object) +{ + GThreadedSocketService *service = G_THREADED_SOCKET_SERVICE (object); + + g_object_unref (service->priv->thread_pool); + + G_OBJECT_CLASS (g_threaded_socket_service_parent_class) + ->finalize (object); +} + + +static void +g_threaded_socket_service_class_init (GThreadedSocketServiceClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + GSocketServiceClass *ss_class = &class->parent_class; + + g_type_class_add_private (class, sizeof (GThreadedSocketServicePrivate)); + + gobject_class->constructed = g_threaded_socket_service_constructed; + gobject_class->finalize = g_threaded_socket_service_finalize; + + ss_class->incoming = g_threaded_socket_service_incoming; + + /** + * GThreadedSocketService::run: + * @service: the #GThreadedSocketService. + * @connection: a new #GSocketConnection object. + * @source_object: the source_object passed to g_socket_listener_add_address(). + * @returns: %TRUE if @connection has been handled. + * + * The ::run signal is emitted in a worker thread in response to an + * incoming connection. This thread is dedicated to handling + * @connection and may perform blocking IO. The signal handler need + * not return until the connection is closed. + * + * If %TRUE is returned then no other handlers are called. + **/ + g_threaded_socket_service_run_signal = + g_signal_new ("run", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GThreadedSocketServiceClass, run), + g_signal_accumulator_true_handled, NULL, + _gio_marshal_BOOLEAN__OBJECT_OBJECT, G_TYPE_BOOLEAN, + 2, G_TYPE_SOCKET_CONNECTION, G_TYPE_OBJECT); +} + +/** + * g_threaded_socket_service_new: + * @returns: a new #GSocketService. + * + * Creates a new #GThreadedSocketService with no listeners. Listeners + * must be added with g_socket_service_add_listeners(). + **/ +GSocketService * +g_threaded_socket_service_new (void) +{ + return g_object_new (G_TYPE_THREADED_SOCKET_SERVICE, NULL); +} + +#define __G_THREADED_SOCKET_SERVICE_C__ +#include "gioaliasdef.c" diff --git a/gio/gthreadedsocketservice.h b/gio/gthreadedsocketservice.h new file mode 100644 index 000000000..209f83ca4 --- /dev/null +++ b/gio/gthreadedsocketservice.h @@ -0,0 +1,81 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2009 Codethink Limited + * Copyright © 2009 Red Hat, Inc + * + * This program 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 licence 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, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Ryan Lortie + * Alexander Larsson + */ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __G_THREADED_SOCKET_SERVICE_H__ +#define __G_THREADED_SOCKET_SERVICE_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_THREADED_SOCKET_SERVICE (g_threaded_socket_service_get_type ()) +#define G_THREADED_SOCKET_SERVICE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_THREADED_SOCKET_SERVICE, \ + GThreadedSocketService)) +#define G_THREADED_SOCKET_SERVICE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \ + G_TYPE_THREADED_SOCKET_SERVICE, \ + GThreadedSocketServiceClass)) +#define G_IS_THREADED_SOCKET_SERVICE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_THREADED_SOCKET_SERVICE)) +#define G_IS_THREADED_SOCKET_SERVICE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \ + G_TYPE_THREADED_SOCKET_SERVICE)) +#define G_THREADED_SOCKET_SERVICE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \ + G_TYPE_THREADED_SOCKET_SERVICE, \ + GThreadedSocketServiceClass)) + +typedef struct _GThreadedSocketServicePrivate GThreadedSocketServicePrivate; +typedef struct _GThreadedSocketServiceClass GThreadedSocketServiceClass; + +struct _GThreadedSocketServiceClass +{ + GSocketServiceClass parent_class; + + gboolean (* run) (GThreadedSocketService *service, + GSocketConnection *connection, + GObject *source_object); + + /* Padding for future expansion */ + void (*_g_reserved1) (void); + void (*_g_reserved2) (void); + void (*_g_reserved3) (void); + void (*_g_reserved4) (void); + void (*_g_reserved5) (void); +}; + +struct _GThreadedSocketService +{ + GSocketService parent_instance; + GThreadedSocketServicePrivate *priv; +}; + +GType g_threaded_socket_service_get_type (void); +GSocketService * g_threaded_socket_service_new (void); + +G_END_DECLS + +#endif /* __G_THREADED_SOCKET_SERVICE_H__ */ diff --git a/gio/gunixconnection.c b/gio/gunixconnection.c new file mode 100644 index 000000000..b7416513f --- /dev/null +++ b/gio/gunixconnection.c @@ -0,0 +1,293 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2009 Codethink Limited + * + * This program 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 licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Authors: Ryan Lortie + */ + +#include "config.h" +#include "gunixconnection.h" +#include "glibintl.h" + +/** + * SECTION: gunixconnection + * @title: GUnixConnection + * @short_description: a TCP #GSocketConnection + * @see_also: #GSocketConnection. + * + * This is the subclass of #GSocketConnection that is created + * for UNIX domain sockets. + * + * It contains functions to do some of the unix socket specific + * functionallity like passing file descriptors. + * + * Since: 2.22 + **/ + +#include +#include +#include +#include + +#include "gioalias.h" + +G_DEFINE_TYPE_WITH_CODE (GUnixConnection, g_unix_connection, + G_TYPE_SOCKET_CONNECTION, + g_socket_connection_factory_register_type (g_define_type_id, + G_SOCKET_FAMILY_UNIX, + G_SOCKET_TYPE_STREAM, + 0); + ); + +/** + * g_unix_connection_send_fd: + * @connection: a #GUnixConnection. + * @fd: a file descriptor + * @cancellable: optional #GCancellable object, %NULL to ignore. + * @error: #GError for error reporting, or %NULL to ignore. + * + * Passes a file descriptor to the recieving side of the + * connection. The recieving end has to call g_unix_connection_receive_fd() + * to accept the file descriptor. + * + * As well as sending the fd this also writes a single byte to the + * stream, as this is required for fd passing to work on some + * implementations. + * + * Returns: a %TRUE on success, %NULL on error. + * + * Since: 2.22 + **/ +gboolean +g_unix_connection_send_fd (GUnixConnection *connection, + gint fd, + GCancellable *cancellable, + GError **error) +{ + GSocketControlMessage *scm; + GSocket *socket; + + g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), FALSE); + g_return_val_if_fail (fd >= 0, FALSE); + + scm = g_unix_fd_message_new (); + + if (!g_unix_fd_message_append_fd (G_UNIX_FD_MESSAGE (scm), fd, error)) + { + g_object_unref (scm); + return FALSE; + } + + g_object_get (connection, "socket", &socket, NULL); + if (!g_socket_condition_wait (socket, G_IO_OUT, cancellable, error) || + g_socket_send_message (socket, NULL, NULL, 0, &scm, 1, 0, error) != 1) + /* XXX could it 'fail' with zero? */ + { + g_object_unref (socket); + g_object_unref (scm); + + return FALSE; + } + + g_object_unref (socket); + g_object_unref (scm); + + return TRUE; +} + +/** + * g_unix_connection_receive_fd: + * @connection: a #GUnixConnection. + * @cancellable: optional #GCancellable object, %NULL to ignore. + * @error: #GError for error reporting, or %NULL to ignore. + * + * Recieves a file descriptor from the sending end of the + * connection. The sending end has to call g_unix_connection_send_fd() + * for this to work. + * + * As well as reading the fd this also reads a single byte from the + * stream, as this is required for fd passing to work on some + * implementations. + * + * Returns: a file descriptor on success, -1 on error. + * + * Since: 2.22 + **/ +gint +g_unix_connection_receive_fd (GUnixConnection *connection, + GCancellable *cancellable, + GError **error) +{ + GSocketControlMessage **scms; + gint *fds, nfd, fd, nscm; + GUnixFDMessage *fdmsg; + GSocket *socket; + + g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), -1); + + g_object_get (connection, "socket", &socket, NULL); + if (!g_socket_condition_wait (socket, G_IO_IN, cancellable, error) || + g_socket_receive_message (socket, NULL, NULL, 0, + &scms, &nscm, NULL, error) != 1) + /* XXX it _could_ 'fail' with zero. */ + { + g_object_unref (socket); + + return -1; + } + + g_object_unref (socket); + + if (nscm != 1) + { + gint i; + + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Expecting 1 control message, got %d"), nscm); + + for (i = 0; i < nscm; i++) + g_object_unref (scms[i]); + + g_free (scms); + + return -1; + } + + if (!G_IS_UNIX_FD_MESSAGE (scms[0])) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Unexpected type of ancillary data")); + g_object_unref (scms[0]); + g_free (scms); + + return -1; + } + + fdmsg = G_UNIX_FD_MESSAGE (scms[0]); + g_free (scms); + + fds = g_unix_fd_message_steal_fds (fdmsg, &nfd); + g_object_unref (fdmsg); + + if (nfd != 1) + { + gint i; + + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Expecting one fd, but got %d\n"), nfd); + + for (i = 0; i < nfd; i++) + close (fds[i]); + + g_free (fds); + + return -1; + } + + fd = *fds; + g_free (fds); + + if (fd < 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Received invalid fd")); + fd = -1; + } + + return fd; +} + +static void +g_unix_connection_init (GUnixConnection *connection) +{ +} + +static void +g_unix_connection_class_init (GUnixConnectionClass *class) +{ +} + +/* TODO: Other stuff we might want to add are: +void g_unix_connection_send_fd_async (GUnixConnection *connection, + gint fd, + gboolean close, + gint io_priority, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean g_unix_connection_send_fd_finish (GUnixConnection *connection, + GError **error); + +gboolean g_unix_connection_send_fds (GUnixConnection *connection, + gint *fds, + gint nfds, + GError **error); +void g_unix_connection_send_fds_async (GUnixConnection *connection, + gint *fds, + gint nfds, + gint io_priority, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean g_unix_connection_send_fds_finish (GUnixConnection *connection, + GError **error); + +void g_unix_connection_receive_fd_async (GUnixConnection *connection, + gint io_priority, + GAsyncReadyCallback callback, + gpointer user_data); +gint g_unix_connection_receive_fd_finish (GUnixConnection *connection, + GError **error); + + +gboolean g_unix_connection_send_credentials (GUnixConnection *connection, + GError **error); +void g_unix_connection_send_credentials_async (GUnixConnection *connection, + gint io_priority, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean g_unix_connection_send_credentials_finish (GUnixConnection *connection, + GError **error); + +gboolean g_unix_connection_send_fake_credentials (GUnixConnection *connection, + guint64 pid, + guint64 uid, + guint64 gid, + GError **error); +void g_unix_connection_send_fake_credentials_async (GUnixConnection *connection, + guint64 pid, + guint64 uid, + guint64 gid, + gint io_priority, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean g_unix_connection_send_fake_credentials_finish (GUnixConnection *connection, + GError **error); + +gboolean g_unix_connection_receive_credentials (GUnixConnection *connection, + guint64 *pid, + guint64 *uid, + guint64 *gid, + GError **error); +void g_unix_connection_receive_credentials_async (GUnixConnection *connection, + gint io_priority, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean g_unix_connection_receive_credentials_finish (GUnixConnection *connection, + guint64 *pid, + guint64 *uid, + guint64 *gid, + GError **error); + +gboolean g_unix_connection_create_pair (GUnixConnection **one, + GUnixConnection **two, + GError **error); +*/ + +#define __G_UNIX_CONNECTION_C__ +#include "gioaliasdef.c" diff --git a/gio/gunixconnection.h b/gio/gunixconnection.h new file mode 100644 index 000000000..b51ef90ca --- /dev/null +++ b/gio/gunixconnection.h @@ -0,0 +1,77 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2009 Codethink Limited + * + * This program 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 licence 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, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Ryan Lortie + */ + +#ifndef __G_UNIX_CONNECTION_H__ +#define __G_UNIX_CONNECTION_H__ + +#include +#include + +G_BEGIN_DECLS + +#define G_TYPE_UNIX_CONNECTION (g_unix_connection_get_type ()) +#define G_UNIX_CONNECTION(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_UNIX_CONNECTION, GUnixConnection)) +#define G_UNIX_CONNECTION_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \ + G_TYPE_UNIX_CONNECTION, GUnixConnectionClass)) +#define G_IS_UNIX_CONNECTION(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_UNIX_CONNECTION)) +#define G_IS_UNIX_CONNECTION_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \ + G_TYPE_UNIX_CONNECTION)) +#define G_UNIX_CONNECTION_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \ + G_TYPE_UNIX_CONNECTION, GUnixConnectionClass)) + +/** + * GTcpConnection: + * + * A #GSocketConnection for UNIX domain socket connections. + * + * Since: 2.22 + **/ +typedef struct _GUnixConnection GUnixConnection; +typedef struct _GUnixConnectionPrivate GUnixConnectionPrivate; +typedef struct _GUnixConnectionClass GUnixConnectionClass; + +struct _GUnixConnectionClass +{ + GSocketConnectionClass parent_class; +}; + +struct _GUnixConnection +{ + GSocketConnection parent_instance; + GUnixConnectionPrivate *priv; +}; + +GType g_unix_connection_get_type (void); + +gboolean g_unix_connection_send_fd (GUnixConnection *connection, + gint fd, + GCancellable *cancellable, + GError **error); +gint g_unix_connection_receive_fd (GUnixConnection *connection, + GCancellable *cancellable, + GError **error); + +G_END_DECLS + +#endif /* __G_UNIX_CONNECTION_H__ */