1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager -- Network link manager
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Copyright (C) 2006 - 2013 Red Hat, Inc.
19 * Copyright (C) 2006 - 2008 Novell, Inc.
20 */
21
22 #include "config.h"
23 #include <unistd.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26
27 #include "NetworkManager.h"
28 #include "nm-dbus-manager.h"
29 #include "nm-glib-compat.h"
30 #include "nm-properties-changed-signal.h"
31
32 #include <dbus/dbus.h>
33 #include <dbus/dbus-glib.h>
34 #include <dbus/dbus-glib-lowlevel.h>
35 #include <string.h>
36 #include "nm-logging.h"
37
38 #define PRIV_SOCK_PATH NMRUNDIR "/private"
39 #define PRIV_SOCK_TAG "private"
40
41 enum {
42 DBUS_CONNECTION_CHANGED = 0,
43 NAME_OWNER_CHANGED,
44 PRIVATE_CONNECTION_NEW,
45 PRIVATE_CONNECTION_DISCONNECTED,
46 NUMBER_OF_SIGNALS
47 };
48
49 static guint signals[NUMBER_OF_SIGNALS];
50
51 G_DEFINE_TYPE(NMDBusManager, nm_dbus_manager, G_TYPE_OBJECT)
52
53 #define NM_DBUS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
54 NM_TYPE_DBUS_MANAGER, \
55 NMDBusManagerPrivate))
56
57 typedef struct _PrivateServer PrivateServer;
58
59 typedef struct {
60 DBusConnection *connection;
61 DBusGConnection *g_connection;
62 GHashTable *exported;
63 gboolean started;
64
65 GSList *private_servers;
66 PrivateServer *priv_server;
67
68 DBusGProxy *proxy;
69 guint proxy_destroy_id;
70
71 guint reconnect_id;
72 } NMDBusManagerPrivate;
73
74 static gboolean nm_dbus_manager_init_bus (NMDBusManager *self);
75 static void nm_dbus_manager_cleanup (NMDBusManager *self, gboolean dispose);
76 static void start_reconnection_timeout (NMDBusManager *self);
77 static void object_destroyed (NMDBusManager *self, gpointer object);
78
79 NMDBusManager *
80 nm_dbus_manager_get (void)
81 {
82 static NMDBusManager *singleton = NULL;
83 static gsize once = 0;
84
85 if (g_once_init_enter (&once)) {
86 singleton = (NMDBusManager *) g_object_new (NM_TYPE_DBUS_MANAGER, NULL);
87 g_assert (singleton);
88 if (!nm_dbus_manager_init_bus (singleton))
89 start_reconnection_timeout (singleton);
90 g_once_init_leave (&once, 1);
91 }
92 return singleton;
93 }
94
95 /**************************************************************/
96
97 struct _PrivateServer {
98 char *tag;
99 GQuark detail;
100 char *address;
101 DBusServer *server;
102 GHashTable *connections;
103 NMDBusManager *manager;
104 };
105
106 static DBusHandlerResult
107 private_server_message_filter (DBusConnection *conn,
108 DBusMessage *message,
109 void *data)
110 {
111 PrivateServer *s = data;
112
113 /* Clean up after the connection */
114 if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
115 nm_log_dbg (LOGD_CORE, "(%s) closed connection %p on private socket.",
116 s->tag, conn);
117
118 /* Emit this for the manager */
119 g_signal_emit (s->manager,
120 signals[PRIVATE_CONNECTION_DISCONNECTED],
121 s->detail,
122 dbus_connection_get_g_connection (conn));
123
124 g_hash_table_remove (s->connections, conn);
125
126 /* Let dbus-glib process the message too */
127 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
128 }
129
130 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
131 }
132
133 static dbus_bool_t
134 allow_only_root (DBusConnection *connection, unsigned long uid, void *data)
135 {
136 return uid == 0;
137 }
138
139 static void
140 private_server_new_connection (DBusServer *server,
141 DBusConnection *conn,
142 gpointer user_data)
143 {
144 PrivateServer *s = user_data;
145 static guint32 counter = 0;
146 char *sender;
147
148 if (!dbus_connection_add_filter (conn, private_server_message_filter, s, NULL)) {
149 dbus_connection_close (conn);
150 return;
151 }
152 dbus_connection_set_unix_user_function (conn, allow_only_root, NULL, NULL);
153 dbus_connection_setup_with_g_main (conn, NULL);
154
155 /* Fake a sender since private connections don't have one */
156 sender = g_strdup_printf ("x:y:%d", counter++);
157 g_hash_table_insert (s->connections, dbus_connection_ref (conn), sender);
158
159 nm_log_dbg (LOGD_CORE, "(%s) accepted connection %p on private socket.", s->tag, conn);
160
161 /* Emit this for the manager */
162 g_signal_emit (s->manager,
163 signals[PRIVATE_CONNECTION_NEW],
164 s->detail,
165 dbus_connection_get_g_connection (conn));
166 }
167
168 static void
169 private_server_dbus_connection_destroy (DBusConnection *conn)
170 {
171 if (dbus_connection_get_is_connected (conn))
172 dbus_connection_close (conn);
173 dbus_connection_unref (conn);
174 }
175
176 static PrivateServer *
177 private_server_new (const char *path,
178 const char *tag,
179 NMDBusManager *manager)
180 {
181 PrivateServer *s;
182 DBusServer *server;
183 DBusError error;
184 char *address;
185
186 unlink (path);
187 address = g_strdup_printf ("unix:path=%s", path);
188
189 nm_log_dbg (LOGD_CORE, "(%s) creating private socket %s.", tag, address);
190
191 dbus_error_init (&error);
192 server = dbus_server_listen (address, &error);
193 if (!server) {
194 nm_log_warn (LOGD_CORE, "(%s) failed to set up private socket %s: %s",
195 tag, address, error.message);
196 dbus_error_free (&error);
197 return NULL;
198 }
199
200 s = g_malloc0 (sizeof (*s));
201 s->address = address;
202 s->server = server;
203 dbus_server_setup_with_g_main (s->server, NULL);
204 dbus_server_set_new_connection_function (s->server, private_server_new_connection, s, NULL);
205
206 s->connections = g_hash_table_new_full (g_direct_hash, g_direct_equal,
207 (GDestroyNotify) private_server_dbus_connection_destroy,
208 g_free);
209 s->manager = manager;
210 s->tag = g_strdup (tag);
211 s->detail = g_quark_from_string (s->tag);
212
213 return s;
214 }
215
216 static void
217 private_server_free (gpointer ptr)
218 {
219 PrivateServer *s = ptr;
220
221 unlink (s->address);
222 g_free (s->address);
223 g_free (s->tag);
224 g_hash_table_destroy (s->connections);
225 dbus_server_unref (s->server);
226 memset (s, 0, sizeof (*s));
227 g_free (s);
228 }
229
230 void
231 nm_dbus_manager_private_server_register (NMDBusManager *self,
232 const char *path,
233 const char *tag)
234 {
235 NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
236 PrivateServer *s;
237 GSList *iter;
238
239 #if !HAVE_DBUS_GLIB_100
240 g_assert_not_reached ();
241 #endif
242
243 g_return_if_fail (self != NULL);
244 g_return_if_fail (path != NULL);
245 g_return_if_fail (tag != NULL);
246
247 /* Only one instance per tag; but don't warn */
248 for (iter = priv->private_servers; iter; iter = g_slist_next (iter)) {
249 s = iter->data;
250 if (g_strcmp0 (tag, s->tag) == 0)
251 return;
252 }
253
254 s = private_server_new (path, tag, self);
255 if (s)
256 priv->private_servers = g_slist_append (priv->private_servers, s);
257 }
258
259 static const char *
260 private_server_get_connection_owner (PrivateServer *s, DBusGConnection *connection)
261 {
262 g_return_val_if_fail (s != NULL, NULL);
263 g_return_val_if_fail (connection != NULL, NULL);
264
265 return g_hash_table_lookup (s->connections, dbus_g_connection_get_connection (connection));
266 }
267
268 /**************************************************************/
269
270 /**
271 * _get_caller_info_from_context():
272 *
273 * Given a dbus-glib method invocation, or a DBusConnection + DBusMessage,
274 * return the sender and the UID of the sender.
275 */
276 static gboolean
277 _get_caller_info (NMDBusManager *self,
278 DBusGMethodInvocation *context,
279 DBusConnection *connection,
280 DBusMessage *message,
281 char **out_sender,
282 gulong *out_uid)
283 {
284 NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
285 DBusGConnection *gconn;
286 char *sender;
287 const char *priv_sender;
288 DBusError error;
289 GSList *iter;
290
291 if (context) {
292 gconn = dbus_g_method_invocation_get_g_connection (context);
293 g_assert (gconn);
294 connection = dbus_g_connection_get_connection (gconn);
295
296 /* only bus connections will have a sender */
297 sender = dbus_g_method_get_sender (context);
298 } else {
299 g_assert (message);
300 sender = g_strdup (dbus_message_get_sender (message));
301 }
302 g_assert (connection);
303
304 if (!sender) {
305 /* Might be a private connection, for which we fake a sender */
306 for (iter = priv->private_servers; iter; iter = g_slist_next (iter)) {
307 PrivateServer *s = iter->data;
308
309 priv_sender = g_hash_table_lookup (s->connections, connection);
310 if (priv_sender) {
311 if (out_uid)
312 *out_uid = 0;
313 if (out_sender)
314 *out_sender = g_strdup (priv_sender);
315 return TRUE;
316 }
317 }
318 return FALSE;
319 }
320
321 /* Bus connections always have a sender */
322 g_assert (sender);
323 if (out_uid) {
324 dbus_error_init (&error);
325 *out_uid = dbus_bus_get_unix_user (connection, sender, &error);
326 if (dbus_error_is_set (&error)) {
327 dbus_error_free (&error);
328 *out_uid = G_MAXULONG;
329 g_free (sender);
330 return FALSE;
331 }
332 }
333
334 if (out_sender)
335 *out_sender = g_strdup (sender);
336
337 g_free (sender);
338 return TRUE;
339 }
340
341 gboolean
342 nm_dbus_manager_get_caller_info (NMDBusManager *self,
343 DBusGMethodInvocation *context,
344 char **out_sender,
345 gulong *out_uid)
346 {
347 return _get_caller_info (self, context, NULL, NULL, out_sender, out_uid);
348 }
349
350 gboolean
351 nm_dbus_manager_get_caller_info_from_message (NMDBusManager *self,
352 DBusConnection *connection,
353 DBusMessage *message,
354 char **out_sender,
355 gulong *out_uid)
356 {
357 return _get_caller_info (self, NULL, connection, message, out_sender, out_uid);
358 }
359
360 gboolean
361 nm_dbus_manager_get_unix_user (NMDBusManager *self,
362 const char *sender,
363 gulong *out_uid)
364 {
365 NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
366 GSList *iter;
367 DBusError error;
368
369 g_return_val_if_fail (sender != NULL, FALSE);
370 g_return_val_if_fail (out_uid != NULL, FALSE);
371
372 /* Check if it's a private connection sender, which we fake */
373 for (iter = priv->private_servers; iter; iter = g_slist_next (iter)) {
374 PrivateServer *s = iter->data;
375 GHashTableIter hiter;
376 const char *priv_sender;
377
378 g_hash_table_iter_init (&hiter, s->connections);
379 while (g_hash_table_iter_next (&hiter, NULL, (gpointer) &priv_sender)) {
380 if (g_strcmp0 (sender, priv_sender) == 0) {
381 *out_uid = 0;
382 return TRUE;
383 }
384 }
385 }
386
387 /* Otherwise, a bus connection */
388 dbus_error_init (&error);
389 *out_uid = dbus_bus_get_unix_user (priv->connection, sender, &error);
390 if (dbus_error_is_set (&error)) {
391 nm_log_warn (LOGD_CORE, "Failed to get unix user for dbus sender '%s': %s",
392 sender, error.message);
393 return FALSE;
394 }
395
396 return TRUE;
397 }
398
399 /**************************************************************/
400
401 #if HAVE_DBUS_GLIB_100
402 static void
403 private_connection_new (NMDBusManager *self, DBusGConnection *connection)
404 {
405 NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
406 GHashTableIter iter;
407 GObject *object;
408 const char *path;
409
410 /* Register all exported objects on this private connection */
411 g_hash_table_iter_init (&iter, priv->exported);
412 while (g_hash_table_iter_next (&iter, (gpointer) &object, (gpointer) &path)) {
413 dbus_g_connection_register_g_object (connection, path, object);
414 nm_log_dbg (LOGD_CORE, "(%s) registered %p (%s) at '%s' on private socket.",
415 PRIV_SOCK_TAG, object, G_OBJECT_TYPE_NAME (object), path);
416 }
417 }
418
419 static void
420 private_connection_disconnected (NMDBusManager *self, DBusGConnection *connection)
421 {
422 NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
423 const char *owner;
424
425 owner = private_server_get_connection_owner (priv->priv_server, connection);
426 g_assert (owner);
427
428 /* Fake a NameOwnerChanged to let listerners know this owner has quit */
429 g_signal_emit (G_OBJECT (self), signals[NAME_OWNER_CHANGED],
430 0, owner, owner, NULL);
431 }
432 #endif /* HAVE_DBUS_GLIB_100 */
433
434 static void
435 nm_dbus_manager_init (NMDBusManager *self)
436 {
437 NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
438
439 priv->exported = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
440
441 #if HAVE_DBUS_GLIB_100
442 /* Set up our main private DBus socket */
(1) Event check_return: |
Calling function "mkdir("/var/run/NetworkManager", 448U)" without checking return value. This library function may fail and return an error code. |
(2) Event unchecked_value: |
No check of the return value of "mkdir("/var/run/NetworkManager", 448U)". |
443 mkdir (NMRUNDIR, 0700);
444 priv->priv_server = private_server_new (PRIV_SOCK_PATH, PRIV_SOCK_TAG, self);
445 if (priv->priv_server) {
446 priv->private_servers = g_slist_append (priv->private_servers, priv->priv_server);
447
448 g_signal_connect (self,
449 NM_DBUS_MANAGER_PRIVATE_CONNECTION_NEW "::" PRIV_SOCK_TAG,
450 (GCallback) private_connection_new,
451 NULL);
452 g_signal_connect (self,
453 NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED "::" PRIV_SOCK_TAG,
454 (GCallback) private_connection_disconnected,
455 NULL);
456 }
457 #endif
458 }
459
460 static void
461 nm_dbus_manager_dispose (GObject *object)
462 {
463 NMDBusManager *self = NM_DBUS_MANAGER (object);
464 NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
465 GHashTableIter iter;
466 GObject *exported;
467
468 if (priv->exported) {
469 g_hash_table_iter_init (&iter, priv->exported);
470 while (g_hash_table_iter_next (&iter, (gpointer) &exported, NULL))
471 g_object_weak_unref (exported, (GWeakNotify) object_destroyed, self);
472
473 g_hash_table_destroy (priv->exported);
474 priv->exported = NULL;
475 }
476
477 g_slist_free_full (priv->private_servers, private_server_free);
478 priv->private_servers = NULL;
479 priv->priv_server = NULL;
480
481 nm_dbus_manager_cleanup (self, TRUE);
482
483 if (priv->reconnect_id) {
484 g_source_remove (priv->reconnect_id);
485 priv->reconnect_id = 0;
486 }
487
488 G_OBJECT_CLASS (nm_dbus_manager_parent_class)->dispose (object);
489 }
490
491 static void
492 nm_dbus_manager_class_init (NMDBusManagerClass *klass)
493 {
494 GObjectClass *object_class = G_OBJECT_CLASS (klass);
495
496 g_type_class_add_private (klass, sizeof (NMDBusManagerPrivate));
497
498 object_class->dispose = nm_dbus_manager_dispose;
499
500 signals[DBUS_CONNECTION_CHANGED] =
501 g_signal_new (NM_DBUS_MANAGER_DBUS_CONNECTION_CHANGED,
502 G_OBJECT_CLASS_TYPE (object_class),
503 G_SIGNAL_RUN_LAST,
504 G_STRUCT_OFFSET (NMDBusManagerClass, dbus_connection_changed),
505 NULL, NULL, NULL,
506 G_TYPE_NONE, 1, G_TYPE_POINTER);
507
508 signals[NAME_OWNER_CHANGED] =
509 g_signal_new (NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
510 G_OBJECT_CLASS_TYPE (object_class),
511 G_SIGNAL_RUN_LAST,
512 G_STRUCT_OFFSET (NMDBusManagerClass, name_owner_changed),
513 NULL, NULL, NULL,
514 G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
515
516 signals[PRIVATE_CONNECTION_NEW] =
517 g_signal_new (NM_DBUS_MANAGER_PRIVATE_CONNECTION_NEW,
518 G_OBJECT_CLASS_TYPE (object_class),
519 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
520 G_STRUCT_OFFSET (NMDBusManagerClass, private_connection_new),
521 NULL, NULL, NULL,
522 G_TYPE_NONE, 1, G_TYPE_POINTER);
523
524 signals[PRIVATE_CONNECTION_DISCONNECTED] =
525 g_signal_new (NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED,
526 G_OBJECT_CLASS_TYPE (object_class),
527 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
528 G_STRUCT_OFFSET (NMDBusManagerClass, private_connection_disconnected),
529 NULL, NULL, NULL,
530 G_TYPE_NONE, 1, G_TYPE_POINTER);
531 }
532
533
534 /* Only cleanup a specific dbus connection, not all our private data */
535 static void
536 nm_dbus_manager_cleanup (NMDBusManager *self, gboolean dispose)
537 {
538 NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
539
540 if (priv->proxy) {
541 if (dispose) {
542 g_signal_handler_disconnect (priv->proxy, priv->proxy_destroy_id);
543 priv->proxy_destroy_id = 0;
544 }
545 g_object_unref (priv->proxy);
546 priv->proxy = NULL;
547 }
548
549 if (priv->g_connection) {
550 dbus_g_connection_unref (priv->g_connection);
551 priv->g_connection = NULL;
552 priv->connection = NULL;
553 }
554
555 priv->started = FALSE;
556 }
557
558 static gboolean
559 nm_dbus_manager_reconnect (gpointer user_data)
560 {
561 NMDBusManager *self = NM_DBUS_MANAGER (user_data);
562 NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
563
564 g_assert (self != NULL);
565
566 if (nm_dbus_manager_init_bus (self)) {
567 if (nm_dbus_manager_start_service (self)) {
568 nm_log_info (LOGD_CORE, "reconnected to the system bus.");
569 g_signal_emit (self, signals[DBUS_CONNECTION_CHANGED],
570 0, priv->connection);
571 priv->reconnect_id = 0;
572 return FALSE;
573 }
574 }
575
576 /* Try again */
577 nm_dbus_manager_cleanup (self, FALSE);
578 return TRUE;
579 }
580
581 static void
582 start_reconnection_timeout (NMDBusManager *self)
583 {
584 NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
585
586 if (priv->reconnect_id)
587 g_source_remove (priv->reconnect_id);
588
589 /* Schedule timeout for reconnection attempts */
590 priv->reconnect_id = g_timeout_add_seconds (3, nm_dbus_manager_reconnect, self);
591 }
592
593 char *
594 nm_dbus_manager_get_name_owner (NMDBusManager *self,
595 const char *name,
596 GError **error)
597 {
598 char *owner = NULL;
599
600 g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), NULL);
601 g_return_val_if_fail (name != NULL, NULL);
602 if (error)
603 g_return_val_if_fail (*error == NULL, NULL);
604
605 if (!NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy)
606 return NULL;
607
608 if (!dbus_g_proxy_call_with_timeout (NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy,
609 "GetNameOwner", 2000, error,
610 G_TYPE_STRING, name,
611 G_TYPE_INVALID,
612 G_TYPE_STRING, &owner,
613 G_TYPE_INVALID)) {
614 return NULL;
615 }
616
617 return owner;
618 }
619
620 gboolean
621 nm_dbus_manager_name_has_owner (NMDBusManager *self,
622 const char *name)
623 {
624 gboolean has_owner = FALSE;
625 GError *err = NULL;
626
627 g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), FALSE);
628 g_return_val_if_fail (name != NULL, FALSE);
629
630 if (!NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy)
631 return FALSE;
632
633 if (!dbus_g_proxy_call (NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy,
634 "NameHasOwner", &err,
635 G_TYPE_STRING, name,
636 G_TYPE_INVALID,
637 G_TYPE_BOOLEAN, &has_owner,
638 G_TYPE_INVALID)) {
639 nm_log_warn (LOGD_CORE, "NameHasOwner request failed: %s",
640 (err && err->message) ? err->message : "(unknown)");
641 g_clear_error (&err);
642 }
643
644 return has_owner;
645 }
646
647 static void
648 proxy_name_owner_changed (DBusGProxy *proxy,
649 const char *name,
650 const char *old_owner,
651 const char *new_owner,
652 gpointer user_data)
653 {
654 g_signal_emit (G_OBJECT (user_data), signals[NAME_OWNER_CHANGED],
655 0, name, old_owner, new_owner);
656 }
657
658 static void
659 destroy_cb (DBusGProxy *proxy, gpointer user_data)
660 {
661 NMDBusManager *self = NM_DBUS_MANAGER (user_data);
662
663 /* Clean up existing connection */
664 nm_log_warn (LOGD_CORE, "disconnected by the system bus.");
665 NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy = NULL;
666
667 nm_dbus_manager_cleanup (self, FALSE);
668
669 g_signal_emit (G_OBJECT (self), signals[DBUS_CONNECTION_CHANGED], 0, NULL);
670
671 start_reconnection_timeout (self);
672 }
673
674 static gboolean
675 nm_dbus_manager_init_bus (NMDBusManager *self)
676 {
677 NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
678
679 if (priv->connection) {
680 nm_log_warn (LOGD_CORE, "DBus Manager already has a valid connection.");
681 return FALSE;
682 }
683
684 dbus_connection_set_change_sigpipe (TRUE);
685
686 priv->g_connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL);
687 if (!priv->g_connection) {
688 /* Log with 'info' severity; there won't be a bus daemon in minimal
689 * environments (eg, initrd) where we only want to use the private
690 * socket.
691 */
692 nm_log_info (LOGD_CORE, "Could not connect to the system bus; only the "
693 "private D-Bus socket will be available.");
694 return FALSE;
695 }
696
697 priv->connection = dbus_g_connection_get_connection (priv->g_connection);
698 dbus_connection_set_exit_on_disconnect (priv->connection, FALSE);
699
700 priv->proxy = dbus_g_proxy_new_for_name (priv->g_connection,
701 "org.freedesktop.DBus",
702 "/org/freedesktop/DBus",
703 "org.freedesktop.DBus");
704
705 priv->proxy_destroy_id = g_signal_connect (priv->proxy, "destroy",
706 G_CALLBACK (destroy_cb), self);
707
708 dbus_g_proxy_add_signal (priv->proxy, "NameOwnerChanged",
709 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
710 G_TYPE_INVALID);
711 dbus_g_proxy_connect_signal (priv->proxy,
712 "NameOwnerChanged",
713 G_CALLBACK (proxy_name_owner_changed),
714 self, NULL);
715 return TRUE;
716 }
717
718 /* Register our service on the bus; shouldn't be called until
719 * all necessary message handlers have been registered, because
720 * when we register on the bus, clients may start to call.
721 */
722 gboolean
723 nm_dbus_manager_start_service (NMDBusManager *self)
724 {
725 NMDBusManagerPrivate *priv;
726 int result;
727 GError *err = NULL;
728
729 g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), FALSE);
730
731 priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
732
733 if (priv->started) {
734 nm_log_err (LOGD_CORE, "Service has already started.");
735 return FALSE;
736 }
737
738 /* Pointless to request a name when we aren't connected to the bus */
739 if (!priv->proxy)
740 return FALSE;
741
742 if (!dbus_g_proxy_call (priv->proxy, "RequestName", &err,
743 G_TYPE_STRING, NM_DBUS_SERVICE,
744 G_TYPE_UINT, DBUS_NAME_FLAG_DO_NOT_QUEUE,
745 G_TYPE_INVALID,
746 G_TYPE_UINT, &result,
747 G_TYPE_INVALID)) {
748 nm_log_err (LOGD_CORE, "Could not acquire the NetworkManager service.\n"
749 " Error: '%s'",
750 (err && err->message) ? err->message : "(unknown)");
751 g_error_free (err);
752 return FALSE;
753 }
754
755 if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
756 nm_log_err (LOGD_CORE, "Could not acquire the NetworkManager service as it is already taken.");
757 return FALSE;
758 }
759
760 priv->started = TRUE;
761 return priv->started;
762 }
763
764 DBusConnection *
765 nm_dbus_manager_get_dbus_connection (NMDBusManager *self)
766 {
767 g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), NULL);
768
769 return NM_DBUS_MANAGER_GET_PRIVATE (self)->connection;
770 }
771
772 DBusGConnection *
773 nm_dbus_manager_get_connection (NMDBusManager *self)
774 {
775 g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), NULL);
776
777 return NM_DBUS_MANAGER_GET_PRIVATE (self)->g_connection;
778 }
779
780 static void
781 object_destroyed (NMDBusManager *self, gpointer object)
782 {
783 g_hash_table_remove (NM_DBUS_MANAGER_GET_PRIVATE (self)->exported, object);
784 }
785
786 void
787 nm_dbus_manager_register_exported_type (NMDBusManager *self,
788 GType object_type,
789 const DBusGObjectInfo *info)
790 {
791 const char *properties_info, *dbus_name, *gobject_name, *tmp_access;
792
793 dbus_g_object_type_install_info (object_type, info);
794 if (!info->exported_properties)
795 return;
796
797 properties_info = info->exported_properties;
798 while (*properties_info) {
799 /* The format is: "interface\0DBusPropertyName\0gobject_property_name\0access\0" */
800 dbus_name = strchr (properties_info, '\0') + 1;
801 gobject_name = strchr (dbus_name, '\0') + 1;
802 tmp_access = strchr (gobject_name, '\0') + 1;
803 properties_info = strchr (tmp_access, '\0') + 1;
804
805 /* Note that nm-properties-changed-signal takes advantage of the
806 * fact that @dbus_name and @gobject_name are static data that won't
807 * ever be freed.
808 */
809 nm_properties_changed_signal_add_property (object_type, dbus_name, gobject_name);
810 }
811 }
812
813 void
814 nm_dbus_manager_register_object (NMDBusManager *self,
815 const char *path,
816 gpointer object)
817 {
818 NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
819 GHashTableIter iter;
820 DBusConnection *connection;
821
822 g_assert (G_IS_OBJECT (object));
823
824 g_warn_if_fail (g_hash_table_lookup (priv->exported, object) == NULL);
825 g_hash_table_insert (priv->exported, G_OBJECT (object), g_strdup (path));
826
827 if (priv->g_connection)
828 dbus_g_connection_register_g_object (priv->g_connection, path, G_OBJECT (object));
829
830 if (priv->priv_server) {
831 g_hash_table_iter_init (&iter, priv->priv_server->connections);
832 while (g_hash_table_iter_next (&iter, (gpointer) &connection, NULL)) {
833 dbus_g_connection_register_g_object (dbus_connection_get_g_connection (connection),
834 path,
835 G_OBJECT (object));
836 }
837 }
838
839 g_object_weak_ref (G_OBJECT (object), (GWeakNotify) object_destroyed, self);
840 }
841
842 void
843 nm_dbus_manager_unregister_object (NMDBusManager *self, gpointer object)
844 {
845 NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
846 GHashTableIter iter;
847 DBusConnection *connection;
848
849 g_assert (G_IS_OBJECT (object));
850
851 g_hash_table_remove (NM_DBUS_MANAGER_GET_PRIVATE (self)->exported, G_OBJECT (object));
852 g_object_weak_unref (G_OBJECT (object), (GWeakNotify) object_destroyed, self);
853
854 if (priv->g_connection)
855 dbus_g_connection_unregister_g_object (priv->g_connection, G_OBJECT (object));
856
857 if (priv->priv_server) {
858 g_hash_table_iter_init (&iter, priv->priv_server->connections);
859 while (g_hash_table_iter_next (&iter, (gpointer) &connection, NULL)) {
860 dbus_g_connection_unregister_g_object (dbus_connection_get_g_connection (connection),
861 G_OBJECT (object));
862 }
863 }
864 }
865
866 /**
867 * nm_dbus_manager_new_proxy:
868 * @self: the #NMDBusManager
869 * @context: the method call context this proxy should be created
870 * @name: any name on the message bus
871 * @path: name of the object instance to call methods on
872 * @iface: name of the interface to call methods on
873 *
874 * Creates a new proxy for a name on a given bus. Since the process which
875 * called the D-Bus method could be coming from a private connection or the
876 * system bus connection, differnet proxies must be created for each case. This
877 * function abstracts that.
878 *
879 * Returns: a #DBusGProxy capable of calling D-Bus methods of the calling process
880 */
881 DBusGProxy *
882 nm_dbus_manager_new_proxy (NMDBusManager *self,
883 DBusGMethodInvocation *context,
884 const char *name,
885 const char *path,
886 const char *iface)
887 {
888 NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
889 DBusGConnection *connection;
890 GSList *iter;
891 const char *owner;
892
893 connection = dbus_g_method_invocation_get_g_connection (context);
894 g_assert (connection);
895
896 /* Might be a private connection, for which we fake a sender */
897 for (iter = priv->private_servers; iter; iter = g_slist_next (iter)) {
898 PrivateServer *s = iter->data;
899
900 owner = private_server_get_connection_owner (s, connection);
901 if (owner) {
902 g_assert_cmpstr (owner, ==, name);
903 return dbus_g_proxy_new_for_peer (connection, path, iface);
904 }
905 }
906
907 return dbus_g_proxy_new_for_name (connection, name, path, iface);
908 }
909
910 #if !HAVE_DBUS_GLIB_GMI_GET_CONNECTION
911 struct _HACKDBusGMethodInvocation {
912 DBusGConnection *connection;
913 /* ... */
914 };
915
916 DBusGConnection *
917 dbus_g_method_invocation_get_g_connection (DBusGMethodInvocation *context)
918 {
919 /* Evil hack; this method exists in dbus-glib >= 101, but if we don't
920 * have that, emulate it.
921 */
922 return ((struct _HACKDBusGMethodInvocation *) context)->connection;
923 }
924 #endif /* HAVE_DBUS_GLIB_GMI_GET_CONNECTION */
925