(root)/
glib-2.79.0/
gio/
gunixfdlist.c
       1  /* GIO - GLib Input, Output and Streaming Library
       2   *
       3   * Copyright © 2009 Codethink Limited
       4   *
       5   * SPDX-License-Identifier: LGPL-2.1-or-later
       6   *
       7   * This library is free software; you can redistribute it and/or
       8   * modify it under the terms of the GNU Lesser General Public
       9   * License as published by the Free Software Foundation; either
      10   * version 2.1 of the License, or (at your option) any later version.
      11   *
      12   * See the included COPYING file for more information.
      13   *
      14   * Authors: Ryan Lortie <desrt@desrt.ca>
      15   */
      16  
      17  /**
      18   * GUnixFDList:
      19   *
      20   * A `GUnixFDList` contains a list of file descriptors.  It owns the file
      21   * descriptors that it contains, closing them when finalized.
      22   *
      23   * It may be wrapped in a [class@Gio.UnixFDMessage] and sent over a
      24   * [class@Gio.Socket] in the `G_SOCKET_FAMILY_UNIX` family by using
      25   * [method@Gio.Socket.send_message] and received using
      26   * [method@Gio.Socket.receive_message].
      27   *
      28   * Before 2.74, `<gio/gunixfdlist.h>` belonged to the UNIX-specific GIO
      29   * interfaces, thus you had to use the `gio-unix-2.0.pc` pkg-config file when
      30   * using it.
      31   *
      32   * Since 2.74, the API is available for Windows.
      33   */
      34  
      35  #include "config.h"
      36  
      37  #include <fcntl.h>
      38  #include <string.h>
      39  #include <errno.h>
      40  
      41  #include "gunixfdlist.h"
      42  #include "gnetworking.h"
      43  #include "gioerror.h"
      44  #include "glib/glib-private.h"
      45  #include "glib/gstdio.h"
      46  
      47  #ifdef G_OS_WIN32
      48  #include <io.h>
      49  #endif
      50  
      51  struct _GUnixFDListPrivate
      52  {
      53    gint *fds;
      54    gint nfd;
      55  };
      56  
      57  G_DEFINE_TYPE_WITH_PRIVATE (GUnixFDList, g_unix_fd_list, G_TYPE_OBJECT)
      58  
      59  static void
      60  g_unix_fd_list_init (GUnixFDList *list)
      61  {
      62    list->priv = g_unix_fd_list_get_instance_private (list);
      63  }
      64  
      65  static void
      66  g_unix_fd_list_finalize (GObject *object)
      67  {
      68    GUnixFDList *list = G_UNIX_FD_LIST (object);
      69    gint i;
      70  
      71    for (i = 0; i < list->priv->nfd; i++)
      72      g_close (list->priv->fds[i], NULL);
      73    g_free (list->priv->fds);
      74  
      75    G_OBJECT_CLASS (g_unix_fd_list_parent_class)
      76      ->finalize (object);
      77  }
      78  
      79  static void
      80  g_unix_fd_list_class_init (GUnixFDListClass *class)
      81  {
      82    GObjectClass *object_class = G_OBJECT_CLASS (class);
      83  
      84    object_class->finalize = g_unix_fd_list_finalize;
      85  }
      86  
      87  static int
      88  dup_close_on_exec_fd (gint     fd,
      89                        GError **error)
      90  {
      91    gint new_fd;
      92  #ifndef G_OS_WIN32
      93    gint s;
      94  #endif
      95  
      96  #ifdef F_DUPFD_CLOEXEC
      97    do
      98      new_fd = fcntl (fd, F_DUPFD_CLOEXEC, 0l);
      99    while (new_fd < 0 && (errno == EINTR));
     100  
     101    if (new_fd >= 0)
     102      return new_fd;
     103  
     104    /* if that didn't work (new libc/old kernel?), try it the other way. */
     105  #endif
     106  
     107    do
     108      new_fd = dup (fd);
     109    while (new_fd < 0 && (errno == EINTR));
     110  
     111    if (new_fd < 0)
     112      {
     113        int saved_errno = errno;
     114  
     115        g_set_error (error, G_IO_ERROR,
     116                     g_io_error_from_errno (saved_errno),
     117                     "dup: %s", g_strerror (saved_errno));
     118  
     119        return -1;
     120      }
     121  
     122  #ifdef G_OS_WIN32
     123    new_fd = GLIB_PRIVATE_CALL (g_win32_reopen_noninherited) (new_fd, 0, error);
     124  #else
     125    do
     126      {
     127        s = fcntl (new_fd, F_GETFD);
     128  
     129        if (s >= 0)
     130          s = fcntl (new_fd, F_SETFD, (long) (s | FD_CLOEXEC));
     131      }
     132    while (s < 0 && (errno == EINTR));
     133  
     134    if (s < 0)
     135      {
     136        int saved_errno = errno;
     137  
     138        g_set_error (error, G_IO_ERROR,
     139                     g_io_error_from_errno (saved_errno),
     140                     "fcntl: %s", g_strerror (saved_errno));
     141        g_close (new_fd, NULL);
     142  
     143        return -1;
     144      }
     145  #endif
     146  
     147    return new_fd;
     148  }
     149  
     150  /**
     151   * g_unix_fd_list_new:
     152   *
     153   * Creates a new #GUnixFDList containing no file descriptors.
     154   *
     155   * Returns: a new #GUnixFDList
     156   *
     157   * Since: 2.24
     158   **/
     159  GUnixFDList *
     160  g_unix_fd_list_new (void)
     161  {
     162    return g_object_new (G_TYPE_UNIX_FD_LIST, NULL);
     163  }
     164  
     165  /**
     166   * g_unix_fd_list_new_from_array:
     167   * @fds: (array length=n_fds): the initial list of file descriptors
     168   * @n_fds: the length of #fds, or -1
     169   *
     170   * Creates a new #GUnixFDList containing the file descriptors given in
     171   * @fds.  The file descriptors become the property of the new list and
     172   * may no longer be used by the caller.  The array itself is owned by
     173   * the caller.
     174   *
     175   * Each file descriptor in the array should be set to close-on-exec.
     176   *
     177   * If @n_fds is -1 then @fds must be terminated with -1.
     178   *
     179   * Returns: a new #GUnixFDList
     180   *
     181   * Since: 2.24
     182   **/
     183  GUnixFDList *
     184  g_unix_fd_list_new_from_array (const gint *fds,
     185                                 gint        n_fds)
     186  {
     187    GUnixFDList *list;
     188  
     189    g_return_val_if_fail (fds != NULL || n_fds == 0, NULL);
     190  
     191    if (n_fds == -1)
     192      for (n_fds = 0; fds[n_fds] != -1; n_fds++);
     193  
     194    list = g_object_new (G_TYPE_UNIX_FD_LIST, NULL);
     195    list->priv->fds = g_new (gint, n_fds + 1);
     196    list->priv->nfd = n_fds;
     197  
     198    if (n_fds > 0)
     199      memcpy (list->priv->fds, fds, sizeof (gint) * n_fds);
     200    list->priv->fds[n_fds] = -1;
     201  
     202    return list;
     203  }
     204  
     205  /**
     206   * g_unix_fd_list_steal_fds:
     207   * @list: a #GUnixFDList
     208   * @length: (out) (optional): pointer to the length of the returned
     209   *     array, or %NULL
     210   *
     211   * Returns the array of file descriptors that is contained in this
     212   * object.
     213   *
     214   * After this call, the descriptors are no longer contained in
     215   * @list. Further calls will return an empty list (unless more
     216   * descriptors have been added).
     217   *
     218   * The return result of this function must be freed with g_free().
     219   * The caller is also responsible for closing all of the file
     220   * descriptors.  The file descriptors in the array are set to
     221   * close-on-exec.
     222   *
     223   * If @length is non-%NULL then it is set to the number of file
     224   * descriptors in the returned array. The returned array is also
     225   * terminated with -1.
     226   *
     227   * This function never returns %NULL. In case there are no file
     228   * descriptors contained in @list, an empty array is returned.
     229   *
     230   * Returns: (array length=length) (transfer full): an array of file
     231   *     descriptors
     232   *
     233   * Since: 2.24
     234   */
     235  gint *
     236  g_unix_fd_list_steal_fds (GUnixFDList *list,
     237                            gint        *length)
     238  {
     239    gint *result;
     240  
     241    g_return_val_if_fail (G_IS_UNIX_FD_LIST (list), NULL);
     242  
     243    /* will be true for fresh object or if we were just called */
     244    if (list->priv->fds == NULL)
     245      {
     246        list->priv->fds = g_new (gint, 1);
     247        list->priv->fds[0] = -1;
     248        list->priv->nfd = 0;
     249      }
     250  
     251    if (length)
     252      *length = list->priv->nfd;
     253    result = list->priv->fds;
     254  
     255    list->priv->fds = NULL;
     256    list->priv->nfd = 0;
     257  
     258    return result;
     259  }
     260  
     261  /**
     262   * g_unix_fd_list_peek_fds:
     263   * @list: a #GUnixFDList
     264   * @length: (out) (optional): pointer to the length of the returned
     265   *     array, or %NULL
     266   *
     267   * Returns the array of file descriptors that is contained in this
     268   * object.
     269   *
     270   * After this call, the descriptors remain the property of @list.  The
     271   * caller must not close them and must not free the array.  The array is
     272   * valid only until @list is changed in any way.
     273   *
     274   * If @length is non-%NULL then it is set to the number of file
     275   * descriptors in the returned array. The returned array is also
     276   * terminated with -1.
     277   *
     278   * This function never returns %NULL. In case there are no file
     279   * descriptors contained in @list, an empty array is returned.
     280   *
     281   * Returns: (array length=length) (transfer none): an array of file
     282   *     descriptors
     283   *
     284   * Since: 2.24
     285   */
     286  const gint *
     287  g_unix_fd_list_peek_fds (GUnixFDList *list,
     288                           gint        *length)
     289  {
     290    g_return_val_if_fail (G_IS_UNIX_FD_LIST (list), NULL);
     291  
     292    /* will be true for fresh object or if steal() was just called */
     293    if (list->priv->fds == NULL)
     294      {
     295        list->priv->fds = g_new (gint, 1);
     296        list->priv->fds[0] = -1;
     297        list->priv->nfd = 0;
     298      }
     299  
     300    if (length)
     301      *length = list->priv->nfd;
     302  
     303    return list->priv->fds;
     304  }
     305  
     306  /**
     307   * g_unix_fd_list_append:
     308   * @list: a #GUnixFDList
     309   * @fd: a valid open file descriptor
     310   * @error: a #GError pointer
     311   *
     312   * Adds a file descriptor to @list.
     313   *
     314   * The file descriptor is duplicated using dup(). You keep your copy
     315   * of the descriptor and the copy contained in @list will be closed
     316   * when @list is finalized.
     317   *
     318   * A possible cause of failure is exceeding the per-process or
     319   * system-wide file descriptor limit.
     320   *
     321   * The index of the file descriptor in the list is returned.  If you use
     322   * this index with g_unix_fd_list_get() then you will receive back a
     323   * duplicated copy of the same file descriptor.
     324   *
     325   * Returns: the index of the appended fd in case of success, else -1
     326   *          (and @error is set)
     327   *
     328   * Since: 2.24
     329   */
     330  gint
     331  g_unix_fd_list_append (GUnixFDList  *list,
     332                         gint          fd,
     333                         GError      **error)
     334  {
     335    gint new_fd;
     336  
     337    g_return_val_if_fail (G_IS_UNIX_FD_LIST (list), -1);
     338    g_return_val_if_fail (fd >= 0, -1);
     339    g_return_val_if_fail (error == NULL || *error == NULL, -1);
     340  
     341    if ((new_fd = dup_close_on_exec_fd (fd, error)) < 0)
     342      return -1;
     343  
     344    list->priv->fds = g_realloc (list->priv->fds,
     345                                    sizeof (gint) *
     346                                     (list->priv->nfd + 2));
     347    list->priv->fds[list->priv->nfd++] = new_fd;
     348    list->priv->fds[list->priv->nfd] = -1;
     349  
     350    return list->priv->nfd - 1;
     351  }
     352  
     353  /**
     354   * g_unix_fd_list_get:
     355   * @list: a #GUnixFDList
     356   * @index_: the index into the list
     357   * @error: a #GError pointer
     358   *
     359   * Gets a file descriptor out of @list.
     360   *
     361   * @index_ specifies the index of the file descriptor to get.  It is a
     362   * programmer error for @index_ to be out of range; see
     363   * g_unix_fd_list_get_length().
     364   *
     365   * The file descriptor is duplicated using dup() and set as
     366   * close-on-exec before being returned.  You must call close() on it
     367   * when you are done.
     368   *
     369   * A possible cause of failure is exceeding the per-process or
     370   * system-wide file descriptor limit.
     371   *
     372   * Returns: the file descriptor, or -1 in case of error
     373   *
     374   * Since: 2.24
     375   **/
     376  gint
     377  g_unix_fd_list_get (GUnixFDList  *list,
     378                      gint          index_,
     379                      GError      **error)
     380  {
     381    g_return_val_if_fail (G_IS_UNIX_FD_LIST (list), -1);
     382    g_return_val_if_fail (index_ < list->priv->nfd, -1);
     383    g_return_val_if_fail (error == NULL || *error == NULL, -1);
     384  
     385    return dup_close_on_exec_fd (list->priv->fds[index_], error);
     386  }
     387  
     388  /**
     389   * g_unix_fd_list_get_length:
     390   * @list: a #GUnixFDList
     391   *
     392   * Gets the length of @list (ie: the number of file descriptors
     393   * contained within).
     394   *
     395   * Returns: the length of @list
     396   *
     397   * Since: 2.24
     398   **/
     399  gint
     400  g_unix_fd_list_get_length (GUnixFDList *list)
     401  {
     402    g_return_val_if_fail (G_IS_UNIX_FD_LIST (list), 0);
     403  
     404    return list->priv->nfd;
     405  }