diff --git a/configure.in b/configure.in index c5c9aa5c2..c9285fbc2 100644 --- a/configure.in +++ b/configure.in @@ -975,6 +975,14 @@ if $glib_failed ; then AC_MSG_ERROR([Could not determine values for AF_INET* constants]) fi +glib_failed=false +GLIB_CHECK_VALUE(MSG_PEEK, $glib_inet_includes, glib_failed=true) +GLIB_CHECK_VALUE(MSG_OOB, $glib_inet_includes, glib_failed=true) +GLIB_CHECK_VALUE(MSG_DONTROUTE, $glib_inet_includes, glib_failed=true) +if $glib_failed ; then + AC_MSG_ERROR([Could not determine values for MSG_* constants]) +fi + AC_CHECK_FUNCS(getprotobyname_r endservent) AC_CHECK_HEADERS([netdb.h winsock2.h mswsock.h]) @@ -3038,6 +3046,10 @@ typedef $g_pid_type GPid; #define GLIB_SYSDEF_AF_INET $g_af_inet #define GLIB_SYSDEF_AF_INET6 $g_af_inet6 +#define GLIB_SYSDEF_MSG_OOB $g_msg_oob +#define GLIB_SYSDEF_MSG_PEEK $g_msg_peek +#define GLIB_SYSDEF_MSG_DONTROUTE $g_msg_dontroute + G_END_DECLS #endif /* GLIBCONFIG_H */ @@ -3328,6 +3340,10 @@ g_af_unix=$glib_cv_value_AF_UNIX g_af_inet=$glib_cv_value_AF_INET g_af_inet6=$glib_cv_value_AF_INET6 +g_msg_peek=$glib_cv_value_MSG_PEEK +g_msg_oob=$glib_cv_value_MSG_OOB +g_msg_dontroute=$glib_cv_value_MSG_DONTROUTE + g_stack_grows=$glib_cv_stack_grows g_have_eilseq=$have_eilseq diff --git a/gio/Makefile.am b/gio/Makefile.am index 8481a7aed..12413c14d 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 = \ + gunixfdmessage.c \ gunixmount.c \ gunixmount.h \ gunixmounts.c \ @@ -150,6 +151,7 @@ giounixincludedir=$(includedir)/gio-unix-2.0/gio giounixinclude_HEADERS = \ gdesktopappinfo.h \ gunixmounts.h \ + gunixfdmessage.h \ gunixinputstream.h \ gunixoutputstream.h \ gunixsocketaddress.h \ @@ -176,6 +178,7 @@ libgio_2_0_la_SOURCES = \ gappinfo.c \ gasynchelper.c \ gasynchelper.h \ + gasyncinitable.c \ gasyncresult.c \ gbufferedinputstream.c \ gbufferedoutputstream.c \ @@ -207,6 +210,7 @@ libgio_2_0_la_SOURCES = \ gicon.c \ ginetaddress.c \ ginetsocketaddress.c \ + ginitable.c \ ginputstream.c \ gioenums.h \ gioerror.c \ @@ -230,6 +234,8 @@ libgio_2_0_la_SOURCES = \ gresolver.c \ gseekable.c \ gsimpleasyncresult.c \ + gsocket.c \ + gsocketcontrolmessage.c \ gsocketaddress.c \ gsocketaddressenumerator.c \ gsocketconnectable.c \ @@ -299,6 +305,7 @@ libgio_2_0_la_DEPENDENCIES = $(gio_def) $(platform_deps) gio_headers = \ gappinfo.h \ + gasyncinitable.h \ gasyncresult.h \ gbufferedinputstream.h \ gbufferedoutputstream.h \ @@ -325,6 +332,7 @@ gio_headers = \ ginetaddress.h \ ginetsocketaddress.h \ ginputstream.h \ + ginitable.h \ gio.h \ giotypes.h \ gioenums.h \ @@ -344,6 +352,8 @@ gio_headers = \ gresolver.h \ gseekable.h \ gsimpleasyncresult.h \ + gsocket.h \ + gsocketcontrolmessage.h \ gsocketaddress.h \ gsocketaddressenumerator.h \ gsocketconnectable.h \ diff --git a/gio/gasyncinitable.c b/gio/gasyncinitable.c new file mode 100644 index 000000000..5b9b2c722 --- /dev/null +++ b/gio/gasyncinitable.c @@ -0,0 +1,383 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 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. + * + * Author: Alexander Larsson + */ + +#include "config.h" +#include "gasyncinitable.h" +#include "gasyncresult.h" +#include "gsimpleasyncresult.h" +#include "glibintl.h" + +#include "gioalias.h" + +/** + * SECTION:gasyncinitable + * @short_description: Asynchronously failable object initialization interface + * @include: gio/gio.h + * @see_also: #GInitable + * + * This is the asynchronous version of #GInitable, it behaves the same + * in all ways except that initialization is asynchronous. For more details + * see the descriptions on #GInitable. + * + * A class may implement both the #GInitable and #GAsyncInitable interfaces + * or both. + * + * Users of objects implementing this are not intended to use + * the interface method directly, instead it will be used automatically + * in various ways. For C applications you generally just call + * g_async_initable_new() directly, or indirectly via a foo_thing_new_async() wrapper. + * This will call g_async_initable_init() under the cover, calling back with %NULL and + * a set %GError on failure. + **/ + +static void g_async_initable_base_init (gpointer g_iface); +static void g_async_initable_real_init_async (GAsyncInitable *initable, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +static gboolean g_async_initable_real_init_finish (GAsyncInitable *initable, + GAsyncResult *res, + GError **error); + +GType +g_async_initable_get_type (void) +{ + static volatile gsize g_define_type_id__volatile = 0; + + if (g_once_init_enter (&g_define_type_id__volatile)) + { + const GTypeInfo initable_info = + { + sizeof (GAsyncInitableIface), /* class_size */ + g_async_initable_base_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + GType g_define_type_id = + g_type_register_static (G_TYPE_INTERFACE, I_("GAsyncInitable"), + &initable_info, 0); + + g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT); + + g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); + } + + return g_define_type_id__volatile; +} + +static void +g_async_initable_base_init (gpointer g_iface) +{ + GAsyncInitableIface *iface = g_iface; + + iface->init_async = g_async_initable_real_init_async; + iface->init_finish = g_async_initable_real_init_finish; +} + +/** + * g_async_initable_init_async: + * @initable: a #GAsyncInitable. + * @io_priority: the I/O priority + * of the operation. + * @cancellable: optional #GCancellable object, %NULL to ignore. + * @callback: a #GAsyncReadyCallback to call when the request is satisfied + * @user_data: the data to pass to callback function + * + * Starts asynchronous initialization of the object implementing the interface. + * This must be done before any real use of the object after initial construction. + * If the object also implements #GInitable you can optionally call g_initable_init() + * instead. + * + * When the initialization is finished, @callback will be called. You can then call + * g_async_initable_init_finish() to get the result of the initialization. + * + * Implementations may also support cancellation. If @cancellable is not %NULL, + * then initialization 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. If @cancellable is not %NULL and + * the object doesn't support cancellable initialization the error + * %G_IO_ERROR_NOT_SUPPORTED will be returned. + * + * If this function is not called, or returns with an error then all + * operations on the object should fail, generally returning the + * error %G_IO_ERROR_NOT_INITIALIZED. + * + * Implementations of this method must be idempotent, i.e. multiple calls + * to this function with the same argument should return the same results. + * Only the first call initializes the object, further calls return the result + * of the first call. This is so that its safe to implement the singleton + * pattern in the GObject constructor function. + * + * For classes that also support the #GInitable interface the default + * implementation of this method will run the g_initable_init() function + * in a thread, so if you want to support asynchronous initialization via + * threads, just implement the #GAsyncInitable interface without overriding + * any interface methods. + * + * Since: 2.22 + **/ +void +g_async_initable_init_async (GAsyncInitable *initable, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GAsyncInitableIface *iface; + + g_return_if_fail (G_IS_ASYNC_INITABLE (initable)); + + iface = G_ASYNC_INITABLE_GET_IFACE (initable); + + return (* iface->init_async) (initable, io_priority, cancellable, callback, user_data); +} + +/** + * g_async_initable_init_finish: + * @initable: a #GAsyncInitable. + * @res: a #GAsyncResult. + * @error: a #GError location to store the error occuring, or %NULL to + * ignore. + * + * Finishes asynchronous initialization and returns the result. + * See g_async_initable_init_async(). + * + * Returns: %TRUE if successful. If an error + * has occurred, this function will return %FALSE and set @error + * appropriately if present. + * + * Since: 2.22 + **/ +gboolean +g_async_initable_init_finish (GAsyncInitable *initable, + GAsyncResult *res, + GError **error) +{ + GAsyncInitableIface *iface; + + g_return_val_if_fail (G_IS_ASYNC_INITABLE (initable), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE); + + if (G_IS_SIMPLE_ASYNC_RESULT (res)) + { + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + } + + iface = G_ASYNC_INITABLE_GET_IFACE (initable); + + return (* iface->init_finish) (initable, res, error); +} + +static void +async_init_thread (GSimpleAsyncResult *res, + GObject *object, + GCancellable *cancellable) +{ + GError *error = NULL; + + if (!g_initable_init (G_INITABLE (res), cancellable, &error)) + { + g_simple_async_result_set_from_error (res, error); + g_error_free (error); + } +} + +static void +g_async_initable_real_init_async (GAsyncInitable *initable, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + + g_return_if_fail (G_IS_INITABLE (initable)); + + res = g_simple_async_result_new (G_OBJECT (initable), callback, user_data, + g_async_initable_real_init_async); + g_simple_async_result_run_in_thread (res, async_init_thread, + io_priority, cancellable); + g_object_unref (res); +} + +static gboolean +g_async_initable_real_init_finish (GAsyncInitable *initable, + GAsyncResult *res, + GError **error) +{ + return TRUE; /* Errors handled by base impl */ +} + +/** + * g_async_initable_new_async: + * @object_type: a #GType supporting #GAsyncInitable. + * @io_priority: the I/O priority + * of the operation. + * @cancellable: optional #GCancellable object, %NULL to ignore. + * @callback: a #GAsyncReadyCallback to call when the initialization is finished + * @user_data: the data to pass to callback function + * @first_property_name: the name of the first property, followed by + * the value, and other property value pairs, and ended by %NULL. + * + * Helper function for constructing #GAsyncInitiable object. This is + * similar to g_object_new() but also initializes the object asyncronously. + * + * When the initialization is finished, @callback will be called. You can then call + * g_async_initable_new_finish() to get new object and check for any errors. + * + * Since: 2.22 + **/ +void +g_async_initable_new_async (GType object_type, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data, + const gchar *first_property_name, + ...) +{ + va_list var_args; + + va_start (var_args, first_property_name); + g_async_initable_new_valist_async (object_type, + first_property_name, var_args, + io_priority, cancellable, + callback, user_data); + va_end (var_args); +} + +/** + * g_async_initable_newv_async: + * @object_type: a #GType supporting #GAsyncInitable. + * @n_parameters: the number of parameters in @parameters + * @parameters: the parameters to use to construct the object + * @io_priority: the I/O priority + * of the operation. + * @cancellable: optional #GCancellable object, %NULL to ignore. + * @callback: a #GAsyncReadyCallback to call when the initialization is finished + * @user_data: the data to pass to callback function + * + * Helper function for constructing #GAsyncInitiable object. This is + * similar to g_object_newv() but also initializes the object asyncronously. + * + * When the initialization is finished, @callback will be called. You can then call + * g_async_initable_new_finish() to get new object and check for any errors. + * + * Since: 2.22 + **/ +void +g_async_initable_newv_async (GType object_type, + guint n_parameters, + GParameter *parameters, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GObject *obj; + + g_return_if_fail (G_TYPE_IS_ASYNC_INITABLE (object_type)); + + obj = g_object_newv (object_type, n_parameters, parameters); + + g_async_initable_init_async (G_ASYNC_INITABLE (obj), + io_priority, cancellable, + callback, user_data); +} + +/** + * g_async_initable_new_async: + * @object_type: a #GType supporting #GAsyncInitable. + * @first_property_name: the name of the first property, followed by + * the value, and other property value pairs, and ended by %NULL. + * @var_args: The var args list generated from @first_property_name. + * @io_priority: the I/O priority + * of the operation. + * @cancellable: optional #GCancellable object, %NULL to ignore. + * @callback: a #GAsyncReadyCallback to call when the initialization is finished + * @user_data: the data to pass to callback function + * + * Helper function for constructing #GAsyncInitiable object. This is + * similar to g_object_new_valist() but also initializes the object asyncronously. + * + * When the initialization is finished, @callback will be called. You can then call + * g_async_initable_new_finish() to get new object and check for any errors. + * + * Since: 2.22 + **/ +void +g_async_initable_new_valist_async (GType object_type, + const gchar *first_property_name, + va_list var_args, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GObject *obj; + + g_return_if_fail (G_TYPE_IS_ASYNC_INITABLE (object_type)); + + obj = g_object_new_valist (object_type, + first_property_name, + var_args); + + g_async_initable_init_async (G_ASYNC_INITABLE (obj), + io_priority, cancellable, + callback, user_data); + g_object_unref (obj); /* Passed ownership to async call */ +} +/** + * g_async_initable_new_finish: + * @initable: the #GAsyncInitable from the callback + * @res: the #GAsyncResult.from the callback + * @error: a #GError location to store the error occuring, or %NULL to + * ignore. + * + * Finishes the async construction for the various g_async_initable_new calls, + * returning the created object or %NULL on error. + * + * Returns: a newly created #GObject, or %NULL on error. Free with g_object_unref(). + * + * Since: 2.22 + **/ +GObject * +g_async_initable_new_finish (GAsyncInitable *initable, + GAsyncResult *res, + GError **error) +{ + if (g_async_initable_init_finish (initable, res, error)) + return g_object_ref (initable); + else + return NULL; +} + +#define __G_ASYNC_INITABLE_C__ +#include "gioaliasdef.c" diff --git a/gio/gasyncinitable.h b/gio/gasyncinitable.h new file mode 100644 index 000000000..844282157 --- /dev/null +++ b/gio/gasyncinitable.h @@ -0,0 +1,119 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 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. + * + * Author: Alexander Larsson + */ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __G_ASYNC_INITABLE_H__ +#define __G_ASYNC_INITABLE_H__ + +#include +#include + +G_BEGIN_DECLS + +#define G_TYPE_ASYNC_INITABLE (g_async_initable_get_type ()) +#define G_ASYNC_INITABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_ASYNC_INITABLE, GAsyncInitable)) +#define G_IS_ASYNC_INITABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_ASYNC_INITABLE)) +#define G_ASYNC_INITABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_ASYNC_INITABLE, GAsyncInitableIface)) +#define G_TYPE_IS_ASYNC_INITABLE(type) (g_type_is_a ((type), G_TYPE_ASYNC_INITABLE)) + +/** + * GAsyncInitable: + * + * Interface for asynchronously initializable objects. + * + * Since: 2.22 + **/ +typedef struct _GAsyncInitableIface GAsyncInitableIface; + +/** + * GAsyncInitableIface: + * @g_iface: The parent interface. + * @init_async: Starts initialization of the object. + * @init_finish: Finishes initialization of the object. + * + * Provides an interface for asynchronous initializing object such that + * initialization may fail. + * + * Since: 2.22 + **/ +struct _GAsyncInitableIface +{ + GTypeInterface g_iface; + + /* Virtual Table */ + + void (* init_async) (GAsyncInitable *initable, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (* init_finish) (GAsyncInitable *initable, + GAsyncResult *res, + GError **error); +}; + +GType g_async_initable_get_type (void) G_GNUC_CONST; + + +void g_async_initable_init_async (GAsyncInitable *initable, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean g_async_initable_init_finish (GAsyncInitable *initable, + GAsyncResult *res, + GError **error); + +void g_async_initable_new_async (GType object_type, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data, + const gchar *first_property_name, + ...); +void g_async_initable_newv_async (GType object_type, + guint n_parameters, + GParameter *parameters, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +void g_async_initable_new_valist_async (GType object_type, + const gchar *first_property_name, + va_list var_args, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GObject *g_async_initable_new_finish (GAsyncInitable *initable, + GAsyncResult *res, + GError **error); + + + +G_END_DECLS + + +#endif /* __G_ASYNC_INITABLE_H__ */ diff --git a/gio/ginitable.c b/gio/ginitable.c new file mode 100644 index 000000000..cc4ca3f53 --- /dev/null +++ b/gio/ginitable.c @@ -0,0 +1,251 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 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. + * + * Author: Alexander Larsson + */ + +#include "config.h" +#include "ginitable.h" +#include "glibintl.h" + +#include "gioalias.h" + +/** + * SECTION:ginitable + * @short_description: Failable object initialization interface + * @include: gio/gio.h + * @see_also: #GAsyncInitable + * + * #GInitable is implemented by objects that can fail during + * initialization. If an object implements this interface the + * g_initable_init() function must be called as the first thing + * after construction. If g_initable_init() is not called, or if + * it returns an error, all further operations on the object + * should fail, generally with a %G_IO_ERROR_NOT_INITIALIZED error. + * + * Users of objects implementing this are not intended to use + * the interface method directly, instead it will be used automatically + * in various ways. For C applications you generally just call + * g_initable_new() directly, or indirectly via a foo_thing_new() wrapper. + * This will call g_initable_init() under the cover, returning %NULL and + * setting a %GError on failure. + * + * For bindings in languages where the native constructor supports + * exceptions the binding could check for objects implemention %GInitable + * during normal construction and automatically initialize them, throwing + * an exception on failure. + **/ + +GType +g_initable_get_type (void) +{ + static volatile gsize g_define_type_id__volatile = 0; + + if (g_once_init_enter (&g_define_type_id__volatile)) + { + const GTypeInfo initable_info = + { + sizeof (GInitableIface), /* class_size */ + NULL, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + GType g_define_type_id = + g_type_register_static (G_TYPE_INTERFACE, I_("GInitable"), + &initable_info, 0); + + g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT); + + g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); + } + + return g_define_type_id__volatile; +} + +/** + * g_initable_init: + * @initable: a #GInitable. + * @cancellable: optional #GCancellable object, %NULL to ignore. + * @error: a #GError location to store the error occuring, or %NULL to + * ignore. + * + * Initializes the object implementing the interface. This must be + * done before any real use of the object after initial construction. + * + * Implementations may also support cancellation. If @cancellable is not %NULL, + * then initialization 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. If @cancellable is not %NULL and + * the object doesn't support cancellable initialization the error + * %G_IO_ERROR_NOT_SUPPORTED will be returned. + * + * If this function is not called, or returns with an error then all + * operations on the object should fail, generally returning the + * error %G_IO_ERROR_NOT_INITIALIZED. + * + * Implementations of this method must be idempotent, i.e. multiple calls + * to this function with the same argument should return the same results. + * Only the first call initializes the object, further calls return the result + * of the first call. This is so that its safe to implement the singleton + * pattern in the GObject constructor function. + * + * Returns: %TRUE if successful. If an error + * has occurred, this function will return %FALSE and set @error + * appropriately if present. + * + * Since: 2.22 + **/ +gboolean +g_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + GInitableIface *iface; + + g_return_val_if_fail (G_IS_INITABLE (initable), FALSE); + + iface = G_INITABLE_GET_IFACE (initable); + + return (* iface->init) (initable, cancellable, error); +} + +/** + * g_initable_new: + * @object_type: a #GType supporting #GInitable. + * @cancellable: optional #GCancellable object, %NULL to ignore. + * @error: a #GError location to store the error occuring, or %NULL to + * ignore. + * @first_property_name: the name of the first property, followed by + * the value, and other property value pairs, and ended by %NULL. + * + * Helper function for constructing #GInitiable object. This is + * similar to g_object_new() but also initializes the object + * and returns %NULL, setting an error on failure. + * + * Return value: a newly allocated #GObject, or %NULL on error + * + * Since: 2.22 + **/ +gpointer +g_initable_new (GType object_type, + GCancellable *cancellable, + GError **error, + const gchar *first_property_name, + ...) +{ + GObject *object; + va_list var_args; + + va_start (var_args, first_property_name); + object = g_initable_new_valist (object_type, + first_property_name, var_args, + cancellable, error); + va_end (var_args); + + return object; +} + +/** + * g_initable_new: + * @object_type: a #GType supporting #GInitable. + * @n_parameters: the number of parameters in @parameters + * @parameters: the parameters to use to construct the object + * @cancellable: optional #GCancellable object, %NULL to ignore. + * @error: a #GError location to store the error occuring, or %NULL to + * ignore. + * + * Helper function for constructing #GInitiable object. This is + * similar to g_object_newv() but also initializes the object + * and returns %NULL, setting an error on failure. + * + * Return value: a newly allocated #GObject, or %NULL on error + * + * Since: 2.22 + **/ +gpointer +g_initable_newv (GType object_type, + guint n_parameters, + GParameter *parameters, + GCancellable *cancellable, + GError **error) +{ + GObject *obj; + + g_return_val_if_fail (G_TYPE_IS_INITABLE (object_type), NULL); + + obj = g_object_newv (object_type, n_parameters, parameters); + + if (!g_initable_init (G_INITABLE (obj), cancellable, error)) + { + g_object_unref (obj); + return NULL; + } + + return (gpointer)obj; +} + +/** + * g_initable_new_valist: + * @object_type: a #GType supporting #GInitable. + * @first_property_name: the name of the first property, followed by + * the value, and other property value pairs, and ended by %NULL. + * @var_args: The var args list generated from @first_property_name. + * @cancellable: optional #GCancellable object, %NULL to ignore. + * @error: a #GError location to store the error occuring, or %NULL to + * ignore. + * + * Helper function for constructing #GInitiable object. This is + * similar to g_object_new_valist() but also initializes the object + * and returns %NULL, setting an error on failure. + * + * Return value: a newly allocated #GObject, or %NULL on error + * + * Since: 2.22 + **/ +GObject* +g_initable_new_valist (GType object_type, + const gchar *first_property_name, + va_list var_args, + GCancellable *cancellable, + GError **error) +{ + GObject *obj; + + g_return_val_if_fail (G_TYPE_IS_INITABLE (object_type), NULL); + + obj = g_object_new_valist (object_type, + first_property_name, + var_args); + + if (!g_initable_init (G_INITABLE (obj), cancellable, error)) + { + g_object_unref (obj); + return NULL; + } + + return obj; +} + +#define __G_INITABLE_C__ +#include "gioaliasdef.c" diff --git a/gio/ginitable.h b/gio/ginitable.h new file mode 100644 index 000000000..5336ad8ad --- /dev/null +++ b/gio/ginitable.h @@ -0,0 +1,95 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 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. + * + * Author: Alexander Larsson + */ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __G_INITABLE_H__ +#define __G_INITABLE_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_INITABLE (g_initable_get_type ()) +#define G_INITABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_INITABLE, GInitable)) +#define G_IS_INITABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_INITABLE)) +#define G_INITABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_INITABLE, GInitableIface)) +#define G_TYPE_IS_INITABLE(type) (g_type_is_a ((type), G_TYPE_INITABLE)) + +/** + * GInitable: + * + * Interface for initializable objects. + * + * Since: 2.22 + **/ +typedef struct _GInitableIface GInitableIface; + +/** + * GInitableIface: + * @g_iface: The parent interface. + * @init: Initializes the object. + * + * Provides an interface for initializing object such that initialization may fail. + * + * Since: 2.22 + **/ +struct _GInitableIface +{ + GTypeInterface g_iface; + + /* Virtual Table */ + + gboolean (* init) (GInitable *initable, + GCancellable *cancellable, + GError **error); +}; + + +GType g_initable_get_type (void) G_GNUC_CONST; + +gboolean g_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error); + +gpointer g_initable_new (GType object_type, + GCancellable *cancellable, + GError **error, + const gchar *first_property_name, + ...); +gpointer g_initable_newv (GType object_type, + guint n_parameters, + GParameter *parameters, + GCancellable *cancellable, + GError **error); +GObject* g_initable_new_valist (GType object_type, + const gchar *first_property_name, + va_list var_args, + GCancellable *cancellable, + GError **error); + +G_END_DECLS + + +#endif /* __G_INITABLE_H__ */ diff --git a/gio/gio.h b/gio/gio.h index f12d2ed90..0160b8266 100644 --- a/gio/gio.h +++ b/gio/gio.h @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -56,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -71,6 +73,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/gio/gio.symbols b/gio/gio.symbols index e21eaf60b..d24979e9f 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -868,6 +868,8 @@ g_ask_password_flags_get_type G_GNUC_CONST g_password_save_get_type G_GNUC_CONST g_emblem_origin_get_type G_GNUC_CONST g_socket_family_get_type G_GNUC_CONST +g_socket_type_get_type G_GNUC_CONST +g_socket_msg_flags_get_type G_GNUC_CONST g_resolver_error_get_type G_GNUC_CONST #endif #endif @@ -1035,3 +1037,82 @@ g_socket_address_enumerator_next_async g_socket_address_enumerator_next_finish #endif #endif + +#if IN_HEADER(__G_ASYNC_INITABLE_H__) +#if IN_FILE(__G_ASYNC_INITABLE_C__) +g_async_initable_get_type G_GNUC_CONST +g_async_initable_init_async +g_async_initable_init_finish +g_async_initable_new_async +g_async_initable_new_finish +g_async_initable_new_valist_async +g_async_initable_newv_async +#endif +#endif + +#if IN_HEADER(__G_INITABLE_H__) +#if IN_FILE(__G_INITABLE_C__) +g_initable_get_type G_GNUC_CONST +g_initable_init +g_initable_new +g_initable_newv +g_initable_new_valist +#endif +#endif + +#if IN_HEADER(__G_SOCKET_H__) +#if IN_FILE(__G_SOCKET_C__) +g_socket_get_type G_GNUC_CONST +g_socket_accept +g_socket_bind +g_socket_check_pending_error +g_socket_close +g_socket_condition_check +g_socket_condition_wait +g_socket_connect +g_socket_create_source +g_socket_get_blocking +g_socket_get_family +g_socket_get_fd +g_socket_get_keepalive +g_socket_get_listen_backlog +g_socket_get_local_address +g_socket_get_protocol +g_socket_get_remote_address +g_socket_get_socket_type +g_socket_is_closed +g_socket_is_connected +g_socket_listen +g_socket_new +g_socket_new_from_fd +g_socket_receive +g_socket_receive_from +g_socket_receive_message +g_socket_send +g_socket_send_message +g_socket_send_to +g_socket_set_blocking +g_socket_set_keepalive +g_socket_set_listen_backlog +#endif +#endif + +#if IN_HEADER(__G_SOCKET_CONTROL_MESSAGE_H__) +#if IN_FILE(__G_SOCKET_CONTROL_MESSAGE_C__) +g_socket_control_message_get_type G_GNUC_CONST +g_socket_control_message_deserialize +g_socket_control_message_get_level +g_socket_control_message_get_msg_type +g_socket_control_message_get_size +g_socket_control_message_serialize +#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 +g_unix_fd_message_append_fd +g_unix_fd_message_new +g_unix_fd_message_steal_fds +#endif +#endif diff --git a/gio/gioenums.h b/gio/gioenums.h index e9f440bc3..9d9581d7e 100644 --- a/gio/gioenums.h +++ b/gio/gioenums.h @@ -342,6 +342,8 @@ typedef enum { * @G_IO_ERROR_TOO_MANY_OPEN_FILES: The current process has too many files * open and can't open any more. Duplicate descriptors do count toward * this limit. Since 2.20 + * @G_IO_ERROR_NOT_INITIALIZED: The object has not been initialized. Since 2.22 + * @G_IO_ERROR_ADDRESS_IN_USE: The requested address is already in use. Since 2.22 * * Error codes returned by GIO functions. * @@ -378,7 +380,9 @@ typedef enum { G_IO_ERROR_HOST_NOT_FOUND, G_IO_ERROR_WOULD_MERGE, G_IO_ERROR_FAILED_HANDLED, - G_IO_ERROR_TOO_MANY_OPEN_FILES + G_IO_ERROR_TOO_MANY_OPEN_FILES, + G_IO_ERROR_NOT_INITIALIZED, + G_IO_ERROR_ADDRESS_IN_USE } GIOErrorEnum; @@ -511,6 +515,46 @@ typedef enum { G_SOCKET_FAMILY_IPV6 = GLIB_SYSDEF_AF_INET6 } GSocketFamily; +/** + * GSocketType: + * @G_SOCKET_TYPE_INVALID: Type unknown or wrong + * @G_SOCKET_TYPE_STREAM: Reliable connection-based byte streams (e.g. TCP). + * @G_SOCKET_TYPE_DATAGRAM: Connectionless, unreliable datagram passing. (e.g. UDP) + * @G_SOCKET_TYPE_SEQPACKET: Reliable connection-based passing of datagrams of fixed maximum length (e.g. SCTP). + * + * Flags used when creating a #GSocket. Some protocols may not implement all the socket types. + * + * Since: 2.22 + */ +typedef enum +{ + G_SOCKET_TYPE_INVALID, + G_SOCKET_TYPE_STREAM, + G_SOCKET_TYPE_DATAGRAM, + G_SOCKET_TYPE_SEQPACKET, +} GSocketType; + +/** + * GSocketMsgFlags: + * @G_SOCKET_MSG_OOB: Request to send/receive out of band data. + * @G_SOCKET_MSG_PEEK: Read data from the socket without removing it from the queue. + * @G_SOCKET_MSG_DONTROUTE: Don't use a gateway to send out the packet, only send to hosts on directly connected networks. + * + * Flags used in g_socket_receive_message() and g_socket_send_message(). The flags listed in the enum are + * some commonly available flags, but the values used for them are the same as on the platform, and any other + * flags are passed in/out as is. So to use a platform specific flag, just include the right system header and + * pass in the flag. + * + * Since: 2.22 + */ +typedef enum +{ + G_SOCKET_MSG_INVALID, + G_SOCKET_MSG_OOB = GLIB_SYSDEF_MSG_OOB, + G_SOCKET_MSG_PEEK = GLIB_SYSDEF_MSG_PEEK, + G_SOCKET_MSG_DONTROUTE = GLIB_SYSDEF_MSG_DONTROUTE +} GSocketMsgFlags; + G_END_DECLS #endif /* __GIO_ENUMS_H__ */ diff --git a/gio/gioerror.c b/gio/gioerror.c index 697070bb7..287cb2b46 100644 --- a/gio/gioerror.c +++ b/gio/gioerror.c @@ -194,6 +194,12 @@ g_io_error_from_errno (gint err_no) break; #endif +#ifdef EADDRINUSE + case EADDRINUSE: + return G_IO_ERROR_ADDRESS_IN_USE; + break; +#endif + default: return G_IO_ERROR_FAILED; break; diff --git a/gio/giotypes.h b/gio/giotypes.h index 3a3c361c4..9ca45401b 100644 --- a/gio/giotypes.h +++ b/gio/giotypes.h @@ -34,6 +34,7 @@ G_BEGIN_DECLS typedef struct _GAppLaunchContext GAppLaunchContext; typedef struct _GAppInfo GAppInfo; /* Dummy typedef */ typedef struct _GAsyncResult GAsyncResult; /* Dummy typedef */ +typedef struct _GAsyncInitable GAsyncInitable; typedef struct _GBufferedInputStream GBufferedInputStream; typedef struct _GBufferedOutputStream GBufferedOutputStream; typedef struct _GCancellable GCancellable; @@ -79,6 +80,7 @@ typedef struct _GIcon GIcon; /* Dummy typedef */ typedef struct _GInetAddress GInetAddress; typedef struct _GInetSocketAddress GInetSocketAddress; typedef struct _GInputStream GInputStream; +typedef struct _GInitable GInitable; typedef struct _GIOModule GIOModule; typedef struct _GIOExtensionPoint GIOExtensionPoint; typedef struct _GIOExtension GIOExtension; @@ -107,6 +109,23 @@ typedef struct _GIOStream GIOStream; typedef struct _GResolver GResolver; typedef struct _GSeekable GSeekable; typedef struct _GSimpleAsyncResult GSimpleAsyncResult; + +/** + * GSocket: + * + * A lowlevel network socket object. + * + * Since: 2.22 + **/ +typedef struct _GSocket GSocket; + +/** + * GSocketControlMessage: + * + * Base class for socket-type specific control messages that can be sent and + * received over #GSocket. + **/ +typedef struct _GSocketControlMessage GSocketControlMessage; typedef struct _GSocketAddress GSocketAddress; typedef struct _GSocketAddressEnumerator GSocketAddressEnumerator; typedef struct _GSocketConnectable GSocketConnectable; @@ -202,6 +221,46 @@ typedef void (*GSimpleAsyncThreadFunc) (GSimpleAsyncResult *res, GObject *object, GCancellable *cancellable); +/** + * GSocketSourceFunc: + * @user_data: data passed in by the user. + * @condition: the current condition at the source fired. + * + * This is the function type of the callback used for the #GSource + * returned by g_socket_create_source(). + * + * Since: 2.22 + */ +typedef gboolean (*GSocketSourceFunc) (gpointer user_data, + GIOCondition condition); + +/** + * GInputVector: + * + * Structure used for scatter/gather data input. + * + * Since: 2.22 + */ +typedef struct +{ + gpointer buffer; + gsize size; +} GInputVector; + +/** + * GOutputVector: + * + * Structure used for scatter/gather data output. + * + * Since: 2.22 + */ +typedef struct +{ + gconstpointer buffer; + gsize size; +} GOutputVector; + + G_END_DECLS #endif /* __GIO_TYPES_H__ */ diff --git a/gio/gsocket.c b/gio/gsocket.c new file mode 100644 index 000000000..ba78e240b --- /dev/null +++ b/gio/gsocket.c @@ -0,0 +1,2970 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2008 Christian Kellner, Samuel Cormier-Iijima + * Copyright © 2009 Codethink Limited + * 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 + +#ifndef G_OS_WIN32 +# include +# include +# include +# include +# include +# include +#else +# include +# include +#endif + +#include "gsocket.h" +#include "gcancellable.h" +#include "gioenumtypes.h" +#include "ginitable.h" +#include "gasynchelper.h" +#include "gioerror.h" +#include "gioenums.h" +#include "gioerror.h" +#include "glibintl.h" + +#include "gioalias.h" + +/** + * SECTION:gsocket + * @short_description: Low-level network socket handling + * @include: gio/gio.h + * @see_also: #GInitable + * + * A #GSocket is a low-level networking primitive. It is a more or less + * direct mapping of the BSD socket API in a portable GObject based API. + * It supports both the unix socket implementations and winsock2 on Windows. + * + * #GSocket is the platform independent base upon which the higher level + * network primitives are based. Applications are not typically meant to + * use it directly, but rather through classes like #GSocketClient, etc. + * However there may be cases where direct use of #GSocket is useful. + * + * TODO: Add more references to the highlevel API once that is more + * finalized. + * + * #GSocket implements the #GInitable interface, so if it is manually constructed + * by e.g. g_object_new() you must call g_initable_init() and check the + * results before using the object. This is done automatically in + * g_socket_new() and g_socket_new_from_fd(), so these functions can return + * %NULL. + * + * Sockets operate in two general modes, blocking or non-blocking. When + * in blocking mode all operations block until the requested operation + * is finished or there is an error. In non-blocking mode all calls that + * would block return immediately with a %G_IO_ERROR_WOULD_BLOCK error. + * To know when a call would successfully run you can call g_socket_condition_check(), + * or g_socket_condition_wait(). You can also use g_socket_create_source() and + * attach it to a #GMainContext to get callbacks when I/O is possible. + * Note that all sockets are always set to non blocking mode in the system, and + * blocking mode is emulated in GSocket. + * + * When working in non-blocking mode applications should always be able to + * handle getting a %G_IO_ERROR_WOULD_BLOCK error even when some other + * function said that I/O was possible. This can easily happen in case + * of a race condition in the application, but it can also happen for other + * reasons. For instance, on Windows a socket is always seen as writable + * until a write returns %G_IO_ERROR_WOULD_BLOCK. + * + * #GSockets can be either connection oriented or datagram based. + * For connection oriented types you must first establish a connection by + * either connecting to an address or accepting a connection from another + * address. For connectionless socket types the target/source address is + * specified or received in each I/O operation. + * + * All socket file descriptors are set to be close-on-exec. + * + * Since: 2.22 + **/ + +static void g_socket_initable_iface_init (GInitableIface *iface); +static gboolean g_socket_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error); + +G_DEFINE_TYPE_WITH_CODE (GSocket, g_socket, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + g_socket_initable_iface_init)); + +enum +{ + PROP_0, + PROP_FAMILY, + PROP_TYPE, + PROP_PROTOCOL, + PROP_FD, + PROP_BLOCKING, + PROP_LISTEN_BACKLOG, + PROP_KEEPALIVE, + PROP_LOCAL_ADDRESS, + PROP_REMOTE_ADDRESS +}; + +struct _GSocketPrivate +{ + GSocketFamily family; + GSocketType type; + char *protocol; + gint fd; + gint listen_backlog; + GError *construct_error; + GSocketAddress *local_address; + GSocketAddress *remote_address; + guint inited : 1; + guint blocking : 1; + guint keepalive : 1; + guint closed : 1; +#ifdef G_OS_WIN32 + WSAEVENT event; + int current_events; + int current_errors; + int selected_events; + GList *requested_conditions; /* list of requested GIOCondition * */ +#endif +}; + +static int +get_socket_errno (void) +{ +#ifndef G_OS_WIN32 + return errno; +#else + return WSAGetLastError (); +#endif +} + +static GIOErrorEnum +socket_io_error_from_errno (int err) +{ +#ifndef G_OS_WIN32 + return g_io_error_from_errno (err); +#else + switch (err) + { + case WSAEADDRINUSE: + return G_IO_ERROR_ADDRESS_IN_USE; + case WSAEWOULDBLOCK: + return G_IO_ERROR_WOULD_BLOCK; + case WSAEACCES: + return G_IO_ERROR_PERMISSION_DENIED; + case WSA_INVALID_HANDLE: + case WSA_INVALID_PARAMETER: + case WSAEBADF: + case WSAENOTSOCK: + return G_IO_ERROR_INVALID_ARGUMENT; + case WSAEPROTONOSUPPORT: + return G_IO_ERROR_NOT_SUPPORTED; + case WSAECANCELLED: + return G_IO_ERROR_CANCELLED; + case WSAESOCKTNOSUPPORT: + case WSAEOPNOTSUPP: + case WSAEPFNOSUPPORT: + case WSAEAFNOSUPPORT: + return G_IO_ERROR_NOT_SUPPORTED; + default: + return G_IO_ERROR_FAILED; + } +#endif +} + +static const char * +socket_strerror (int err) +{ +#ifndef G_OS_WIN32 + return g_strerror (err); +#else + static GStaticPrivate msg_private = G_STATIC_PRIVATE_INIT; + char *buf, *msg; + + buf = g_static_private_get (&msg_private); + if (!buf) + { + buf = g_new (gchar, 128); + g_static_private_set (&msg_private, buf, g_free); + } + + msg = g_win32_error_message (err); + strncpy (buf, msg, 128); + g_free (msg); + return buf; +#endif +} + +#ifdef G_OS_WIN32 +#define win32_unset_event_mask(_socket, _mask) _win32_unset_event_mask (_socket, _mask) +static void +_win32_unset_event_mask (GSocket *socket, int mask) +{ + socket->priv->current_events &= ~mask; + socket->priv->current_errors &= ~mask; +} +#else +#define win32_unset_event_mask(_socket, _mask) +#endif + +static void +set_fd_nonblocking (int fd) +{ +#ifndef G_OS_WIN32 + glong arg; +#else + gulong arg; +#endif + +#ifndef G_OS_WIN32 + if ((arg = fcntl (fd, F_GETFL, NULL)) < 0) + { + g_warning ("Error getting socket status flags: %s", socket_strerror (errno)); + arg = 0; + } + + arg = arg | O_NONBLOCK; + + if (fcntl (fd, F_SETFL, arg) < 0) + g_warning ("Error setting socket status flags: %s", socket_strerror (errno)); +#else + arg = TRUE; + + if (ioctlsocket (fd, FIONBIO, &arg) == SOCKET_ERROR) + { + int errsv = get_socket_errno (); + g_warning ("Error setting socket status flags: %s", socket_strerror (errsv)); + } +#endif +} + +static gboolean +check_socket (GSocket *socket, + GError **error) +{ + if (!socket->priv->inited) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, + _("Invalid socket, not initialized")); + return FALSE; + } + + if (socket->priv->construct_error) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, + _("Invalid socket, initialization failed due to: %s"), + socket->priv->construct_error->message); + return FALSE; + } + + if (socket->priv->closed) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED, + _("Socket is already closed")); + return FALSE; + } + return TRUE; +} + +static void +g_socket_details_from_fd (GSocket *socket) +{ + struct sockaddr_storage address; + gint fd; + guint addrlen; + guint optlen; + int value; + int errsv; +#ifdef G_OS_WIN32 + BOOL bool_val; +#else + int bool_val; +#endif + + fd = socket->priv->fd; + optlen = sizeof value; + if (getsockopt (fd, SOL_SOCKET, SO_TYPE, (void *)&value, &optlen) != 0) + { + errsv = get_socket_errno (); + + switch (errsv) + { +#ifdef ENOTSOCK + case ENOTSOCK: +#endif +#ifdef WSAENOTSOCK + case WSAENOTSOCK: +#endif + case EBADF: + /* programmer error */ + g_error ("creating GSocket from fd %d: %s\n", + fd, socket_strerror (errsv)); + default: + break; + } + + goto err; + } + + g_assert (optlen == sizeof value); + switch (value) + { + case SOCK_STREAM: + socket->priv->type = G_SOCKET_TYPE_STREAM; + break; + + case SOCK_DGRAM: + socket->priv->type = G_SOCKET_TYPE_DATAGRAM; + break; + + case SOCK_SEQPACKET: + socket->priv->type = G_SOCKET_TYPE_SEQPACKET; + break; + + default: + socket->priv->type = G_SOCKET_TYPE_INVALID; + break; + } + + addrlen = sizeof address; + if (getsockname (fd, (struct sockaddr *) &address, &addrlen) != 0) + { + errsv = get_socket_errno (); + goto err; + } + + g_assert (G_STRUCT_OFFSET (struct sockaddr, sa_family) + + sizeof address.ss_family <= addrlen); + switch (address.ss_family) + { + case G_SOCKET_FAMILY_IPV4: + case G_SOCKET_FAMILY_IPV6: + case G_SOCKET_FAMILY_UNIX: + socket->priv->family = address.ss_family; + break; + + default: + socket->priv->family = G_SOCKET_FAMILY_INVALID; + break; + } + + if (socket->priv->family != G_SOCKET_FAMILY_INVALID) + { + socket->priv->local_address = + g_socket_address_new_from_native (&address, addrlen); + + addrlen = sizeof address; + if (getpeername (fd, (struct sockaddr *) &address, &addrlen) >= 0) + socket->priv->remote_address = + g_socket_address_new_from_native (&address, addrlen); + } + + optlen = sizeof bool_val; + if (getsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, + (void *)&bool_val, &optlen) == 0) + { + g_assert (optlen == sizeof bool_val); + socket->priv->keepalive = !!bool_val; + } + else + { + /* Can't read, maybe not supported, assume FALSE */ + socket->priv->keepalive = FALSE; + } + + return; + + err: + g_set_error (&socket->priv->construct_error, G_IO_ERROR, + socket_io_error_from_errno (errsv), + _("creating GSocket from fd: %s"), + socket_strerror (errsv)); +} + +static gint +g_socket_create_socket (GSocketFamily family, + GSocketType type, + const char *protocol_name, + GError **error) +{ + gint native_type; + gint fd; + gint protocol; + struct protoent *protoent; + + switch (type) + { + case G_SOCKET_TYPE_STREAM: + native_type = SOCK_STREAM; + break; + + case G_SOCKET_TYPE_DATAGRAM: + native_type = SOCK_DGRAM; + break; + + case G_SOCKET_TYPE_SEQPACKET: + native_type = SOCK_SEQPACKET; + break; + + default: + g_assert_not_reached (); + } + + protocol = 0; + if (protocol_name) + { +#ifdef HAVE_GETPROTOBYNAME_R + char buffer[1024]; + struct protoent my_protoent; + + protoent = NULL; + getprotobyname_r (protocol_name, + &my_protoent, buffer, sizeof (buffer), + &protoent); +#else + protoent = getprotobyname (protocol_name); +#endif + if (protoent == NULL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("unable to create socket: Protocol %s not supported"), + protocol_name); + return -1; + } + protocol = protoent->p_proto; + } + +#ifdef SOCK_CLOEXEC + native_type |= SOCK_CLOEXEC; +#endif + fd = socket (family, native_type, protocol); + + if (fd < 0) + { + int errsv = get_socket_errno (); + + g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv), + _("unable to create socket: %s"), socket_strerror (errsv)); + } + +#ifndef G_OS_WIN32 + { + int flags; + + /* We always want to set close-on-exec to protect users. If you + need to so some weird inheritance to exec you can re-enable this + using lower level hacks with g_socket_get_fd(). */ + flags = fcntl (fd, F_GETFD, 0); + if (flags != -1 && + (flags & FD_CLOEXEC) == 0) + { + flags |= FD_CLOEXEC; + fcntl (fd, F_SETFD, flags); + } + } +#endif + + return fd; +} + +static void +g_socket_constructed (GObject *object) +{ + GSocket *socket = G_SOCKET (object); + + if (socket->priv->fd >= 0) + /* create socket->priv info from the fd */ + g_socket_details_from_fd (socket); + + else + /* create the fd from socket->priv info */ + socket->priv->fd = g_socket_create_socket (socket->priv->family, + socket->priv->type, + socket->priv->protocol, + &socket->priv->construct_error); + + /* Always use native nonblocking sockets, as + windows sets sockets to nonblocking automatically + in certain operations. This way we make things work + the same on all platforms */ + if (socket->priv->fd != -1) + set_fd_nonblocking (socket->priv->fd); +} + +static void +g_socket_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GSocket *socket = G_SOCKET (object); + + switch (prop_id) + { + case PROP_FAMILY: + g_value_set_enum (value, socket->priv->family); + break; + + case PROP_TYPE: + g_value_set_enum (value, socket->priv->type); + break; + + case PROP_PROTOCOL: + g_value_set_string (value, socket->priv->protocol); + break; + + case PROP_FD: + g_value_set_int (value, socket->priv->fd); + break; + + case PROP_BLOCKING: + g_value_set_boolean (value, socket->priv->blocking); + break; + + case PROP_LISTEN_BACKLOG: + g_value_set_int (value, socket->priv->listen_backlog); + break; + + case PROP_KEEPALIVE: + g_value_set_boolean (value, socket->priv->keepalive); + break; + + case PROP_LOCAL_ADDRESS: + g_value_set_object (value, socket->priv->local_address); + break; + + case PROP_REMOTE_ADDRESS: + g_value_set_object (value, socket->priv->remote_address); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_socket_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GSocket *socket = G_SOCKET (object); + + switch (prop_id) + { + case PROP_FAMILY: + socket->priv->family = g_value_get_enum (value); + break; + + case PROP_TYPE: + socket->priv->type = g_value_get_enum (value); + break; + + case PROP_PROTOCOL: + socket->priv->protocol = g_value_dup_string (value); + break; + + case PROP_FD: + socket->priv->fd = g_value_get_int (value); + break; + + case PROP_BLOCKING: + g_socket_set_blocking (socket, g_value_get_boolean (value)); + break; + + case PROP_LISTEN_BACKLOG: + g_socket_set_listen_backlog (socket, g_value_get_int (value)); + break; + + case PROP_KEEPALIVE: + g_socket_set_keepalive (socket, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_socket_finalize (GObject *object) +{ + GSocket *socket = G_SOCKET (object); + + g_clear_error (&socket->priv->construct_error); + + if (socket->priv->fd != -1 && + !socket->priv->closed) + g_socket_close (socket, NULL); + + g_free (socket->priv->protocol); + +#ifdef G_OS_WIN32 + g_assert (socket->priv->requested_conditions == NULL); +#endif + + if (G_OBJECT_CLASS (g_socket_parent_class)->finalize) + (*G_OBJECT_CLASS (g_socket_parent_class)->finalize) (object); +} + +static void +g_socket_dispose (GObject *object) +{ + GSocket *socket = G_SOCKET (object); + + if (socket->priv->local_address) + { + g_object_unref (socket->priv->local_address); + socket->priv->local_address = NULL; + } + + if (socket->priv->remote_address) + { + g_object_unref (socket->priv->remote_address); + socket->priv->remote_address = NULL; + } + + if (G_OBJECT_CLASS (g_socket_parent_class)->dispose) + (*G_OBJECT_CLASS (g_socket_parent_class)->dispose) (object); +} + +static void +g_socket_class_init (GSocketClass *klass) +{ + GObjectClass *gobject_class G_GNUC_UNUSED = G_OBJECT_CLASS (klass); + volatile GType type; + + /* Make sure winsock has been initialized */ + type = g_inet_address_get_type (); + + g_type_class_add_private (klass, sizeof (GSocketPrivate)); + + gobject_class->finalize = g_socket_finalize; + gobject_class->dispose = g_socket_dispose; + gobject_class->constructed = g_socket_constructed; + gobject_class->set_property = g_socket_set_property; + gobject_class->get_property = g_socket_get_property; + + g_object_class_install_property (gobject_class, PROP_FAMILY, + g_param_spec_enum ("family", + P_("Socket family"), + P_("The sockets address family"), + G_TYPE_SOCKET_FAMILY, + G_SOCKET_FAMILY_INVALID, + G_PARAM_CONSTRUCT_ONLY | 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"), + G_TYPE_SOCKET_TYPE, + G_SOCKET_TYPE_STREAM, + G_PARAM_CONSTRUCT_ONLY | 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 name of the protocol to use, or NULL"), + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_FD, + g_param_spec_int ("fd", + P_("File descriptor"), + P_("The sockets file descriptor"), + G_MININT, + G_MAXINT, + -1, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_BLOCKING, + g_param_spec_boolean ("blocking", + P_("blocking"), + P_("Whether or not I/O on this socket is blocking"), + TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + 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, + SOMAXCONN, + 10, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_KEEPALIVE, + g_param_spec_boolean ("keepalive", + P_("Keep connection alive"), + P_("Keep connection alive by sending periodic pings"), + FALSE, + 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 the socket is bound to"), + G_TYPE_SOCKET_ADDRESS, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_REMOTE_ADDRESS, + g_param_spec_object ("remote-address", + P_("Remote address"), + P_("The remote address the socket is connected to"), + G_TYPE_SOCKET_ADDRESS, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); +} + +static void +g_socket_initable_iface_init (GInitableIface *iface) +{ + iface->init = g_socket_initable_init; +} + +static void +g_socket_init (GSocket *socket) +{ + socket->priv = G_TYPE_INSTANCE_GET_PRIVATE (socket, G_TYPE_SOCKET, GSocketPrivate); + + socket->priv->fd = -1; + socket->priv->blocking = TRUE; + socket->priv->listen_backlog = 10; + socket->priv->construct_error = NULL; + socket->priv->remote_address = NULL; + socket->priv->local_address = NULL; +#ifdef G_OS_WIN32 + socket->priv->event = WSA_INVALID_EVENT; +#endif +} + +static gboolean +g_socket_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + GSocket *socket; + + g_return_val_if_fail (G_IS_SOCKET (initable), FALSE); + + socket = G_SOCKET (initable); + + if (cancellable != NULL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("Cancellable initialization not supported")); + return FALSE; + } + + socket->priv->inited = TRUE; + + if (socket->priv->construct_error) + { + if (error) + *error = g_error_copy (socket->priv->construct_error); + return FALSE; + } + + + return TRUE; +} + +/** + * g_socket_new: + * @family: the socket family to use, e.g. %G_SOCKET_FAMILY_IPV4. + * @type: the socket type to use. + * @protocol: the name of the protocol to use, or %NULL. + * @error: #GError for error reporting, or %NULL to ignore. + * + * Creates a new #GSocket with the defined family, type and protocol. + * If @protocol is %NULL the default protocol type for the family and + * type is used. + * + * Returns: a #GSocket or %NULL on error. + * Free the returned object with g_object_unref(). + * + * Since: 2.22 + **/ +GSocket * +g_socket_new (GSocketFamily family, + GSocketType type, + const char *protocol, + GError **error) +{ + return G_SOCKET (g_initable_new (G_TYPE_SOCKET, + NULL, error, + "family", family, + "type", type, + "protocol", protocol, + NULL)); +} + +/** + * g_socket_new_from_fd: + * @fd: a native socket file descriptor. + * @error: #GError for error reporting, or %NULL to ignore. + * + * Creates a new #GSocket from a native file descriptor + * or winsock SOCKET handle. + * + * This reads all the settings from the file descriptor so that + * all properties should work. Note that the file descriptor + * will be set to non-blocking mode, independent on the blocking + * mode of the #GSocket. + * + * Returns: a #GSocket or %NULL on error. + * Free the returned object with g_object_unref(). + * + * Since: 2.22 + **/ +GSocket * +g_socket_new_from_fd (gint fd, + GError **error) +{ + return G_SOCKET (g_initable_new (G_TYPE_SOCKET, + NULL, error, + "fd", fd, + NULL)); +} + +/** + * g_socket_set_blocking: + * @socket: a #GSocket. + * @blocking: Whether to use blocking I/O or not. + * + * Sets the blocking mode of the socket. In blocking mode + * all operations block until they succeed or there is an error. In + * non-blocking mode all functions return results immediately or + * with a %G_IO_ERROR_WOULD_BLOCK error. + * + * All sockets are created in blocking mode. However, note that the + * platform level socket is always non-blocking, and blocking mode + * is a GSocket level feature. + * + * Since: 2.22 + **/ +void +g_socket_set_blocking (GSocket *socket, + gboolean blocking) +{ + g_return_if_fail (G_IS_SOCKET (socket)); + + blocking = !!blocking; + + if (socket->priv->blocking == blocking) + return; + + socket->priv->blocking = blocking; + g_object_notify (G_OBJECT (socket), "blocking"); +} + +/** + * g_socket_get_blocking: + * @socket: a #GSocket. + * + * Gets the blocking mode of the socket. For details on blocking I/O, + * see g_socket_set_blocking(). + * + * Returns: %TRUE if blocking I/O is used, %FALSE otherwise. + * + * Since: 2.22 + **/ +gboolean +g_socket_get_blocking (GSocket *socket) +{ + g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); + + return socket->priv->blocking; +} + +/** + * g_socket_set_keepalive: + * @socket: a #GSocket. + * @keepalive: Whether to use try to keep the connection alive or not. + * + * Setting @keepalive to %TRUE enables the sending of periodic ping requests + * on idle connections in order to keep the connection alive. This is only + * useful for connection oriented sockets. The exact period used between + * each ping is system and protocol dependent. + * + * Sending keepalive requests like this has a few disadvantages. For instance, + * it uses more network bandwidth, and it makes your application more sensitive + * to temporary outages in the network (i.e. if a cable is pulled your otherwise + * idle connection could be terminated, whereas otherwise it would survive unless + * actually used before the cable was reinserted). However, it is sometimes + * useful to ensure that connections are eventually terminated if e.g. the + * remote side is disconnected, so as to avoid leaking resources forever. + * + * Since: 2.22 + **/ +void +g_socket_set_keepalive (GSocket *socket, + gboolean keepalive) +{ + int value; + + g_return_if_fail (G_IS_SOCKET (socket)); + + keepalive = !!keepalive; + if (socket->priv->keepalive == keepalive) + return; + + value = (gint) keepalive; + if (setsockopt (socket->priv->fd, SOL_SOCKET, SO_KEEPALIVE, + (gpointer) &value, sizeof (value)) < 0) + { + int errsv = get_socket_errno (); + g_warning ("error setting keepalive: %s", socket_strerror (errsv)); + return; + } + + socket->priv->keepalive = keepalive; + g_object_notify (G_OBJECT (socket), "keepalive"); +} + +/** + * g_socket_get_keepalive: + * @socket: a #GSocket. + * + * Gets the keepalive mode of the socket. For details on this, + * see g_socket_set_keepalive(). + * + * Returns: %TRUE if keepalive is active, %FALSE otherwise. + * + * Since: 2.22 + **/ +gboolean +g_socket_get_keepalive (GSocket *socket) +{ + g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); + + return socket->priv->keepalive; +} + +/** + * g_socket_get_listen_backlog: + * @socket: a #GSocket. + * + * Gets the listen backlog setting of the socket. For details on this, + * see g_socket_set_listen_backlog(). + * + * Returns: the maximum number of pending connections. + * + * Since: 2.22 + **/ +gint +g_socket_get_listen_backlog (GSocket *socket) +{ + g_return_val_if_fail (G_IS_SOCKET (socket), 0); + + return socket->priv->listen_backlog; +} + +/** + * g_socket_set_listen_backlog: + * @socket: a #GSocket. + * @backlog: the maximum number of pending connections. + * + * Sets the maximum number of outstanding connections allowed + * when listening on this socket. If more clients than this are + * connecting to the socket and the application is not handling them + * on time then the new connections will be refused. + * + * Since: 2.22 + **/ +void +g_socket_set_listen_backlog (GSocket *socket, + gint backlog) +{ + g_return_if_fail (G_IS_SOCKET (socket)); + + if (backlog != socket->priv->listen_backlog) + { + socket->priv->listen_backlog = backlog; + g_object_notify (G_OBJECT (socket), "listen-backlog"); + } +} + +/** + * g_socket_get_family: + * @socket: a #GSocket. + * + * Gets the socket family of the socket. + * + * Returns: a #GSocketFamily + * + * Since: 2.22 + **/ +GSocketFamily +g_socket_get_family (GSocket *socket) +{ + g_return_val_if_fail (G_IS_SOCKET (socket), G_SOCKET_FAMILY_INVALID); + + return socket->priv->family; +} + +/** + * g_socket_get_socket_type: + * @socket: a #GSocket. + * + * Gets the socket type of the socket. + * + * Returns: a #GSocketType + * + * Since: 2.22 + **/ +GSocketType +g_socket_get_socket_type (GSocket *socket) +{ + g_return_val_if_fail (G_IS_SOCKET (socket), G_SOCKET_TYPE_INVALID); + + return socket->priv->type; +} + +/** + * g_socket_get_protocol: + * @socket: a #GSocket. + * + * Gets the socket protocol type name the socket was created with. + * This can be %NULL if the socket was created with a NULL protocol. + * + * Returns: a string or %NULL, do not free + * + * Since: 2.22 + **/ +const char * +g_socket_get_protocol (GSocket *socket) +{ + g_return_val_if_fail (G_IS_SOCKET (socket), NULL); + + return socket->priv->protocol; +} + +/** + * g_socket_get_fd: + * @socket: a #GSocket. + * + * Returns the underlying OS socket object. On unix this + * is a socket file descriptor, and on windows this is + * a Winsock2 SOCKET handle. This may be useful for + * doing platform specific or otherwise unusual operations + * on the socket. + * + * Returns: the file descriptor of the socket. + * + * Since: 2.22 + **/ +int +g_socket_get_fd (GSocket *socket) +{ + g_return_val_if_fail (G_IS_SOCKET (socket), -1); + + return socket->priv->fd; +} + +/** + * g_socket_get_local_address: + * @socket: a #GSocket. + * @error: #GError for error reporting, or %NULL to ignore. + * + * Try to get the local address of a bound socket. This is only + * useful if the socket has been bound to a local address. + * + * Returns: a #GSocketAddress or %NULL on error. + * + * Since: 2.22 + **/ +GSocketAddress * +g_socket_get_local_address (GSocket *socket, + GError **error) +{ + gchar buffer[256]; + guint32 len = 256; + + g_return_val_if_fail (G_IS_SOCKET (socket), NULL); + + if (socket->priv->local_address) + return socket->priv->local_address; + + if (getsockname (socket->priv->fd, (struct sockaddr *) buffer, &len) < 0) + { + int errsv = get_socket_errno (); + g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv), + _("could not get local address: %s"), socket_strerror (errsv)); + return NULL; + } + + socket->priv->local_address = g_socket_address_new_from_native (buffer, len); + return socket->priv->local_address; +} + +/** + * g_socket_get_remote_address: + * @socket: a #GSocket. + * @error: #GError for error reporting, or %NULL to ignore. + * + * Try to get the remove address of a connected socket. This is only + * useful for connection oriented sockets that have been connected. + * + * Returns: a #GSocketAddress or %NULL on error. + * + * Since: 2.22 + **/ +GSocketAddress * +g_socket_get_remote_address (GSocket *socket, + GError **error) +{ + gchar buffer[256]; + guint32 len = 256; + + g_return_val_if_fail (G_IS_SOCKET (socket), NULL); + + if (socket->priv->remote_address) + return socket->priv->remote_address; + + if (getpeername (socket->priv->fd, (struct sockaddr *) buffer, &len) < 0) + { + int errsv = get_socket_errno (); + g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv), + _("could not get remote address: %s"), socket_strerror (errsv)); + return NULL; + } + + socket->priv->remote_address = g_socket_address_new_from_native (buffer, len); + return socket->priv->remote_address; +} + +/** + * g_socket_is_connected: + * @socket: a #GSocket. + * + * Check whether the socket is connected. This is only useful for + * connection-oriented sockets. + * + * Returns: %TRUE if socket is connected, %FALSE otherwise. + * + * Since: 2.22 + **/ +gboolean +g_socket_is_connected (GSocket *socket) +{ + g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); + + return socket->priv->remote_address != NULL; +} + +/** + * g_socket_listen: + * @socket: a #GSocket. + * @error: #GError for error reporting, or %NULL to ignore. + * + * Marks the socket as a server socket, i.e. a socket that is used + * to accept incoming requests using g_socket_accept(). + * + * Before calling this the socket must be bound to a local address using + * g_socket_bind(). + * + * Returns: %TRUE on success, %FALSE on error. + * + * Since: 2.22 + **/ +gboolean +g_socket_listen (GSocket *socket, + GError **error) +{ + g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); + + if (!check_socket (socket, error)) + return FALSE; + + if (listen (socket->priv->fd, socket->priv->listen_backlog) < 0) + { + int errsv = get_socket_errno (); + + g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv), + _("could not listen: %s"), socket_strerror (errsv)); + return FALSE; + } + + return TRUE; +} + +/** + * g_socket_bind: + * @socket: a #GSocket. + * @address: a #GSocketAddress specifying the local address. + * @allow_reuse: whether to allow reusing this address + * @error: #GError for error reporting, or %NULL to ignore. + * + * When a socket is created it is attached to an address family, but it + * doesn't have an address in this family. g_socket_bind() assigns the + * address (sometimes called name) of the socket. + * + * It is generally required to bind to a local address before you can + * receive connections. (See g_socket_listen() and g_socket_accept() ). + * + * If @allow_reuse is %TRUE this allows the bind call to succeed in some + * situation where it would otherwise return a %G_IO_ERROR_ADDRESS_IN_USE + * error. The main example is for a TCP server socket where there are + * outstanding connections in the WAIT state, which are generally safe + * to ignore. However, setting it to %TRUE doesn't mean the call will + * succeed if there is a socket actively bound to the address. + * + * In general, pass %TRUE if the socket will be used to accept connections, + * otherwise pass %FALSE. + * + * Returns: %TRUE on success, %FALSE on error. + * + * Since: 2.22 + **/ +gboolean +g_socket_bind (GSocket *socket, + GSocketAddress *address, + gboolean reuse_address, + GError **error) +{ + gchar addr[256]; + int value; + + g_return_val_if_fail (G_IS_SOCKET (socket) && G_IS_SOCKET_ADDRESS (address), FALSE); + + if (!check_socket (socket, error)) + return FALSE; + + /* SO_REUSEADDR on windows means something else and is not what we want. + It always allows the unix variant of SO_REUSEADDR anyway */ +#ifndef G_OS_WIN32 + value = (int) !!reuse_address; + if (setsockopt (socket->priv->fd, SOL_SOCKET, SO_REUSEADDR, + (gpointer) &value, sizeof (value)) < 0) + { + int errsv = get_socket_errno (); + g_set_error (error, + G_IO_ERROR, socket_io_error_from_errno (errsv), + _("Error setting reuse_address: %s"), socket_strerror (errsv)); + return FALSE; + } +#endif + + if (!g_socket_address_to_native (address, addr, sizeof addr)) + return FALSE; + + if (bind (socket->priv->fd, (struct sockaddr *) addr, + g_socket_address_get_native_size (address)) < 0) + { + int errsv = get_socket_errno (); + g_set_error (error, + G_IO_ERROR, socket_io_error_from_errno (errsv), + _("Error binding to address: %s"), socket_strerror (errsv)); + return FALSE; + } + + socket->priv->local_address = g_object_ref (address); + + return TRUE; +} + +/** + * g_socket_accept: + * @socket: a #GSocket. + * @error: #GError for error reporting, or %NULL to ignore. + * + * Accept incoming connections on a connection-based socket. This removes + * the first outstanding connection request from the listening socket and + * creates a #GSocket object for it. + * + * The @socket must be bound to a local address with g_socket_bind() and + * must be listening for incoming connections (g_socket_listen()). + * + * If there are no outstanding connections then the operation will block + * or return %G_IO_ERROR_WOULD_BLOCK if non-blocking I/O is enabled. + * To be notified of an incoming connection, wait for the %G_IO_IN condition. + * + * Returns: a new #GSocket, or %NULL on error. + * Free the returned object with g_object_unref(). + * + * Since: 2.22 + **/ +GSocket * +g_socket_accept (GSocket *socket, + GError **error) +{ + GSocket *new_socket; + gint ret; + + g_return_val_if_fail (G_IS_SOCKET (socket), NULL); + + if (!check_socket (socket, error)) + return NULL; + + while (1) + { + if (socket->priv->blocking && + !g_socket_condition_wait (socket, + G_IO_IN, NULL, error)) + return NULL; + + if ((ret = accept (socket->priv->fd, NULL, 0)) < 0) + { + int errsv = get_socket_errno (); + + win32_unset_event_mask (socket, FD_ACCEPT); + + if (errsv == EINTR) + continue; + + if (socket->priv->blocking) + { +#ifdef WSAEWOULDBLOCK + if (errsv == WSAEWOULDBLOCK) + continue; +#else + if (errsv == EWOULDBLOCK || + errsv == EAGAIN) + continue; +#endif + } + + g_set_error (error, G_IO_ERROR, + socket_io_error_from_errno (errsv), + _("Error accepting connection: %s"), socket_strerror (errsv)); + return NULL; + } + break; + } + + win32_unset_event_mask (socket, FD_ACCEPT); + +#ifdef G_OS_WIN32 + { + /* The socket inherits the accepting sockets event mask and even object, + we need to remove that */ + WSAEventSelect (ret, NULL, 0); + } +#else + { + int flags; + + /* We always want to set close-on-exec to protect users. If you + need to so some weird inheritance to exec you can re-enable this + using lower level hacks with g_socket_get_fd(). */ + flags = fcntl (ret, F_GETFD, 0); + if (flags != -1 && + (flags & FD_CLOEXEC) == 0) + { + flags |= FD_CLOEXEC; + fcntl (ret, F_SETFD, flags); + } + } +#endif + + new_socket = g_socket_new_from_fd (ret, error); + if (new_socket == NULL) + { +#ifdef G_OS_WIN32 + closesocket (ret); +#else + close (ret); +#endif + } + + return new_socket; +} + +/** + * g_socket_connect: + * @socket: a #GSocket. + * @address: a #GSocketAddress specifying the remote address. + * @error: #GError for error reporting, or %NULL to ignore. + * + * Connect the socket to the specified remote address. + * + * For connection oriented socket this generally means we attempt to make + * a connection to the @address. For a connection-less socket it sets + * the default address for g_socket_send() and discards all incoming datagrams + * from other sources. + * + * Generally connection oriented sockets can only connect once, but connection-less + * sockets can connect multiple times to change the default address. + * + * If the connect call needs to do network I/O it will block, unless + * non-blocking I/O is enabled. Then %G_IO_ERROR_PENDING is returned + * and the user can be notified of the connection finishing by waiting + * for the G_IO_OUT condition. The result of the connection can then be + * checked with g_socket_check_pending_error(). + * + * Returns: %TRUE if connected, %FALSE on error. + * + * Since: 2.22 + **/ +gboolean +g_socket_connect (GSocket *socket, + GSocketAddress *address, + GError **error) +{ + gchar buffer[256]; + + g_return_val_if_fail (G_IS_SOCKET (socket) && G_IS_SOCKET_ADDRESS (address), FALSE); + + if (!check_socket (socket, error)) + return FALSE; + + g_socket_address_to_native (address, buffer, sizeof buffer); + + while (1) + { + if (socket->priv->blocking && + !g_socket_condition_wait (socket, + G_IO_IN, NULL, error)) + return FALSE; + + if (connect (socket->priv->fd, (struct sockaddr *) buffer, + g_socket_address_get_native_size (address)) < 0) + { + int errsv = get_socket_errno (); + + if (errsv == EINTR) + continue; + +#ifndef G_OS_WIN32 + if (errsv == EINPROGRESS) +#else + if (errsv == WSAEINPROGRESS) +#endif + { + if (socket->priv->blocking) + { + g_socket_condition_wait (socket, G_IO_OUT, NULL, NULL); + if (g_socket_check_pending_error (socket, error)) + break; + else + g_prefix_error (error, _("Error connecting: ")); + } + else + g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING, + _("Connection in progress")); + } + else + g_set_error (error, G_IO_ERROR, + socket_io_error_from_errno (errsv), + _("Error connecting: %s"), socket_strerror (errsv)); + + return FALSE; + } + break; + } + + win32_unset_event_mask (socket, FD_CONNECT); + + socket->priv->remote_address = g_object_ref (address); + + return TRUE; +} + +/** + * g_socket_check_pending_error: + * @socket: a #GSocket + * @error: #GError for error reporting, or %NULL to ignore. + * + * Checks and resets the pending error for the socket. This is typically + * used to check for errors when g_socket_connect() is used in non-blocking mode. + * + * Returns: %TRUE if no error, %FALSE otherwise, setting @error to the error + * + * Since: 2.22 + **/ +gboolean +g_socket_check_pending_error (GSocket *socket, + GError **error) +{ + guint optlen; + int value; + + if (getsockopt (socket->priv->fd, SOL_SOCKET, SO_ERROR, (void *)&value, &optlen) != 0) + { + int errsv = get_socket_errno (); + + g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (errsv), + _("Unable to get pending error: %s"), socket_strerror (errsv)); + return FALSE; + } + + if (value != 0) + { + g_set_error (error, G_IO_ERROR, socket_io_error_from_errno (value), + "%s", socket_strerror (value)); + return FALSE; + } + return TRUE; +} + +/** + * g_socket_receive: + * @socket: a #GSocket + * @buffer: a buffer to read data into (which should be at least count bytes long). + * @size: the number of bytes that will be read from the stream + * @error: #GError for error reporting, or %NULL to ignore. + * + * Receive data (up to @size bytes) from a socket. This is mainly used by + * connection oriented sockets, it is identical to g_socket_receive_from() + * with @address set to %NULL. + * + * If a message is too long to fit in @buffer, excess bytes may be discarded + * depending on the type of socket the message is received from. + * + * If the socket is in blocking mode the call will block until there is + * some data to receive or there is an error. If there is no data available + * and the socket is in non-blocking mode a %G_IO_ERROR_WOULD_BLOCK error + * will be returned. To be notified of available data, wait for the %G_IO_IN + * condition. + * + * On error -1 is returned and @error is set accordingly. + * + * Returns: Number of bytes read, or -1 on error + * + * Since: 2.22 + **/ +gssize +g_socket_receive (GSocket *socket, + gchar *buffer, + gsize size, + GError **error) +{ + gssize ret; + + g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, FALSE); + + if (!check_socket (socket, error)) + return -1; + + while (1) + { + if (socket->priv->blocking && + !g_socket_condition_wait (socket, + G_IO_IN, NULL, error)) + return -1; + + if ((ret = recv (socket->priv->fd, buffer, size, 0)) < 0) + { + int errsv = get_socket_errno (); + + if (errsv == EINTR) + continue; + + if (socket->priv->blocking) + { +#ifdef WSAEWOULDBLOCK + if (errsv == WSAEWOULDBLOCK) + continue; +#else + if (errsv == EWOULDBLOCK || + errsv == EAGAIN) + continue; +#endif + } + + win32_unset_event_mask (socket, FD_READ); + + g_set_error (error, G_IO_ERROR, + socket_io_error_from_errno (errsv), + _("Error receiving data: %s"), socket_strerror (errsv)); + return -1; + } + + win32_unset_event_mask (socket, FD_READ); + + break; + } + + return ret; +} + +/** + * g_socket_receive_from: + * @socket: a #GSocket + * @address: a pointer to a #GSocketAddress pointer, or %NULL + * @buffer: a buffer to read data into (which should be at least count bytes long). + * @size: the number of bytes that will be read from the stream + * @error: #GError for error reporting, or %NULL to ignore. + * + * Receive data (up to @size bytes) from a socket. + * + * If @address is non-%NULL then @address will be set equal to the + * source address of the received packet. + * @address is owned by the caller. + * + * If the socket is in blocking mode the call will block until there is + * some data to receive or there is an error. If there is no data available + * and the socket is in non-blocking mode a %G_IO_ERROR_WOULD_BLOCK error + * will be returned. To be notified of available data, wait for the %G_IO_IN + * condition. + * + * On error -1 is returned and @error is set accordingly. + * + * Returns: Number of bytes read, or -1 on error + * + * Since: 2.22 + **/ +gssize +g_socket_receive_from (GSocket *socket, + GSocketAddress **address, + gchar *buffer, + gsize size, + GError **error) +{ + GInputVector v; + + v.buffer = buffer; + v.size = size; + + return g_socket_receive_message (socket, + address, + &v, 1, + NULL, 0, NULL, + error); +} + +/** + * g_socket_send: + * @socket: a #GSocket + * @buffer: the buffer containing the data to send. + * @size: the number of bytes to send + * @error: #GError for error reporting, or %NULL to ignore. + * + * Tries to send @size bytes from @buffer on the socket. This is mainly used by + * connection oriented sockets, it is identical to g_socket_send_to() + * with @address set to %NULL. + * + * If the socket is in blocking mode the call will block until there is + * space for the data in the socket queue. If there is no space available + * and the socket is in non-blocking mode a %G_IO_ERROR_WOULD_BLOCK error + * will be returned. To be notified of available space, wait for the %G_IO_OUT + * condition. + * + * Note that on Windows you can't rely on a %G_IO_OUT condition + * not producing a %G_IO_ERROR_WOULD_BLOCK error, as this is how Winsock + * write notification works. However, robust apps should always be able to + * handle this since it can easily appear in other cases too. + * + * On error -1 is returned and @error is set accordingly. + * + * Returns: Number of bytes read, or -1 on error + * + * Since: 2.22 + **/ +gssize +g_socket_send (GSocket *socket, + const gchar *buffer, + gsize size, + GError **error) +{ + gssize ret; + + g_return_val_if_fail (G_IS_SOCKET (socket) && buffer != NULL, FALSE); + + if (!check_socket (socket, error)) + return -1; + + while (1) + { + if (socket->priv->blocking && + !g_socket_condition_wait (socket, + G_IO_OUT, NULL, error)) + return -1; + + if ((ret = send (socket->priv->fd, buffer, size, 0)) < 0) + { + int errsv = get_socket_errno (); + + if (errsv == EINTR) + continue; + +#ifdef WSAEWOULDBLOCK + if (errsv == WSAEWOULDBLOCK) + win32_unset_event_mask (socket, FD_WRITE); +#endif + + if (socket->priv->blocking) + { +#ifdef WSAEWOULDBLOCK + if (errsv == WSAEWOULDBLOCK) + continue; +#else + if (errsv == EWOULDBLOCK || + errsv == EAGAIN) + continue; +#endif + } + + g_set_error (error, G_IO_ERROR, + socket_io_error_from_errno (errsv), + _("Error sending data: %s"), socket_strerror (errsv)); + return -1; + } + break; + } + + return ret; +} + +/** + * g_socket_send_to: + * @socket: a #GSocket + * @address: a #GSocketAddress, or %NULL + * @buffer: the buffer containing the data to send. + * @size: the number of bytes to send + * @error: #GError for error reporting, or %NULL to ignore. + * + * Tries to send @size bytes from @buffer to @address. If @address is + * %NULL then the message is sent to the default receiver (set by + * g_socket_connect()). + * + * If the socket is in blocking mode the call will block until there is + * space for the data in the socket queue. If there is no space available + * and the socket is in non-blocking mode a %G_IO_ERROR_WOULD_BLOCK error + * will be returned. To be notified of available space, wait for the %G_IO_OUT + * condition. + * + * Note that on Windows you can't rely on a %G_IO_OUT condition + * not producing a %G_IO_ERROR_WOULD_BLOCK error, as this is how Winsock + * write notification works. However, robust apps should always be able to + * handle this since it can easily appear in other cases too. + * + * On error -1 is returned and @error is set accordingly. + * + * Returns: Number of bytes read, or -1 on error + * + * Since: 2.22 + **/ +gssize +g_socket_send_to (GSocket *socket, + GSocketAddress *address, + const gchar *buffer, + gsize size, + GError **error) +{ + GOutputVector v; + + v.buffer = buffer; + v.size = size; + + return g_socket_send_message (socket, + address, + &v, 1, + NULL, 0, + 0, error); +} + +/** + * g_socket_close: + * @socket: a #GSocket + * @error: #GError for error reporting, or %NULL to ignore. + * + * Closes the socket, shutting down any active connection. + * + * Closing a socket does not wait for all outstanding I/O operations to finish, + * so the caller should not rely on them to be guaranteed to complete even + * if the close returns with no error. + * + * Once the socket is closed, all other operations will return %G_IO_ERROR_CLOSED. + * Closing a stream multiple times will not return an error. + * + * Sockets will be automatically closed when the last reference + * is dropped, but you might want to call this function to make sure + * resources are released as early as possible. + * + * Returns: %TRUE on success, %FALSE on error + * + * Since: 2.22 + **/ +gboolean +g_socket_close (GSocket *socket, + GError **error) +{ + int res; + + g_return_val_if_fail (G_IS_SOCKET (socket), TRUE); + + if (socket->priv->closed) + return TRUE; /* Multiple close not an error */ + + if (!check_socket (socket, NULL)) + return FALSE; + + while (1) + { +#ifdef G_OS_WIN32 + res = closesocket (socket->priv->fd); +#else + res = close (socket->priv->fd); +#endif + if (res == -1) + { + int errsv = get_socket_errno (); + + if (errsv == EINTR) + continue; + + g_set_error (error, G_IO_ERROR, + socket_io_error_from_errno (errsv), + _("Error closing socket: %s"), + socket_strerror (errsv)); + return FALSE; + } + break; + } + +#ifdef G_OS_WIN32 + if (socket->priv->event != WSA_INVALID_EVENT) + { + WSACloseEvent (socket->priv->event); + socket->priv->event = WSA_INVALID_EVENT; + } +#endif + + socket->priv->closed = TRUE; + + return TRUE; +} + +/** + * g_socket_is_closed: + * @socket: a #GSocket + * + * Checks whether a socket is closed. + * + * Returns: %TRUE if socket is closed, %FALSE otherwise + * + * Since: 2.22 + **/ +gboolean +g_socket_is_closed (GSocket *socket) +{ + return socket->priv->closed; +} + +#ifdef G_OS_WIN32 +/* Broken source, used on errors */ +static gboolean +broken_prepare (GSource *source, + gint *timeout) +{ + return FALSE; +} + +static gboolean +broken_check (GSource *source) +{ + return FALSE; +} + +static gboolean +broken_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + return TRUE; +} + +static GSourceFuncs broken_funcs = +{ + broken_prepare, + broken_check, + broken_dispatch, + NULL +}; + +static gint +network_events_for_condition (GIOCondition condition) +{ + int event_mask = 0; + + if (condition & G_IO_IN) + event_mask |= (FD_READ | FD_ACCEPT); + if (condition & G_IO_OUT) + event_mask |= (FD_WRITE | FD_CONNECT); + event_mask |= FD_CLOSE; + + return event_mask; +} + +static void +ensure_event (GSocket *socket) +{ + if (socket->priv->event == WSA_INVALID_EVENT) + socket->priv->event = WSACreateEvent(); +} + +static void +update_select_events (GSocket *socket) +{ + int event_mask; + GIOCondition *ptr; + GList *l; + WSAEVENT event; + + ensure_event (socket); + + event_mask = 0; + for (l = socket->priv->requested_conditions; l != NULL; l = l->next) + { + ptr = l->data; + event_mask |= network_events_for_condition (*ptr); + } + + if (event_mask != socket->priv->selected_events) + { + /* If no events selected, disable event so we can unset + nonblocking mode */ + + if (event_mask == 0) + event = NULL; + else + event = socket->priv->event; + + if (WSAEventSelect (socket->priv->fd, event, event_mask) == 0) + socket->priv->selected_events = event_mask; + } +} + +static void +add_condition_watch (GSocket *socket, + GIOCondition *condition) +{ + g_assert (g_list_find (socket->priv->requested_conditions, condition) == NULL); + + socket->priv->requested_conditions = + g_list_prepend (socket->priv->requested_conditions, condition); + + update_select_events (socket); +} + +static void +remove_condition_watch (GSocket *socket, + GIOCondition *condition) +{ + g_assert (g_list_find (socket->priv->requested_conditions, condition) != NULL); + + socket->priv->requested_conditions = + g_list_remove (socket->priv->requested_conditions, condition); + + update_select_events (socket); +} + +static GIOCondition +update_condition (GSocket *socket) +{ + WSANETWORKEVENTS events; + GIOCondition condition; + + if (WSAEnumNetworkEvents (socket->priv->fd, + socket->priv->event, + &events) == 0) + { + socket->priv->current_events |= events.lNetworkEvents; + if (events.lNetworkEvents & FD_WRITE && + events.iErrorCode[FD_WRITE_BIT] != 0) + socket->priv->current_errors |= FD_WRITE; + if (events.lNetworkEvents & FD_CONNECT && + events.iErrorCode[FD_CONNECT_BIT] != 0) + socket->priv->current_errors |= FD_CONNECT; + } + + condition = 0; + if (socket->priv->current_events & (FD_READ | FD_ACCEPT)) + condition |= G_IO_IN; + + if (socket->priv->current_events & FD_CLOSE || + socket->priv->closed) + condition |= G_IO_HUP; + + /* Never report both G_IO_OUT and HUP, these are + mutually exclusive (can't write to a closed socket) */ + if ((condition & G_IO_HUP) == 0 && + socket->priv->current_events & FD_WRITE) + { + if (socket->priv->current_errors & FD_WRITE) + condition |= G_IO_ERR; + else + condition |= G_IO_OUT; + } + else + { + if (socket->priv->current_events & FD_CONNECT) + { + if (socket->priv->current_errors & FD_CONNECT) + condition |= (G_IO_HUP | G_IO_ERR); + else + condition |= G_IO_OUT; + } + } + + return condition; +} + +typedef struct { + GSource source; + GPollFD pollfd; + GSocket *socket; + GIOCondition condition; + GCancellable *cancellable; + GPollFD cancel_pollfd; + GIOCondition result_condition; +} GWinsockSource; + +static gboolean +winsock_prepare (GSource *source, + gint *timeout) +{ + GWinsockSource *winsock_source = (GWinsockSource *)source; + GIOCondition current_condition; + + current_condition = update_condition (winsock_source->socket); + + if (g_cancellable_is_cancelled (winsock_source->cancellable)) + { + winsock_source->result_condition = current_condition; + return TRUE; + } + + if ((winsock_source->condition & current_condition) != 0) + { + winsock_source->result_condition = current_condition; + return TRUE; + } + + return FALSE; +} + +static gboolean +winsock_check (GSource *source) +{ + GWinsockSource *winsock_source = (GWinsockSource *)source; + GIOCondition current_condition; + + current_condition = update_condition (winsock_source->socket); + + if (g_cancellable_is_cancelled (winsock_source->cancellable)) + { + winsock_source->result_condition = current_condition; + return TRUE; + } + + if ((winsock_source->condition & current_condition) != 0) + { + winsock_source->result_condition = current_condition; + return TRUE; + } + + return FALSE; +} + +static gboolean +winsock_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + GSocketSourceFunc func = (GSocketSourceFunc)callback; + GWinsockSource *winsock_source = (GWinsockSource *)source; + + return (*func) (user_data, + winsock_source->result_condition & winsock_source->condition); +} + +static void +winsock_finalize (GSource *source) +{ + GWinsockSource *winsock_source = (GWinsockSource *)source; + GSocket *socket; + + socket = winsock_source->socket; + + remove_condition_watch (socket, &winsock_source->condition); + g_object_unref (socket); + + if (winsock_source->cancellable) + g_object_unref (winsock_source->cancellable); +} + +static GSourceFuncs winsock_funcs = +{ + winsock_prepare, + winsock_check, + winsock_dispatch, + winsock_finalize +}; + +static GSource * +winsock_source_new (GSocket *socket, + GIOCondition condition, + GCancellable *cancellable) +{ + GSource *source; + GWinsockSource *winsock_source; + + ensure_event (socket); + + if (socket->priv->event == WSA_INVALID_EVENT) + { + g_warning ("Failed to create WSAEvent"); + return g_source_new (&broken_funcs, sizeof (GSource)); + } + + condition |= G_IO_HUP | G_IO_ERR; + + source = g_source_new (&winsock_funcs, sizeof (GWinsockSource)); + winsock_source = (GWinsockSource *)source; + + winsock_source->socket = g_object_ref (socket); + winsock_source->condition = condition; + add_condition_watch (socket, &winsock_source->condition); + + if (cancellable) + { + winsock_source->cancellable = g_object_ref (cancellable); + g_cancellable_make_pollfd (cancellable, + &winsock_source->cancel_pollfd); + g_source_add_poll (source, &winsock_source->cancel_pollfd); + } + + winsock_source->pollfd.fd = (gintptr) socket->priv->event; + winsock_source->pollfd.events = condition; + g_source_add_poll (source, &winsock_source->pollfd); + + return source; +} +#endif + +/** + * g_socket_create_source: + * @socket: a #GSocket + * @condition: a #GIOCondition mask to monitor + * @cancellable: a %GCancellable or %NULL + * + * Creates a %GSource that can be attached to a %GMainContext to monitor + * for the availibility of the specified @condition on the socket. + * + * The callback on the source is of the #GSocketSourceFunc type. + * + * It is meaningless to specify %G_IO_ERR or %G_IO_HUP in condition; + * these conditions will always be reported output if they are true. + * + * @cancellable if not %NULL can be used to cancel the source, which will + * cause the source to trigger, reporting the current condition. You can + * check for this in the callback using g_cancellable_is_cancelled(). + * + * Returns: a newly allocated %GSource, free with g_source_unref(). + * + * Since: 2.22 + **/ +GSource * +g_socket_create_source (GSocket *socket, + GIOCondition condition, + GCancellable *cancellable) +{ + GSource *source; + g_return_val_if_fail (G_IS_SOCKET (socket) && (cancellable == NULL || G_IS_CANCELLABLE (cancellable)), NULL); + +#ifdef G_OS_WIN32 + source = winsock_source_new (socket, condition, cancellable); +#else + source =_g_fd_source_new (socket->priv->fd, condition, cancellable); +#endif + return source; +} + +/** + * g_socket_condition_check: + * @socket: a #GSocket + * @condition: a #GIOCondition mask to check + * + * Checks on the readiness of @socket to perform operations. The + * operations specified in @condition are checked for and masked + * against the currently-satisfied conditions on @socket. The result + * is returned. + * + * It is meaningless to specify %G_IO_ERR or %G_IO_HUP in condition; + * these conditions will always be set in the output if they are true. + * + * This call never blocks. + * + * Returns: the @GIOCondition mask of the current state + * + * Since: 2.22 + **/ +GIOCondition +g_socket_condition_check (GSocket *socket, + GIOCondition condition) +{ + if (!check_socket (socket, NULL)) + return 0; + +#ifdef G_OS_WIN32 + { + GIOCondition current_condition; + + condition |= G_IO_ERR | G_IO_HUP; + + add_condition_watch (socket, &condition); + current_condition = update_condition (socket); + remove_condition_watch (socket, &condition); + return condition & current_condition; + } +#else + { + GPollFD poll_fd; + gint result; + poll_fd.fd = socket->priv->fd; + poll_fd.events = condition; + + do + result = g_poll (&poll_fd, 1, 0); + while (result == -1 && get_socket_errno () == EINTR); + + return poll_fd.revents; + } +#endif +} + +/** + * g_socket_condition_wait: + * @socket: a #GSocket + * @condition: a #GIOCondition mask to wait for + * @cancellable: a #GCancellable, or %NULL + * @error: a #GError pointer, or %NULL + * + * Waits for @condition to become true on @socket. When the condition + * becomes true, %TRUE is returned. + * + * If @cancellable is cancelled before the condition becomes true then + * %FALSE is returned and @error, if non-%NULL, is set to %G_IO_ERROR_CANCELLED. + * + * Returns: %TRUE if the condition was met, %FALSE otherwise + * + * Since: 2.22 + **/ +gboolean +g_socket_condition_wait (GSocket *socket, + GIOCondition condition, + GCancellable *cancellable, + GError **error) +{ + if (!check_socket (socket, error)) + return FALSE; + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + +#ifdef G_OS_WIN32 + { + GIOCondition current_condition; + WSAEVENT events[2]; + DWORD res; + GPollFD cancel_fd; + int num_events; + + /* Always check these */ + condition |= G_IO_ERR | G_IO_HUP; + + add_condition_watch (socket, &condition); + + num_events = 0; + events[num_events++] = socket->priv->event; + + if (cancellable) + { + g_cancellable_make_pollfd (cancellable, &cancel_fd); + events[num_events++] = (WSAEVENT)cancel_fd.fd; + } + + current_condition = update_condition (socket); + while ((condition & current_condition) == 0) + { + res = WSAWaitForMultipleEvents(num_events, events, + FALSE, WSA_INFINITE, FALSE); + if (res == WSA_WAIT_FAILED) + { + int errsv = get_socket_errno (); + + g_set_error (error, G_IO_ERROR, + socket_io_error_from_errno (errsv), + _("Waiting for socket condition: %s"), + socket_strerror (errsv)); + break; + } + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + break; + + current_condition = update_condition (socket); + } + remove_condition_watch (socket, &condition); + + return (condition & current_condition) != 0; + } +#else + { + GPollFD poll_fd[2]; + gint result; + gint num; + + poll_fd[0].fd = socket->priv->fd; + poll_fd[0].events = condition; + num = 1; + + if (cancellable) + { + g_cancellable_make_pollfd (cancellable, &poll_fd[1]); + num++; + } + + do + result = g_poll (poll_fd, num, -1); + while (result == -1 && get_socket_errno () == EINTR); + + return cancellable == NULL || + !g_cancellable_set_error_if_cancelled (cancellable, error); + } + #endif +} + +/** + * g_socket_send_to: + * @socket: a #GSocket + * @address: a #GSocketAddress, or %NULL + * @vectors: an array of #GOutputVector structs + * @num_vectors: the number of elements in @vectors, or -1 + * @messages: a pointer to an array of #GSocketControlMessages, or + * %NULL. + * @num_messages: number of elements in @messages, or -1. + * @flags: an int containing #GSocketMsgFlags flags + * @error: #GError for error reporting, or %NULL to ignore. + * + * Send data to @address on @socket. This is the most complicated and + * fully-featured version of this call. For easier use, see + * g_socket_send() and g_socket_send_to(). + * + * If @address is %NULL then the message is sent to the default receiver + * (set by g_socket_connect()). + * + * @vector must point to an array of #GOutputVector structs and + * @num_vectors must be the length of this array. These structs + * describe the buffers that the sent data will be gathered from. + * If @num_vector is -1, then @vector is assumed to be terminated + * by a #GOutputVector with a %NULL buffer pointer. + * + * + * @messages, if non-%NULL, is taken to point to an array of @num_messages + * #GSocketControlMessage instances. These correspond to the control + * messages to be sent on the socket. + * If @num_messages is -1 then @messages is treated as a %NULL-terminated + * array. + * + * @flags modify how the message sent. The commonly available arguments + * for this is available in the #GSocketMsgFlags enum, but the + * values there are the same as the system values, and the flags + * are passed in as-is, so you can pass in system specific flags too. + * + * If the socket is in blocking mode the call will block until there is + * space for the data in the socket queue. If there is no space available + * and the socket is in non-blocking mode a %G_IO_ERROR_WOULD_BLOCK error + * will be returned. To be notified of available space, wait for the %G_IO_OUT + * condition. + * + * Note that on Windows you can't rely on a %G_IO_OUT condition + * not producing a %G_IO_ERROR_WOULD_BLOCK error, as this is how Winsock + * write notification works. However, robust apps should always be able to + * handle this since it can easily appear in other cases too. + * + * On error -1 is returned and @error is set accordingly. + * + * Returns: Number of bytes read, or -1 on error + * + * Since: 2.22 + **/ +gssize +g_socket_send_message (GSocket *socket, + GSocketAddress *address, + GOutputVector *vectors, + gint num_vectors, + GSocketControlMessage **messages, + gint num_messages, + gint flags, + GError **error) +{ + GOutputVector one_vector; + char zero; + + if (!check_socket (socket, error)) + return -1; + + if (num_vectors == -1) + { + for (num_vectors = 0; + vectors[num_vectors].buffer != NULL; + num_vectors++) + ; + } + + if (num_messages == -1) + { + for (num_messages = 0; + messages != NULL && messages[num_messages] != NULL; + num_messages++) + ; + } + + if (num_vectors == 0) + { + zero = '\0'; + + one_vector.buffer = &zero; + one_vector.size = 1; + num_vectors = 1; + vectors = &one_vector; + } + +#ifndef G_OS_WIN32 + { + struct msghdr msg; + gssize result; + + /* name */ + if (address) + { + msg.msg_namelen = g_socket_address_get_native_size (address); + msg.msg_name = g_alloca (msg.msg_namelen); + g_socket_address_to_native (address, msg.msg_name, msg.msg_namelen); + } + + /* iov */ + { + /* this entire expression will be evaluated at compile time */ + if (sizeof *msg.msg_iov == sizeof *vectors && + sizeof msg.msg_iov->iov_base == sizeof vectors->buffer && + G_STRUCT_OFFSET (struct iovec, iov_base) == + G_STRUCT_OFFSET (GOutputVector, buffer) && + sizeof msg.msg_iov->iov_len == sizeof vectors->size && + G_STRUCT_OFFSET (struct iovec, iov_len) == + G_STRUCT_OFFSET (GOutputVector, size)) + /* ABI is compatible */ + { + msg.msg_iov = (struct iovec *) vectors; + msg.msg_iovlen = num_vectors; + } + else + /* ABI is incompatible */ + { + gint i; + + msg.msg_iov = g_newa (struct iovec, num_vectors); + for (i = 0; i < num_vectors; i++) + { + msg.msg_iov[i].iov_base = (void *) vectors[i].buffer; + msg.msg_iov[i].iov_len = vectors[i].size; + } + msg.msg_iovlen = num_vectors; + } + } + + /* control */ + { + struct cmsghdr *cmsg; + gint i; + + msg.msg_controllen = 0; + for (i = 0; i < num_messages; i++) + msg.msg_controllen += CMSG_SPACE (g_socket_control_message_get_size (messages[i])); + + msg.msg_control = g_alloca (msg.msg_controllen); + + cmsg = CMSG_FIRSTHDR (&msg); + for (i = 0; i < num_messages; i++) + { + cmsg->cmsg_level = g_socket_control_message_get_level (messages[i]); + cmsg->cmsg_type = g_socket_control_message_get_msg_type (messages[i]); + cmsg->cmsg_len = CMSG_LEN (g_socket_control_message_get_size (messages[i])); + g_socket_control_message_serialize (messages[i], + CMSG_DATA (cmsg)); + cmsg = CMSG_NXTHDR (&msg, cmsg); + } + g_assert (cmsg == NULL); + } + + while (1) + { + if (socket->priv->blocking && + !g_socket_condition_wait (socket, + G_IO_OUT, NULL, error)) + return -1; + + result = sendmsg (socket->priv->fd, &msg, flags); + if (result < 0) + { + int errsv = get_socket_errno (); + + if (errsv == EINTR) + continue; + + if (socket->priv->blocking && + (errsv == EWOULDBLOCK || + errsv == EAGAIN)) + continue; + + g_set_error (error, G_IO_ERROR, + socket_io_error_from_errno (errsv), + _("Error sending message: %s"), socket_strerror (errsv)); + + return -1; + } + break; + } + + return result; + } +#else + { + struct sockaddr_storage addr; + guint addrlen; + DWORD bytes_sent; + int result; + WSABUF *bufs; + gint i; + + /* Win32 doesn't support control messages. + Actually this is possible for raw and datagram sockets + via WSASendMessage on Vista or later, but that doesn't + seem very useful */ + if (num_messages != 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("GSocketControlMessage not supported on windows")); + return -1; + } + + /* iov */ + bufs = g_newa (WSABUF, num_vectors); + for (i = 0; i < num_vectors; i++) + { + bufs[i].buf = (char *)vectors[i].buffer; + bufs[i].len = (gulong)vectors[i].size; + } + + /* name */ + if (address) + { + addrlen = g_socket_address_get_native_size (address); + g_socket_address_to_native (address, &addr, sizeof addr); + } + + while (1) + { + if (socket->priv->blocking && + !g_socket_condition_wait (socket, + G_IO_OUT, NULL, error)) + return -1; + + if (address) + result = WSASendTo (socket->priv->fd, + bufs, num_vectors, + &bytes_sent, flags, + (const struct sockaddr *)&addr, addrlen, + NULL, NULL); + else + result = WSASend (socket->priv->fd, + bufs, num_vectors, + &bytes_sent, flags, + NULL, NULL); + + if (result != 0) + { + int errsv = get_socket_errno (); + + if (errsv == WSAEINTR) + continue; + + if (errsv == WSAEWOULDBLOCK) + win32_unset_event_mask (socket, FD_WRITE); + + if (socket->priv->blocking && + errsv == WSAEWOULDBLOCK) + continue; + + g_set_error (error, G_IO_ERROR, + socket_io_error_from_errno (errsv), + _("Error sending message: %s"), socket_strerror (errsv)); + + return -1; + } + break; + } + + return bytes_sent; + } +#endif +} + +/** + * g_socket_receive_message: + * @socket: a #GSocket + * @address: a pointer to a #GSocketAddress pointer, or %NULL + * @vectors: an array of #GInputVector structs + * @num_vectors: the number of elements in @vectors, or -1 + * @messages: a pointer which will be filled with an array of + * #GSocketControlMessages, or %NULL + * @num_messages: a pointer which will be filled with the number of + * elements in @messages, or %NULL + * @flags: a pointer to an int containing #GSocketMsgFlags flags + * @error: a #GError pointer, or %NULL + * + * Receive data from a socket. This is the most complicated and + * fully-featured version of this call. For easier use, see + * g_socket_receive() and g_socket_receive_from(). + * + * If @address is non-%NULL then @address will be set equal to the + * source address of the received packet. + * @address is owned by the caller. + * + * @vector must point to an array of #GInputVector structs and + * @num_vectors must be the length of this array. These structs + * describe the buffers that received data will be scattered into. + * If @num_vector is -1, then @vector is assumed to be terminated + * by a #GInputVector with a %NULL buffer pointer. + * + * As a special case, if the size of the array is zero (in which case, + * @vectors may of course be %NULL), then a single byte is received + * and discarded. This is to facilitate the common practice of + * sending a single '\0' byte for the purposes of transferring + * ancillary data. + * + * @messages, if non-%NULL, is taken to point to a pointer that will + * be set to point to a newly-allocated array of + * #GSocketControlMessage instances. These correspond to the control + * messages received from the kernel, one #GSocketControlMessage per + * message from the kernel. This array is %NULL-terminated and must be + * freed by the caller using g_free(). + * + * @num_messages, if non-%NULL, will be set to the number of control + * messages received. + * + * If both @messages and @num_messages are non-%NULL, then + * @num_messages gives the number of #GSocketControlMessage instances + * in @messages (ie: not including the %NULL terminator). + * + * @flags is an in/out parameter. The commonly available arguments + * for this is available in the #GSocketMsgFlags enum, but the + * values there are the same as the system values, and the flags + * are passed in as-is, so you can pass in system specific flags too. + * + * If the socket is in blocking mode the call will block until there is + * some data to receive or there is an error. If there is no data available + * and the socket is in non-blocking mode a %G_IO_ERROR_WOULD_BLOCK error + * will be returned. To be notified of available data, wait for the %G_IO_IN + * condition. + * + * On error -1 is returned and @error is set accordingly. + * + * Returns: Number of bytes read, or -1 on error + * + * Since: 2.22 + **/ +gssize +g_socket_receive_message (GSocket *socket, + GSocketAddress **address, + GInputVector *vectors, + gint num_vectors, + GSocketControlMessage ***messages, + gint *num_messages, + gint *flags, + GError **error) +{ + GInputVector one_vector; + char one_byte; + + if (!check_socket (socket, error)) + return -1; + + if (num_vectors == -1) + { + for (num_vectors = 0; + vectors[num_vectors].buffer != NULL; + num_vectors++) + ; + } + + if (num_vectors == 0) + { + one_vector.buffer = &one_byte; + one_vector.size = 1; + num_vectors = 1; + vectors = &one_vector; + } + +#ifndef G_OS_WIN32 + { + struct msghdr msg; + gssize result; + struct sockaddr_storage one_sockaddr; + + /* name */ + if (address) + { + msg.msg_name = &one_sockaddr; + msg.msg_namelen = sizeof (struct sockaddr_storage); + } + else + { + msg.msg_name = NULL; + msg.msg_namelen = 0; + } + + /* iov */ + /* this entire expression will be evaluated at compile time */ + if (sizeof *msg.msg_iov == sizeof *vectors && + sizeof msg.msg_iov->iov_base == sizeof vectors->buffer && + G_STRUCT_OFFSET (struct iovec, iov_base) == + G_STRUCT_OFFSET (GInputVector, buffer) && + sizeof msg.msg_iov->iov_len == sizeof vectors->size && + G_STRUCT_OFFSET (struct iovec, iov_len) == + G_STRUCT_OFFSET (GInputVector, size)) + /* ABI is compatible */ + { + msg.msg_iov = (struct iovec *) vectors; + msg.msg_iovlen = num_vectors; + } + else + /* ABI is incompatible */ + { + gint i; + + msg.msg_iov = g_newa (struct iovec, num_vectors); + for (i = 0; i < num_vectors; i++) + { + msg.msg_iov[i].iov_base = vectors[i].buffer; + msg.msg_iov[i].iov_len = vectors[i].size; + } + msg.msg_iovlen = num_vectors; + } + + /* control */ + msg.msg_control = g_alloca (2048); + msg.msg_controllen = 2048; + + /* flags */ + if (flags != NULL) + msg.msg_flags = *flags; + else + msg.msg_flags = 0; + + /* do it */ + while (1) + { + if (socket->priv->blocking && + !g_socket_condition_wait (socket, + G_IO_IN, NULL, error)) + return -1; + + result = recvmsg (socket->priv->fd, &msg, msg.msg_flags); + + if (result < 0) + { + int errsv = get_socket_errno (); + + if (errsv == EINTR) + continue; + + if (socket->priv->blocking && + (errsv == EWOULDBLOCK || + errsv == EAGAIN)) + continue; + + g_set_error (error, G_IO_ERROR, + socket_io_error_from_errno (errsv), + _("Error receiving message: %s"), socket_strerror (errsv)); + + return -1; + } + break; + } + + /* decode address */ + if (address != NULL) + { + if (msg.msg_namelen > 0) + *address = g_socket_address_new_from_native (msg.msg_name, + msg.msg_namelen); + else + *address = NULL; + } + + /* decode control messages */ + { + GSocketControlMessage **my_messages = NULL; + gint allocated = 0, index = 0; + const gchar *scm_pointer; + struct cmsghdr *cmsg; + gsize scm_size; + + scm_pointer = (const gchar *) msg.msg_control; + scm_size = msg.msg_controllen; + + for (cmsg = CMSG_FIRSTHDR (&msg); cmsg; cmsg = CMSG_NXTHDR (&msg, cmsg)) + { + GSocketControlMessage *message; + + message = g_socket_control_message_deserialize (cmsg->cmsg_level, + cmsg->cmsg_type, + cmsg->cmsg_len - ((char *)CMSG_DATA (cmsg) - (char *)cmsg), + CMSG_DATA (cmsg)); + if (message == NULL) + /* We've already spewed about the problem in the + deserialization code, so just continue */ + continue; + + if (index == allocated) + { + /* estimated 99% case: exactly 1 control message */ + allocated = MIN (allocated * 2, 1); + my_messages = g_new (GSocketControlMessage *, (allocated + 1)); + allocated = 1; + } + + my_messages[index++] = message; + } + + if (num_messages) + *num_messages = index; + + if (messages) + { + my_messages[index++] = NULL; + *messages = my_messages; + } + else + { + gint i; + + /* free all those messages we just constructed. + * we have to do it this way if the user ignores the + * messages so that we will close any received fds. + */ + for (i = 0; i < index; i++) + g_object_unref (my_messages[i]); + g_free (my_messages); + } + } + + /* capture the flags */ + if (flags != NULL) + *flags = msg.msg_flags; + + return result; + } +#else + { + struct sockaddr_storage addr; + int addrlen; + DWORD bytes_received; + DWORD win_flags; + int result; + WSABUF *bufs; + gint i; + + /* iov */ + bufs = g_newa (WSABUF, num_vectors); + for (i = 0; i < num_vectors; i++) + { + bufs[i].buf = (char *)vectors[i].buffer; + bufs[i].len = (gulong)vectors[i].size; + } + + /* flags */ + if (flags != NULL) + win_flags = *flags; + else + win_flags = 0; + + /* do it */ + while (1) + { + if (socket->priv->blocking && + !g_socket_condition_wait (socket, + G_IO_IN, NULL, error)) + return -1; + + addrlen = sizeof addr; + if (address) + result = WSARecvFrom (socket->priv->fd, + bufs, num_vectors, + &bytes_received, &win_flags, + (struct sockaddr *)&addr, &addrlen, + NULL, NULL); + else + result = WSARecv (socket->priv->fd, + bufs, num_vectors, + &bytes_received, &win_flags, + NULL, NULL); + if (result != 0) + { + int errsv = get_socket_errno (); + + if (errsv == WSAEINTR) + continue; + + win32_unset_event_mask (socket, FD_READ); + + if (socket->priv->blocking && + errsv == WSAEWOULDBLOCK) + continue; + + g_set_error (error, G_IO_ERROR, + socket_io_error_from_errno (errsv), + _("Error receiving message: %s"), socket_strerror (errsv)); + + return -1; + } + win32_unset_event_mask (socket, FD_READ); + break; + } + + /* decode address */ + if (address != NULL) + { + if (addrlen > 0) + *address = g_socket_address_new_from_native (&addr, addrlen); + else + *address = NULL; + } + + /* capture the flags */ + if (flags != NULL) + *flags = win_flags; + + return bytes_received; + } +#endif +} + +#define __G_SOCKET_C__ +#include "gioaliasdef.c" diff --git a/gio/gsocket.h b/gio/gsocket.h new file mode 100644 index 000000000..b3893339a --- /dev/null +++ b/gio/gsocket.h @@ -0,0 +1,172 @@ +/* + * Copyright © 2008 Christian Kellner, Samuel Cormier-Iijima + * Copyright © 2009 Codethink Limited + * + * 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 + */ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __G_SOCKET_H__ +#define __G_SOCKET_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_SOCKET (g_socket_get_type ()) +#define G_SOCKET(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_SOCKET, GSocket)) +#define G_SOCKET_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \ + G_TYPE_SOCKET, GSocketClass)) +#define G_IS_SOCKET(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_SOCKET)) +#define G_IS_SOCKET_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \ + G_TYPE_SOCKET)) +#define G_SOCKET_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \ + G_TYPE_SOCKET, GSocketClass)) + +typedef struct _GSocketPrivate GSocketPrivate; +typedef struct _GSocketClass GSocketClass; + +struct _GSocketClass +{ + GObjectClass parent_class; + + /*< private >*/ + + /* 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); + void (*_g_reserved7) (void); + void (*_g_reserved8) (void); + void (*_g_reserved9) (void); + void (*_g_reserved10) (void); +}; + +struct _GSocket +{ + GObject parent_instance; + GSocketPrivate *priv; +}; + +GType g_socket_get_type (void) G_GNUC_CONST; +GSocket * g_socket_new (GSocketFamily family, + GSocketType type, + const char *protocol, + GError **error); +GSocket * g_socket_new_from_fd (gint fd, + GError **error); +int g_socket_get_fd (GSocket *socket); +GSocketFamily g_socket_get_family (GSocket *socket); +GSocketType g_socket_get_socket_type (GSocket *socket); +const char * g_socket_get_protocol (GSocket *socket); +GSocketAddress * g_socket_get_local_address (GSocket *socket, + GError **error); +GSocketAddress * g_socket_get_remote_address (GSocket *socket, + GError **error); +void g_socket_set_blocking (GSocket *socket, + gboolean blocking); +gboolean g_socket_get_blocking (GSocket *socket); +void g_socket_set_reuse_address (GSocket *socket, + gboolean reuse); +gboolean g_socket_get_reuse_address (GSocket *socket); +void g_socket_set_keepalive (GSocket *socket, + gboolean keepalive); +gboolean g_socket_get_keepalive (GSocket *socket); +gint g_socket_get_listen_backlog (GSocket *socket); +void g_socket_set_listen_backlog (GSocket *socket, + gint backlog); +gboolean g_socket_is_connected (GSocket *socket); +gboolean g_socket_bind (GSocket *socket, + GSocketAddress *address, + gboolean allow_reuse, + GError **error); +gboolean g_socket_connect (GSocket *socket, + GSocketAddress *address, + GError **error); +gboolean g_socket_check_pending_error (GSocket *socket, + GError **error); +GIOCondition g_socket_condition_check (GSocket *socket, + GIOCondition condition); +gboolean g_socket_condition_wait (GSocket *socket, + GIOCondition condition, + GCancellable *cancellable, + GError **error); +GSocket * g_socket_accept (GSocket *socket, + GError **error); +gboolean g_socket_listen (GSocket *socket, + GError **error); +gssize g_socket_receive (GSocket *socket, + gchar *buffer, + gsize size, + GError **error); +gssize g_socket_receive_from (GSocket *socket, + GSocketAddress **address, + gchar *buffer, + gsize size, + GError **error); +gssize g_socket_send (GSocket *socket, + const gchar *buffer, + gsize size, + GError **error); +gssize g_socket_send_to (GSocket *socket, + GSocketAddress *address, + const gchar *buffer, + gsize size, + GError **error); +GSocketControlMessage *g_socket_receive_control_message (GSocket *socket, + GError **error); +gboolean g_socket_send_control_message (GSocket *socket, + GSocketControlMessage *message, + GError **error); +gssize g_socket_receive_message (GSocket *socket, + GSocketAddress **address, + GInputVector *vectors, + gint num_vectors, + GSocketControlMessage ***messages, + gint *num_messages, + gint *flags, + GError **error); +gssize g_socket_send_message (GSocket *socket, + GSocketAddress *address, + GOutputVector *vectors, + gint num_vectors, + GSocketControlMessage **messages, + gint num_messages, + gint flags, + GError **error); +gboolean g_socket_close (GSocket *socket, + GError **error); +gboolean g_socket_is_closed (GSocket *socket); +GSource * g_socket_create_source (GSocket *socket, + GIOCondition condition, + GCancellable *cancellable); + + +G_END_DECLS + +#endif /* __G_SOCKET_H__ */ diff --git a/gio/gsocketcontrolmessage.c b/gio/gsocketcontrolmessage.c new file mode 100644 index 000000000..27420d747 --- /dev/null +++ b/gio/gsocketcontrolmessage.c @@ -0,0 +1,200 @@ +/* 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 + */ + +/** + * SECTION: gsocketcontrolmessage + * @title: GSocketControlMessage + * @short_description: a #GSocket control message + * @see_also: #GSocket. + * + * A #GSocketControlMessage is a special-purpose utility message that + * can be sent to or received from a #GSocket. These types of + * messages are often called "ancillary data". + * + * The message can represent some sort of special instruction to or + * information from the socket or can represent a special kind of + * transfer to the peer (for example, sending a file description over + * a UNIX socket). + * + * These messages are sent with g_socket_send_message() and received + * with g_socket_receive_message(). + * + * To extend the set of control message that can be sent, subclass this + * class and override the get_size, get_level, get_type and serialize + * methods. + * + * To extend the set of control messages that can be received, subclass + * this class and implement the deserialize method. Also, make sure your + * class is registered with the GType typesystem before calling + * g_socket_receive_message() to read such a message. + * + * Since: 2.22 + **/ + +#include "config.h" +#include "gsocketcontrolmessage.h" +#include "glibintl.h" + +#ifndef G_OS_WIN32 +#include "gunixfdmessage.h" +#include +#else +# include +# include +#endif + +#include "gioalias.h" + +G_DEFINE_ABSTRACT_TYPE (GSocketControlMessage, + g_socket_control_message, + G_TYPE_OBJECT); + +/** + * g_socket_control_message_get_size: + * @message: a #GSocketControlMessage + * + * Returns the space required for the control message, not including + * headers or alignment. + * + * Returns: The number of bytes required. + * + * Since: 2.22 + **/ +gsize +g_socket_control_message_get_size (GSocketControlMessage *message) +{ + g_return_val_if_fail (G_IS_SOCKET_CONTROL_MESSAGE (message), 0); + + return G_SOCKET_CONTROL_MESSAGE_GET_CLASS (message)->get_size (message); +} + +/** + * g_socket_control_message_get_level: + * @message: a #GSocketControlMessage + * + * Returns the "level" (i.e. the originating protocol) of the control message. + * This is often SOL_SOCKET. + * + * Returns: and int describing the level + * + * Since: 2.22 + **/ +int +g_socket_control_message_get_level (GSocketControlMessage *message) +{ + g_return_val_if_fail (G_IS_SOCKET_CONTROL_MESSAGE (message), 0); + + return G_SOCKET_CONTROL_MESSAGE_GET_CLASS (message)->get_level (message); +} + +/** + * g_socket_control_message_get_msg_type: + * @message: a #GSocketControlMessage + * + * Returns the protocol specify type of the control message. + * For instance, for unix fd passing this would be SCM_RIGHTS. + * + * Returns: and int describing the level + * + * Since: 2.22 + **/ +int +g_socket_control_message_get_msg_type (GSocketControlMessage *message) +{ + g_return_val_if_fail (G_IS_SOCKET_CONTROL_MESSAGE (message), 0); + + return G_SOCKET_CONTROL_MESSAGE_GET_CLASS (message)->get_type (message); +} + +/** + * g_socket_control_message_serialize: + * @message: a #GSocketControlMessage + * @data: A buffer to write data to + * + * Converts the data in the message to bytes placed in the + * message. + * + * @data is guaranteed to have enough space to fit the size + * returned by g_socket_control_message_get_size() on this + * object. + * + * Since: 2.22 + **/ +void +g_socket_control_message_serialize (GSocketControlMessage *message, + gpointer data) +{ + g_return_if_fail (G_IS_SOCKET_CONTROL_MESSAGE (message)); + + return G_SOCKET_CONTROL_MESSAGE_GET_CLASS (message)->serialize (message, data); +} + + +static void +g_socket_control_message_init (GSocketControlMessage *message) +{ +} + +static void +g_socket_control_message_class_init (GSocketControlMessageClass *class) +{ +} + +GSocketControlMessage * +g_socket_control_message_deserialize (int level, + int type, + gsize size, + gpointer data) +{ + GSocketControlMessageClass *klass; + GSocketControlMessage *message; + GType *message_types; + guint n_message_types; + int i; +#ifndef G_OS_WIN32 + volatile GType a_type; +#endif + + /* Ensure we know about the built in types */ +#ifndef G_OS_WIN32 + a_type = g_unix_fd_message_get_type (); +#endif + + message_types = g_type_children (G_TYPE_SOCKET_CONTROL_MESSAGE, &n_message_types); + + message = NULL; + for (i = 0; i < n_message_types; i++) + { + klass = (GSocketControlMessageClass *)g_type_class_ref (type); + + if (klass && klass->deserialize) + { + message = klass->deserialize (level, type, size, data); + g_type_class_unref ((GTypeClass *) klass); + } + + if (message != NULL) + break; + } + + g_free (message_types); + + if (message == NULL) + g_warning ("unknown control message type %d:%d", level, type); + + return message; +} + +#define __G_SOCKET_CONTROL_MESSAGE_C__ +#include "gioaliasdef.c" diff --git a/gio/gsocketcontrolmessage.h b/gio/gsocketcontrolmessage.h new file mode 100644 index 000000000..5e37d3f24 --- /dev/null +++ b/gio/gsocketcontrolmessage.h @@ -0,0 +1,96 @@ +/* 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 + */ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __G_SOCKET_CONTROL_MESSAGE_H__ +#define __G_SOCKET_CONTROL_MESSAGE_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_SOCKET_CONTROL_MESSAGE (g_socket_control_message_get_type ()) +#define G_SOCKET_CONTROL_MESSAGE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_SOCKET_CONTROL_MESSAGE, \ + GSocketControlMessage)) +#define G_SOCKET_CONTROL_MESSAGE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \ + G_TYPE_SOCKET_CONTROL_MESSAGE, \ + GSocketControlMessageClass)) +#define G_IS_SOCKET_CONTROL_MESSAGE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_SOCKET_CONTROL_MESSAGE)) +#define G_IS_SOCKET_CONTROL_MESSAGE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \ + G_TYPE_SOCKET_CONTROL_MESSAGE)) +#define G_SOCKET_CONTROL_MESSAGE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \ + G_TYPE_SOCKET_CONTROL_MESSAGE, \ + GSocketControlMessageClass)) + +typedef struct _GSocketControlMessagePrivate GSocketControlMessagePrivate; +typedef struct _GSocketControlMessageClass GSocketControlMessageClass; + +/** + * GSocketControlMessageClass: + * @get_size: gets the size of the message. + * @get_level: gets the protocol of the message. + * @get_type: gets the protocol specific type of the message. + * @serialize: Writes out the message data. + * @deserialize: Tries to deserialize a message. + **/ + +struct _GSocketControlMessageClass +{ + GObjectClass parent_class; + + gsize (* get_size) (GSocketControlMessage *message); + int (* get_level) (GSocketControlMessage *message); + int (* get_type) (GSocketControlMessage *message); + void (* serialize) (GSocketControlMessage *message, + gpointer data); + GSocketControlMessage *(* deserialize) (int level, + int type, + gsize size, + gpointer data); +}; + +struct _GSocketControlMessage +{ + GObject parent_instance; + GSocketControlMessagePrivate *priv; +}; + +GType g_socket_control_message_get_type (void) G_GNUC_CONST; +gsize g_socket_control_message_get_size (GSocketControlMessage *message); +int g_socket_control_message_get_level (GSocketControlMessage *message); +int g_socket_control_message_get_msg_type (GSocketControlMessage *message); +void g_socket_control_message_serialize (GSocketControlMessage *message, + gpointer data); +GSocketControlMessage *g_socket_control_message_deserialize (int level, + int type, + gsize size, + gpointer data); + + +G_END_DECLS + +#endif /* __G_SOCKET_CONTROL_MESSAGE_H__ */ diff --git a/gio/gunixfdmessage.c b/gio/gunixfdmessage.c new file mode 100644 index 000000000..7ba0e11b4 --- /dev/null +++ b/gio/gunixfdmessage.c @@ -0,0 +1,259 @@ +/* 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 + */ + +/** + * SECTION: gunixfdmessage + * @title: GUnixFDMessage + * @short_description: a #GSocketControlMessage containing a list of + * file descriptors + * @see_also: #GUnixConnection + * + * This #GSocketControlMessage contains a list of file descriptors. + * It may be sent using g_socket_send_message() and received using + * g_socket_receive_message() over UNIX sockets (ie: sockets in the + * %G_SOCKET_ADDRESS_UNIX family). + * + * For an easier way to send and receive file descriptors over + * stream-oriented UNIX sockets, see g_unix_connection_send_fd() and + * g_unix_connection_receive_fd(). + **/ + +#include "config.h" + +#include +#include +#include +#include + +#include "gunixfdmessage.h" +#include "gioerror.h" + +#include "gioalias.h" + + +G_DEFINE_TYPE (GUnixFDMessage, g_unix_fd_message, + G_TYPE_SOCKET_CONTROL_MESSAGE); + +struct _GUnixFDMessagePrivate +{ + gint *fds; + gint nfd; +}; + +static gsize +g_unix_fd_message_get_size (GSocketControlMessage *message) +{ + GUnixFDMessage *fd_message = G_UNIX_FD_MESSAGE (message); + + return fd_message->priv->nfd * sizeof (gint); +} + +static int +g_unix_fd_message_get_level (GSocketControlMessage *message) +{ + return SOL_SOCKET; +} + +static int +g_unix_fd_message_get_msg_type (GSocketControlMessage *message) +{ + return SCM_RIGHTS; +} + +static GSocketControlMessage * +g_unix_fd_message_deserialize (int level, + int type, + gsize size, + gpointer data) +{ + GUnixFDMessage *message; + + if (level != SOL_SOCKET || + level != SCM_RIGHTS) + return NULL; + + if (size % 4 > 0) + { + g_warning ("Kernel returned non-integral number of fds"); + return NULL; + } + + message = g_object_new (G_TYPE_UNIX_FD_MESSAGE, NULL); + message->priv->nfd = size / sizeof (gint); + message->priv->fds = g_new (gint, message->priv->nfd + 1); + memcpy (message->priv->fds, data, size); + message->priv->fds[message->priv->nfd] = -1; + + return G_SOCKET_CONTROL_MESSAGE (message); +} + +static void +g_unix_fd_message_serialize (GSocketControlMessage *message, + gpointer data) +{ + GUnixFDMessage *fd_message = G_UNIX_FD_MESSAGE (message); + memcpy (data, fd_message->priv->fds, + sizeof (gint) * fd_message->priv->nfd); +} +static void +g_unix_fd_message_init (GUnixFDMessage *message) +{ + message->priv = G_TYPE_INSTANCE_GET_PRIVATE (message, + G_TYPE_UNIX_FD_MESSAGE, + GUnixFDMessagePrivate); +} + +static void +g_unix_fd_message_finalize (GObject *object) +{ + GUnixFDMessage *message = G_UNIX_FD_MESSAGE (object); + gint i; + + for (i = 0; i < message->priv->nfd; i++) + close (message->priv->fds[i]); + g_free (message->priv->fds); + + G_OBJECT_CLASS (g_unix_fd_message_parent_class) + ->finalize (object); +} + +static void +g_unix_fd_message_class_init (GUnixFDMessageClass *class) +{ + GSocketControlMessageClass *scm_class = G_SOCKET_CONTROL_MESSAGE_CLASS (class); + GObjectClass *object_class = G_OBJECT_CLASS (class); + + g_type_class_add_private (class, sizeof (GUnixFDMessagePrivate)); + scm_class->get_size = g_unix_fd_message_get_size; + scm_class->get_level = g_unix_fd_message_get_level; + scm_class->get_type = g_unix_fd_message_get_msg_type; + scm_class->serialize = g_unix_fd_message_serialize; + scm_class->deserialize = g_unix_fd_message_deserialize; + object_class->finalize = g_unix_fd_message_finalize; +} + +/** + * g_unix_fd_message_new: + * @returns: a new #GUnixFDMessage + * + * Creates a new #GUnixFDMessage containing no file descriptors. + **/ +GSocketControlMessage * +g_unix_fd_message_new (void) +{ + return g_object_new (G_TYPE_UNIX_FD_MESSAGE, NULL); +} + +/** + * g_unix_fd_message_steal_fds: + * @message: a #GUnixFDMessage + * @length: pointer to the length of the returned array, or %NULL + * @returns: an array of file descriptors + * + * Returns the array of file descriptors that is contained in this + * object. + * + * After this call, the descriptors are no longer contained in + * @message. Further calls will return an empty list (unless more + * descriptors have been added). + * + * The return result of this function must be freed with g_free(). + * The caller is also responsible for closing all of the file + * descriptors. + * + * If @length is non-%NULL then it is set to the number of file + * descriptors in the returned array. The returned array is also + * terminated with -1. + * + * This function never returns NULL. In case there are no file + * descriptors contained in @message, an empty array is returned. + **/ +gint * +g_unix_fd_message_steal_fds (GUnixFDMessage *message, + gint *length) +{ + gint *result; + + g_return_val_if_fail (G_IS_UNIX_FD_MESSAGE (message), NULL); + + /* will be true for fresh object or if we were just called */ + if (message->priv->fds == NULL) + { + message->priv->fds = g_new (gint, 1); + message->priv->fds[0] = -1; + message->priv->nfd = 0; + } + + if (length) + *length = message->priv->nfd; + result = message->priv->fds; + + message->priv->fds = NULL; + message->priv->nfd = 0; + + return result; +} + +/** + * g_unix_fd_message_append_fd: + * @message: a #GUnixFDMessage + * @fd: a valid open file descriptor + * @error: a #GError pointer + * @returns: %TRUE in case of success, else %FALSE (and @error is set) + * + * Adds a file descriptor to @message. + * + * The file descriptor is duplicated using dup(). You keep your copy + * of the descriptor and the copy contained in @message will be closed + * when @message is finalized. + * + * A possible cause of failure is exceeding the per-process or + * system-wide file descriptor limit. + **/ +gboolean +g_unix_fd_message_append_fd (GUnixFDMessage *message, + gint fd, + GError **error) +{ + gint new_fd; + + g_return_val_if_fail (G_IS_UNIX_FD_MESSAGE (message), FALSE); + g_return_val_if_fail (fd >= 0, FALSE); + + do + new_fd = dup (fd); + while (new_fd < 0 && (errno == EINTR)); + + if (fd < 0) + { + int saved_errno = errno; + + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (saved_errno), + "dup: %s", g_strerror (saved_errno)); + + return FALSE; + } + + message->priv->fds = g_realloc (message->priv->fds, + sizeof (gint) * + (message->priv->nfd + 2)); + message->priv->fds[message->priv->nfd++] = new_fd; + message->priv->fds[message->priv->nfd] = -1; + + return TRUE; +} + +#define __G_UNIX_FD_MESSAGE_C__ +#include "gioaliasdef.c" diff --git a/gio/gunixfdmessage.h b/gio/gunixfdmessage.h new file mode 100644 index 000000000..5c86e09fa --- /dev/null +++ b/gio/gunixfdmessage.h @@ -0,0 +1,67 @@ +/* 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_FD_MESSAGE_H__ +#define __G_UNIX_FD_MESSAGE_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_UNIX_FD_MESSAGE (g_unix_fd_message_get_type ()) +#define G_UNIX_FD_MESSAGE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_UNIX_FD_MESSAGE, GUnixFDMessage)) +#define G_UNIX_FD_MESSAGE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \ + G_TYPE_UNIX_FD_MESSAGE, GUnixFDMessageClass)) +#define G_IS_UNIX_FD_MESSAGE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_UNIX_FD_MESSAGE)) +#define G_IS_UNIX_FD_MESSAGE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \ + G_TYPE_UNIX_FD_MESSAGE)) +#define G_UNIX_FD_MESSAGE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \ + G_TYPE_UNIX_FD_MESSAGE, GUnixFDMessageClass)) + +typedef struct _GUnixFDMessagePrivate GUnixFDMessagePrivate; +typedef struct _GUnixFDMessageClass GUnixFDMessageClass; +typedef struct _GUnixFDMessage GUnixFDMessage; + +struct _GUnixFDMessageClass +{ + GSocketControlMessageClass parent_class; +}; + +struct _GUnixFDMessage +{ + GSocketControlMessage parent_instance; + GUnixFDMessagePrivate *priv; +}; + +GType g_unix_fd_message_get_type (void) G_GNUC_CONST; +GSocketControlMessage * g_unix_fd_message_new (void); +gint * g_unix_fd_message_steal_fds (GUnixFDMessage *message, + gint *length); +gboolean g_unix_fd_message_append_fd (GUnixFDMessage *message, + gint fd, + GError **error); + +G_END_DECLS + +#endif /* __G_UNIX_FD_MESSAGE_H__ */