/src/glib/gio/gunixfdmessage.c
Line | Count | Source (jump to first uncovered line) |
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 | | * SECTION:gunixfdmessage |
19 | | * @title: GUnixFDMessage |
20 | | * @short_description: A GSocketControlMessage containing a GUnixFDList |
21 | | * @include: gio/gunixfdmessage.h |
22 | | * @see_also: #GUnixConnection, #GUnixFDList, #GSocketControlMessage |
23 | | * |
24 | | * This #GSocketControlMessage contains a #GUnixFDList. |
25 | | * It may be sent using g_socket_send_message() and received using |
26 | | * g_socket_receive_message() over UNIX sockets (ie: sockets in the |
27 | | * %G_SOCKET_FAMILY_UNIX family). The file descriptors are copied |
28 | | * between processes by the kernel. |
29 | | * |
30 | | * For an easier way to send and receive file descriptors over |
31 | | * stream-oriented UNIX sockets, see g_unix_connection_send_fd() and |
32 | | * g_unix_connection_receive_fd(). |
33 | | * |
34 | | * Note that `<gio/gunixfdmessage.h>` belongs to the UNIX-specific GIO |
35 | | * interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config |
36 | | * file when using it. |
37 | | */ |
38 | | |
39 | | /** |
40 | | * GUnixFDMessage: |
41 | | * |
42 | | * #GUnixFDMessage is an opaque data structure and can only be accessed |
43 | | * using the following functions. |
44 | | **/ |
45 | | |
46 | | #include "config.h" |
47 | | |
48 | | #include <unistd.h> |
49 | | #include <string.h> |
50 | | #include <fcntl.h> |
51 | | #include <errno.h> |
52 | | |
53 | | #include "gunixfdmessage.h" |
54 | | #include "gunixfdlist.h" |
55 | | #include "gnetworking.h" |
56 | | #include "gioerror.h" |
57 | | |
58 | | struct _GUnixFDMessagePrivate |
59 | | { |
60 | | GUnixFDList *list; |
61 | | }; |
62 | | |
63 | | G_DEFINE_TYPE_WITH_PRIVATE (GUnixFDMessage, g_unix_fd_message, G_TYPE_SOCKET_CONTROL_MESSAGE) |
64 | | |
65 | | static gsize |
66 | | g_unix_fd_message_get_size (GSocketControlMessage *message) |
67 | 0 | { |
68 | 0 | GUnixFDMessage *fd_message = G_UNIX_FD_MESSAGE (message); |
69 | |
|
70 | 0 | return g_unix_fd_list_get_length (fd_message->priv->list) * sizeof (gint); |
71 | 0 | } |
72 | | |
73 | | static int |
74 | | g_unix_fd_message_get_level (GSocketControlMessage *message) |
75 | 0 | { |
76 | 0 | return SOL_SOCKET; |
77 | 0 | } |
78 | | |
79 | | static int |
80 | | g_unix_fd_message_get_msg_type (GSocketControlMessage *message) |
81 | 0 | { |
82 | 0 | return SCM_RIGHTS; |
83 | 0 | } |
84 | | |
85 | | static GSocketControlMessage * |
86 | | g_unix_fd_message_deserialize (int level, |
87 | | int type, |
88 | | gsize size, |
89 | | gpointer data) |
90 | 0 | { |
91 | 0 | GSocketControlMessage *message; |
92 | 0 | GUnixFDList *list; |
93 | 0 | gint n, s, i; |
94 | 0 | gint *fds; |
95 | |
|
96 | 0 | if (level != SOL_SOCKET || |
97 | 0 | type != SCM_RIGHTS) |
98 | 0 | return NULL; |
99 | | |
100 | 0 | if (size % 4 > 0) |
101 | 0 | { |
102 | 0 | g_warning ("Kernel returned non-integral number of fds"); |
103 | 0 | return NULL; |
104 | 0 | } |
105 | | |
106 | 0 | fds = data; |
107 | 0 | n = size / sizeof (gint); |
108 | | |
109 | | /* Note we probably handled this in gsocket.c already if we're on |
110 | | * Linux and have MSG_CMSG_CLOEXEC, but this code remains as a fallback |
111 | | * in case the kernel is too old for MSG_CMSG_CLOEXEC. |
112 | | */ |
113 | 0 | for (i = 0; i < n; i++) |
114 | 0 | { |
115 | 0 | int errsv; |
116 | |
|
117 | 0 | do |
118 | 0 | { |
119 | 0 | s = fcntl (fds[i], F_SETFD, FD_CLOEXEC); |
120 | 0 | errsv = errno; |
121 | 0 | } |
122 | 0 | while (s < 0 && errsv == EINTR); |
123 | |
|
124 | 0 | if (s < 0) |
125 | 0 | { |
126 | 0 | g_warning ("Error setting close-on-exec flag on incoming fd: %s", |
127 | 0 | g_strerror (errsv)); |
128 | 0 | return NULL; |
129 | 0 | } |
130 | 0 | } |
131 | | |
132 | 0 | list = g_unix_fd_list_new_from_array (fds, n); |
133 | 0 | message = g_unix_fd_message_new_with_fd_list (list); |
134 | 0 | g_object_unref (list); |
135 | |
|
136 | 0 | return message; |
137 | 0 | } |
138 | | |
139 | | static void |
140 | | g_unix_fd_message_serialize (GSocketControlMessage *message, |
141 | | gpointer data) |
142 | 0 | { |
143 | 0 | GUnixFDMessage *fd_message = G_UNIX_FD_MESSAGE (message); |
144 | 0 | const gint *fds; |
145 | 0 | gint n_fds; |
146 | |
|
147 | 0 | fds = g_unix_fd_list_peek_fds (fd_message->priv->list, &n_fds); |
148 | 0 | memcpy (data, fds, sizeof (gint) * n_fds); |
149 | 0 | } |
150 | | |
151 | | static void |
152 | | g_unix_fd_message_set_property (GObject *object, guint prop_id, |
153 | | const GValue *value, GParamSpec *pspec) |
154 | 0 | { |
155 | 0 | GUnixFDMessage *message = G_UNIX_FD_MESSAGE (object); |
156 | |
|
157 | 0 | g_assert (message->priv->list == NULL); |
158 | 0 | g_assert_cmpint (prop_id, ==, 1); |
159 | |
|
160 | 0 | message->priv->list = g_value_dup_object (value); |
161 | |
|
162 | 0 | if (message->priv->list == NULL) |
163 | 0 | message->priv->list = g_unix_fd_list_new (); |
164 | 0 | } |
165 | | |
166 | | /** |
167 | | * g_unix_fd_message_get_fd_list: |
168 | | * @message: a #GUnixFDMessage |
169 | | * |
170 | | * Gets the #GUnixFDList contained in @message. This function does not |
171 | | * return a reference to the caller, but the returned list is valid for |
172 | | * the lifetime of @message. |
173 | | * |
174 | | * Returns: (transfer none): the #GUnixFDList from @message |
175 | | * |
176 | | * Since: 2.24 |
177 | | **/ |
178 | | GUnixFDList * |
179 | | g_unix_fd_message_get_fd_list (GUnixFDMessage *message) |
180 | 0 | { |
181 | 0 | return message->priv->list; |
182 | 0 | } |
183 | | |
184 | | static void |
185 | | g_unix_fd_message_get_property (GObject *object, guint prop_id, |
186 | | GValue *value, GParamSpec *pspec) |
187 | 0 | { |
188 | 0 | GUnixFDMessage *message = G_UNIX_FD_MESSAGE (object); |
189 | |
|
190 | 0 | g_assert_cmpint (prop_id, ==, 1); |
191 | |
|
192 | 0 | g_value_set_object (value, g_unix_fd_message_get_fd_list (message)); |
193 | 0 | } |
194 | | |
195 | | static void |
196 | | g_unix_fd_message_init (GUnixFDMessage *message) |
197 | 0 | { |
198 | 0 | message->priv = g_unix_fd_message_get_instance_private (message); |
199 | 0 | } |
200 | | |
201 | | static void |
202 | | g_unix_fd_message_finalize (GObject *object) |
203 | 0 | { |
204 | 0 | GUnixFDMessage *message = G_UNIX_FD_MESSAGE (object); |
205 | |
|
206 | 0 | g_object_unref (message->priv->list); |
207 | |
|
208 | 0 | G_OBJECT_CLASS (g_unix_fd_message_parent_class) |
209 | 0 | ->finalize (object); |
210 | 0 | } |
211 | | |
212 | | static void |
213 | | g_unix_fd_message_class_init (GUnixFDMessageClass *class) |
214 | 0 | { |
215 | 0 | GSocketControlMessageClass *scm_class = G_SOCKET_CONTROL_MESSAGE_CLASS (class); |
216 | 0 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
217 | |
|
218 | 0 | scm_class->get_size = g_unix_fd_message_get_size; |
219 | 0 | scm_class->get_level = g_unix_fd_message_get_level; |
220 | 0 | scm_class->get_type = g_unix_fd_message_get_msg_type; |
221 | 0 | scm_class->serialize = g_unix_fd_message_serialize; |
222 | 0 | scm_class->deserialize = g_unix_fd_message_deserialize; |
223 | 0 | object_class->finalize = g_unix_fd_message_finalize; |
224 | 0 | object_class->set_property = g_unix_fd_message_set_property; |
225 | 0 | object_class->get_property = g_unix_fd_message_get_property; |
226 | |
|
227 | 0 | g_object_class_install_property (object_class, 1, |
228 | 0 | g_param_spec_object ("fd-list", "file descriptor list", |
229 | 0 | "The GUnixFDList object to send with the message", |
230 | 0 | G_TYPE_UNIX_FD_LIST, G_PARAM_STATIC_STRINGS | |
231 | 0 | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); |
232 | 0 | } |
233 | | |
234 | | /** |
235 | | * g_unix_fd_message_new: |
236 | | * |
237 | | * Creates a new #GUnixFDMessage containing an empty file descriptor |
238 | | * list. |
239 | | * |
240 | | * Returns: a new #GUnixFDMessage |
241 | | * |
242 | | * Since: 2.22 |
243 | | **/ |
244 | | GSocketControlMessage * |
245 | | g_unix_fd_message_new (void) |
246 | 0 | { |
247 | 0 | return g_object_new (G_TYPE_UNIX_FD_MESSAGE, NULL); |
248 | 0 | } |
249 | | |
250 | | /** |
251 | | * g_unix_fd_message_new_with_fd_list: |
252 | | * @fd_list: a #GUnixFDList |
253 | | * |
254 | | * Creates a new #GUnixFDMessage containing @list. |
255 | | * |
256 | | * Returns: a new #GUnixFDMessage |
257 | | * |
258 | | * Since: 2.24 |
259 | | **/ |
260 | | GSocketControlMessage * |
261 | | g_unix_fd_message_new_with_fd_list (GUnixFDList *fd_list) |
262 | 0 | { |
263 | 0 | return g_object_new (G_TYPE_UNIX_FD_MESSAGE, |
264 | 0 | "fd-list", fd_list, |
265 | 0 | NULL); |
266 | 0 | } |
267 | | |
268 | | /** |
269 | | * g_unix_fd_message_steal_fds: |
270 | | * @message: a #GUnixFDMessage |
271 | | * @length: (out) (optional): pointer to the length of the returned |
272 | | * array, or %NULL |
273 | | * |
274 | | * Returns the array of file descriptors that is contained in this |
275 | | * object. |
276 | | * |
277 | | * After this call, the descriptors are no longer contained in |
278 | | * @message. Further calls will return an empty list (unless more |
279 | | * descriptors have been added). |
280 | | * |
281 | | * The return result of this function must be freed with g_free(). |
282 | | * The caller is also responsible for closing all of the file |
283 | | * descriptors. |
284 | | * |
285 | | * If @length is non-%NULL then it is set to the number of file |
286 | | * descriptors in the returned array. The returned array is also |
287 | | * terminated with -1. |
288 | | * |
289 | | * This function never returns %NULL. In case there are no file |
290 | | * descriptors contained in @message, an empty array is returned. |
291 | | * |
292 | | * Returns: (array length=length) (transfer full): an array of file |
293 | | * descriptors |
294 | | * |
295 | | * Since: 2.22 |
296 | | **/ |
297 | | gint * |
298 | | g_unix_fd_message_steal_fds (GUnixFDMessage *message, |
299 | | gint *length) |
300 | 0 | { |
301 | 0 | g_return_val_if_fail (G_UNIX_FD_MESSAGE (message), NULL); |
302 | | |
303 | 0 | return g_unix_fd_list_steal_fds (message->priv->list, length); |
304 | 0 | } |
305 | | |
306 | | /** |
307 | | * g_unix_fd_message_append_fd: |
308 | | * @message: a #GUnixFDMessage |
309 | | * @fd: a valid open file descriptor |
310 | | * @error: a #GError pointer |
311 | | * |
312 | | * Adds a file descriptor to @message. |
313 | | * |
314 | | * The file descriptor is duplicated using dup(). You keep your copy |
315 | | * of the descriptor and the copy contained in @message will be closed |
316 | | * when @message is finalized. |
317 | | * |
318 | | * A possible cause of failure is exceeding the per-process or |
319 | | * system-wide file descriptor limit. |
320 | | * |
321 | | * Returns: %TRUE in case of success, else %FALSE (and @error is set) |
322 | | * |
323 | | * Since: 2.22 |
324 | | **/ |
325 | | gboolean |
326 | | g_unix_fd_message_append_fd (GUnixFDMessage *message, |
327 | | gint fd, |
328 | | GError **error) |
329 | 0 | { |
330 | 0 | g_return_val_if_fail (G_UNIX_FD_MESSAGE (message), FALSE); |
331 | | |
332 | 0 | return g_unix_fd_list_append (message->priv->list, fd, error) >= 0; |
333 | 0 | } |