1 | /* GDBus - GLib D-Bus Library |
2 | * |
3 | * Copyright (C) 2008-2010 Red Hat, Inc. |
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 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General |
16 | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
17 | * |
18 | * Author: David Zeuthen <davidz@redhat.com> |
19 | */ |
20 | |
21 | #include "config.h" |
22 | |
23 | #include <stdlib.h> |
24 | #include <string.h> |
25 | #include <errno.h> |
26 | |
27 | #include "giotypes.h" |
28 | #include "gioerror.h" |
29 | #include "gdbusaddress.h" |
30 | #include "gdbusutils.h" |
31 | #include "gdbusconnection.h" |
32 | #include "gdbusserver.h" |
33 | #include "gioenumtypes.h" |
34 | #include "gdbusprivate.h" |
35 | #include "gdbusauthobserver.h" |
36 | #include "ginitable.h" |
37 | #include "gsocketservice.h" |
38 | #include "gthreadedsocketservice.h" |
39 | #include "gresolver.h" |
40 | #include "glib/gstdio.h" |
41 | #include "ginetaddress.h" |
42 | #include "ginetsocketaddress.h" |
43 | #include "ginputstream.h" |
44 | #include "giostream.h" |
45 | #include "gmarshal-internal.h" |
46 | |
47 | #ifdef G_OS_UNIX |
48 | #include <unistd.h> |
49 | #endif |
50 | #ifdef G_OS_WIN32 |
51 | #include <io.h> |
52 | #endif |
53 | |
54 | #ifdef G_OS_UNIX |
55 | #include "gunixsocketaddress.h" |
56 | #endif |
57 | |
58 | #include "glibintl.h" |
59 | |
60 | #define G_DBUS_SERVER_FLAGS_ALL \ |
61 | (G_DBUS_SERVER_FLAGS_RUN_IN_THREAD | \ |
62 | G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS | \ |
63 | G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER) |
64 | |
65 | /** |
66 | * SECTION:gdbusserver |
67 | * @short_description: Helper for accepting connections |
68 | * @include: gio/gio.h |
69 | * |
70 | * #GDBusServer is a helper for listening to and accepting D-Bus |
71 | * connections. This can be used to create a new D-Bus server, allowing two |
72 | * peers to use the D-Bus protocol for their own specialized communication. |
73 | * A server instance provided in this way will not perform message routing or |
74 | * implement the org.freedesktop.DBus interface. |
75 | * |
76 | * To just export an object on a well-known name on a message bus, such as the |
77 | * session or system bus, you should instead use g_bus_own_name(). |
78 | * |
79 | * An example of peer-to-peer communication with GDBus can be found |
80 | * in [gdbus-example-peer.c](https://git.gnome.org/browse/glib/tree/gio/tests/gdbus-example-peer.c). |
81 | * |
82 | * Note that a minimal #GDBusServer will accept connections from any |
83 | * peer. In many use-cases it will be necessary to add a #GDBusAuthObserver |
84 | * that only accepts connections that have successfully authenticated |
85 | * as the same user that is running the #GDBusServer. Since GLib 2.68 this can |
86 | * be achieved more simply by passing the |
87 | * %G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER flag to the server. |
88 | */ |
89 | |
90 | /** |
91 | * GDBusServer: |
92 | * |
93 | * The #GDBusServer structure contains only private data and |
94 | * should only be accessed using the provided API. |
95 | * |
96 | * Since: 2.26 |
97 | */ |
98 | struct _GDBusServer |
99 | { |
100 | /*< private >*/ |
101 | GObject parent_instance; |
102 | |
103 | GDBusServerFlags flags; |
104 | gchar *address; |
105 | gchar *guid; |
106 | |
107 | guchar *nonce; |
108 | gchar *nonce_file; |
109 | |
110 | gchar *client_address; |
111 | |
112 | gchar *unix_socket_path; |
113 | GSocketListener *listener; |
114 | gboolean is_using_listener; |
115 | gulong run_signal_handler_id; |
116 | |
117 | /* The result of g_main_context_ref_thread_default() when the object |
118 | * was created (the GObject _init() function) - this is used for delivery |
119 | * of the :new-connection GObject signal. |
120 | */ |
121 | GMainContext *main_context_at_construction; |
122 | |
123 | gboolean active; |
124 | |
125 | GDBusAuthObserver *authentication_observer; |
126 | }; |
127 | |
128 | typedef struct _GDBusServerClass GDBusServerClass; |
129 | |
130 | /** |
131 | * GDBusServerClass: |
132 | * @new_connection: Signal class handler for the #GDBusServer::new-connection signal. |
133 | * |
134 | * Class structure for #GDBusServer. |
135 | * |
136 | * Since: 2.26 |
137 | */ |
138 | struct _GDBusServerClass |
139 | { |
140 | /*< private >*/ |
141 | GObjectClass parent_class; |
142 | |
143 | /*< public >*/ |
144 | /* Signals */ |
145 | gboolean (*new_connection) (GDBusServer *server, |
146 | GDBusConnection *connection); |
147 | }; |
148 | |
149 | enum |
150 | { |
151 | PROP_0, |
152 | PROP_ADDRESS, |
153 | PROP_CLIENT_ADDRESS, |
154 | PROP_FLAGS, |
155 | PROP_GUID, |
156 | PROP_ACTIVE, |
157 | PROP_AUTHENTICATION_OBSERVER, |
158 | }; |
159 | |
160 | enum |
161 | { |
162 | NEW_CONNECTION_SIGNAL, |
163 | LAST_SIGNAL, |
164 | }; |
165 | |
166 | static guint _signals[LAST_SIGNAL] = {0}; |
167 | |
168 | static void initable_iface_init (GInitableIface *initable_iface); |
169 | |
170 | G_DEFINE_TYPE_WITH_CODE (GDBusServer, g_dbus_server, G_TYPE_OBJECT, |
171 | G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)) |
172 | |
173 | static void |
174 | g_dbus_server_dispose (GObject *object) |
175 | { |
176 | GDBusServer *server = G_DBUS_SERVER (object); |
177 | |
178 | if (server->active) |
179 | g_dbus_server_stop (server); |
180 | |
181 | G_OBJECT_CLASS (g_dbus_server_parent_class)->dispose (object); |
182 | } |
183 | |
184 | static void |
185 | g_dbus_server_finalize (GObject *object) |
186 | { |
187 | GDBusServer *server = G_DBUS_SERVER (object); |
188 | |
189 | g_assert (!server->active); |
190 | |
191 | if (server->authentication_observer != NULL) |
192 | g_object_unref (object: server->authentication_observer); |
193 | |
194 | if (server->run_signal_handler_id > 0) |
195 | g_signal_handler_disconnect (instance: server->listener, handler_id: server->run_signal_handler_id); |
196 | |
197 | if (server->listener != NULL) |
198 | g_object_unref (object: server->listener); |
199 | |
200 | g_free (mem: server->address); |
201 | g_free (mem: server->guid); |
202 | g_free (mem: server->client_address); |
203 | if (server->nonce != NULL) |
204 | { |
205 | memset (s: server->nonce, c: '\0', n: 16); |
206 | g_free (mem: server->nonce); |
207 | } |
208 | |
209 | g_free (mem: server->unix_socket_path); |
210 | g_free (mem: server->nonce_file); |
211 | |
212 | g_main_context_unref (context: server->main_context_at_construction); |
213 | |
214 | G_OBJECT_CLASS (g_dbus_server_parent_class)->finalize (object); |
215 | } |
216 | |
217 | static void |
218 | g_dbus_server_get_property (GObject *object, |
219 | guint prop_id, |
220 | GValue *value, |
221 | GParamSpec *pspec) |
222 | { |
223 | GDBusServer *server = G_DBUS_SERVER (object); |
224 | |
225 | switch (prop_id) |
226 | { |
227 | case PROP_FLAGS: |
228 | g_value_set_flags (value, v_flags: server->flags); |
229 | break; |
230 | |
231 | case PROP_GUID: |
232 | g_value_set_string (value, v_string: server->guid); |
233 | break; |
234 | |
235 | case PROP_ADDRESS: |
236 | g_value_set_string (value, v_string: server->address); |
237 | break; |
238 | |
239 | case PROP_CLIENT_ADDRESS: |
240 | g_value_set_string (value, v_string: server->client_address); |
241 | break; |
242 | |
243 | case PROP_ACTIVE: |
244 | g_value_set_boolean (value, v_boolean: server->active); |
245 | break; |
246 | |
247 | case PROP_AUTHENTICATION_OBSERVER: |
248 | g_value_set_object (value, v_object: server->authentication_observer); |
249 | break; |
250 | |
251 | default: |
252 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
253 | break; |
254 | } |
255 | } |
256 | |
257 | static void |
258 | g_dbus_server_set_property (GObject *object, |
259 | guint prop_id, |
260 | const GValue *value, |
261 | GParamSpec *pspec) |
262 | { |
263 | GDBusServer *server = G_DBUS_SERVER (object); |
264 | |
265 | switch (prop_id) |
266 | { |
267 | case PROP_FLAGS: |
268 | server->flags = g_value_get_flags (value); |
269 | break; |
270 | |
271 | case PROP_GUID: |
272 | server->guid = g_value_dup_string (value); |
273 | break; |
274 | |
275 | case PROP_ADDRESS: |
276 | server->address = g_value_dup_string (value); |
277 | break; |
278 | |
279 | case PROP_AUTHENTICATION_OBSERVER: |
280 | server->authentication_observer = g_value_dup_object (value); |
281 | break; |
282 | |
283 | default: |
284 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
285 | break; |
286 | } |
287 | } |
288 | |
289 | static void |
290 | g_dbus_server_class_init (GDBusServerClass *klass) |
291 | { |
292 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
293 | |
294 | gobject_class->dispose = g_dbus_server_dispose; |
295 | gobject_class->finalize = g_dbus_server_finalize; |
296 | gobject_class->set_property = g_dbus_server_set_property; |
297 | gobject_class->get_property = g_dbus_server_get_property; |
298 | |
299 | /** |
300 | * GDBusServer:flags: |
301 | * |
302 | * Flags from the #GDBusServerFlags enumeration. |
303 | * |
304 | * Since: 2.26 |
305 | */ |
306 | g_object_class_install_property (oclass: gobject_class, |
307 | property_id: PROP_FLAGS, |
308 | pspec: g_param_spec_flags (name: "flags" , |
309 | P_("Flags" ), |
310 | P_("Flags for the server" ), |
311 | flags_type: G_TYPE_DBUS_SERVER_FLAGS, |
312 | default_value: G_DBUS_SERVER_FLAGS_NONE, |
313 | flags: G_PARAM_READABLE | |
314 | G_PARAM_WRITABLE | |
315 | G_PARAM_CONSTRUCT_ONLY | |
316 | G_PARAM_STATIC_NAME | |
317 | G_PARAM_STATIC_BLURB | |
318 | G_PARAM_STATIC_NICK)); |
319 | |
320 | /** |
321 | * GDBusServer:guid: |
322 | * |
323 | * The guid of the server. |
324 | * |
325 | * Since: 2.26 |
326 | */ |
327 | g_object_class_install_property (oclass: gobject_class, |
328 | property_id: PROP_GUID, |
329 | pspec: g_param_spec_string (name: "guid" , |
330 | P_("GUID" ), |
331 | P_("The guid of the server" ), |
332 | NULL, |
333 | flags: G_PARAM_READABLE | |
334 | G_PARAM_WRITABLE | |
335 | G_PARAM_CONSTRUCT_ONLY | |
336 | G_PARAM_STATIC_NAME | |
337 | G_PARAM_STATIC_BLURB | |
338 | G_PARAM_STATIC_NICK)); |
339 | |
340 | /** |
341 | * GDBusServer:address: |
342 | * |
343 | * The D-Bus address to listen on. |
344 | * |
345 | * Since: 2.26 |
346 | */ |
347 | g_object_class_install_property (oclass: gobject_class, |
348 | property_id: PROP_ADDRESS, |
349 | pspec: g_param_spec_string (name: "address" , |
350 | P_("Address" ), |
351 | P_("The address to listen on" ), |
352 | NULL, |
353 | flags: G_PARAM_READABLE | |
354 | G_PARAM_WRITABLE | |
355 | G_PARAM_CONSTRUCT_ONLY | |
356 | G_PARAM_STATIC_NAME | |
357 | G_PARAM_STATIC_BLURB | |
358 | G_PARAM_STATIC_NICK)); |
359 | |
360 | /** |
361 | * GDBusServer:client-address: |
362 | * |
363 | * The D-Bus address that clients can use. |
364 | * |
365 | * Since: 2.26 |
366 | */ |
367 | g_object_class_install_property (oclass: gobject_class, |
368 | property_id: PROP_CLIENT_ADDRESS, |
369 | pspec: g_param_spec_string (name: "client-address" , |
370 | P_("Client Address" ), |
371 | P_("The address clients can use" ), |
372 | NULL, |
373 | flags: G_PARAM_READABLE | |
374 | G_PARAM_STATIC_NAME | |
375 | G_PARAM_STATIC_BLURB | |
376 | G_PARAM_STATIC_NICK)); |
377 | |
378 | /** |
379 | * GDBusServer:active: |
380 | * |
381 | * Whether the server is currently active. |
382 | * |
383 | * Since: 2.26 |
384 | */ |
385 | g_object_class_install_property (oclass: gobject_class, |
386 | property_id: PROP_ACTIVE, |
387 | pspec: g_param_spec_boolean (name: "active" , |
388 | P_("Active" ), |
389 | P_("Whether the server is currently active" ), |
390 | FALSE, |
391 | flags: G_PARAM_READABLE | |
392 | G_PARAM_STATIC_NAME | |
393 | G_PARAM_STATIC_BLURB | |
394 | G_PARAM_STATIC_NICK)); |
395 | |
396 | /** |
397 | * GDBusServer:authentication-observer: |
398 | * |
399 | * A #GDBusAuthObserver object to assist in the authentication process or %NULL. |
400 | * |
401 | * Since: 2.26 |
402 | */ |
403 | g_object_class_install_property (oclass: gobject_class, |
404 | property_id: PROP_AUTHENTICATION_OBSERVER, |
405 | pspec: g_param_spec_object (name: "authentication-observer" , |
406 | P_("Authentication Observer" ), |
407 | P_("Object used to assist in the authentication process" ), |
408 | G_TYPE_DBUS_AUTH_OBSERVER, |
409 | flags: G_PARAM_READABLE | |
410 | G_PARAM_WRITABLE | |
411 | G_PARAM_CONSTRUCT_ONLY | |
412 | G_PARAM_STATIC_NAME | |
413 | G_PARAM_STATIC_BLURB | |
414 | G_PARAM_STATIC_NICK)); |
415 | |
416 | /** |
417 | * GDBusServer::new-connection: |
418 | * @server: The #GDBusServer emitting the signal. |
419 | * @connection: A #GDBusConnection for the new connection. |
420 | * |
421 | * Emitted when a new authenticated connection has been made. Use |
422 | * g_dbus_connection_get_peer_credentials() to figure out what |
423 | * identity (if any), was authenticated. |
424 | * |
425 | * If you want to accept the connection, take a reference to the |
426 | * @connection object and return %TRUE. When you are done with the |
427 | * connection call g_dbus_connection_close() and give up your |
428 | * reference. Note that the other peer may disconnect at any time - |
429 | * a typical thing to do when accepting a connection is to listen to |
430 | * the #GDBusConnection::closed signal. |
431 | * |
432 | * If #GDBusServer:flags contains %G_DBUS_SERVER_FLAGS_RUN_IN_THREAD |
433 | * then the signal is emitted in a new thread dedicated to the |
434 | * connection. Otherwise the signal is emitted in the |
435 | * [thread-default main context][g-main-context-push-thread-default] |
436 | * of the thread that @server was constructed in. |
437 | * |
438 | * You are guaranteed that signal handlers for this signal runs |
439 | * before incoming messages on @connection are processed. This means |
440 | * that it's suitable to call g_dbus_connection_register_object() or |
441 | * similar from the signal handler. |
442 | * |
443 | * Returns: %TRUE to claim @connection, %FALSE to let other handlers |
444 | * run. |
445 | * |
446 | * Since: 2.26 |
447 | */ |
448 | _signals[NEW_CONNECTION_SIGNAL] = g_signal_new (I_("new-connection" ), |
449 | G_TYPE_DBUS_SERVER, |
450 | signal_flags: G_SIGNAL_RUN_LAST, |
451 | G_STRUCT_OFFSET (GDBusServerClass, new_connection), |
452 | accumulator: g_signal_accumulator_true_handled, |
453 | NULL, /* accu_data */ |
454 | c_marshaller: _g_cclosure_marshal_BOOLEAN__OBJECT, |
455 | G_TYPE_BOOLEAN, |
456 | n_params: 1, |
457 | G_TYPE_DBUS_CONNECTION); |
458 | g_signal_set_va_marshaller (signal_id: _signals[NEW_CONNECTION_SIGNAL], |
459 | G_TYPE_FROM_CLASS (klass), |
460 | va_marshaller: _g_cclosure_marshal_BOOLEAN__OBJECTv); |
461 | } |
462 | |
463 | static void |
464 | g_dbus_server_init (GDBusServer *server) |
465 | { |
466 | server->main_context_at_construction = g_main_context_ref_thread_default (); |
467 | } |
468 | |
469 | static gboolean |
470 | on_run (GSocketService *service, |
471 | GSocketConnection *socket_connection, |
472 | GObject *source_object, |
473 | gpointer user_data); |
474 | |
475 | /** |
476 | * g_dbus_server_new_sync: |
477 | * @address: A D-Bus address. |
478 | * @flags: Flags from the #GDBusServerFlags enumeration. |
479 | * @guid: A D-Bus GUID. |
480 | * @observer: (nullable): A #GDBusAuthObserver or %NULL. |
481 | * @cancellable: (nullable): A #GCancellable or %NULL. |
482 | * @error: Return location for server or %NULL. |
483 | * |
484 | * Creates a new D-Bus server that listens on the first address in |
485 | * @address that works. |
486 | * |
487 | * Once constructed, you can use g_dbus_server_get_client_address() to |
488 | * get a D-Bus address string that clients can use to connect. |
489 | * |
490 | * To have control over the available authentication mechanisms and |
491 | * the users that are authorized to connect, it is strongly recommended |
492 | * to provide a non-%NULL #GDBusAuthObserver. |
493 | * |
494 | * Connect to the #GDBusServer::new-connection signal to handle |
495 | * incoming connections. |
496 | * |
497 | * The returned #GDBusServer isn't active - you have to start it with |
498 | * g_dbus_server_start(). |
499 | * |
500 | * #GDBusServer is used in this [example][gdbus-peer-to-peer]. |
501 | * |
502 | * This is a synchronous failable constructor. There is currently no |
503 | * asynchronous version. |
504 | * |
505 | * Returns: A #GDBusServer or %NULL if @error is set. Free with |
506 | * g_object_unref(). |
507 | * |
508 | * Since: 2.26 |
509 | */ |
510 | GDBusServer * |
511 | g_dbus_server_new_sync (const gchar *address, |
512 | GDBusServerFlags flags, |
513 | const gchar *guid, |
514 | GDBusAuthObserver *observer, |
515 | GCancellable *cancellable, |
516 | GError **error) |
517 | { |
518 | GDBusServer *server; |
519 | |
520 | g_return_val_if_fail (address != NULL, NULL); |
521 | g_return_val_if_fail (g_dbus_is_guid (guid), NULL); |
522 | g_return_val_if_fail ((flags & ~G_DBUS_SERVER_FLAGS_ALL) == 0, NULL); |
523 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
524 | |
525 | server = g_initable_new (G_TYPE_DBUS_SERVER, |
526 | cancellable, |
527 | error, |
528 | first_property_name: "address" , address, |
529 | "flags" , flags, |
530 | "guid" , guid, |
531 | "authentication-observer" , observer, |
532 | NULL); |
533 | |
534 | return server; |
535 | } |
536 | |
537 | /** |
538 | * g_dbus_server_get_client_address: |
539 | * @server: A #GDBusServer. |
540 | * |
541 | * Gets a |
542 | * [D-Bus address](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses) |
543 | * string that can be used by clients to connect to @server. |
544 | * |
545 | * Returns: A D-Bus address string. Do not free, the string is owned |
546 | * by @server. |
547 | * |
548 | * Since: 2.26 |
549 | */ |
550 | const gchar * |
551 | g_dbus_server_get_client_address (GDBusServer *server) |
552 | { |
553 | g_return_val_if_fail (G_IS_DBUS_SERVER (server), NULL); |
554 | return server->client_address; |
555 | } |
556 | |
557 | /** |
558 | * g_dbus_server_get_guid: |
559 | * @server: A #GDBusServer. |
560 | * |
561 | * Gets the GUID for @server. |
562 | * |
563 | * Returns: A D-Bus GUID. Do not free this string, it is owned by @server. |
564 | * |
565 | * Since: 2.26 |
566 | */ |
567 | const gchar * |
568 | g_dbus_server_get_guid (GDBusServer *server) |
569 | { |
570 | g_return_val_if_fail (G_IS_DBUS_SERVER (server), NULL); |
571 | return server->guid; |
572 | } |
573 | |
574 | /** |
575 | * g_dbus_server_get_flags: |
576 | * @server: A #GDBusServer. |
577 | * |
578 | * Gets the flags for @server. |
579 | * |
580 | * Returns: A set of flags from the #GDBusServerFlags enumeration. |
581 | * |
582 | * Since: 2.26 |
583 | */ |
584 | GDBusServerFlags |
585 | g_dbus_server_get_flags (GDBusServer *server) |
586 | { |
587 | g_return_val_if_fail (G_IS_DBUS_SERVER (server), G_DBUS_SERVER_FLAGS_NONE); |
588 | return server->flags; |
589 | } |
590 | |
591 | /** |
592 | * g_dbus_server_is_active: |
593 | * @server: A #GDBusServer. |
594 | * |
595 | * Gets whether @server is active. |
596 | * |
597 | * Returns: %TRUE if server is active, %FALSE otherwise. |
598 | * |
599 | * Since: 2.26 |
600 | */ |
601 | gboolean |
602 | g_dbus_server_is_active (GDBusServer *server) |
603 | { |
604 | g_return_val_if_fail (G_IS_DBUS_SERVER (server), G_DBUS_SERVER_FLAGS_NONE); |
605 | return server->active; |
606 | } |
607 | |
608 | /** |
609 | * g_dbus_server_start: |
610 | * @server: A #GDBusServer. |
611 | * |
612 | * Starts @server. |
613 | * |
614 | * Since: 2.26 |
615 | */ |
616 | void |
617 | g_dbus_server_start (GDBusServer *server) |
618 | { |
619 | g_return_if_fail (G_IS_DBUS_SERVER (server)); |
620 | if (server->active) |
621 | return; |
622 | /* Right now we don't have any transport not using the listener... */ |
623 | g_assert (server->is_using_listener); |
624 | server->run_signal_handler_id = g_signal_connect_data (G_SOCKET_SERVICE (server->listener), |
625 | detailed_signal: "run" , |
626 | G_CALLBACK (on_run), |
627 | g_object_ref (server), |
628 | destroy_data: (GClosureNotify) g_object_unref, |
629 | connect_flags: 0 /* flags */); |
630 | g_socket_service_start (G_SOCKET_SERVICE (server->listener)); |
631 | server->active = TRUE; |
632 | g_object_notify (G_OBJECT (server), property_name: "active" ); |
633 | } |
634 | |
635 | /** |
636 | * g_dbus_server_stop: |
637 | * @server: A #GDBusServer. |
638 | * |
639 | * Stops @server. |
640 | * |
641 | * Since: 2.26 |
642 | */ |
643 | void |
644 | g_dbus_server_stop (GDBusServer *server) |
645 | { |
646 | g_return_if_fail (G_IS_DBUS_SERVER (server)); |
647 | if (!server->active) |
648 | return; |
649 | /* Right now we don't have any transport not using the listener... */ |
650 | g_assert (server->is_using_listener); |
651 | g_assert (server->run_signal_handler_id > 0); |
652 | g_clear_signal_handler (&server->run_signal_handler_id, server->listener); |
653 | g_socket_service_stop (G_SOCKET_SERVICE (server->listener)); |
654 | server->active = FALSE; |
655 | g_object_notify (G_OBJECT (server), property_name: "active" ); |
656 | |
657 | if (server->unix_socket_path) |
658 | { |
659 | if (g_unlink (filename: server->unix_socket_path) != 0) |
660 | g_warning ("Failed to delete %s: %s" , server->unix_socket_path, g_strerror (errno)); |
661 | } |
662 | |
663 | if (server->nonce_file) |
664 | { |
665 | if (g_unlink (filename: server->nonce_file) != 0) |
666 | g_warning ("Failed to delete %s: %s" , server->nonce_file, g_strerror (errno)); |
667 | } |
668 | } |
669 | |
670 | /* ---------------------------------------------------------------------------------------------------- */ |
671 | |
672 | #ifdef G_OS_UNIX |
673 | |
674 | static gint |
675 | random_ascii (void) |
676 | { |
677 | gint ret; |
678 | ret = g_random_int_range (begin: 0, end: 60); |
679 | if (ret < 25) |
680 | ret += 'A'; |
681 | else if (ret < 50) |
682 | ret += 'a' - 25; |
683 | else |
684 | ret += '0' - 50; |
685 | return ret; |
686 | } |
687 | |
688 | /* note that address_entry has already been validated => exactly one of path, dir, tmpdir, or abstract keys are set */ |
689 | static gboolean |
690 | try_unix (GDBusServer *server, |
691 | const gchar *address_entry, |
692 | GHashTable *key_value_pairs, |
693 | GError **error) |
694 | { |
695 | gboolean ret; |
696 | const gchar *path; |
697 | const gchar *dir; |
698 | const gchar *tmpdir; |
699 | const gchar *abstract; |
700 | GSocketAddress *address; |
701 | |
702 | ret = FALSE; |
703 | address = NULL; |
704 | |
705 | path = g_hash_table_lookup (hash_table: key_value_pairs, key: "path" ); |
706 | dir = g_hash_table_lookup (hash_table: key_value_pairs, key: "dir" ); |
707 | tmpdir = g_hash_table_lookup (hash_table: key_value_pairs, key: "tmpdir" ); |
708 | abstract = g_hash_table_lookup (hash_table: key_value_pairs, key: "abstract" ); |
709 | |
710 | if (path != NULL) |
711 | { |
712 | address = g_unix_socket_address_new (path); |
713 | } |
714 | else if (dir != NULL || tmpdir != NULL) |
715 | { |
716 | gint n; |
717 | GString *s; |
718 | GError *local_error; |
719 | |
720 | retry: |
721 | s = g_string_new (init: tmpdir != NULL ? tmpdir : dir); |
722 | g_string_append (string: s, val: "/dbus-" ); |
723 | for (n = 0; n < 8; n++) |
724 | g_string_append_c (s, random_ascii ()); |
725 | |
726 | /* prefer abstract namespace if available for tmpdir: addresses |
727 | * abstract namespace is disallowed for dir: addresses */ |
728 | if (tmpdir != NULL && g_unix_socket_address_abstract_names_supported ()) |
729 | address = g_unix_socket_address_new_with_type (path: s->str, |
730 | path_len: -1, |
731 | type: G_UNIX_SOCKET_ADDRESS_ABSTRACT); |
732 | else |
733 | address = g_unix_socket_address_new (path: s->str); |
734 | g_string_free (string: s, TRUE); |
735 | |
736 | local_error = NULL; |
737 | if (!g_socket_listener_add_address (listener: server->listener, |
738 | address, |
739 | type: G_SOCKET_TYPE_STREAM, |
740 | protocol: G_SOCKET_PROTOCOL_DEFAULT, |
741 | NULL, /* source_object */ |
742 | NULL, /* effective_address */ |
743 | error: &local_error)) |
744 | { |
745 | if (local_error->domain == G_IO_ERROR && local_error->code == G_IO_ERROR_ADDRESS_IN_USE) |
746 | { |
747 | g_error_free (error: local_error); |
748 | goto retry; |
749 | } |
750 | g_propagate_error (dest: error, src: local_error); |
751 | goto out; |
752 | } |
753 | ret = TRUE; |
754 | goto out; |
755 | } |
756 | else if (abstract != NULL) |
757 | { |
758 | if (!g_unix_socket_address_abstract_names_supported ()) |
759 | { |
760 | g_set_error_literal (err: error, |
761 | G_IO_ERROR, |
762 | code: G_IO_ERROR_NOT_SUPPORTED, |
763 | _("Abstract namespace not supported" )); |
764 | goto out; |
765 | } |
766 | address = g_unix_socket_address_new_with_type (path: abstract, |
767 | path_len: -1, |
768 | type: G_UNIX_SOCKET_ADDRESS_ABSTRACT); |
769 | } |
770 | else |
771 | { |
772 | g_assert_not_reached (); |
773 | } |
774 | |
775 | if (!g_socket_listener_add_address (listener: server->listener, |
776 | address, |
777 | type: G_SOCKET_TYPE_STREAM, |
778 | protocol: G_SOCKET_PROTOCOL_DEFAULT, |
779 | NULL, /* source_object */ |
780 | NULL, /* effective_address */ |
781 | error)) |
782 | goto out; |
783 | |
784 | ret = TRUE; |
785 | |
786 | out: |
787 | |
788 | if (address != NULL) |
789 | { |
790 | /* Fill out client_address if the connection attempt worked */ |
791 | if (ret) |
792 | { |
793 | const char *address_path; |
794 | char *escaped_path; |
795 | |
796 | server->is_using_listener = TRUE; |
797 | address_path = g_unix_socket_address_get_path (G_UNIX_SOCKET_ADDRESS (address)); |
798 | escaped_path = g_dbus_address_escape_value (string: address_path); |
799 | |
800 | switch (g_unix_socket_address_get_address_type (G_UNIX_SOCKET_ADDRESS (address))) |
801 | { |
802 | case G_UNIX_SOCKET_ADDRESS_ABSTRACT: |
803 | server->client_address = g_strdup_printf (format: "unix:abstract=%s" , escaped_path); |
804 | break; |
805 | |
806 | case G_UNIX_SOCKET_ADDRESS_PATH: |
807 | server->client_address = g_strdup_printf (format: "unix:path=%s" , escaped_path); |
808 | server->unix_socket_path = g_strdup (str: address_path); |
809 | break; |
810 | |
811 | default: |
812 | g_assert_not_reached (); |
813 | break; |
814 | } |
815 | |
816 | g_free (mem: escaped_path); |
817 | } |
818 | g_object_unref (object: address); |
819 | } |
820 | return ret; |
821 | } |
822 | #endif |
823 | |
824 | /* ---------------------------------------------------------------------------------------------------- */ |
825 | |
826 | /* note that address_entry has already been validated => |
827 | * both host and port (guaranteed to be a number in [0, 65535]) are set (family is optional) |
828 | */ |
829 | static gboolean |
830 | try_tcp (GDBusServer *server, |
831 | const gchar *address_entry, |
832 | GHashTable *key_value_pairs, |
833 | gboolean do_nonce, |
834 | GError **error) |
835 | { |
836 | gboolean ret; |
837 | const gchar *host; |
838 | const gchar *port; |
839 | gint port_num; |
840 | GResolver *resolver; |
841 | GList *resolved_addresses; |
842 | GList *l; |
843 | |
844 | ret = FALSE; |
845 | resolver = NULL; |
846 | resolved_addresses = NULL; |
847 | |
848 | host = g_hash_table_lookup (hash_table: key_value_pairs, key: "host" ); |
849 | port = g_hash_table_lookup (hash_table: key_value_pairs, key: "port" ); |
850 | /* family = g_hash_table_lookup (key_value_pairs, "family"); */ |
851 | if (g_hash_table_lookup (hash_table: key_value_pairs, key: "noncefile" ) != NULL) |
852 | { |
853 | g_set_error_literal (err: error, |
854 | G_IO_ERROR, |
855 | code: G_IO_ERROR_INVALID_ARGUMENT, |
856 | _("Cannot specify nonce file when creating a server" )); |
857 | goto out; |
858 | } |
859 | |
860 | if (host == NULL) |
861 | host = "localhost" ; |
862 | if (port == NULL) |
863 | port = "0" ; |
864 | port_num = strtol (nptr: port, NULL, base: 10); |
865 | |
866 | resolver = g_resolver_get_default (); |
867 | resolved_addresses = g_resolver_lookup_by_name (resolver, |
868 | hostname: host, |
869 | NULL, |
870 | error); |
871 | if (resolved_addresses == NULL) |
872 | goto out; |
873 | |
874 | /* TODO: handle family */ |
875 | for (l = resolved_addresses; l != NULL; l = l->next) |
876 | { |
877 | GInetAddress *address = G_INET_ADDRESS (l->data); |
878 | GSocketAddress *socket_address; |
879 | GSocketAddress *effective_address; |
880 | |
881 | socket_address = g_inet_socket_address_new (address, port: port_num); |
882 | if (!g_socket_listener_add_address (listener: server->listener, |
883 | address: socket_address, |
884 | type: G_SOCKET_TYPE_STREAM, |
885 | protocol: G_SOCKET_PROTOCOL_TCP, |
886 | NULL, /* GObject *source_object */ |
887 | effective_address: &effective_address, |
888 | error)) |
889 | { |
890 | g_object_unref (object: socket_address); |
891 | goto out; |
892 | } |
893 | if (port_num == 0) |
894 | /* make sure we allocate the same port number for other listeners */ |
895 | port_num = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (effective_address)); |
896 | |
897 | g_object_unref (object: effective_address); |
898 | g_object_unref (object: socket_address); |
899 | } |
900 | |
901 | if (do_nonce) |
902 | { |
903 | gint fd; |
904 | guint n; |
905 | gsize bytes_written; |
906 | gsize bytes_remaining; |
907 | char *file_escaped; |
908 | char *host_escaped; |
909 | |
910 | server->nonce = g_new0 (guchar, 16); |
911 | for (n = 0; n < 16; n++) |
912 | server->nonce[n] = g_random_int_range (begin: 0, end: 256); |
913 | fd = g_file_open_tmp (tmpl: "gdbus-nonce-file-XXXXXX" , |
914 | name_used: &server->nonce_file, |
915 | error); |
916 | if (fd == -1) |
917 | { |
918 | g_socket_listener_close (listener: server->listener); |
919 | goto out; |
920 | } |
921 | again: |
922 | bytes_written = 0; |
923 | bytes_remaining = 16; |
924 | while (bytes_remaining > 0) |
925 | { |
926 | gssize ret; |
927 | int errsv; |
928 | |
929 | ret = write (fd: fd, buf: server->nonce + bytes_written, n: bytes_remaining); |
930 | errsv = errno; |
931 | if (ret == -1) |
932 | { |
933 | if (errsv == EINTR) |
934 | goto again; |
935 | g_set_error (err: error, |
936 | G_IO_ERROR, |
937 | code: g_io_error_from_errno (err_no: errsv), |
938 | _("Error writing nonce file at “%s”: %s" ), |
939 | server->nonce_file, |
940 | g_strerror (errnum: errsv)); |
941 | goto out; |
942 | } |
943 | bytes_written += ret; |
944 | bytes_remaining -= ret; |
945 | } |
946 | if (!g_close (fd, error)) |
947 | goto out; |
948 | host_escaped = g_dbus_address_escape_value (string: host); |
949 | file_escaped = g_dbus_address_escape_value (string: server->nonce_file); |
950 | server->client_address = g_strdup_printf (format: "nonce-tcp:host=%s,port=%d,noncefile=%s" , |
951 | host_escaped, |
952 | port_num, |
953 | file_escaped); |
954 | g_free (mem: host_escaped); |
955 | g_free (mem: file_escaped); |
956 | } |
957 | else |
958 | { |
959 | server->client_address = g_strdup_printf (format: "tcp:host=%s,port=%d" , host, port_num); |
960 | } |
961 | server->is_using_listener = TRUE; |
962 | ret = TRUE; |
963 | |
964 | out: |
965 | g_list_free_full (list: resolved_addresses, free_func: g_object_unref); |
966 | if (resolver) |
967 | g_object_unref (object: resolver); |
968 | return ret; |
969 | } |
970 | |
971 | /* ---------------------------------------------------------------------------------------------------- */ |
972 | |
973 | typedef struct |
974 | { |
975 | GDBusServer *server; |
976 | GDBusConnection *connection; |
977 | } EmitIdleData; |
978 | |
979 | static void |
980 | emit_idle_data_free (EmitIdleData *data) |
981 | { |
982 | g_object_unref (object: data->server); |
983 | g_object_unref (object: data->connection); |
984 | g_free (mem: data); |
985 | } |
986 | |
987 | static gboolean |
988 | emit_new_connection_in_idle (gpointer user_data) |
989 | { |
990 | EmitIdleData *data = user_data; |
991 | gboolean claimed; |
992 | |
993 | claimed = FALSE; |
994 | g_signal_emit (instance: data->server, |
995 | signal_id: _signals[NEW_CONNECTION_SIGNAL], |
996 | detail: 0, |
997 | data->connection, |
998 | &claimed); |
999 | |
1000 | if (claimed) |
1001 | g_dbus_connection_start_message_processing (connection: data->connection); |
1002 | g_object_unref (object: data->connection); |
1003 | |
1004 | return FALSE; |
1005 | } |
1006 | |
1007 | /* Called in new thread */ |
1008 | static gboolean |
1009 | on_run (GSocketService *service, |
1010 | GSocketConnection *socket_connection, |
1011 | GObject *source_object, |
1012 | gpointer user_data) |
1013 | { |
1014 | GDBusServer *server = G_DBUS_SERVER (user_data); |
1015 | GDBusConnection *connection; |
1016 | GDBusConnectionFlags connection_flags; |
1017 | |
1018 | if (server->nonce != NULL) |
1019 | { |
1020 | gchar buf[16]; |
1021 | gsize bytes_read; |
1022 | |
1023 | if (!g_input_stream_read_all (stream: g_io_stream_get_input_stream (G_IO_STREAM (socket_connection)), |
1024 | buffer: buf, |
1025 | count: 16, |
1026 | bytes_read: &bytes_read, |
1027 | NULL, /* GCancellable */ |
1028 | NULL)) /* GError */ |
1029 | goto out; |
1030 | |
1031 | if (bytes_read != 16) |
1032 | goto out; |
1033 | |
1034 | if (memcmp (s1: buf, s2: server->nonce, n: 16) != 0) |
1035 | goto out; |
1036 | } |
1037 | |
1038 | connection_flags = |
1039 | G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER | |
1040 | G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING; |
1041 | if (server->flags & G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS) |
1042 | connection_flags |= G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS; |
1043 | if (server->flags & G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER) |
1044 | connection_flags |= G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER; |
1045 | |
1046 | connection = g_dbus_connection_new_sync (G_IO_STREAM (socket_connection), |
1047 | guid: server->guid, |
1048 | flags: connection_flags, |
1049 | observer: server->authentication_observer, |
1050 | NULL, /* GCancellable */ |
1051 | NULL); /* GError */ |
1052 | if (connection == NULL) |
1053 | goto out; |
1054 | |
1055 | if (server->flags & G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) |
1056 | { |
1057 | gboolean claimed; |
1058 | |
1059 | claimed = FALSE; |
1060 | g_signal_emit (instance: server, |
1061 | signal_id: _signals[NEW_CONNECTION_SIGNAL], |
1062 | detail: 0, |
1063 | connection, |
1064 | &claimed); |
1065 | if (claimed) |
1066 | g_dbus_connection_start_message_processing (connection); |
1067 | g_object_unref (object: connection); |
1068 | } |
1069 | else |
1070 | { |
1071 | GSource *idle_source; |
1072 | EmitIdleData *data; |
1073 | |
1074 | data = g_new0 (EmitIdleData, 1); |
1075 | data->server = g_object_ref (server); |
1076 | data->connection = g_object_ref (connection); |
1077 | |
1078 | idle_source = g_idle_source_new (); |
1079 | g_source_set_priority (source: idle_source, G_PRIORITY_DEFAULT); |
1080 | g_source_set_callback (source: idle_source, |
1081 | func: emit_new_connection_in_idle, |
1082 | data, |
1083 | notify: (GDestroyNotify) emit_idle_data_free); |
1084 | g_source_set_name (source: idle_source, name: "[gio] emit_new_connection_in_idle" ); |
1085 | g_source_attach (source: idle_source, context: server->main_context_at_construction); |
1086 | g_source_unref (source: idle_source); |
1087 | } |
1088 | |
1089 | out: |
1090 | return TRUE; |
1091 | } |
1092 | |
1093 | static gboolean |
1094 | initable_init (GInitable *initable, |
1095 | GCancellable *cancellable, |
1096 | GError **error) |
1097 | { |
1098 | GDBusServer *server = G_DBUS_SERVER (initable); |
1099 | gboolean ret; |
1100 | guint n; |
1101 | gchar **addr_array; |
1102 | GError *last_error; |
1103 | |
1104 | ret = FALSE; |
1105 | addr_array = NULL; |
1106 | last_error = NULL; |
1107 | |
1108 | if (!g_dbus_is_guid (string: server->guid)) |
1109 | { |
1110 | g_set_error (err: &last_error, |
1111 | G_IO_ERROR, |
1112 | code: G_IO_ERROR_INVALID_ARGUMENT, |
1113 | _("The string “%s” is not a valid D-Bus GUID" ), |
1114 | server->guid); |
1115 | goto out; |
1116 | } |
1117 | |
1118 | server->listener = G_SOCKET_LISTENER (g_threaded_socket_service_new (-1)); |
1119 | |
1120 | addr_array = g_strsplit (string: server->address, delimiter: ";" , max_tokens: 0); |
1121 | last_error = NULL; |
1122 | for (n = 0; addr_array != NULL && addr_array[n] != NULL; n++) |
1123 | { |
1124 | const gchar *address_entry = addr_array[n]; |
1125 | GHashTable *key_value_pairs; |
1126 | gchar *transport_name; |
1127 | GError *this_error; |
1128 | |
1129 | this_error = NULL; |
1130 | if (g_dbus_is_supported_address (string: address_entry, |
1131 | error: &this_error) && |
1132 | _g_dbus_address_parse_entry (address_entry, |
1133 | out_transport_name: &transport_name, |
1134 | out_key_value_pairs: &key_value_pairs, |
1135 | error: &this_error)) |
1136 | { |
1137 | |
1138 | if (FALSE) |
1139 | { |
1140 | } |
1141 | #ifdef G_OS_UNIX |
1142 | else if (g_strcmp0 (str1: transport_name, str2: "unix" ) == 0) |
1143 | ret = try_unix (server, address_entry, key_value_pairs, error: &this_error); |
1144 | #endif |
1145 | else if (g_strcmp0 (str1: transport_name, str2: "tcp" ) == 0) |
1146 | ret = try_tcp (server, address_entry, key_value_pairs, FALSE, error: &this_error); |
1147 | else if (g_strcmp0 (str1: transport_name, str2: "nonce-tcp" ) == 0) |
1148 | ret = try_tcp (server, address_entry, key_value_pairs, TRUE, error: &this_error); |
1149 | else |
1150 | g_set_error (err: &this_error, |
1151 | G_IO_ERROR, |
1152 | code: G_IO_ERROR_INVALID_ARGUMENT, |
1153 | _("Cannot listen on unsupported transport “%s”" ), |
1154 | transport_name); |
1155 | |
1156 | g_free (mem: transport_name); |
1157 | if (key_value_pairs != NULL) |
1158 | g_hash_table_unref (hash_table: key_value_pairs); |
1159 | |
1160 | if (ret) |
1161 | { |
1162 | g_assert (this_error == NULL); |
1163 | goto out; |
1164 | } |
1165 | } |
1166 | |
1167 | if (this_error != NULL) |
1168 | { |
1169 | if (last_error != NULL) |
1170 | g_error_free (error: last_error); |
1171 | last_error = this_error; |
1172 | } |
1173 | } |
1174 | |
1175 | out: |
1176 | |
1177 | g_strfreev (str_array: addr_array); |
1178 | |
1179 | if (ret) |
1180 | { |
1181 | g_clear_error (err: &last_error); |
1182 | } |
1183 | else |
1184 | { |
1185 | g_assert (last_error != NULL); |
1186 | g_propagate_error (dest: error, src: last_error); |
1187 | } |
1188 | return ret; |
1189 | } |
1190 | |
1191 | |
1192 | static void |
1193 | initable_iface_init (GInitableIface *initable_iface) |
1194 | { |
1195 | initable_iface->init = initable_init; |
1196 | } |
1197 | |
1198 | /* ---------------------------------------------------------------------------------------------------- */ |
1199 | |