diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml index c0345bc4c..c9ad4acd4 100644 --- a/docs/reference/gio/gio-docs.xml +++ b/docs/reference/gio/gio-docs.xml @@ -105,6 +105,7 @@ + diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index de989f8a6..7964c9009 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -1449,6 +1449,27 @@ g_io_extension_point_set_required_type g_io_extension_ref_class +
+gunixfdlist +GUnixFDList +GUnixFDList +g_unix_fd_list_new_from_array +g_unix_fd_list_new +g_unix_fd_list_get_length +g_unix_fd_list_get +g_unix_fd_list_peek_fds +g_unix_fd_list_steal_fds +g_unix_fd_list_append + +GUnixFDListClass +g_unix_fd_list_get_type +G_UNIX_FD_LIST +G_UNIX_FD_LIST_CLASS +G_IS_UNIX_FD_LIST +G_IS_UNIX_FD_LIST_CLASS +G_UNIX_FD_LIST_GET_CLASS +
+
ginetaddress GInetAddress @@ -1906,7 +1927,9 @@ g_threaded_socket_service_get_type gunixfdmessage GUnixFDMessage GUnixFDMessage +g_unix_fd_message_new_with_fd_list g_unix_fd_message_new +g_unix_fd_message_get_fd_list g_unix_fd_message_append_fd g_unix_fd_message_steal_fds diff --git a/docs/reference/gio/gio.types b/docs/reference/gio/gio.types index 598010075..29fbd6e4d 100644 --- a/docs/reference/gio/gio.types +++ b/docs/reference/gio/gio.types @@ -84,6 +84,7 @@ g_tcp_connection_get_type g_themed_icon_get_type g_threaded_socket_service_get_type g_unix_connection_get_type +g_unix_fd_list_get_type g_unix_fd_message_get_type g_unix_input_stream_get_type g_unix_mount_monitor_get_type diff --git a/gio/Makefile.am b/gio/Makefile.am index f5fd0450f..9dc2e02cd 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -131,6 +131,7 @@ platform_libadd += libasyncns/libasyncns.la xdgmime/libxdgmime.la platform_deps += libasyncns/libasyncns.la xdgmime/libxdgmime.la unix_sources = \ gunixconnection.c \ + gunixfdlist.c \ gunixfdmessage.c \ gunixmount.c \ gunixmount.h \ @@ -153,6 +154,7 @@ giounixinclude_HEADERS = \ gdesktopappinfo.h \ gunixconnection.h \ gunixmounts.h \ + gunixfdlist.h \ gunixfdmessage.h \ gunixinputstream.h \ gunixoutputstream.h \ diff --git a/gio/gunixfdlist.c b/gio/gunixfdlist.c new file mode 100644 index 000000000..df715b21a --- /dev/null +++ b/gio/gunixfdlist.c @@ -0,0 +1,391 @@ +/* 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: gunixfdlist + * @title: GUnixFDList + * @short_description: An object containing a set of file descriptors + * @see_also: #GUnixFDMessage + * + * A #GUnixFDList contains a list of file descriptors. It owns the file + * descriptors that it contains, closing them when finalized. + * + * It may be wrapped in a #GUnixFDMessage and sent over a #GSocket in + * the %G_SOCKET_ADDRESS_UNIX family by using g_socket_send_message() + * and received using g_socket_receive_message(). + */ + +#define _GNU_SOURCE /* for F_DUPFD_CLOEXEC */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "gunixfdlist.h" +#include "gioerror.h" + +#include "gioalias.h" + + +G_DEFINE_TYPE (GUnixFDList, g_unix_fd_list, G_TYPE_OBJECT) + +struct _GUnixFDListPrivate +{ + gint *fds; + gint nfd; +}; + +static void +g_unix_fd_list_init (GUnixFDList *list) +{ + list->priv = G_TYPE_INSTANCE_GET_PRIVATE (list, + G_TYPE_UNIX_FD_LIST, + GUnixFDListPrivate); +} + +static void +g_unix_fd_list_finalize (GObject *object) +{ + GUnixFDList *list = G_UNIX_FD_LIST (object); + gint i; + + for (i = 0; i < list->priv->nfd; i++) + close (list->priv->fds[i]); + g_free (list->priv->fds); + + G_OBJECT_CLASS (g_unix_fd_list_parent_class) + ->finalize (object); +} + +static void +g_unix_fd_list_class_init (GUnixFDListClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + g_type_class_add_private (class, sizeof (GUnixFDListPrivate)); + object_class->finalize = g_unix_fd_list_finalize; +} + +static int +dup_close_on_exec_fd (gint fd, + GError **error) +{ + gint new_fd; + gint s; + +#ifdef F_DUPFD_CLOEXEC + do + new_fd = fcntl (fd, F_DUPFD_CLOEXEC, 0l); + while (new_fd < 0 && (errno == EINTR)); + + if (new_fd >= 0) + return new_fd; + + /* if that didn't work (new libc/old kernel?), try it the other way. */ +#endif + + do + new_fd = dup (fd); + while (new_fd < 0 && (errno == EINTR)); + + if (new_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)); + close (new_fd); + + return -1; + } + + do + s = fcntl (new_fd, F_SETFD, FD_CLOEXEC); + while (s < 0 && (errno == EINTR)); + + if (new_fd < 0) + { + int saved_errno = errno; + + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (saved_errno), + "fcntl: %s", g_strerror (saved_errno)); + close (new_fd); + + return -1; + } + + return new_fd; +} + +/** + * g_unix_fd_list_new: + * + * Creates a new #GUnixFDList containing no file descriptors. + * + * Returns: a new #GUnixFDList + * + * Since: 2.24 + **/ +GUnixFDList * +g_unix_fd_list_new (void) +{ + return g_object_new (G_TYPE_UNIX_FD_LIST, NULL); +} + +/** + * g_unix_fd_list_new_from_array: + * @fds: the initial list of file descriptors + * @n_fds: the length of #fds, or -1 + * + * Creates a new #GUnixFDList containing the file descriptors given in + * @fds. The file descriptors become the property of the new list and + * may no longer be used by the caller. The array itself is owned by + * the caller. + * + * Each file descriptor in the array should be set to close-on-exec. + * + * If @n_fds is -1 then @fds must be terminated with -1. + * + * Returns: a new #GUnixFDList + * + * Since: 2.24 + **/ +GUnixFDList * +g_unix_fd_list_new_from_array (const gint *fds, + gint n_fds) +{ + GUnixFDList *list; + + g_return_val_if_fail (fds != NULL || n_fds == 0, NULL); + + if (n_fds == -1) + for (n_fds = 0; fds[n_fds] != -1; n_fds++); + + list = g_object_new (G_TYPE_UNIX_FD_LIST, NULL); + list->priv->fds = g_new (gint, n_fds + 1); + list->priv->nfd = n_fds; + + memcpy (list->priv->fds, fds, sizeof (gint) * n_fds); + list->priv->fds[n_fds] = -1; + + return list; +} + +/** + * g_unix_fd_list_steal_fds: + * @list: a #GUnixFDList + * @length: pointer to the length of the returned array, or %NULL + * + * Returns the array of file descriptors that is contained in this + * object. + * + * After this call, the descriptors are no longer contained in + * @list. 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. The file descriptors in the array are set to + * close-on-exec. + * + * 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 @list, an empty array is returned. + * + * Returns: an array of file descriptors + * + * Since: 2.24 + */ +gint * +g_unix_fd_list_steal_fds (GUnixFDList *list, + gint *length) +{ + gint *result; + + g_return_val_if_fail (G_IS_UNIX_FD_LIST (list), NULL); + + /* will be true for fresh object or if we were just called */ + if (list->priv->fds == NULL) + { + list->priv->fds = g_new (gint, 1); + list->priv->fds[0] = -1; + list->priv->nfd = 0; + } + + if (length) + *length = list->priv->nfd; + result = list->priv->fds; + + list->priv->fds = NULL; + list->priv->nfd = 0; + + return result; +} + +/** + * g_unix_fd_list_peek_fds: + * @list: a #GUnixFDList + * @length: pointer to the length of the returned array, or %NULL + * + * Returns the array of file descriptors that is contained in this + * object. + * + * After this call, the descriptors remain the property of @list. The + * caller must not close them and must not free the array. The array is + * valid only until @list is changed in any way. + * + * 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 @list, an empty array is returned. + * + * Returns: an array of file descriptors + * + * Since: 2.24 + */ +const gint * +g_unix_fd_list_peek_fds (GUnixFDList *list, + gint *length) +{ + g_return_val_if_fail (G_IS_UNIX_FD_LIST (list), NULL); + + /* will be true for fresh object or if steal() was just called */ + if (list->priv->fds == NULL) + { + list->priv->fds = g_new (gint, 1); + list->priv->fds[0] = -1; + list->priv->nfd = 0; + } + + if (length) + *length = list->priv->nfd; + + return list->priv->fds; +} + +/** + * g_unix_fd_list_append: + * @list: a #GUnixFDList + * @fd: a valid open file descriptor + * @error: a #GError pointer + * + * Adds a file descriptor to @list. + * + * The file descriptor is duplicated using dup(). You keep your copy + * of the descriptor and the copy contained in @list will be closed + * when @list is finalized. + * + * A possible cause of failure is exceeding the per-process or + * system-wide file descriptor limit. + * + * The index of the file descriptor in the list is returned. If you use + * this index with g_unix_fd_list_get() then you will receive back a + * duplicated copy of the same file descriptor. + * + * Returns: the index of the appended fd in case of success, else -1 + * (and @error is set) + * + * Since: 2.24 + */ +gint +g_unix_fd_list_append (GUnixFDList *list, + gint fd, + GError **error) +{ + gint new_fd; + + g_return_val_if_fail (G_IS_UNIX_FD_LIST (list), -1); + g_return_val_if_fail (fd >= 0, -1); + g_return_val_if_fail (error == NULL || *error == NULL, -1); + + if ((new_fd = dup_close_on_exec_fd (fd, error)) < 0) + return -1; + + list->priv->fds = g_realloc (list->priv->fds, + sizeof (gint) * + (list->priv->nfd + 2)); + list->priv->fds[list->priv->nfd++] = new_fd; + list->priv->fds[list->priv->nfd] = -1; + + return list->priv->nfd - 1; +} + +/** + * g_unix_fd_list_get: + * @list: a #GUnixFDList + * @index_: the index into the list + * @error: a #GError pointer + * + * Gets a file descriptor out of @list. + * + * @index_ specifies the index of the file descriptor to get. It is a + * programmer error for @index_ to be out of range; see + * g_unix_fd_list_get_length(). + * + * The file descriptor is duplicated using dup() and set as + * close-on-exec before being returned. You must call close() on it + * when you are done. + * + * A possible cause of failure is exceeding the per-process or + * system-wide file descriptor limit. + * + * Returns: the file descriptor, or -1 in case of error + * + * Since: 2.24 + **/ +gint +g_unix_fd_list_get (GUnixFDList *list, + gint index_, + GError **error) +{ + g_return_val_if_fail (G_IS_UNIX_FD_LIST (list), -1); + g_return_val_if_fail (index_ < list->priv->nfd, -1); + g_return_val_if_fail (error == NULL || *error == NULL, -1); + + return dup_close_on_exec_fd (list->priv->fds[index_], error); +} + +/** + * g_unix_fd_list_get_length: + * @list: a #GUnixFDList + * + * Gets the length of @list (ie: the number of file descriptors + * contained within). + * + * Returns: the length of @list + * + * Since: 2.24 + **/ +gint +g_unix_fd_list_get_length (GUnixFDList *list) +{ + g_return_val_if_fail (G_IS_UNIX_FD_LIST (list), 0); + + return list->priv->nfd; +} + +#define __G_UNIX_FD_LIST_C__ +#include "gioaliasdef.c" diff --git a/gio/gunixfdlist.h b/gio/gunixfdlist.h new file mode 100644 index 000000000..638b685e7 --- /dev/null +++ b/gio/gunixfdlist.h @@ -0,0 +1,89 @@ +/* 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_LIST_H__ +#define __G_UNIX_FD_LIST_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_UNIX_FD_LIST (g_unix_fd_list_get_type ()) +#define G_UNIX_FD_LIST(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_UNIX_FD_LIST, GUnixFDList)) +#define G_UNIX_FD_LIST_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \ + G_TYPE_UNIX_FD_LIST, GUnixFDListClass)) +#define G_IS_UNIX_FD_LIST(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_UNIX_FD_LIST)) +#define G_IS_UNIX_FD_LIST_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \ + G_TYPE_UNIX_FD_LIST)) +#define G_UNIX_FD_LIST_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \ + G_TYPE_UNIX_FD_LIST, GUnixFDListClass)) + +typedef struct _GUnixFDListPrivate GUnixFDListPrivate; +typedef struct _GUnixFDListClass GUnixFDListClass; +typedef struct _GUnixFDList GUnixFDList; + +struct _GUnixFDListClass +{ + 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); +}; + +struct _GUnixFDList +{ + GObject parent_instance; + GUnixFDListPrivate *priv; +}; + +GType g_unix_fd_list_get_type (void) G_GNUC_CONST; +GUnixFDList * g_unix_fd_list_new (void); +GUnixFDList * g_unix_fd_list_new_from_array (const gint *fds, + gint n_fds); + +gint g_unix_fd_list_append (GUnixFDList *list, + gint fd, + GError **error); + +gint g_unix_fd_list_get_length (GUnixFDList *list); + +gint g_unix_fd_list_get (GUnixFDList *list, + gint index_, + GError **error); + +const gint * g_unix_fd_list_peek_fds (GUnixFDList *list, + gint *length); + +gint * g_unix_fd_list_steal_fds (GUnixFDList *list, + gint *length); + +G_END_DECLS + +#endif /* __G_UNIX_FD_LIST_H__ */ diff --git a/gio/gunixfdmessage.c b/gio/gunixfdmessage.c index 03c282541..9ca497ebd 100644 --- a/gio/gunixfdmessage.c +++ b/gio/gunixfdmessage.c @@ -15,19 +15,19 @@ /** * SECTION: gunixfdmessage * @title: GUnixFDMessage - * @short_description: A GSocketControlMessage containing a list of - * file descriptors - * @see_also: #GUnixConnection + * @short_description: A GSocketControlMessage containing a #GUnixFDList + * @see_also: #GUnixConnection, #GUnixFDList, #GSocketControlMessage * - * This #GSocketControlMessage contains a list of file descriptors. - * It may be sent using g_socket_send_message() and received using + * This #GSocketControlMessage contains a #GUnixFDList. 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). + * %G_SOCKET_ADDRESS_UNIX family). The file descriptors are copied + * between processes by the kernel. * * 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" @@ -35,6 +35,7 @@ #include #include #include +#include #include #include "gunixfdmessage.h" @@ -48,8 +49,7 @@ G_DEFINE_TYPE (GUnixFDMessage, g_unix_fd_message, struct _GUnixFDMessagePrivate { - gint *fds; - gint nfd; + GUnixFDList *list; }; static gsize @@ -57,7 +57,7 @@ g_unix_fd_message_get_size (GSocketControlMessage *message) { GUnixFDMessage *fd_message = G_UNIX_FD_MESSAGE (message); - return fd_message->priv->nfd * sizeof (gint); + return g_unix_fd_list_get_length (fd_message->priv->list) * sizeof (gint); } static int @@ -78,7 +78,10 @@ g_unix_fd_message_deserialize (int level, gsize size, gpointer data) { - GUnixFDMessage *message; + GSocketControlMessage *message; + GUnixFDList *list; + gint n, s, i; + gint *fds; if (level != SOL_SOCKET || level != SCM_RIGHTS) @@ -90,13 +93,28 @@ g_unix_fd_message_deserialize (int level, 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; + fds = data; + n = size / sizeof (gint); - return G_SOCKET_CONTROL_MESSAGE (message); + for (i = 0; i < n; i++) + { + do + s = fcntl (fds[i], F_SETFD, FD_CLOEXEC); + while (s < 0 && errno == EINTR); + + if (s < 0) + { + g_warning ("Error setting close-on-exec flag on incoming fd: %s", + g_strerror (errno)); + return NULL; + } + } + + list = g_unix_fd_list_new_from_array (fds, n); + message = g_unix_fd_message_new_with_fd_list (list); + g_object_unref (list); + + return message; } static void @@ -104,9 +122,57 @@ 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); + const gint *fds; + gint n_fds; + + fds = g_unix_fd_list_peek_fds (fd_message->priv->list, &n_fds); + memcpy (data, fds, sizeof (gint) * n_fds); } + +static void +g_unix_fd_message_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + GUnixFDMessage *message = G_UNIX_FD_MESSAGE (object); + + g_assert (message->priv->list == NULL); + g_assert_cmpint (prop_id, ==, 1); + + message->priv->list = g_value_dup_object (value); + + if (message->priv->list == NULL) + message->priv->list = g_unix_fd_list_new (); +} + +/** + * g_unix_fd_message_get_fd_list: + * @message: a #GUnixFDMessage + * + * Gets the #GUnixFDList contained in @message. This function does not + * return a reference to the caller, but the returned list is valid for + * the lifetime of @message. + * + * Returns: the #GUnixFDList from @message + * + * Since: 2.24 + **/ +GUnixFDList * +g_unix_fd_message_get_fd_list (GUnixFDMessage *message) +{ + return message->priv->list; +} + +static void +g_unix_fd_message_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + GUnixFDMessage *message = G_UNIX_FD_MESSAGE (object); + + g_assert_cmpint (prop_id, ==, 1); + + g_value_set_object (value, g_unix_fd_message_get_fd_list (message)); +} + static void g_unix_fd_message_init (GUnixFDMessage *message) { @@ -119,11 +185,8 @@ 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_unref (message->priv->list); G_OBJECT_CLASS (g_unix_fd_message_parent_class) ->finalize (object); @@ -142,23 +205,50 @@ g_unix_fd_message_class_init (GUnixFDMessageClass *class) scm_class->serialize = g_unix_fd_message_serialize; scm_class->deserialize = g_unix_fd_message_deserialize; object_class->finalize = g_unix_fd_message_finalize; + object_class->set_property = g_unix_fd_message_set_property; + object_class->get_property = g_unix_fd_message_get_property; + + g_object_class_install_property (object_class, 1, + g_param_spec_object ("fd-list", "file descriptor list", + "The GUnixFDList object to send with the message", + G_TYPE_UNIX_FD_LIST, G_PARAM_STATIC_STRINGS | + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } /** * g_unix_fd_message_new: * - * Creates a new #GUnixFDMessage containing no file descriptors. + * Creates a new #GUnixFDMessage containing an empty file descriptor + * list. * * Returns: a new #GUnixFDMessage * * Since: 2.22 - */ + **/ GSocketControlMessage * g_unix_fd_message_new (void) { return g_object_new (G_TYPE_UNIX_FD_MESSAGE, NULL); } +/** + * g_unix_fd_message_new_with_fd_list: + * @fd_list: a #GUnixFDList + * + * Creates a new #GUnixFDMessage containing @list. + * + * Returns: a new #GUnixFDMessage + * + * Since: 2.24 + **/ +GSocketControlMessage * +g_unix_fd_message_new_with_fd_list (GUnixFDList *fd_list) +{ + return g_object_new (G_TYPE_UNIX_FD_MESSAGE, + "fd-list", fd_list, + NULL); +} + /** * g_unix_fd_message_steal_fds: * @message: a #GUnixFDMessage @@ -185,31 +275,14 @@ g_unix_fd_message_new (void) * Returns: an array of file descriptors * * Since: 2.22 - */ + **/ gint * g_unix_fd_message_steal_fds (GUnixFDMessage *message, gint *length) { - gint *result; + g_return_val_if_fail (G_UNIX_FD_MESSAGE (message), FALSE); - 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; + return g_unix_fd_list_steal_fds (message->priv->list, length); } /** @@ -230,39 +303,15 @@ g_unix_fd_message_steal_fds (GUnixFDMessage *message, * Returns: %TRUE in case of success, else %FALSE (and @error is set) * * Since: 2.22 - */ + **/ gboolean g_unix_fd_message_append_fd (GUnixFDMessage *message, gint fd, GError **error) { - gint new_fd; + g_return_val_if_fail (G_UNIX_FD_MESSAGE (message), FALSE); - 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; + return g_unix_fd_list_append (message->priv->list, fd, error) > 0; } #define __G_UNIX_FD_MESSAGE_C__ diff --git a/gio/gunixfdmessage.h b/gio/gunixfdmessage.h index 76fe806dc..44b47c119 100644 --- a/gio/gunixfdmessage.h +++ b/gio/gunixfdmessage.h @@ -23,7 +23,8 @@ #ifndef __G_UNIX_FD_MESSAGE_H__ #define __G_UNIX_FD_MESSAGE_H__ -#include +#include +#include G_BEGIN_DECLS @@ -61,7 +62,11 @@ struct _GUnixFDMessage }; GType g_unix_fd_message_get_type (void) G_GNUC_CONST; +GSocketControlMessage * g_unix_fd_message_new_with_fd_list (GUnixFDList *fd_list); GSocketControlMessage * g_unix_fd_message_new (void); + +GUnixFDList * g_unix_fd_message_get_fd_list (GUnixFDMessage *message); + gint * g_unix_fd_message_steal_fds (GUnixFDMessage *message, gint *length); gboolean g_unix_fd_message_append_fd (GUnixFDMessage *message, diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am index 62b4b0dc9..0fa8710b3 100644 --- a/gio/tests/Makefile.am +++ b/gio/tests/Makefile.am @@ -38,7 +38,7 @@ TEST_PROGS += \ SAMPLE_PROGS = resolver socket-server socket-client echo-server httpd send-data if OS_UNIX -TEST_PROGS += live-g-file unix-streams desktop-app-info +TEST_PROGS += live-g-file unix-streams desktop-app-info unix-fd endif memory_input_stream_SOURCES = memory-input-stream.c @@ -84,6 +84,9 @@ unix_streams_SOURCES = unix-streams.c unix_streams_LDADD = $(progs_ldadd) \ $(top_builddir)/gthread/libgthread-2.0.la +unix_fd_SOURCES = unix-fd.c +unix_fd_LDADD = $(progs_ldadd) + simple_async_result_SOURCES = simple-async-result.c simple_async_result_LDADD = $(progs_ldadd) diff --git a/gio/tests/unix-fd.c b/gio/tests/unix-fd.c new file mode 100644 index 000000000..a84e03f5c --- /dev/null +++ b/gio/tests/unix-fd.c @@ -0,0 +1,197 @@ +#include +#include +#include +#include +#include + +/* ensures that no FDs are left open at the end */ +static void +check_fd_list (const gint *fd_list) +{ + gint i; + + for (i = 0; i < 40; i++) + { + int my_fd; + + my_fd = dup (0); + g_assert_cmpint (fd_list[i], ==, my_fd); + } + + for (i = 0; i < 40; i++) + close (fd_list[i]); +} + +static void +create_fd_list (gint *fd_list) +{ + gint i; + + for (i = 0; i < 40; i++) + { + fd_list[i] = dup (0); + g_assert_cmpint (fd_list[i], >, 0); + } + + for (i = 0; i < 40; i++) + close (fd_list[i]); +} + +static void +test_fds (void) +{ + GUnixFDMessage *message; + GUnixFDMessage **mv; + GUnixFDList *list, *l2; + GSocket *sockets[2]; + const gint *peek; + char buffer[1024]; + gint fd_list[40]; + GOutputVector ov; + GInputVector iv; + gint *stolen; + gint sv[3]; + gint flags; + gint nm; + gint s; + + create_fd_list (fd_list); + + s = socketpair (PF_UNIX, SOCK_STREAM, 0, sv); + g_assert_cmpint (s, ==, 0); + sv[2] = -1; + + list = g_unix_fd_list_new_from_array (sv, -1); + message = G_UNIX_FD_MESSAGE (g_unix_fd_message_new_with_fd_list (list)); + g_object_unref (list); + + g_assert (g_unix_fd_message_get_fd_list (message) == list); + g_object_get (message, "fd-list", &l2, NULL); + g_assert (l2 == list); + g_assert_cmpint (g_unix_fd_list_get_length (list), ==, 2); + + peek = g_unix_fd_list_peek_fds (list, &s); + g_assert_cmpint (s, ==, 2); + stolen = g_unix_fd_message_steal_fds (message, &s); + g_assert_cmpint (s, ==, 2); + g_assert (stolen == peek); + + g_assert_cmpint (stolen[0], ==, sv[0]); + g_assert_cmpint (stolen[1], ==, sv[1]); + g_assert_cmpint (stolen[2], ==, sv[2]); + g_free (stolen); + + g_unix_fd_message_append_fd (message, sv[0], NULL); + s = close (sv[0]); + g_assert_cmpint (s, ==, 0); + g_unix_fd_message_append_fd (message, sv[1], NULL); + s = close (sv[1]); + g_assert_cmpint (s, ==, 0); + + s = close (g_unix_fd_list_get (list, 0, NULL)); + g_assert_cmpint (s, ==, 0); + s = close (g_unix_fd_list_get (list, 1, NULL)); + g_assert_cmpint (s, ==, 0); + s = close (g_unix_fd_list_get (list, 0, NULL)); + g_assert_cmpint (s, ==, 0); + s = close (g_unix_fd_list_get (list, 1, NULL)); + g_assert_cmpint (s, ==, 0); + s = close (g_unix_fd_list_get (list, 0, NULL)); + g_assert_cmpint (s, ==, 0); + s = close (g_unix_fd_list_get (list, 1, NULL)); + g_assert_cmpint (s, ==, 0); + + g_object_unref (message); + g_object_unref (list); + + + + message = G_UNIX_FD_MESSAGE (g_unix_fd_message_new ()); + list = g_unix_fd_message_get_fd_list (message); + s = pipe (sv); + g_assert_cmpint (s, ==, 0); + + s = g_unix_fd_list_append (list, sv[0], NULL); + g_assert_cmpint (s, >=, 0); + s = g_unix_fd_list_append (list, sv[1], NULL); + g_assert_cmpint (s, >=, 0); + + s = close (sv[0]); + g_assert_cmpint (s, ==, 0); + s = close (sv[1]); + g_assert_cmpint (s, ==, 0); + s = close (g_unix_fd_list_get (list, 0, NULL)); + g_assert_cmpint (s, ==, 0); + s = close (g_unix_fd_list_get (list, 1, NULL)); + g_assert_cmpint (s, ==, 0); + + s = socketpair (PF_UNIX, SOCK_STREAM, 0, sv); + g_assert_cmpint (s, ==, 0); + + sockets[0] = g_socket_new_from_fd (sv[0], NULL); + g_assert (G_IS_SOCKET (sockets[0])); + sockets[1] = g_socket_new_from_fd (sv[1], NULL); + g_assert (G_IS_SOCKET (sockets[1])); + + buffer[0] = 0xff; + ov.buffer = buffer; + ov.size = 1; + s = g_socket_send_message (sockets[0], NULL, &ov, 1, + (GSocketControlMessage **) &message, + 1, 0, NULL, NULL); + g_assert_cmpint (s, ==, 1); + g_object_unref (message); + + message = NULL; + + flags = 0; + iv.buffer = buffer; + iv.size = 1; + s = g_socket_receive_message (sockets[1], NULL, &iv, 1, + (GSocketControlMessage ***) &mv, + &nm, &flags, NULL, NULL); + g_assert_cmpint (s, ==, 1); + g_object_unref (sockets[0]); + g_object_unref (sockets[1]); + + g_assert_cmpint (nm, ==, 1); + message = mv[0]; + g_free (mv); + + g_assert (G_IS_UNIX_FD_MESSAGE (message)); + list = g_object_ref (g_unix_fd_message_get_fd_list (message)); + g_object_unref (message); + + peek = g_unix_fd_list_peek_fds (list, &s); + g_assert_cmpint (s, ==, 2); + sv[0] = g_unix_fd_list_get (list, 1, NULL); + + strcpy (buffer, "failure to say failure to say 'i love gnome-panel!'."); + s = write (sv[0], buffer, strlen (buffer) + 1); + g_assert_cmpint (s, ==, strlen (buffer) + 1); + + close (sv[0]); + memset (buffer, 0xff, sizeof buffer); + + s = read (peek[0], buffer, sizeof buffer); + g_assert_cmpint (s, ==, 53); + g_assert_cmpstr (buffer, ==, + "failure to say failure to say 'i love gnome-panel!'."); + + g_object_unref (list); + + check_fd_list (fd_list); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + + g_type_init (); + + g_test_add_func ("/unix-streams/file-descriptors", test_fds); + + return g_test_run(); + +}