Branch data Line data Source code
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 : 362 : 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 : 21 : g_network_monitor_nm_init (GNetworkMonitorNM *nm)
95 : : {
96 : 21 : nm->priv = g_network_monitor_nm_get_instance_private (nm);
97 : 21 : }
98 : :
99 : : static void
100 : 0 : g_network_monitor_nm_get_property (GObject *object,
101 : : guint prop_id,
102 : : GValue *value,
103 : : GParamSpec *pspec)
104 : : {
105 : 0 : GNetworkMonitorNM *nm = G_NETWORK_MONITOR_NM (object);
106 : :
107 : 0 : switch (prop_id)
108 : : {
109 : 0 : case PROP_NETWORK_AVAILABLE:
110 : 0 : g_value_set_boolean (value, nm->priv->network_available);
111 : 0 : break;
112 : :
113 : 0 : case PROP_NETWORK_METERED:
114 : 0 : g_value_set_boolean (value, nm->priv->network_metered);
115 : 0 : break;
116 : :
117 : 0 : case PROP_CONNECTIVITY:
118 : 0 : g_value_set_enum (value, nm->priv->connectivity);
119 : 0 : break;
120 : :
121 : 0 : default:
122 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
123 : 0 : break;
124 : : }
125 : 0 : }
126 : :
127 : : static GNetworkConnectivity
128 : 0 : nm_conn_to_g_conn (int nm_state)
129 : : {
130 : 0 : switch (nm_state)
131 : : {
132 : 0 : case NM_CONNECTIVITY_UNKNOWN:
133 : 0 : return G_NETWORK_CONNECTIVITY_LOCAL;
134 : 0 : case NM_CONNECTIVITY_NONE:
135 : 0 : return G_NETWORK_CONNECTIVITY_LOCAL;
136 : 0 : case NM_CONNECTIVITY_PORTAL:
137 : 0 : return G_NETWORK_CONNECTIVITY_PORTAL;
138 : 0 : case NM_CONNECTIVITY_LIMITED:
139 : 0 : return G_NETWORK_CONNECTIVITY_LIMITED;
140 : 0 : case NM_CONNECTIVITY_FULL:
141 : 0 : return G_NETWORK_CONNECTIVITY_FULL;
142 : 0 : default:
143 : 0 : g_warning ("Unknown NM connectivity state %d", nm_state);
144 : 0 : return G_NETWORK_CONNECTIVITY_LOCAL;
145 : : }
146 : : }
147 : :
148 : : static gboolean
149 : 0 : nm_metered_to_bool (guint nm_metered)
150 : : {
151 : 0 : switch (nm_metered)
152 : : {
153 : 0 : case 1: /* yes */
154 : : case 3: /* guess-yes */
155 : 0 : return TRUE;
156 : 0 : 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 : 0 : return FALSE;
163 : 0 : default:
164 : 0 : g_warning ("Unknown NM metered state %d", nm_metered);
165 : 0 : return FALSE;
166 : : }
167 : : }
168 : :
169 : : static void
170 : 0 : 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 : 0 : v = g_dbus_proxy_get_cached_property (nm->priv->proxy, "State");
181 : 0 : if (!v)
182 : 0 : return;
183 : :
184 : 0 : nm_state = g_variant_get_uint32 (v);
185 : 0 : g_variant_unref (v);
186 : :
187 : 0 : v = g_dbus_proxy_get_cached_property (nm->priv->proxy, "Connectivity");
188 : 0 : if (!v)
189 : 0 : return;
190 : :
191 : 0 : nm_connectivity = g_variant_get_uint32 (v);
192 : 0 : g_variant_unref (v);
193 : :
194 : 0 : if (nm_state <= NM_STATE_CONNECTED_LOCAL)
195 : : {
196 : 0 : new_network_available = FALSE;
197 : 0 : new_network_metered = FALSE;
198 : 0 : new_connectivity = G_NETWORK_CONNECTIVITY_LOCAL;
199 : : }
200 : 0 : else if (nm_state <= NM_STATE_CONNECTED_SITE)
201 : : {
202 : 0 : new_network_available = TRUE;
203 : 0 : new_network_metered = FALSE;
204 : 0 : if (nm_connectivity == NM_CONNECTIVITY_PORTAL)
205 : : {
206 : 0 : new_connectivity = G_NETWORK_CONNECTIVITY_PORTAL;
207 : : }
208 : : else
209 : : {
210 : 0 : 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 : 0 : v = g_dbus_proxy_get_cached_property (nm->priv->proxy, "Metered");
218 : 0 : if (v == NULL)
219 : : {
220 : 0 : new_network_metered = FALSE;
221 : : }
222 : : else
223 : : {
224 : 0 : new_network_metered = nm_metered_to_bool (g_variant_get_uint32 (v));
225 : 0 : g_variant_unref (v);
226 : : }
227 : :
228 : 0 : new_network_available = TRUE;
229 : 0 : new_connectivity = nm_conn_to_g_conn (nm_connectivity);
230 : : }
231 : :
232 : 0 : if (!emit_signals)
233 : : {
234 : 0 : nm->priv->network_metered = new_network_metered;
235 : 0 : nm->priv->network_available = new_network_available;
236 : 0 : nm->priv->connectivity = new_connectivity;
237 : 0 : return;
238 : : }
239 : :
240 : 0 : gboolean changed = FALSE;
241 : :
242 : 0 : if (new_network_available != nm->priv->network_available)
243 : : {
244 : 0 : nm->priv->network_available = new_network_available;
245 : 0 : g_object_notify (G_OBJECT (nm), "network-available");
246 : 0 : changed = TRUE;
247 : : }
248 : 0 : if (new_network_metered != nm->priv->network_metered)
249 : : {
250 : 0 : nm->priv->network_metered = new_network_metered;
251 : 0 : g_object_notify (G_OBJECT (nm), "network-metered");
252 : 0 : changed = TRUE;
253 : : }
254 : 0 : if (new_connectivity != nm->priv->connectivity)
255 : : {
256 : 0 : nm->priv->connectivity = new_connectivity;
257 : 0 : g_object_notify (G_OBJECT (nm), "connectivity");
258 : 0 : changed = TRUE;
259 : : }
260 : :
261 : 0 : if (changed)
262 : 0 : g_signal_emit_by_name (nm, "network-changed", new_network_available);
263 : : }
264 : :
265 : : static void
266 : 0 : proxy_properties_changed_cb (GDBusProxy *proxy,
267 : : GVariant *changed_properties,
268 : : GStrv invalidated_properties,
269 : : GNetworkMonitorNM *nm)
270 : : {
271 : 0 : sync_properties (nm, TRUE);
272 : 0 : }
273 : :
274 : : static gboolean
275 : 0 : has_property (GDBusProxy *proxy,
276 : : const char *property_name)
277 : : {
278 : : char **props;
279 : 0 : gboolean prop_found = FALSE;
280 : :
281 : 0 : props = g_dbus_proxy_get_cached_property_names (proxy);
282 : :
283 : 0 : if (!props)
284 : 0 : return FALSE;
285 : :
286 : 0 : prop_found = g_strv_contains ((const gchar * const *) props, property_name);
287 : 0 : g_strfreev (props);
288 : 0 : return prop_found;
289 : : }
290 : :
291 : : static gboolean
292 : 21 : g_network_monitor_nm_initable_init (GInitable *initable,
293 : : GCancellable *cancellable,
294 : : GError **error)
295 : : {
296 : 21 : GNetworkMonitorNM *nm = G_NETWORK_MONITOR_NM (initable);
297 : : GDBusProxy *proxy;
298 : : GInitableIface *parent_iface;
299 : 21 : gchar *name_owner = NULL;
300 : :
301 : 21 : parent_iface = g_type_interface_peek_parent (G_NETWORK_MONITOR_NM_GET_INITABLE_IFACE (initable));
302 : 21 : if (!parent_iface->init (initable, cancellable, error))
303 : 0 : return FALSE;
304 : :
305 : 21 : proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
306 : : G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
307 : : NULL,
308 : : "org.freedesktop.NetworkManager",
309 : : "/org/freedesktop/NetworkManager",
310 : : "org.freedesktop.NetworkManager",
311 : : cancellable,
312 : : error);
313 : 21 : if (!proxy)
314 : 21 : return FALSE;
315 : :
316 : 0 : name_owner = g_dbus_proxy_get_name_owner (proxy);
317 : :
318 : 0 : if (!name_owner)
319 : : {
320 : 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
321 : : _("NetworkManager not running"));
322 : 0 : g_object_unref (proxy);
323 : 0 : return FALSE;
324 : : }
325 : :
326 : 0 : g_free (name_owner);
327 : :
328 : : /* Verify it has the PrimaryConnection and Connectivity properties */
329 : 0 : if (!has_property (proxy, "Connectivity"))
330 : : {
331 : 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
332 : : _("NetworkManager version too old"));
333 : 0 : g_object_unref (proxy);
334 : 0 : return FALSE;
335 : : }
336 : :
337 : 0 : nm->priv->signal_id = g_signal_connect (G_OBJECT (proxy), "g-properties-changed",
338 : : G_CALLBACK (proxy_properties_changed_cb), nm);
339 : 0 : nm->priv->proxy = proxy;
340 : 0 : sync_properties (nm, FALSE);
341 : :
342 : 0 : return TRUE;
343 : : }
344 : :
345 : : static void
346 : 21 : g_network_monitor_nm_finalize (GObject *object)
347 : : {
348 : 21 : GNetworkMonitorNM *nm = G_NETWORK_MONITOR_NM (object);
349 : :
350 : 21 : if (nm->priv->proxy != NULL &&
351 : 0 : nm->priv->signal_id != 0)
352 : : {
353 : 0 : g_signal_handler_disconnect (nm->priv->proxy,
354 : 0 : nm->priv->signal_id);
355 : 0 : nm->priv->signal_id = 0;
356 : : }
357 : 21 : g_clear_object (&nm->priv->proxy);
358 : :
359 : 21 : G_OBJECT_CLASS (g_network_monitor_nm_parent_class)->finalize (object);
360 : 21 : }
361 : :
362 : : static void
363 : 21 : g_network_monitor_nm_class_init (GNetworkMonitorNMClass *nl_class)
364 : : {
365 : 21 : GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class);
366 : :
367 : 21 : gobject_class->finalize = g_network_monitor_nm_finalize;
368 : 21 : gobject_class->get_property = g_network_monitor_nm_get_property;
369 : :
370 : 21 : g_object_class_override_property (gobject_class, PROP_NETWORK_AVAILABLE, "network-available");
371 : 21 : g_object_class_override_property (gobject_class, PROP_NETWORK_METERED, "network-metered");
372 : 21 : g_object_class_override_property (gobject_class, PROP_CONNECTIVITY, "connectivity");
373 : 21 : }
374 : :
375 : : static void
376 : 21 : g_network_monitor_nm_iface_init (GNetworkMonitorInterface *monitor_iface)
377 : : {
378 : 21 : }
379 : :
380 : : static void
381 : 21 : g_network_monitor_nm_initable_iface_init (GInitableIface *iface)
382 : : {
383 : 21 : iface->init = g_network_monitor_nm_initable_init;
384 : 21 : }
|