(root)/
glib-2.79.0/
gio/
gnetworkmonitornm.c
       1  /* GIO - GLib Input, Output and Streaming Library
       2   *
       3   * Copyright 2014 Red Hat, Inc.
       4   *
       5   * SPDX-License-Identifier: LGPL-2.1-or-later
       6   *
       7   * This library is free software; you can redistribute it and/or
       8   * modify it under the terms of the GNU Lesser General Public
       9   * License as published by the Free Software Foundation; either
      10   * version 2.1 of the License, or (at your option) any later version.
      11   *
      12   * This library is distributed in the hope that it will be useful,
      13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15   * Lesser General Public License for more details.
      16   *
      17   * You should have received a copy of the GNU Lesser General
      18   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      19   */
      20  
      21  #include "config.h"
      22  
      23  #include <errno.h>
      24  #include <string.h>
      25  #include <unistd.h>
      26  
      27  #include "gnetworkmonitornm.h"
      28  #include "gioerror.h"
      29  #include "ginitable.h"
      30  #include "giomodule-priv.h"
      31  #include "glibintl.h"
      32  #include "glib/gstdio.h"
      33  #include "gnetworkingprivate.h"
      34  #include "gnetworkmonitor.h"
      35  #include "gdbusproxy.h"
      36  
      37  static void g_network_monitor_nm_iface_init (GNetworkMonitorInterface *iface);
      38  static void g_network_monitor_nm_initable_iface_init (GInitableIface *iface);
      39  
      40  enum
      41  {
      42    PROP_0,
      43  
      44    PROP_NETWORK_AVAILABLE,
      45    PROP_NETWORK_METERED,
      46    PROP_CONNECTIVITY
      47  };
      48  
      49  typedef enum {
      50    NM_CONNECTIVITY_UNKNOWN,
      51    NM_CONNECTIVITY_NONE,
      52    NM_CONNECTIVITY_PORTAL,
      53    NM_CONNECTIVITY_LIMITED,
      54    NM_CONNECTIVITY_FULL
      55  } NMConnectivityState;
      56  
      57  /* Copied from https://developer.gnome.org/libnm-util/stable/libnm-util-NetworkManager.html#NMState;
      58   * used inline to avoid a NetworkManager dependency from GLib. */
      59  typedef enum {
      60    NM_STATE_UNKNOWN          = 0,
      61    NM_STATE_ASLEEP           = 10,
      62    NM_STATE_DISCONNECTED     = 20,
      63    NM_STATE_DISCONNECTING    = 30,
      64    NM_STATE_CONNECTING       = 40,
      65    NM_STATE_CONNECTED_LOCAL  = 50,
      66    NM_STATE_CONNECTED_SITE   = 60,
      67    NM_STATE_CONNECTED_GLOBAL = 70,
      68  } NMState;
      69  
      70  struct _GNetworkMonitorNMPrivate
      71  {
      72    GDBusProxy *proxy;
      73    guint signal_id;
      74  
      75    GNetworkConnectivity connectivity;
      76    gboolean network_available;
      77    gboolean network_metered;
      78  };
      79  
      80  #define g_network_monitor_nm_get_type _g_network_monitor_nm_get_type
      81  G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorNM, g_network_monitor_nm, G_TYPE_NETWORK_MONITOR_NETLINK,
      82                           G_ADD_PRIVATE (GNetworkMonitorNM)
      83                           G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
      84                                                  g_network_monitor_nm_iface_init)
      85                           G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
      86                                                  g_network_monitor_nm_initable_iface_init)
      87                           _g_io_modules_ensure_extension_points_registered ();
      88                           g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
      89                                                           g_define_type_id,
      90                                                           "networkmanager",
      91                                                           30))
      92  
      93  static void
      94  g_network_monitor_nm_init (GNetworkMonitorNM *nm)
      95  {
      96    nm->priv = g_network_monitor_nm_get_instance_private (nm);
      97  }
      98  
      99  static void
     100  g_network_monitor_nm_get_property (GObject    *object,
     101                                     guint       prop_id,
     102                                     GValue     *value,
     103                                     GParamSpec *pspec)
     104  {
     105    GNetworkMonitorNM *nm = G_NETWORK_MONITOR_NM (object);
     106  
     107    switch (prop_id)
     108      {
     109      case PROP_NETWORK_AVAILABLE:
     110        g_value_set_boolean (value, nm->priv->network_available);
     111        break;
     112  
     113      case PROP_NETWORK_METERED:
     114        g_value_set_boolean (value, nm->priv->network_metered);
     115        break;
     116  
     117      case PROP_CONNECTIVITY:
     118        g_value_set_enum (value, nm->priv->connectivity);
     119        break;
     120  
     121      default:
     122        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     123        break;
     124      }
     125  }
     126  
     127  static GNetworkConnectivity
     128  nm_conn_to_g_conn (int nm_state)
     129  {
     130    switch (nm_state)
     131      {
     132        case NM_CONNECTIVITY_UNKNOWN:
     133          return G_NETWORK_CONNECTIVITY_LOCAL;
     134        case NM_CONNECTIVITY_NONE:
     135          return G_NETWORK_CONNECTIVITY_LOCAL;
     136        case NM_CONNECTIVITY_PORTAL:
     137          return G_NETWORK_CONNECTIVITY_PORTAL;
     138        case NM_CONNECTIVITY_LIMITED:
     139          return G_NETWORK_CONNECTIVITY_LIMITED;
     140        case NM_CONNECTIVITY_FULL:
     141          return G_NETWORK_CONNECTIVITY_FULL;
     142        default:
     143          g_warning ("Unknown NM connectivity state %d", nm_state);
     144          return G_NETWORK_CONNECTIVITY_LOCAL;
     145      }
     146  }
     147  
     148  static gboolean
     149  nm_metered_to_bool (guint nm_metered)
     150  {
     151    switch (nm_metered)
     152      {
     153        case 1: /* yes */
     154        case 3: /* guess-yes */
     155          return TRUE;
     156        case 0: /* unknown */
     157          /* We default to FALSE in the unknown-because-you're-not-running-NM
     158           * case, so we should return FALSE in the
     159           * unknown-when-you-are-running-NM case too. */
     160        case 2: /* no */
     161        case 4: /* guess-no */
     162          return FALSE;
     163        default:
     164          g_warning ("Unknown NM metered state %d", nm_metered);
     165          return FALSE;
     166      }
     167  }
     168  
     169  static void
     170  sync_properties (GNetworkMonitorNM *nm,
     171                   gboolean           emit_signals)
     172  {
     173    GVariant *v;
     174    NMState nm_state;
     175    NMConnectivityState nm_connectivity;
     176    gboolean new_network_available;
     177    gboolean new_network_metered;
     178    GNetworkConnectivity new_connectivity;
     179  
     180    v = g_dbus_proxy_get_cached_property (nm->priv->proxy, "State");
     181    if (!v)
     182      return;
     183  
     184    nm_state = g_variant_get_uint32 (v);
     185    g_variant_unref (v);
     186  
     187    v = g_dbus_proxy_get_cached_property (nm->priv->proxy, "Connectivity");
     188    if (!v)
     189      return;
     190  
     191    nm_connectivity = g_variant_get_uint32 (v);
     192    g_variant_unref (v);
     193  
     194    if (nm_state <= NM_STATE_CONNECTED_LOCAL)
     195      {
     196        new_network_available = FALSE;
     197        new_network_metered = FALSE;
     198        new_connectivity = G_NETWORK_CONNECTIVITY_LOCAL;
     199      }
     200    else if (nm_state <= NM_STATE_CONNECTED_SITE)
     201      {
     202        new_network_available = TRUE;
     203        new_network_metered = FALSE;
     204        if (nm_connectivity == NM_CONNECTIVITY_PORTAL)
     205          {
     206            new_connectivity = G_NETWORK_CONNECTIVITY_PORTAL;
     207          }
     208        else
     209          {
     210            new_connectivity = G_NETWORK_CONNECTIVITY_LIMITED;
     211          }
     212      }
     213    else /* nm_state == NM_STATE_CONNECTED_FULL */
     214      {
     215  
     216        /* this is only available post NM 1.0 */
     217        v = g_dbus_proxy_get_cached_property (nm->priv->proxy, "Metered");
     218        if (v == NULL)
     219          {
     220            new_network_metered = FALSE;
     221          }
     222        else
     223          {
     224            new_network_metered = nm_metered_to_bool (g_variant_get_uint32 (v));
     225            g_variant_unref (v);
     226          }
     227  
     228        new_network_available = TRUE;
     229        new_connectivity = nm_conn_to_g_conn (nm_connectivity);
     230      }
     231  
     232    if (!emit_signals)
     233      {
     234        nm->priv->network_metered = new_network_metered;
     235        nm->priv->network_available = new_network_available;
     236        nm->priv->connectivity = new_connectivity;
     237        return;
     238      }
     239  
     240    if (new_network_available != nm->priv->network_available)
     241      {
     242        nm->priv->network_available = new_network_available;
     243        g_object_notify (G_OBJECT (nm), "network-available");
     244      }
     245    if (new_network_metered != nm->priv->network_metered)
     246      {
     247        nm->priv->network_metered = new_network_metered;
     248        g_object_notify (G_OBJECT (nm), "network-metered");
     249      }
     250    if (new_connectivity != nm->priv->connectivity)
     251      {
     252        nm->priv->connectivity = new_connectivity;
     253        g_object_notify (G_OBJECT (nm), "connectivity");
     254      }
     255  }
     256  
     257  static void
     258  proxy_properties_changed_cb (GDBusProxy        *proxy,
     259                               GVariant          *changed_properties,
     260                               GStrv              invalidated_properties,
     261                               GNetworkMonitorNM *nm)
     262  {
     263    sync_properties (nm, TRUE);
     264  }
     265  
     266  static gboolean
     267  has_property (GDBusProxy *proxy,
     268                const char *property_name)
     269  {
     270    char **props;
     271    gboolean prop_found = FALSE;
     272  
     273    props = g_dbus_proxy_get_cached_property_names (proxy);
     274  
     275    if (!props)
     276      return FALSE;
     277  
     278    prop_found = g_strv_contains ((const gchar * const *) props, property_name);
     279    g_strfreev (props);
     280    return prop_found;
     281  }
     282  
     283  static gboolean
     284  g_network_monitor_nm_initable_init (GInitable     *initable,
     285                                      GCancellable  *cancellable,
     286                                      GError       **error)
     287  {
     288    GNetworkMonitorNM *nm = G_NETWORK_MONITOR_NM (initable);
     289    GDBusProxy *proxy;
     290    GInitableIface *parent_iface;
     291    gchar *name_owner = NULL;
     292  
     293    parent_iface = g_type_interface_peek_parent (G_NETWORK_MONITOR_NM_GET_INITABLE_IFACE (initable));
     294    if (!parent_iface->init (initable, cancellable, error))
     295      return FALSE;
     296  
     297    proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
     298                                           G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
     299                                           NULL,
     300                                           "org.freedesktop.NetworkManager",
     301                                           "/org/freedesktop/NetworkManager",
     302                                           "org.freedesktop.NetworkManager",
     303                                           cancellable,
     304                                           error);
     305    if (!proxy)
     306      return FALSE;
     307  
     308    name_owner = g_dbus_proxy_get_name_owner (proxy);
     309  
     310    if (!name_owner)
     311      {
     312        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
     313                     _("NetworkManager not running"));
     314        g_object_unref (proxy);
     315        return FALSE;
     316      }
     317  
     318    g_free (name_owner);
     319  
     320    /* Verify it has the PrimaryConnection and Connectivity properties */
     321    if (!has_property (proxy, "Connectivity"))
     322      {
     323        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
     324                     _("NetworkManager version too old"));
     325        g_object_unref (proxy);
     326        return FALSE;
     327      }
     328  
     329    nm->priv->signal_id = g_signal_connect (G_OBJECT (proxy), "g-properties-changed",
     330                                            G_CALLBACK (proxy_properties_changed_cb), nm);
     331    nm->priv->proxy = proxy;
     332    sync_properties (nm, FALSE);
     333  
     334    return TRUE;
     335  }
     336  
     337  static void
     338  g_network_monitor_nm_finalize (GObject *object)
     339  {
     340    GNetworkMonitorNM *nm = G_NETWORK_MONITOR_NM (object);
     341  
     342    if (nm->priv->proxy != NULL &&
     343        nm->priv->signal_id != 0)
     344      {
     345        g_signal_handler_disconnect (nm->priv->proxy,
     346                                     nm->priv->signal_id);
     347        nm->priv->signal_id = 0;
     348      }
     349    g_clear_object (&nm->priv->proxy);
     350  
     351    G_OBJECT_CLASS (g_network_monitor_nm_parent_class)->finalize (object);
     352  }
     353  
     354  static void
     355  g_network_monitor_nm_class_init (GNetworkMonitorNMClass *nl_class)
     356  {
     357    GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class);
     358  
     359    gobject_class->finalize = g_network_monitor_nm_finalize;
     360    gobject_class->get_property = g_network_monitor_nm_get_property;
     361  
     362    g_object_class_override_property (gobject_class, PROP_NETWORK_AVAILABLE, "network-available");
     363    g_object_class_override_property (gobject_class, PROP_NETWORK_METERED, "network-metered");
     364    g_object_class_override_property (gobject_class, PROP_CONNECTIVITY, "connectivity");
     365  }
     366  
     367  static void
     368  g_network_monitor_nm_iface_init (GNetworkMonitorInterface *monitor_iface)
     369  {
     370  }
     371  
     372  static void
     373  g_network_monitor_nm_initable_iface_init (GInitableIface *iface)
     374  {
     375    iface->init = g_network_monitor_nm_initable_init;
     376  }