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