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 : 322 : 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 : if (new_network_available != nm->priv->network_available)
241 : : {
242 : 0 : nm->priv->network_available = new_network_available;
243 : 0 : g_object_notify (G_OBJECT (nm), "network-available");
244 : : }
245 : 0 : if (new_network_metered != nm->priv->network_metered)
246 : : {
247 : 0 : nm->priv->network_metered = new_network_metered;
248 : 0 : g_object_notify (G_OBJECT (nm), "network-metered");
249 : : }
250 : 0 : if (new_connectivity != nm->priv->connectivity)
251 : : {
252 : 0 : nm->priv->connectivity = new_connectivity;
253 : 0 : g_object_notify (G_OBJECT (nm), "connectivity");
254 : : }
255 : : }
256 : :
257 : : static void
258 : 0 : proxy_properties_changed_cb (GDBusProxy *proxy,
259 : : GVariant *changed_properties,
260 : : GStrv invalidated_properties,
261 : : GNetworkMonitorNM *nm)
262 : : {
263 : 0 : sync_properties (nm, TRUE);
264 : 0 : }
265 : :
266 : : static gboolean
267 : 0 : has_property (GDBusProxy *proxy,
268 : : const char *property_name)
269 : : {
270 : : char **props;
271 : 0 : gboolean prop_found = FALSE;
272 : :
273 : 0 : props = g_dbus_proxy_get_cached_property_names (proxy);
274 : :
275 : 0 : if (!props)
276 : 0 : return FALSE;
277 : :
278 : 0 : prop_found = g_strv_contains ((const gchar * const *) props, property_name);
279 : 0 : g_strfreev (props);
280 : 0 : return prop_found;
281 : : }
282 : :
283 : : static gboolean
284 : 21 : g_network_monitor_nm_initable_init (GInitable *initable,
285 : : GCancellable *cancellable,
286 : : GError **error)
287 : : {
288 : 21 : GNetworkMonitorNM *nm = G_NETWORK_MONITOR_NM (initable);
289 : : GDBusProxy *proxy;
290 : : GInitableIface *parent_iface;
291 : 21 : gchar *name_owner = NULL;
292 : :
293 : 21 : parent_iface = g_type_interface_peek_parent (G_NETWORK_MONITOR_NM_GET_INITABLE_IFACE (initable));
294 : 21 : if (!parent_iface->init (initable, cancellable, error))
295 : 0 : return FALSE;
296 : :
297 : 21 : 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 : 21 : if (!proxy)
306 : 21 : return FALSE;
307 : :
308 : 0 : name_owner = g_dbus_proxy_get_name_owner (proxy);
309 : :
310 : 0 : if (!name_owner)
311 : : {
312 : 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
313 : : _("NetworkManager not running"));
314 : 0 : g_object_unref (proxy);
315 : 0 : return FALSE;
316 : : }
317 : :
318 : 0 : g_free (name_owner);
319 : :
320 : : /* Verify it has the PrimaryConnection and Connectivity properties */
321 : 0 : if (!has_property (proxy, "Connectivity"))
322 : : {
323 : 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
324 : : _("NetworkManager version too old"));
325 : 0 : g_object_unref (proxy);
326 : 0 : return FALSE;
327 : : }
328 : :
329 : 0 : nm->priv->signal_id = g_signal_connect (G_OBJECT (proxy), "g-properties-changed",
330 : : G_CALLBACK (proxy_properties_changed_cb), nm);
331 : 0 : nm->priv->proxy = proxy;
332 : 0 : sync_properties (nm, FALSE);
333 : :
334 : 0 : return TRUE;
335 : : }
336 : :
337 : : static void
338 : 21 : g_network_monitor_nm_finalize (GObject *object)
339 : : {
340 : 21 : GNetworkMonitorNM *nm = G_NETWORK_MONITOR_NM (object);
341 : :
342 : 21 : if (nm->priv->proxy != NULL &&
343 : 0 : nm->priv->signal_id != 0)
344 : : {
345 : 0 : g_signal_handler_disconnect (nm->priv->proxy,
346 : 0 : nm->priv->signal_id);
347 : 0 : nm->priv->signal_id = 0;
348 : : }
349 : 21 : g_clear_object (&nm->priv->proxy);
350 : :
351 : 21 : G_OBJECT_CLASS (g_network_monitor_nm_parent_class)->finalize (object);
352 : 21 : }
353 : :
354 : : static void
355 : 21 : g_network_monitor_nm_class_init (GNetworkMonitorNMClass *nl_class)
356 : : {
357 : 21 : GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class);
358 : :
359 : 21 : gobject_class->finalize = g_network_monitor_nm_finalize;
360 : 21 : gobject_class->get_property = g_network_monitor_nm_get_property;
361 : :
362 : 21 : g_object_class_override_property (gobject_class, PROP_NETWORK_AVAILABLE, "network-available");
363 : 21 : g_object_class_override_property (gobject_class, PROP_NETWORK_METERED, "network-metered");
364 : 21 : g_object_class_override_property (gobject_class, PROP_CONNECTIVITY, "connectivity");
365 : 21 : }
366 : :
367 : : static void
368 : 21 : g_network_monitor_nm_iface_init (GNetworkMonitorInterface *monitor_iface)
369 : : {
370 : 21 : }
371 : :
372 : : static void
373 : 21 : g_network_monitor_nm_initable_iface_init (GInitableIface *iface)
374 : : {
375 : 21 : iface->init = g_network_monitor_nm_initable_init;
376 : 21 : }
|