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