1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
2 /*
3 * Copyright © 2009–2011 Collabora Ltd.
4 * Copyright © 2013 Intel Corporation
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 * Authors:
21 * Jonny Lamb <jonny.lamb@collabora.co.uk>
22 * Will Thompson <will.thompson@collabora.co.uk>
23 */
24
25 #include "config.h"
26 #include "connectivity-monitor.h"
27
28 #include <errno.h>
29
30 #ifdef HAVE_GIO_UNIX
31 #include <gio/gunixfdlist.h>
32 #endif
33
34 #ifdef HAVE_NM
35 #include <NetworkManager.h>
36 #endif
37
38 #ifdef HAVE_UPOWER
39 #include <upower.h>
40 #endif
41
42 #include <telepathy-glib/telepathy-glib.h>
43
44 #include "mcd-debug.h"
45
46 #define LOGIN1_BUS_NAME "org.freedesktop.login1"
47 #define LOGIN1_MANAGER_OBJECT_PATH "/org/freedesktop/login1"
48 #define LOGIN1_MANAGER_IFACE "org.freedesktop.login1.Manager"
49 #define LOGIN1_MANAGER_PREPARE_FOR_SLEEP "PrepareForSleep"
50 #define LOGIN1_MANAGER_PREPARE_FOR_SHUTDOWN "PrepareForShutdown"
51 #define LOGIN1_MANAGER_INHIBIT "Inhibit"
52
53 struct _McdInhibit {
54 /* The number of reasons why we should delay sleep/shutdown. This behaves
55 * like a refcount: when it reaches 0, we close the fd and free the
56 * McdInhibit structure.
57 *
58 * 1 when we are waiting for PrepareForSleep/PrepareForShutdown;
59 * the number of extra "holds" (currently one per McdAccount) when we have
60 * received that signal and are waiting for each McdAccount to disconnect;
61 * temporarily 1 + number of extra "holds" while we are dealing with the
62 * signal.
63 */
64 gsize holds;
65
66 /* fd encapsulating the delay, provided by logind. We close this
67 * when we no longer have any reason to delay sleep/shutdown. */
68 int fd;
69 };
70
71 typedef enum {
72 CONNECTIVITY_NONE = 0,
73
74 /* Set if the device is not suspended; clear if it is suspending
75 * (or suspended, but we don't get scheduled then). */
76 CONNECTIVITY_AWAKE = (1 << 0),
77 /* Set if GNetworkMonitor says we're up. */
78 CONNECTIVITY_UP = (1 << 1),
79 /* Clear if NetworkManager says we're in a shaky state like
80 * disconnecting (the GNetworkMonitor can't tell this). Set otherwise. */
81 CONNECTIVITY_STABLE = (1 << 2),
82 /* Set if the device is not shutting down, clear if it is. */
83 CONNECTIVITY_RUNNING = (1 << 3)
84 } Connectivity;
85
86 struct _McdConnectivityMonitorPrivate {
87 GNetworkMonitor *network_monitor;
88
89 GDBusConnection *system_bus;
90 guint login1_prepare_for_sleep_id;
91 guint login1_prepare_for_shutdown_id;
92 McdInhibit *login1_inhibit;
93
94 #ifdef HAVE_NM
95 NMClient *nm_client;
96 gulong state_change_signal_id;
97 #endif
98
99 #ifdef HAVE_UPOWER
100 UpClient *upower_client;
101 #endif
102
103 #ifdef ENABLE_CONN_SETTING
104 /* Application settings we steal from under Empathy's nose. */
105 GSettings *settings;
106 #endif
107
108 Connectivity connectivity;
109 gboolean use_conn;
110 };
111
112 enum {
113 STATE_CHANGE,
114 LAST_SIGNAL
115 };
116
117 enum {
118 PROP_0,
119 PROP_USE_CONN,
120 };
121
122 static guint signals[LAST_SIGNAL];
123 static McdConnectivityMonitor *connectivity_monitor_singleton = NULL;
124
125 G_DEFINE_TYPE (McdConnectivityMonitor, mcd_connectivity_monitor, G_TYPE_OBJECT);
126
127 static gboolean
is_connected(Connectivity connectivity)128 is_connected (Connectivity connectivity)
129 {
130 return ((connectivity & CONNECTIVITY_AWAKE) &&
131 (connectivity & CONNECTIVITY_UP) &&
132 (connectivity & CONNECTIVITY_STABLE) &&
133 (connectivity & CONNECTIVITY_RUNNING));
134 }
135
136 static void
connectivity_monitor_change_states(McdConnectivityMonitor * self,Connectivity set,Connectivity clear,McdInhibit * inhibit)137 connectivity_monitor_change_states (
138 McdConnectivityMonitor *self,
139 Connectivity set,
140 Connectivity clear,
141 McdInhibit *inhibit)
142 {
143 McdConnectivityMonitorPrivate *priv = self->priv;
144 Connectivity connectivity = ((priv->connectivity | set) & (~clear));
145 gboolean old_total = is_connected (priv->connectivity);
146 gboolean new_total = is_connected (connectivity);
147
148 if (priv->connectivity == connectivity)
149 return;
150
151 DEBUG ("awake: %d -> %d; up: %d -> %d; stable: %d -> %d; running: %d -> %d",
152 (priv->connectivity & CONNECTIVITY_AWAKE),
153 (connectivity & CONNECTIVITY_AWAKE),
154 (priv->connectivity & CONNECTIVITY_UP),
155 (connectivity & CONNECTIVITY_UP),
156 (priv->connectivity & CONNECTIVITY_STABLE),
157 (connectivity & CONNECTIVITY_STABLE),
158 (priv->connectivity & CONNECTIVITY_RUNNING),
159 (connectivity & CONNECTIVITY_RUNNING));
160
161 priv->connectivity = connectivity;
162
163 if (old_total != new_total)
164 {
165 DEBUG ("%s", new_total ? "connected" : "disconnected");
166 g_signal_emit (self, signals[STATE_CHANGE], 0, new_total,
167 inhibit);
168 }
169 }
170
171 /* Calling this function makes us "more online" or has no effect */
172 static inline void
connectivity_monitor_add_states(McdConnectivityMonitor * self,Connectivity set,McdInhibit * inhibit)173 connectivity_monitor_add_states (
174 McdConnectivityMonitor *self,
175 Connectivity set,
176 McdInhibit *inhibit)
177 {
178 connectivity_monitor_change_states (self, set, CONNECTIVITY_NONE, inhibit);
179 }
180
181 /* Calling this function makes us "less online" or has no effect */
182 static inline void
connectivity_monitor_remove_states(McdConnectivityMonitor * self,Connectivity clear,McdInhibit * inhibit)183 connectivity_monitor_remove_states (
184 McdConnectivityMonitor *self,
185 Connectivity clear,
186 McdInhibit *inhibit)
187 {
188 connectivity_monitor_change_states (self, CONNECTIVITY_NONE, clear, inhibit);
189 }
190
191 #ifdef HAVE_NM
192
193 static void
connectivity_monitor_nm_state_change_cb(NMClient * client,const GParamSpec * pspec,McdConnectivityMonitor * connectivity_monitor)194 connectivity_monitor_nm_state_change_cb (NMClient *client,
195 const GParamSpec *pspec,
196 McdConnectivityMonitor *connectivity_monitor)
197 {
198 McdConnectivityMonitorPrivate *priv;
199 NMState state;
200
201 priv = connectivity_monitor->priv;
202
203 if (!priv->use_conn)
204 return;
205
206 state = nm_client_get_state (priv->nm_client);
207
208 if (state == NM_STATE_CONNECTING
209 || state == NM_STATE_DISCONNECTING
210 || state == NM_STATE_ASLEEP)
211 {
212 DEBUG ("New NetworkManager network state %d (unstable state)", state);
213
214 connectivity_monitor_remove_states (connectivity_monitor,
215 CONNECTIVITY_STABLE, NULL);
216 }
217 else if (state == NM_STATE_DISCONNECTED)
218 {
219 DEBUG ("New NetworkManager network state %d (disconnected)", state);
220
221 connectivity_monitor_remove_states (connectivity_monitor,
222 CONNECTIVITY_UP|CONNECTIVITY_STABLE, NULL);
223 }
224 else
225 {
226 DEBUG ("New NetworkManager network state %d (stable state)", state);
227 connectivity_monitor_add_states (connectivity_monitor,
228 CONNECTIVITY_STABLE, NULL);
229 }
230 }
231 #endif
232
233 static void
connectivity_monitor_network_changed(GNetworkMonitor * monitor,gboolean available,McdConnectivityMonitor * connectivity_monitor)234 connectivity_monitor_network_changed (GNetworkMonitor *monitor,
235 gboolean available,
236 McdConnectivityMonitor *connectivity_monitor)
237 {
238 McdConnectivityMonitorPrivate *priv;
239
240 priv = connectivity_monitor->priv;
241
242 if (!priv->use_conn)
243 return;
244
245 if (available)
246 {
247 DEBUG ("GNetworkMonitor (%s) says we are at least partially online",
248 G_OBJECT_TYPE_NAME (monitor));
249 connectivity_monitor_add_states (connectivity_monitor, CONNECTIVITY_UP,
250 NULL);
251 }
252 else
253 {
254 DEBUG ("GNetworkMonitor (%s) says we are offline",
255 G_OBJECT_TYPE_NAME (monitor));
256 connectivity_monitor_remove_states (connectivity_monitor,
257 CONNECTIVITY_UP, NULL);
258 }
259 }
260
261 #ifdef HAVE_UPOWER
262 static void
connectivity_monitor_set_awake(McdConnectivityMonitor * self,gboolean awake)263 connectivity_monitor_set_awake (
264 McdConnectivityMonitor *self,
265 gboolean awake)
266 {
267 if (awake)
268 connectivity_monitor_add_states (self, CONNECTIVITY_AWAKE, NULL);
269 else
270 connectivity_monitor_remove_states (self, CONNECTIVITY_AWAKE, NULL);
271 }
272
273 static void
notify_sleep_cb(UpClient * upower_client,UpSleepKind sleep_kind,gpointer user_data)274 notify_sleep_cb (
275 UpClient *upower_client,
276 UpSleepKind sleep_kind,
277 gpointer user_data)
278 {
279 McdConnectivityMonitor *self = MCD_CONNECTIVITY_MONITOR (user_data);
280
281 DEBUG ("about to sleep! sleep_kind=%s", up_sleep_kind_to_string (sleep_kind));
282 connectivity_monitor_set_awake (self, FALSE);
283 }
284
285 static void
notify_resume_cb(UpClient * upower_client,UpSleepKind sleep_kind,gpointer user_data)286 notify_resume_cb (
287 UpClient *upower_client,
288 UpSleepKind sleep_kind,
289 gpointer user_data)
290 {
291 McdConnectivityMonitor *self = MCD_CONNECTIVITY_MONITOR (user_data);
292
293 DEBUG ("woke up! sleep_kind=%s", up_sleep_kind_to_string (sleep_kind));
294 connectivity_monitor_set_awake (self, TRUE);
295 }
296 #endif
297
298 #ifdef HAVE_GIO_UNIX
299 static void
login1_inhibit_cb(GObject * source G_GNUC_UNUSED,GAsyncResult * result,gpointer user_data)300 login1_inhibit_cb (GObject *source G_GNUC_UNUSED,
301 GAsyncResult *result,
302 gpointer user_data)
303 {
304 McdConnectivityMonitor *self = MCD_CONNECTIVITY_MONITOR (user_data);
305 GUnixFDList *fds = NULL;
306 GError *error = NULL;
307 GVariant *tuple = g_dbus_connection_call_with_unix_fd_list_finish (
308 self->priv->system_bus, &fds, result, &error);
309
310 if (tuple != NULL)
311 {
312 gint32 i;
313
314 g_variant_get (tuple, "(h)", &i);
315
316 if (g_unix_fd_list_get_length (fds) > i)
317 {
318 g_warn_if_fail (self->priv->login1_inhibit->fd == -1);
319 self->priv->login1_inhibit->fd = g_unix_fd_list_get (fds, i, &error);
320
321 if (self->priv->login1_inhibit->fd >= 0)
322 {
323 DEBUG ("fd %d inhibits login1 sleep/shutdown",
324 self->priv->login1_inhibit->fd);
325 }
326 else
327 {
328 DEBUG ("unable to duplicate fd: %s #%d: %s",
329 g_quark_to_string (error->domain), error->code,
330 error->message);
331 g_error_free (error);
332 mcd_inhibit_release (self->priv->login1_inhibit);
333 self->priv->login1_inhibit = NULL;
334 }
335 }
336 else
337 {
338 DEBUG ("Inhibit() didn't return enough fds?");
339 }
340
341 g_variant_unref (tuple);
342 }
343 else
344 {
345 DEBUG ("unable to delay sleep and shutdown: %s #%d: %s",
346 g_quark_to_string (error->domain), error->code, error->message);
347 g_error_free (error);
348 }
349
350 g_clear_object (&fds);
351 g_object_unref (self);
352 }
353 #endif
354
355 static void
connectivity_monitor_renew_inhibit(McdConnectivityMonitor * self)356 connectivity_monitor_renew_inhibit (McdConnectivityMonitor *self)
357 {
358 #ifdef HAVE_GIO_UNIX
359 if (self->priv->login1_inhibit != NULL)
360 return;
361
362 self->priv->login1_inhibit = g_slice_new (McdInhibit);
363 self->priv->login1_inhibit->holds = 1;
364 self->priv->login1_inhibit->fd = -1;
365
366 g_dbus_connection_call_with_unix_fd_list (self->priv->system_bus,
367 LOGIN1_BUS_NAME, LOGIN1_MANAGER_OBJECT_PATH,
368 LOGIN1_MANAGER_IFACE, LOGIN1_MANAGER_INHIBIT,
369 g_variant_new ("(ssss)", "sleep:shutdown",
370 "Telepathy", "Disconnecting IM accounts before suspend/shutdown...",
371 "delay"),
372 G_VARIANT_TYPE ("(h)"), G_DBUS_CALL_FLAGS_NONE,
373 -1, NULL, NULL, login1_inhibit_cb, g_object_ref (self));
374 #endif
375 }
376
377 static void
login1_prepare_for_sleep_cb(GDBusConnection * system_bus G_GNUC_UNUSED,const gchar * sender_name G_GNUC_UNUSED,const gchar * object_path G_GNUC_UNUSED,const gchar * interface_name G_GNUC_UNUSED,const gchar * signal_name G_GNUC_UNUSED,GVariant * parameters,gpointer user_data)378 login1_prepare_for_sleep_cb (GDBusConnection *system_bus G_GNUC_UNUSED,
379 const gchar *sender_name G_GNUC_UNUSED,
380 const gchar *object_path G_GNUC_UNUSED,
381 const gchar *interface_name G_GNUC_UNUSED,
382 const gchar *signal_name G_GNUC_UNUSED,
383 GVariant *parameters,
384 gpointer user_data)
385 {
386 McdConnectivityMonitor *self = MCD_CONNECTIVITY_MONITOR (user_data);
387
388 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(b)")))
389 {
390 gboolean sleeping;
391
392 g_variant_get (parameters, "(b)", &sleeping);
393
394 if (sleeping)
395 {
396 DEBUG ("about to suspend");
397 connectivity_monitor_remove_states (self, CONNECTIVITY_AWAKE,
398 self->priv->login1_inhibit);
399 tp_clear_pointer (&self->priv->login1_inhibit, mcd_inhibit_release);
400 }
401 else
402 {
403 DEBUG ("woke up, or suspend was cancelled");
404 connectivity_monitor_renew_inhibit (self);
405 connectivity_monitor_add_states (self, CONNECTIVITY_AWAKE,
406 self->priv->login1_inhibit);
407 }
408 }
409 else if (DEBUGGING)
410 {
411 gchar *pretty = g_variant_print (parameters, TRUE);
412
413 DEBUG ("ignoring PrepareForSleep signal not of type (b): %s", pretty);
414 g_free (pretty);
415 }
416 }
417
418 static void
login1_prepare_for_shutdown_cb(GDBusConnection * system_bus G_GNUC_UNUSED,const gchar * sender_name G_GNUC_UNUSED,const gchar * object_path G_GNUC_UNUSED,const gchar * interface_name G_GNUC_UNUSED,const gchar * signal_name G_GNUC_UNUSED,GVariant * parameters,gpointer user_data)419 login1_prepare_for_shutdown_cb (GDBusConnection *system_bus G_GNUC_UNUSED,
420 const gchar *sender_name G_GNUC_UNUSED,
421 const gchar *object_path G_GNUC_UNUSED,
422 const gchar *interface_name G_GNUC_UNUSED,
423 const gchar *signal_name G_GNUC_UNUSED,
424 GVariant *parameters,
425 gpointer user_data)
426 {
427 McdConnectivityMonitor *self = MCD_CONNECTIVITY_MONITOR (user_data);
428
429 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(b)")))
430 {
431 gboolean shutting_down;
432
433 g_variant_get (parameters, "(b)", &shutting_down);
434
435 if (shutting_down)
436 {
437 DEBUG ("about to shut down");
438 connectivity_monitor_remove_states (self, CONNECTIVITY_RUNNING,
439 self->priv->login1_inhibit);
440 tp_clear_pointer (&self->priv->login1_inhibit, mcd_inhibit_release);
441 }
442 else
443 {
444 DEBUG ("shutdown was cancelled");
445 connectivity_monitor_renew_inhibit (self);
446 connectivity_monitor_add_states (self, CONNECTIVITY_RUNNING,
447 self->priv->login1_inhibit);
448 }
449 }
450 else if (DEBUGGING)
451 {
452 gchar *pretty = g_variant_print (parameters, TRUE);
453
454 DEBUG ("ignoring PrepareForShutdown signal not of type (b): %s", pretty);
455 g_free (pretty);
456 }
457 }
458
459 static void
got_system_bus_cb(GObject * source G_GNUC_UNUSED,GAsyncResult * result,gpointer user_data)460 got_system_bus_cb (GObject *source G_GNUC_UNUSED,
461 GAsyncResult *result,
462 gpointer user_data)
463 {
464 McdConnectivityMonitor *self = MCD_CONNECTIVITY_MONITOR (user_data);
465 GError *error = NULL;
466
467 self->priv->system_bus = g_bus_get_finish (result, &error);
468
469 if (self->priv->system_bus != NULL)
470 {
471 self->priv->login1_prepare_for_sleep_id =
472 g_dbus_connection_signal_subscribe (self->priv->system_bus,
473 LOGIN1_BUS_NAME, LOGIN1_MANAGER_IFACE,
474 LOGIN1_MANAGER_PREPARE_FOR_SLEEP, LOGIN1_MANAGER_OBJECT_PATH,
475 NULL, G_DBUS_SIGNAL_FLAGS_NONE, login1_prepare_for_sleep_cb,
476 self, NULL);
477
478 self->priv->login1_prepare_for_shutdown_id =
479 g_dbus_connection_signal_subscribe (self->priv->system_bus,
480 LOGIN1_BUS_NAME, LOGIN1_MANAGER_IFACE,
481 LOGIN1_MANAGER_PREPARE_FOR_SHUTDOWN, LOGIN1_MANAGER_OBJECT_PATH,
482 NULL, G_DBUS_SIGNAL_FLAGS_NONE, login1_prepare_for_shutdown_cb,
483 self, NULL);
484
485 connectivity_monitor_renew_inhibit (self);
486 }
487 else
488 {
489 DEBUG ("unable to connect to system bus: %s #%d: %s",
490 g_quark_to_string (error->domain), error->code, error->message);
491 g_error_free (error);
492 }
493
494 g_object_unref (self);
495 }
496
497 static void
mcd_connectivity_monitor_init(McdConnectivityMonitor * connectivity_monitor)498 mcd_connectivity_monitor_init (McdConnectivityMonitor *connectivity_monitor)
499 {
500 McdConnectivityMonitorPrivate *priv;
501
502 priv = G_TYPE_INSTANCE_GET_PRIVATE (connectivity_monitor,
503 MCD_TYPE_CONNECTIVITY_MONITOR, McdConnectivityMonitorPrivate);
504
505 connectivity_monitor->priv = priv;
506
507 priv->use_conn = TRUE;
508 /* Initially, assume everything is good. */
509 priv->connectivity = CONNECTIVITY_AWAKE | CONNECTIVITY_STABLE |
510 CONNECTIVITY_UP | CONNECTIVITY_RUNNING;
511
512 priv->network_monitor = g_network_monitor_get_default ();
513
514 tp_g_signal_connect_object (priv->network_monitor, "network-changed",
515 G_CALLBACK (connectivity_monitor_network_changed),
516 connectivity_monitor, 0);
517 connectivity_monitor_network_changed (priv->network_monitor,
518 g_network_monitor_get_network_available (priv->network_monitor),
519 connectivity_monitor);
520
521 #ifdef ENABLE_CONN_SETTING
522 priv->settings = g_settings_new ("im.telepathy.MissionControl.FromEmpathy");
523 /* We'll call g_settings_bind() in constructed because default values of
524 * properties haven't been set yet at this point and we don't want them to
525 * override the value from GSettings. */
526 #endif
527
528 #ifdef HAVE_NM
529 {
530 GError *error = NULL;
531 priv->nm_client = nm_client_new (NULL, &error);
532 if (priv->nm_client != NULL)
533 {
534 priv->state_change_signal_id = g_signal_connect (priv->nm_client,
535 "notify::" NM_CLIENT_STATE,
536 G_CALLBACK (connectivity_monitor_nm_state_change_cb),
537 connectivity_monitor);
538
539 connectivity_monitor_nm_state_change_cb (priv->nm_client, NULL,
540 connectivity_monitor);
541 }
542 else
543 {
544 DEBUG ("Failed to get NetworkManager proxy: %s", error->message);
545 }
546 }
547 #endif
548
549 #ifdef HAVE_UPOWER
550 priv->upower_client = up_client_new ();
551 tp_g_signal_connect_object (priv->upower_client,
552 "notify-sleep", G_CALLBACK (notify_sleep_cb), connectivity_monitor,
553 G_CONNECT_AFTER);
554 tp_g_signal_connect_object (priv->upower_client,
555 "notify-resume", G_CALLBACK (notify_resume_cb), connectivity_monitor,
556 G_CONNECT_AFTER);
557 #endif
558
559 g_bus_get (G_BUS_TYPE_SYSTEM, NULL, got_system_bus_cb,
560 g_object_ref (connectivity_monitor));
561 }
562
563 static void
connectivity_monitor_constructed(GObject * object)564 connectivity_monitor_constructed (GObject *object)
565 {
566 #ifdef ENABLE_CONN_SETTING
567 McdConnectivityMonitor *self = MCD_CONNECTIVITY_MONITOR (object);
568
569 g_settings_bind (self->priv->settings, "use-conn",
570 self, "use-conn", G_SETTINGS_BIND_GET);
571 #endif
572 }
573
574 static void
connectivity_monitor_finalize(GObject * object)575 connectivity_monitor_finalize (GObject *object)
576 {
577 #if defined(HAVE_NM) || defined(HAVE_UPOWER)
578 McdConnectivityMonitor *connectivity_monitor = MCD_CONNECTIVITY_MONITOR (object);
579 McdConnectivityMonitorPrivate *priv = connectivity_monitor->priv;
580 #endif
581
582 #ifdef HAVE_NM
583 if (priv->nm_client != NULL)
584 {
585 g_signal_handler_disconnect (priv->nm_client,
586 priv->state_change_signal_id);
587 priv->state_change_signal_id = 0;
588 g_object_unref (priv->nm_client);
589 priv->nm_client = NULL;
590 }
591 #endif
592
593 #ifdef HAVE_UPOWER
594 tp_clear_object (&priv->upower_client);
595 #endif
596
597 G_OBJECT_CLASS (mcd_connectivity_monitor_parent_class)->finalize (object);
598 }
599
600 static inline void
clear_subscription(GDBusConnection * conn,guint * subscription)601 clear_subscription (GDBusConnection *conn,
602 guint *subscription)
603 {
604 if (*subscription == 0)
605 return;
606
607 g_dbus_connection_signal_unsubscribe (conn, *subscription);
608 *subscription = 0;
609 }
610
611 static void
connectivity_monitor_dispose(GObject * object)612 connectivity_monitor_dispose (GObject *object)
613 {
614 McdConnectivityMonitor *self = MCD_CONNECTIVITY_MONITOR (object);
615
616 g_clear_object (&self->priv->network_monitor);
617
618 #ifdef ENABLE_CONN_SETTING
619 g_clear_object (&self->priv->settings);
620 #endif
621
622 clear_subscription (self->priv->system_bus,
623 &self->priv->login1_prepare_for_sleep_id);
624 clear_subscription (self->priv->system_bus,
625 &self->priv->login1_prepare_for_shutdown_id);
626 tp_clear_pointer (&self->priv->login1_inhibit, mcd_inhibit_release);
627
628 g_clear_object (&self->priv->system_bus);
629
630 G_OBJECT_CLASS (mcd_connectivity_monitor_parent_class)->dispose (object);
631 }
632
633 static GObject *
connectivity_monitor_constructor(GType type,guint n_construct_params,GObjectConstructParam * construct_params)634 connectivity_monitor_constructor (GType type,
635 guint n_construct_params,
636 GObjectConstructParam *construct_params)
637 {
638 GObject *retval;
639
640 if (!connectivity_monitor_singleton)
641 {
642 retval = G_OBJECT_CLASS (mcd_connectivity_monitor_parent_class)->constructor
643 (type, n_construct_params, construct_params);
644
645 connectivity_monitor_singleton = MCD_CONNECTIVITY_MONITOR (retval);
646 g_object_add_weak_pointer (retval, (gpointer) &connectivity_monitor_singleton);
647 }
648 else
649 {
650 retval = g_object_ref (connectivity_monitor_singleton);
651 }
652
653 return retval;
654 }
655
656 static void
connectivity_monitor_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)657 connectivity_monitor_get_property (GObject *object,
658 guint param_id,
659 GValue *value,
660 GParamSpec *pspec)
661 {
662 McdConnectivityMonitor *connectivity_monitor = MCD_CONNECTIVITY_MONITOR (object);
663
664 switch (param_id)
665 {
666 case PROP_USE_CONN:
667 g_value_set_boolean (value, mcd_connectivity_monitor_get_use_conn (
668 connectivity_monitor));
669 break;
670 default:
671 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
672 break;
673 };
674 }
675
676 static void
connectivity_monitor_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)677 connectivity_monitor_set_property (GObject *object,
678 guint param_id,
679 const GValue *value,
680 GParamSpec *pspec)
681 {
682 McdConnectivityMonitor *connectivity_monitor = MCD_CONNECTIVITY_MONITOR (object);
683
684 switch (param_id)
685 {
686 case PROP_USE_CONN:
687 mcd_connectivity_monitor_set_use_conn (connectivity_monitor,
688 g_value_get_boolean (value));
689 break;
690 default:
691 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
692 break;
693 };
694 }
695
696 static void
mcd_connectivity_monitor_class_init(McdConnectivityMonitorClass * klass)697 mcd_connectivity_monitor_class_init (McdConnectivityMonitorClass *klass)
698 {
699 GObjectClass *oclass = G_OBJECT_CLASS (klass);
700
701 oclass->finalize = connectivity_monitor_finalize;
702 oclass->dispose = connectivity_monitor_dispose;
703 oclass->constructor = connectivity_monitor_constructor;
704 oclass->constructed = connectivity_monitor_constructed;
705 oclass->get_property = connectivity_monitor_get_property;
706 oclass->set_property = connectivity_monitor_set_property;
707
708 signals[STATE_CHANGE] =
709 g_signal_new ("state-change",
710 G_TYPE_FROM_CLASS (klass),
711 G_SIGNAL_RUN_LAST,
712 0,
713 NULL, NULL, NULL,
714 G_TYPE_NONE,
715 2, G_TYPE_BOOLEAN, G_TYPE_POINTER);
716
717 g_object_class_install_property (oclass,
718 PROP_USE_CONN,
719 g_param_spec_boolean ("use-conn",
720 "Use connectivity managers",
721 "Set presence according to connectivity managers",
722 TRUE,
723 G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
724
725 g_type_class_add_private (oclass, sizeof (McdConnectivityMonitorPrivate));
726 }
727
728 /* public methods */
729
730 McdConnectivityMonitor *
mcd_connectivity_monitor_new(void)731 mcd_connectivity_monitor_new (void)
732 {
733 return g_object_new (MCD_TYPE_CONNECTIVITY_MONITOR, NULL);
734 }
735
736 gboolean
mcd_connectivity_monitor_is_online(McdConnectivityMonitor * connectivity_monitor)737 mcd_connectivity_monitor_is_online (McdConnectivityMonitor *connectivity_monitor)
738 {
739 McdConnectivityMonitorPrivate *priv = connectivity_monitor->priv;
740
741 return is_connected (priv->connectivity);
742 }
743
744 gboolean
mcd_connectivity_monitor_get_use_conn(McdConnectivityMonitor * connectivity_monitor)745 mcd_connectivity_monitor_get_use_conn (McdConnectivityMonitor *connectivity_monitor)
746 {
747 McdConnectivityMonitorPrivate *priv = connectivity_monitor->priv;
748
749 return priv->use_conn;
750 }
751
752 void
mcd_connectivity_monitor_set_use_conn(McdConnectivityMonitor * connectivity_monitor,gboolean use_conn)753 mcd_connectivity_monitor_set_use_conn (McdConnectivityMonitor *connectivity_monitor,
754 gboolean use_conn)
755 {
756 McdConnectivityMonitorPrivate *priv = connectivity_monitor->priv;
757
758 if (use_conn == priv->use_conn)
759 return;
760
761 DEBUG ("use-conn GSettings key changed; new value = %s",
762 use_conn ? "true" : "false");
763
764 priv->use_conn = use_conn;
765
766 if (use_conn)
767 {
768 #if defined(HAVE_NM)
769 connectivity_monitor_nm_state_change_cb (priv->nm_client, NULL, connectivity_monitor);
770 #endif
771
772 connectivity_monitor_network_changed (priv->network_monitor,
773 g_network_monitor_get_network_available (priv->network_monitor),
774 connectivity_monitor);
775 }
776 else
777 {
778 /* !use_conn basically means "always assume it's stable and up". */
779 connectivity_monitor_add_states (connectivity_monitor,
780 CONNECTIVITY_STABLE|CONNECTIVITY_UP, NULL);
781 }
782
783 g_object_notify (G_OBJECT (connectivity_monitor), "use-conn");
784 }
785
786 McdInhibit *
mcd_inhibit_hold(McdInhibit * inhibit)787 mcd_inhibit_hold (McdInhibit *inhibit)
788 {
789 DEBUG ("%p (fd %d): %" G_GSIZE_FORMAT " -> %" G_GSIZE_FORMAT,
790 inhibit, inhibit->fd, inhibit->holds, inhibit->holds + 1);
791
792 inhibit->holds++;
793 return inhibit;
794 }
795
796 void
mcd_inhibit_release(McdInhibit * inhibit)797 mcd_inhibit_release (McdInhibit *inhibit)
798 {
799 DEBUG ("%p (fd %d): %" G_GSIZE_FORMAT " -> %" G_GSIZE_FORMAT,
800 inhibit, inhibit->fd, inhibit->holds, inhibit->holds - 1);
801
802 if (--inhibit->holds == 0)
803 {
804 /* Not using the retry-on-EINTR idiom: see g_close() in GLib 2.36.
805 * After we depend on GLib 2.36, we could use g_close(). */
806 if (inhibit->fd != -1 &&
807 close (inhibit->fd) != 0)
808 {
809 WARNING ("unable to close fd, ignoring: %s", g_strerror (errno));
810 }
811
812 g_slice_free (McdInhibit, inhibit);
813 }
814 }
815