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