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) 2009 - 2010 Red Hat, Inc.
19   	 * Copyright (C) 2009 Novell, Inc.
20   	 * Copyright (C) 2009 Canonical Ltd.
21   	 */
22   	
23   	#include <string.h>
24   	#include "config.h"
25   	#include "nm-modem-manager.h"
26   	#include "nm-logging.h"
27   	#include "nm-modem.h"
28   	#include "nm-modem-old.h"
29   	#include "nm-dbus-manager.h"
30   	#include "nm-modem-old-types.h"
31   	#include "nm-dbus-glib-types.h"
32   	
33   	#if WITH_MODEM_MANAGER_1
34   	#include <libmm-glib.h>
35   	#include "nm-modem-broadband.h"
36   	#endif
37   	
38   	#define MODEM_POKE_INTERVAL 120
39   	
40   	G_DEFINE_TYPE (NMModemManager, nm_modem_manager, G_TYPE_OBJECT)
41   	
42   	struct _NMModemManagerPrivate {
43   		/* ModemManager < 0.7 */
44   		NMDBusManager *dbus_mgr;
45   		DBusGProxy *proxy;
46   		guint poke_id;
47   	
48   	#if WITH_MODEM_MANAGER_1
49   		/* ModemManager >= 0.7 */
50   		GDBusConnection *dbus_connection;
51   		MMManager *modem_manager_1;
52   		guint modem_manager_1_poke_id;
53   		gboolean old_modem_manager_found;
54   		gboolean new_modem_manager_found;
55   		guint modem_manager_1_name_owner_changed_id;
56   		guint modem_manager_1_object_added_id;
57   		guint modem_manager_1_object_removed_id;
58   	#endif
59   	
60   		/* Common */
61   		GHashTable *modems;
62   	};
63   	
64   	enum {
65   		MODEM_ADDED,
66   		MODEM_REMOVED,
67   	
68   		LAST_SIGNAL
69   	};
70   	
71   	static guint signals[LAST_SIGNAL] = { 0 };
72   	
73   	
74   	NMModemManager *
75   	nm_modem_manager_get (void)
76   	{
77   		static NMModemManager *singleton = NULL;
78   	
79   		if (!singleton)
80   			singleton = NM_MODEM_MANAGER (g_object_new (NM_TYPE_MODEM_MANAGER, NULL));
81   		else
82   			g_object_ref (singleton);
83   	
84   		g_assert (singleton);
85   		return singleton;
86   	}
87   	
88   	/************************************************************************/
89   	/* Support for ModemManager < 0.7 */
90   	
91   	static void
92   	clear_modem_manager_support (NMModemManager *self)
93   	{
94   		if (self->priv->poke_id) {
95   			g_source_remove (self->priv->poke_id);
96   			self->priv->poke_id = 0;
97   		}
98   	
99   		if (self->priv->proxy) {
100  			g_object_unref (self->priv->proxy);
101  			self->priv->proxy = NULL;
102  		}
103  	}
104  	
105  	static void
106  	create_modem (NMModemManager *self, const char *path)
107  	{
108  		DBusGProxy *proxy;
109  		GError *error = NULL;
110  		NMModem *modem = NULL;
111  		GHashTable *properties;
112  	
113  		if (g_hash_table_lookup (self->priv->modems, path)) {
114  			nm_log_warn (LOGD_MB, "modem with path %s already exists, ignoring", path);
115  			return;
116  		}
117  	
118  		proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (self->priv->dbus_mgr),
119  		                                   MM_OLD_DBUS_SERVICE,
120  		                                   path,
121  		                                   DBUS_INTERFACE_PROPERTIES);
122  		g_assert (proxy);
123  		if (dbus_g_proxy_call_with_timeout (proxy, "GetAll", 15000, &error,
124  		                                    G_TYPE_STRING, MM_OLD_DBUS_INTERFACE_MODEM,
125  		                                    G_TYPE_INVALID,
126  		                                    DBUS_TYPE_G_MAP_OF_VARIANT, &properties,
127  		                                    G_TYPE_INVALID)) {
128  			/* Success, create the modem */
129  			modem = nm_modem_old_new (path, properties, &error);
130  			if (modem) {
131  				g_hash_table_insert (self->priv->modems, g_strdup (path), modem);
132  				g_signal_emit (self, signals[MODEM_ADDED], 0, modem, nm_modem_get_driver (modem));
133  			} else {
134  				nm_log_warn (LOGD_MB, "failed to create modem: %s",
135  					         error ? error->message : "(unknown)");
136  			}
137  			g_hash_table_destroy (properties);
138  		} else {
139  			nm_log_warn (LOGD_MB, "could not get modem properties: %s %s",
140  			             error ? dbus_g_error_get_name (error) : "(none)",
141  			             error ? error->message : "(unknown)");
142  		}
143  	
144  		g_object_unref (proxy);
145  		g_clear_error (&error);
146  	}
147  	
148  	static void
149  	modem_added (DBusGProxy *proxy, const char *path, gpointer user_data)
150  	{
151  		create_modem (NM_MODEM_MANAGER (user_data), path);
152  	}
153  	
154  	static void
155  	modem_removed (DBusGProxy *proxy, const char *path, gpointer user_data)
156  	{
157  		NMModemManager *self = NM_MODEM_MANAGER (user_data);
158  		NMModem *modem;
159  	
160  		modem = (NMModem *) g_hash_table_lookup (self->priv->modems, path);
161  		if (modem) {
162  			g_signal_emit (self, signals[MODEM_REMOVED], 0, modem);
163  			g_hash_table_remove (self->priv->modems, path);
164  		}
165  	}
166  	
167  	static void
168  	mm_poke_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
169  	{
170  		GPtrArray *modems;
171  		int i;
172  	
173  		if (dbus_g_proxy_end_call (proxy, call, NULL,
174  		                           DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, &modems,
175  		                           G_TYPE_INVALID)) {
176  			/* Don't care about the returned value, just free it */
177  			for (i = 0; i < modems->len; i++)
178  				g_free ((char *) g_ptr_array_index (modems, i));
179  			g_ptr_array_free (modems, TRUE);
180  		}
181  		g_object_unref (proxy);
182  	}
183  	
184  	static gboolean
185  	poke_modem_cb (gpointer user_data)
186  	{
187  		NMModemManager *self = NM_MODEM_MANAGER (user_data);
188  		DBusGConnection *g_connection;
189  		DBusGProxy *proxy;
190  		DBusGProxyCall *call;
191  	
192  		g_connection = nm_dbus_manager_get_connection (self->priv->dbus_mgr);
193  		proxy = dbus_g_proxy_new_for_name (g_connection,
194  										   MM_OLD_DBUS_SERVICE,
195  										   MM_OLD_DBUS_PATH,
196  										   MM_OLD_DBUS_INTERFACE);
197  	
198  		nm_log_dbg (LOGD_MB, "Requesting to (re)launch modem-manager...");
199  	
(1) Event returned_pointer: Pointer "call" returned by "dbus_g_proxy_begin_call_with_timeout(proxy, "EnumerateDevices", mm_poke_cb(DBusGProxy *, DBusGProxyCall *, gpointer), NULL, NULL, 5000, 0UL)" is never used.
200  		call = dbus_g_proxy_begin_call_with_timeout (proxy,
201  		                                             "EnumerateDevices",
202  		                                             mm_poke_cb,
203  		                                             NULL,
204  		                                             NULL,
205  		                                             5000,
206  		                                             G_TYPE_INVALID);
207  		return TRUE;
208  	}
209  	
210  	static void
211  	enumerate_devices_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer data)
212  	{
213  		NMModemManager *manager = NM_MODEM_MANAGER (data);
214  		GPtrArray *modems;
215  		GError *error = NULL;
216  	
217  		if (!dbus_g_proxy_end_call (proxy, call_id, &error,
218  									dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &modems,
219  									G_TYPE_INVALID)) {
220  			nm_log_warn (LOGD_MB, "could not get modem list: %s", error->message);
221  			g_error_free (error);
222  		} else {
223  			int i;
224  	
225  			for (i = 0; i < modems->len; i++) {
226  				char *path = (char *) g_ptr_array_index (modems, i);
227  	
228  				create_modem (manager, path);
229  				g_free (path);
230  			}
231  	
232  			g_ptr_array_free (modems, TRUE);
233  		}
234  	}
235  	
236  	#if WITH_MODEM_MANAGER_1
237  	static void clear_modem_manager_1_support (NMModemManager *self);
238  	#endif
239  	
240  	static void
241  	modem_manager_appeared (NMModemManager *self, gboolean enumerate_devices)
242  	{
243  		if (self->priv->poke_id) {
244  			g_source_remove (self->priv->poke_id);
245  			self->priv->poke_id = 0;
246  		}
247  	
248  		nm_log_info (LOGD_MB, "modem-manager is now available");
249  	
250  	#if WITH_MODEM_MANAGER_1
251  		self->priv->old_modem_manager_found = TRUE;
252  		if (self->priv->new_modem_manager_found)
253  			nm_log_warn (LOGD_MB, "Both the old and the new ModemManager were found");
254  		else
255  			clear_modem_manager_1_support (self);
256  	#endif
257  	
258  		self->priv->proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (self->priv->dbus_mgr),
259  		                                               MM_OLD_DBUS_SERVICE, MM_OLD_DBUS_PATH, MM_OLD_DBUS_INTERFACE);
260  	
261  		dbus_g_proxy_add_signal (self->priv->proxy, "DeviceAdded", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
262  		dbus_g_proxy_connect_signal (self->priv->proxy, "DeviceAdded",
263  									 G_CALLBACK (modem_added), self,
264  									 NULL);
265  	
266  		dbus_g_proxy_add_signal (self->priv->proxy, "DeviceRemoved", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
267  		dbus_g_proxy_connect_signal (self->priv->proxy, "DeviceRemoved",
268  									 G_CALLBACK (modem_removed), self,
269  									 NULL);
270  	
271  		if (enumerate_devices)
272  			dbus_g_proxy_begin_call (self->priv->proxy, "EnumerateDevices", enumerate_devices_done, self, NULL, G_TYPE_INVALID);
273  	}
274  	
275  	static gboolean
276  	remove_one_modem (gpointer key, gpointer value, gpointer user_data)
277  	{
278  		g_signal_emit (user_data, signals[MODEM_REMOVED], 0, value);
279  	
280  		return TRUE;
281  	}
282  	
283  	static void
284  	modem_manager_disappeared (NMModemManager *self)
285  	{
286  		g_hash_table_foreach_remove (self->priv->modems, remove_one_modem, self);
287  	
288  		if (self->priv->proxy) {
289  			g_object_unref (self->priv->proxy);
290  			self->priv->proxy = NULL;
291  		}
292  	
293  		/* Try to activate the modem-manager */
294  		nm_log_dbg (LOGD_MB, "trying to start the modem manager...");
295  		poke_modem_cb (self);
296  		self->priv->poke_id = g_timeout_add_seconds (MODEM_POKE_INTERVAL, poke_modem_cb, self);
297  	}
298  	
299  	static void
300  	nm_modem_manager_name_owner_changed (NMDBusManager *dbus_mgr,
301  										 const char *name,
302  										 const char *old_owner,
303  										 const char *new_owner,
304  										 gpointer user_data)
305  	{
306  		gboolean old_owner_good;
307  		gboolean new_owner_good;
308  	
309  		/* Can't handle the signal if its not from the modem service */
310  		if (strcmp (MM_OLD_DBUS_SERVICE, name) != 0)
311  			return;
312  	
313  		old_owner_good = (old_owner && strlen (old_owner));
314  		new_owner_good = (new_owner && strlen (new_owner));
315  	
316  		if (!old_owner_good && new_owner_good) {
317  			modem_manager_appeared (NM_MODEM_MANAGER (user_data), FALSE);
318  		} else if (old_owner_good && !new_owner_good) {
319  			nm_log_info (LOGD_MB, "the modem manager disappeared");
320  			modem_manager_disappeared (NM_MODEM_MANAGER (user_data));
321  		}
322  	}
323  	
324  	/************************************************************************/
325  	/* Support for ModemManager >= 0.7 */
326  	
327  	#if WITH_MODEM_MANAGER_1
328  	
329  	static void
330  	modem_manager_1_clear_signals (NMModemManager *self)
331  	{
332  		if (!self->priv->modem_manager_1)
333  			return;
334  	
335  		if (self->priv->modem_manager_1_name_owner_changed_id) {
336  			if (g_signal_handler_is_connected (self->priv->modem_manager_1,
337  			                                   self->priv->modem_manager_1_name_owner_changed_id))
338  				g_signal_handler_disconnect (self->priv->modem_manager_1,
339  				                             self->priv->modem_manager_1_name_owner_changed_id);
340  			self->priv->modem_manager_1_name_owner_changed_id = 0;
341  		}
342  	
343  		if (self->priv->modem_manager_1_object_added_id) {
344  			if (g_signal_handler_is_connected (self->priv->modem_manager_1,
345  			                                   self->priv->modem_manager_1_object_added_id))
346  				g_signal_handler_disconnect (self->priv->modem_manager_1,
347  				                             self->priv->modem_manager_1_object_added_id);
348  			self->priv->modem_manager_1_object_added_id = 0;
349  		}
350  	
351  		if (self->priv->modem_manager_1_object_removed_id) {
352  			if (g_signal_handler_is_connected (self->priv->modem_manager_1,
353  			                                   self->priv->modem_manager_1_object_removed_id))
354  				g_signal_handler_disconnect (self->priv->modem_manager_1,
355  				                             self->priv->modem_manager_1_object_removed_id);
356  			self->priv->modem_manager_1_object_removed_id = 0;
357  		}
358  	}
359  	
360  	static void
361  	clear_modem_manager_1_support (NMModemManager *self)
362  	{
363  		if (self->priv->modem_manager_1_poke_id) {
364  			g_source_remove (self->priv->modem_manager_1_poke_id);
365  			self->priv->modem_manager_1_poke_id = 0;
366  		}
367  	
368  		modem_manager_1_clear_signals (self);
369  		g_clear_object (&self->priv->modem_manager_1);
370  		g_clear_object (&self->priv->dbus_connection);
371  	}
372  	
373  	static void
374  	modem_object_added (MMManager *modem_manager,
375  	                    MMObject  *modem_object,
376  	                    NMModemManager *self)
377  	{
378  		const gchar *path;
379  		gchar *drivers;
380  		MMModem *modem_iface;
381  		NMModem *modem;
382  	
383  		/* Ensure we don't have the same modem already */
384  		path = mm_object_get_path (modem_object);
385  		if (g_hash_table_lookup (self->priv->modems, path)) {
386  			nm_log_warn (LOGD_MB, "modem with path %s already exists, ignoring", path);
387  			return;
388  		}
389  	
390  		/* Ensure we have the 'Modem' interface at least */
391  		modem_iface = mm_object_peek_modem (modem_object);
392  		if (!modem_iface) {
393  			nm_log_warn (LOGD_MB, "modem with path %s doesn't have the Modem interface, ignoring", path);
394  			return;
395  		}
396  	
397  		/* Ensure we have a primary port reported */
398  		if (!mm_modem_get_primary_port (modem_iface)) {
399  			nm_log_warn (LOGD_MB, "modem with path %s has unknown primary port, ignoring", path);
400  			return;
401  		}
402  	
403  		/* Create a new modem object */
404  		modem = nm_modem_broadband_new (G_OBJECT (modem_object));
405  		if (!modem)
406  			return;
407  	
408  		/* Build a single string with all drivers listed */
409  		drivers = g_strjoinv (", ", (gchar **)mm_modem_get_drivers (modem_iface));
410  	
411  		/* Keep track of the new modem and notify about it */
412  		g_hash_table_insert (self->priv->modems, g_strdup (path), modem);
413  		g_signal_emit (self, signals[MODEM_ADDED], 0, modem, drivers);
414  		g_free (drivers);
415  	}
416  	
417  	static void
418  	modem_object_removed (MMManager *manager,
419  	                      MMObject  *modem_object,
420  	                      NMModemManager *self)
421  	{
422  		NMModem *modem;
423  		const gchar *path;
424  	
425  		path = mm_object_get_path (modem_object);
426  		modem = (NMModem *) g_hash_table_lookup (self->priv->modems, path);
427  		if (!modem)
428  			return;
429  	
430  		g_signal_emit (self, signals[MODEM_REMOVED], 0, modem);
431  		g_hash_table_remove (self->priv->modems, path);
432  	}
433  	
434  	static void
435  	modem_manager_1_available (NMModemManager *self)
436  	{
437  		GList *modems, *l;
438  	
439  		nm_log_info (LOGD_MB, "ModemManager available in the bus");
440  	
441  		self->priv->new_modem_manager_found = TRUE;
442  		if (self->priv->old_modem_manager_found)
443  			nm_log_warn (LOGD_MB, "Both the old and the new ModemManager were found");
444  		else
445  			clear_modem_manager_support (self);
446  	
447  		/* Update initial modems list */
448  	    modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (self->priv->modem_manager_1));
449  	    for (l = modems; l; l = g_list_next (l))
450  		    modem_object_added (self->priv->modem_manager_1, MM_OBJECT (l->data), self);
451  	    g_list_free_full (modems, (GDestroyNotify) g_object_unref);
452  	}
453  	
454  	static void schedule_modem_manager_1_relaunch (NMModemManager *self,
455  	                                               guint n_seconds);
456  	static void ensure_client                     (NMModemManager *self);
457  	
458  	static void
459  	modem_manager_1_name_owner_changed (MMManager *modem_manager_1,
460  	                                    GParamSpec *pspec,
461  	                                    NMModemManager *self)
462  	{
463  		gchar *name_owner;
464  	
465  		/* Quit poking, if any */
466  		if (self->priv->modem_manager_1_poke_id) {
467  			g_source_remove (self->priv->modem_manager_1_poke_id);
468  			self->priv->modem_manager_1_poke_id = 0;
469  		}
470  	
471  		name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (modem_manager_1));
472  		if (!name_owner) {
473  			nm_log_info (LOGD_MB, "ModemManager disappeared from bus");
474  			schedule_modem_manager_1_relaunch (self, 0);
475  			return;
476  		}
477  	
478  		/* Available! */
479  		g_free (name_owner);
480  	
481  		/* Hack alert: GDBusObjectManagerClient won't signal neither 'object-added'
482  		 * nor 'object-removed' if it was created while there was no ModemManager in
483  		 * the bus. This hack avoids this issue until we get a GIO with the fix
484  		 * included... */
485  		modem_manager_1_clear_signals (self);
486  		g_clear_object (&self->priv->modem_manager_1);
487  		ensure_client (self);
488  	
489  		/* Whenever GDBusObjectManagerClient is fixed, we can just do the following:
490  		 * modem_manager_1_available (self);
491  		 */
492  	}
493  	
494  	static void
495  	modem_manager_1_poke_cb (GDBusConnection *connection,
496  	                         GAsyncResult *res,
497  	                         NMModemManager *self)
498  	{
499  		GError *error = NULL;
500  		GVariant *result;
501  	
502  		result = g_dbus_connection_call_finish (connection, res, &error);
503  		if (error) {
504  			/* Ignore common errors when MM is not installed and such */
505  			if (   !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)
506  			    && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_EXEC_FAILED)
507  			    && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_FORK_FAILED)
508  			    && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_FAILED)
509  			    && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_TIMEOUT)
510  			    && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND)) {
511  				nm_log_dbg (LOGD_MB, "error poking ModemManager: %s", error->message);
512  			}
513  			g_error_free (error);
514  	
515  			/* Setup timeout to relaunch */
516  			schedule_modem_manager_1_relaunch (self, MODEM_POKE_INTERVAL);
517  		} else
518  			g_variant_unref (result);
519  	
520  		/* Balance refcount */
521  		g_object_unref (self);
522  	}
523  	
524  	static void
525  	modem_manager_1_poke (NMModemManager *self)
526  	{
527  		gchar *name_owner;
528  	
529  		/* If there is no current owner right away, ensure we poke to get one */
530  		name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (self->priv->modem_manager_1));
531  		if (name_owner) {
532  			/* Available! */
533  			modem_manager_1_available (self);
534  			g_free (name_owner);
535  			return;
536  		}
537  	
538  		/* Poke! */
539  		g_dbus_connection_call (self->priv->dbus_connection,
540  		                        "org.freedesktop.ModemManager1",
541  		                        "/org/freedesktop/ModemManager1",
542  		                        "org.freedesktop.DBus.Peer",
543  		                        "Ping",
544  		                        NULL, /* inputs */
545  		                        NULL, /* outputs */
546  		                        G_DBUS_CALL_FLAGS_NONE,
547  		                        -1,
548  		                        NULL, /* cancellable */
549  		                        (GAsyncReadyCallback)modem_manager_1_poke_cb, /* callback */
550  		                        g_object_ref (self)); /* user_data */
551  	}
552  	
553  	static void
554  	manager_new_ready (GObject *source,
555  	                   GAsyncResult *res,
556  	                   NMModemManager *self)
557  	{
558  		/* Note we always get an extra reference to self here */
559  	
560  		GError *error = NULL;
561  	
562  		g_assert (!self->priv->modem_manager_1);
563  		self->priv->modem_manager_1 = mm_manager_new_finish (res, &error);
564  		if (!self->priv->modem_manager_1) {
565  			/* We're not really supposed to get any error here. If we do get one,
566  			 * though, just re-schedule the MMManager creation after some time.
567  			 * During this period, name-owner changes won't be followed. */
568  			nm_log_warn (LOGD_MB, "error creating ModemManager client: %s", error->message);
569  			g_error_free (error);
570  			/* Setup timeout to relaunch */
571  			schedule_modem_manager_1_relaunch (self, MODEM_POKE_INTERVAL);
572  		} else if (self->priv->old_modem_manager_found) {
573  			/* If we found the old MM, abort */
574  			clear_modem_manager_1_support (self);
575  		} else {
576  			/* Setup signals in the GDBusObjectManagerClient */
577  			self->priv->modem_manager_1_name_owner_changed_id =
578  				g_signal_connect (self->priv->modem_manager_1,
579  				                  "notify::name-owner",
580  				                  G_CALLBACK (modem_manager_1_name_owner_changed),
581  				                  self);
582  			self->priv->modem_manager_1_object_added_id =
583  				g_signal_connect (self->priv->modem_manager_1,
584  				                  "object-added",
585  				                  G_CALLBACK (modem_object_added),
586  				                  self);
587  			self->priv->modem_manager_1_object_removed_id =
588  				g_signal_connect (self->priv->modem_manager_1,
589  				                  "object-removed",
590  				                  G_CALLBACK (modem_object_removed),
591  				                  self);
592  			/* Poke the MMManager! */
593  			modem_manager_1_poke (self);
594  		}
595  	
596  		/* Balance refcount */
597  		g_object_unref (self);
598  	}
599  	
600  	static void
601  	ensure_client (NMModemManager *self)
602  	{
603  		g_assert (self->priv->dbus_connection);
604  	
605  		/* Create the GDBusObjectManagerClient. We do not request to autostart, as
606  		 * we don't really want the MMManager creation to fail. We can always poke
607  		 * later on if we want to request the autostart */
608  		if (!self->priv->modem_manager_1) {
609  			mm_manager_new (self->priv->dbus_connection,
610  			                G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
611  			                NULL,
612  			                (GAsyncReadyCallback)manager_new_ready,
613  			                g_object_ref (self));
614  			return;
615  		}
616  	
617  		/* If already available, poke! */
618  		modem_manager_1_poke (self);
619  	}
620  	
621  	static void
622  	bus_get_ready (GObject *source,
623  	               GAsyncResult *res,
624  	               NMModemManager *self)
625  	{
626  		/* Note we always get an extra reference to self here */
627  	
628  		GError *error = NULL;
629  	
630  		self->priv->dbus_connection = g_bus_get_finish (res, &error);
631  		if (!self->priv->dbus_connection) {
632  			nm_log_warn (LOGD_CORE, "error getting bus connection: %s", error->message);
633  			g_error_free (error);
634  			/* Setup timeout to relaunch */
635  			schedule_modem_manager_1_relaunch (self, MODEM_POKE_INTERVAL);
636  		} else if (self->priv->old_modem_manager_found) {
637  			/* If we found the old MM, abort */
638  			clear_modem_manager_1_support (self);
639  		} else {
640  			/* Got the bus, create new ModemManager client. */
641  			ensure_client (self);
642  		}
643  	
644  		/* Balance refcount */
645  		g_object_unref (self);
646  	}
647  	
648  	static gboolean
649  	ensure_bus (NMModemManager *self)
650  	{
651  		/* Clear poke ID */
652  		self->priv->modem_manager_1_poke_id = 0;
653  	
654  		if (!self->priv->dbus_connection)
655  			g_bus_get (G_BUS_TYPE_SYSTEM,
656  			           NULL,
657  			           (GAsyncReadyCallback)bus_get_ready,
658  			           g_object_ref (self));
659  		else
660  			/* If bus is already available, ensure client */
661  			ensure_client (self);
662  	
663  		return FALSE;
664  	}
665  	
666  	static void
667  	schedule_modem_manager_1_relaunch (NMModemManager *self,
668  	                                   guint n_seconds)
669  	{
670  		/* No need to pass an extra reference to self; timeout/idle will be
671  		 * cancelled if the object gets disposed. */
672  	
673  		if (n_seconds)
674  			self->priv->modem_manager_1_poke_id = g_timeout_add_seconds (n_seconds, (GSourceFunc)ensure_bus, self);
675  		else
676  			self->priv->modem_manager_1_poke_id = g_idle_add ((GSourceFunc)ensure_bus, self);
677  	}
678  	
679  	#endif /* WITH_MODEM_MANAGER_1 */
680  	
681  	/************************************************************************/
682  	
683  	static void
684  	nm_modem_manager_init (NMModemManager *self)
685  	{
686  		self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_MODEM_MANAGER, NMModemManagerPrivate);
687  	
688  		self->priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
689  	
690  		/* ModemManager < 0.7 */
691  		self->priv->dbus_mgr = nm_dbus_manager_get ();
692  		g_signal_connect (self->priv->dbus_mgr, NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
693  						  G_CALLBACK (nm_modem_manager_name_owner_changed),
694  						  self);
695  		if (nm_dbus_manager_name_has_owner (self->priv->dbus_mgr, MM_OLD_DBUS_SERVICE))
696  			modem_manager_appeared (self, TRUE);
697  		else
698  			modem_manager_disappeared (self);
699  	
700  	#if WITH_MODEM_MANAGER_1
701  		/* ModemManager >= 0.7 */
702  		schedule_modem_manager_1_relaunch (self, 0);
703  	#endif
704  	}
705  	
706  	static void
707  	dispose (GObject *object)
708  	{
709  		NMModemManager *self = NM_MODEM_MANAGER (object);
710  	
711  		/* ModemManager < 0.7 */
712  		clear_modem_manager_support (self);
713  	
714  	#if WITH_MODEM_MANAGER_1
715  		/* ModemManager >= 0.7 */
716  		clear_modem_manager_1_support (self);
717  	#endif
718  	
719  		if (self->priv->modems) {
720  			g_hash_table_foreach_remove (self->priv->modems, remove_one_modem, object);
721  			g_hash_table_destroy (self->priv->modems);
722  		}
723  	
724  		self->priv->dbus_mgr = NULL;
725  	
726  		/* Chain up to the parent class */
727  		G_OBJECT_CLASS (nm_modem_manager_parent_class)->dispose (object);
728  	}
729  	
730  	static void
731  	nm_modem_manager_class_init (NMModemManagerClass *klass)
732  	{
733  		GObjectClass *object_class = G_OBJECT_CLASS (klass);
734  	
735  		g_type_class_add_private (object_class, sizeof (NMModemManagerPrivate));
736  	
737  		object_class->dispose = dispose;
738  	
739  		/* signals */
740  		signals[MODEM_ADDED] =
741  			g_signal_new ("modem-added",
742  						  G_OBJECT_CLASS_TYPE (object_class),
743  						  G_SIGNAL_RUN_FIRST,
744  						  G_STRUCT_OFFSET (NMModemManagerClass, modem_added),
745  						  NULL, NULL, NULL,
746  						  G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_STRING);
747  	
748  		signals[MODEM_REMOVED] =
749  			g_signal_new ("modem-removed",
750  						  G_OBJECT_CLASS_TYPE (object_class),
751  						  G_SIGNAL_RUN_FIRST,
752  						  G_STRUCT_OFFSET (NMModemManagerClass, modem_removed),
753  						  NULL, NULL, NULL,
754  						  G_TYPE_NONE, 1, G_TYPE_OBJECT);
755  	}
756