[glib] Bug 601637 - add GUnixFDList



commit 3d7edc137e447456b7cd6b9df8b6257454c43a5d
Author: Ryan Lortie <desrt desrt ca>
Date:   Wed Nov 11 22:40:28 2009 -0500

    Bug 601637 - add GUnixFDList
    
    change GUnixFDMessage to contain a GUnixFDList.
    
    add test case for GUnixFDMessage and GUnixFDList.
    
    update docs.

 docs/reference/gio/gio-docs.xml     |    1 +
 docs/reference/gio/gio-sections.txt |   23 ++
 docs/reference/gio/gio.types        |    1 +
 gio/Makefile.am                     |    2 +
 gio/gunixfdlist.c                   |  391 +++++++++++++++++++++++++++++++++++
 gio/gunixfdlist.h                   |   89 ++++++++
 gio/gunixfdmessage.c                |  193 +++++++++++-------
 gio/gunixfdmessage.h                |    7 +-
 gio/tests/Makefile.am               |    5 +-
 gio/tests/unix-fd.c                 |  197 ++++++++++++++++++
 10 files changed, 835 insertions(+), 74 deletions(-)
---
diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml
index c0345bc..c9ad4ac 100644
--- a/docs/reference/gio/gio-docs.xml
+++ b/docs/reference/gio/gio-docs.xml
@@ -105,6 +105,7 @@
       <xi:include href="xml/ginetsocketaddress.xml"/>
       <xi:include href="xml/gunixsocketaddress.xml"/>
       <xi:include href="xml/gsocketcontrolmessage.xml"/>
+      <xi:include href="xml/gunixfdlist.xml"/>
       <xi:include href="xml/gunixfdmessage.xml"/>
     </chapter>
     <chapter id="resolver">
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index de989f8..7964c90 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -1450,6 +1450,27 @@ g_io_extension_ref_class
 </SECTION>
 
 <SECTION>
+<FILE>gunixfdlist</FILE>
+<TITLE>GUnixFDList</TITLE>
+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
+<SUBSECTION Standard>
+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
+</SECTION>
+
+<SECTION>
 <FILE>ginetaddress</FILE>
 <TITLE>GInetAddress</TITLE>
 GInetAddress
@@ -1906,7 +1927,9 @@ g_threaded_socket_service_get_type
 <FILE>gunixfdmessage</FILE>
 <TITLE>GUnixFDMessage</TITLE>
 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
 <SUBSECTION Standard>
diff --git a/docs/reference/gio/gio.types b/docs/reference/gio/gio.types
index 5980100..29fbd6e 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 f5fd045..9dc2e02 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 0000000..df715b2
--- /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 <desrt desrt ca>
+ */
+
+/**
+ * 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 <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+#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 0000000..638b685
--- /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 <desrt desrt ca>
+ */
+
+#ifndef __G_UNIX_FD_LIST_H__
+#define __G_UNIX_FD_LIST_H__
+
+#include <glib-object.h>
+
+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 03c2825..9ca497e 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 <sys/socket.h>
 #include <unistd.h>
 #include <string.h>
+#include <fcntl.h>
 #include <errno.h>
 
 #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);
+
+  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 G_SOCKET_CONTROL_MESSAGE (message);
+  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,17 +205,26 @@ 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)
 {
@@ -160,6 +232,24 @@ g_unix_fd_message_new (void)
 }
 
 /**
+ * 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
  * @length: pointer to the length of the returned array, or %NULL
@@ -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_IS_UNIX_FD_MESSAGE (message), NULL);
+  g_return_val_if_fail (G_UNIX_FD_MESSAGE (message), FALSE);
 
-  /* 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_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;
+  g_return_val_if_fail (G_UNIX_FD_MESSAGE (message), FALSE);
 
-  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 76fe806..44b47c1 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 <gio/gsocketcontrolmessage.h>
+#include <gio/gunixfdlist.h>
+#include <gio/gio.h>
 
 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 62b4b0d..0fa8710 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 0000000..a84e03f
--- /dev/null
+++ b/gio/tests/unix-fd.c
@@ -0,0 +1,197 @@
+#include <gio/gio.h>
+#include <gio/gunixfdmessage.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <unistd.h>
+
+/* 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();
+
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]