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