Merge branch '976-disable-assert-checks' into 'master'
[glib.git] / gio / gnetworkmonitorbase.c
blob6ac37c490c548d8353c8f8ec31f6ed571b537f3b
1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright 2011 Red Hat, Inc.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 #include "config.h"
21 #include "gnetworkmonitorbase.h"
22 #include "ginetaddress.h"
23 #include "ginetaddressmask.h"
24 #include "ginetsocketaddress.h"
25 #include "ginitable.h"
26 #include "gioerror.h"
27 #include "giomodule-priv.h"
28 #include "gnetworkmonitor.h"
29 #include "gsocketaddressenumerator.h"
30 #include "gsocketconnectable.h"
31 #include "gtask.h"
32 #include "glibintl.h"
34 static void g_network_monitor_base_iface_init (GNetworkMonitorInterface *iface);
35 static void g_network_monitor_base_initable_iface_init (GInitableIface *iface);
37 enum
39 PROP_0,
41 PROP_NETWORK_AVAILABLE,
42 PROP_NETWORK_METERED,
43 PROP_CONNECTIVITY
46 struct _GNetworkMonitorBasePrivate
48 GPtrArray *networks;
49 gboolean have_ipv4_default_route;
50 gboolean have_ipv6_default_route;
51 gboolean is_available;
53 GMainContext *context;
54 GSource *network_changed_source;
55 gboolean initializing;
58 static guint network_changed_signal = 0;
60 static void queue_network_changed (GNetworkMonitorBase *monitor);
62 G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorBase, g_network_monitor_base, G_TYPE_OBJECT,
63 G_ADD_PRIVATE (GNetworkMonitorBase)
64 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
65 g_network_monitor_base_initable_iface_init)
66 G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
67 g_network_monitor_base_iface_init)
68 _g_io_modules_ensure_extension_points_registered ();
69 g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
70 g_define_type_id,
71 "base",
72 0))
74 static void
75 g_network_monitor_base_init (GNetworkMonitorBase *monitor)
77 monitor->priv = g_network_monitor_base_get_instance_private (monitor);
78 monitor->priv->networks = g_ptr_array_new_with_free_func (g_object_unref);
79 monitor->priv->context = g_main_context_get_thread_default ();
80 if (monitor->priv->context)
81 g_main_context_ref (monitor->priv->context);
83 monitor->priv->initializing = TRUE;
86 static void
87 g_network_monitor_base_constructed (GObject *object)
89 GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
91 if (G_OBJECT_TYPE (monitor) == G_TYPE_NETWORK_MONITOR_BASE)
93 GInetAddressMask *mask;
95 /* We're the dumb base class, not a smarter subclass. So just
96 * assume that the network is available.
98 mask = g_inet_address_mask_new_from_string ("0.0.0.0/0", NULL);
99 g_network_monitor_base_add_network (monitor, mask);
100 g_object_unref (mask);
102 mask = g_inet_address_mask_new_from_string ("::/0", NULL);
103 if (mask)
105 /* On some environments (for example Windows without IPv6 support
106 * enabled) the string "::/0" can't be processed and causes
107 * g_inet_address_mask_new_from_string to return NULL */
108 g_network_monitor_base_add_network (monitor, mask);
109 g_object_unref (mask);
114 static void
115 g_network_monitor_base_get_property (GObject *object,
116 guint prop_id,
117 GValue *value,
118 GParamSpec *pspec)
120 GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
122 switch (prop_id)
124 case PROP_NETWORK_AVAILABLE:
125 g_value_set_boolean (value, monitor->priv->is_available);
126 break;
128 case PROP_NETWORK_METERED:
129 /* Default to FALSE in the unknown case. */
130 g_value_set_boolean (value, FALSE);
131 break;
133 case PROP_CONNECTIVITY:
134 g_value_set_enum (value,
135 monitor->priv->is_available ?
136 G_NETWORK_CONNECTIVITY_FULL :
137 G_NETWORK_CONNECTIVITY_LOCAL);
138 break;
140 default:
141 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
142 break;
147 static void
148 g_network_monitor_base_finalize (GObject *object)
150 GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
152 g_ptr_array_free (monitor->priv->networks, TRUE);
153 if (monitor->priv->network_changed_source)
155 g_source_destroy (monitor->priv->network_changed_source);
156 g_source_unref (monitor->priv->network_changed_source);
158 if (monitor->priv->context)
159 g_main_context_unref (monitor->priv->context);
161 G_OBJECT_CLASS (g_network_monitor_base_parent_class)->finalize (object);
164 static void
165 g_network_monitor_base_class_init (GNetworkMonitorBaseClass *monitor_class)
167 GObjectClass *gobject_class = G_OBJECT_CLASS (monitor_class);
169 gobject_class->constructed = g_network_monitor_base_constructed;
170 gobject_class->get_property = g_network_monitor_base_get_property;
171 gobject_class->finalize = g_network_monitor_base_finalize;
173 g_object_class_override_property (gobject_class, PROP_NETWORK_AVAILABLE, "network-available");
174 g_object_class_override_property (gobject_class, PROP_NETWORK_METERED, "network-metered");
175 g_object_class_override_property (gobject_class, PROP_CONNECTIVITY, "connectivity");
178 static gboolean
179 g_network_monitor_base_can_reach_sockaddr (GNetworkMonitorBase *base,
180 GSocketAddress *sockaddr)
182 GInetAddress *iaddr;
183 int i;
185 if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
186 return FALSE;
188 iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (sockaddr));
189 for (i = 0; i < base->priv->networks->len; i++)
191 if (g_inet_address_mask_matches (base->priv->networks->pdata[i], iaddr))
192 return TRUE;
195 return FALSE;
198 static gboolean
199 g_network_monitor_base_can_reach (GNetworkMonitor *monitor,
200 GSocketConnectable *connectable,
201 GCancellable *cancellable,
202 GError **error)
204 GNetworkMonitorBase *base = G_NETWORK_MONITOR_BASE (monitor);
205 GSocketAddressEnumerator *enumerator;
206 GSocketAddress *addr;
208 if (base->priv->networks->len == 0)
210 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
211 _("Network unreachable"));
212 return FALSE;
215 enumerator = g_socket_connectable_proxy_enumerate (connectable);
216 addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
217 if (!addr)
219 /* Either the user cancelled, or DNS resolution failed */
220 g_object_unref (enumerator);
221 return FALSE;
224 if (base->priv->have_ipv4_default_route &&
225 base->priv->have_ipv6_default_route)
227 g_object_unref (enumerator);
228 g_object_unref (addr);
229 return TRUE;
232 while (addr)
234 if (g_network_monitor_base_can_reach_sockaddr (base, addr))
236 g_object_unref (addr);
237 g_object_unref (enumerator);
238 return TRUE;
241 g_object_unref (addr);
242 addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
244 g_object_unref (enumerator);
246 if (error && !*error)
248 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
249 _("Host unreachable"));
251 return FALSE;
254 static void
255 can_reach_async_got_address (GObject *object,
256 GAsyncResult *result,
257 gpointer user_data)
259 GSocketAddressEnumerator *enumerator = G_SOCKET_ADDRESS_ENUMERATOR (object);
260 GTask *task = user_data;
261 GNetworkMonitorBase *base = g_task_get_source_object (task);
262 GSocketAddress *addr;
263 GError *error = NULL;
265 addr = g_socket_address_enumerator_next_finish (enumerator, result, &error);
266 if (!addr)
268 if (error)
270 /* Either the user cancelled, or DNS resolution failed */
271 g_task_return_error (task, error);
272 g_object_unref (task);
273 return;
275 else
277 /* Resolved all addresses, none matched */
278 g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
279 _("Host unreachable"));
280 g_object_unref (task);
281 return;
285 if (g_network_monitor_base_can_reach_sockaddr (base, addr))
287 g_object_unref (addr);
288 g_task_return_boolean (task, TRUE);
289 g_object_unref (task);
290 return;
292 g_object_unref (addr);
294 g_socket_address_enumerator_next_async (enumerator,
295 g_task_get_cancellable (task),
296 can_reach_async_got_address, task);
299 static void
300 g_network_monitor_base_can_reach_async (GNetworkMonitor *monitor,
301 GSocketConnectable *connectable,
302 GCancellable *cancellable,
303 GAsyncReadyCallback callback,
304 gpointer user_data)
306 GTask *task;
307 GSocketAddressEnumerator *enumerator;
309 task = g_task_new (monitor, cancellable, callback, user_data);
310 g_task_set_source_tag (task, g_network_monitor_base_can_reach_async);
312 if (G_NETWORK_MONITOR_BASE (monitor)->priv->networks->len == 0)
314 g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
315 _("Network unreachable"));
316 g_object_unref (task);
317 return;
320 enumerator = g_socket_connectable_proxy_enumerate (connectable);
321 g_socket_address_enumerator_next_async (enumerator, cancellable,
322 can_reach_async_got_address, task);
323 g_object_unref (enumerator);
326 static gboolean
327 g_network_monitor_base_can_reach_finish (GNetworkMonitor *monitor,
328 GAsyncResult *result,
329 GError **error)
331 g_return_val_if_fail (g_task_is_valid (result, monitor), FALSE);
333 return g_task_propagate_boolean (G_TASK (result), error);
336 static void
337 g_network_monitor_base_iface_init (GNetworkMonitorInterface *monitor_iface)
339 monitor_iface->can_reach = g_network_monitor_base_can_reach;
340 monitor_iface->can_reach_async = g_network_monitor_base_can_reach_async;
341 monitor_iface->can_reach_finish = g_network_monitor_base_can_reach_finish;
343 network_changed_signal = g_signal_lookup ("network-changed", G_TYPE_NETWORK_MONITOR);
346 static gboolean
347 g_network_monitor_base_initable_init (GInitable *initable,
348 GCancellable *cancellable,
349 GError **error)
351 GNetworkMonitorBase *base = G_NETWORK_MONITOR_BASE (initable);
353 base->priv->initializing = FALSE;
355 return TRUE;
358 static void
359 g_network_monitor_base_initable_iface_init (GInitableIface *iface)
361 iface->init = g_network_monitor_base_initable_init;
364 static gboolean
365 emit_network_changed (gpointer user_data)
367 GNetworkMonitorBase *monitor = user_data;
368 gboolean is_available;
370 if (g_source_is_destroyed (g_main_current_source ()))
371 return FALSE;
373 g_object_ref (monitor);
375 is_available = (monitor->priv->have_ipv4_default_route ||
376 monitor->priv->have_ipv6_default_route);
377 if (monitor->priv->is_available != is_available)
379 monitor->priv->is_available = is_available;
380 g_object_notify (G_OBJECT (monitor), "network-available");
383 g_signal_emit (monitor, network_changed_signal, 0, is_available);
385 g_source_unref (monitor->priv->network_changed_source);
386 monitor->priv->network_changed_source = NULL;
388 g_object_unref (monitor);
389 return FALSE;
392 static void
393 queue_network_changed (GNetworkMonitorBase *monitor)
395 if (!monitor->priv->network_changed_source &&
396 !monitor->priv->initializing)
398 GSource *source;
400 source = g_idle_source_new ();
401 /* Use G_PRIORITY_HIGH_IDLE priority so that multiple
402 * network-change-related notifications coming in at
403 * G_PRIORITY_DEFAULT will get coalesced into one signal
404 * emission.
406 g_source_set_priority (source, G_PRIORITY_HIGH_IDLE);
407 g_source_set_callback (source, emit_network_changed, monitor, NULL);
408 g_source_set_name (source, "[gio] emit_network_changed");
409 g_source_attach (source, monitor->priv->context);
410 monitor->priv->network_changed_source = source;
413 /* Normally we wait to update is_available until we emit the signal,
414 * to keep things consistent. But when we're first creating the
415 * object, we want it to be correct right away.
417 if (monitor->priv->initializing)
419 monitor->priv->is_available = (monitor->priv->have_ipv4_default_route ||
420 monitor->priv->have_ipv6_default_route);
425 * g_network_monitor_base_add_network:
426 * @monitor: the #GNetworkMonitorBase
427 * @network: a #GInetAddressMask
429 * Adds @network to @monitor's list of available networks.
431 * Since: 2.32
433 void
434 g_network_monitor_base_add_network (GNetworkMonitorBase *monitor,
435 GInetAddressMask *network)
437 int i;
439 for (i = 0; i < monitor->priv->networks->len; i++)
441 if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
442 return;
445 g_ptr_array_add (monitor->priv->networks, g_object_ref (network));
446 if (g_inet_address_mask_get_length (network) == 0)
448 switch (g_inet_address_mask_get_family (network))
450 case G_SOCKET_FAMILY_IPV4:
451 monitor->priv->have_ipv4_default_route = TRUE;
452 break;
453 case G_SOCKET_FAMILY_IPV6:
454 monitor->priv->have_ipv6_default_route = TRUE;
455 break;
456 default:
457 break;
461 /* Don't emit network-changed when multicast-link-local routing
462 * changes. This rather arbitrary decision is mostly because it
463 * seems to change quite often...
465 if (g_inet_address_get_is_mc_link_local (g_inet_address_mask_get_address (network)))
466 return;
468 queue_network_changed (monitor);
472 * g_network_monitor_base_remove_network:
473 * @monitor: the #GNetworkMonitorBase
474 * @network: a #GInetAddressMask
476 * Removes @network from @monitor's list of available networks.
478 * Since: 2.32
480 void
481 g_network_monitor_base_remove_network (GNetworkMonitorBase *monitor,
482 GInetAddressMask *network)
484 int i;
486 for (i = 0; i < monitor->priv->networks->len; i++)
488 if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
490 g_ptr_array_remove_index_fast (monitor->priv->networks, i);
492 if (g_inet_address_mask_get_length (network) == 0)
494 switch (g_inet_address_mask_get_family (network))
496 case G_SOCKET_FAMILY_IPV4:
497 monitor->priv->have_ipv4_default_route = FALSE;
498 break;
499 case G_SOCKET_FAMILY_IPV6:
500 monitor->priv->have_ipv6_default_route = FALSE;
501 break;
502 default:
503 break;
507 queue_network_changed (monitor);
508 return;
514 * g_network_monitor_base_set_networks:
515 * @monitor: the #GNetworkMonitorBase
516 * @networks: (array length=length): an array of #GInetAddressMask
517 * @length: length of @networks
519 * Drops @monitor's current list of available networks and replaces
520 * it with @networks.
522 void
523 g_network_monitor_base_set_networks (GNetworkMonitorBase *monitor,
524 GInetAddressMask **networks,
525 gint length)
527 int i;
529 g_ptr_array_set_size (monitor->priv->networks, 0);
530 monitor->priv->have_ipv4_default_route = FALSE;
531 monitor->priv->have_ipv6_default_route = FALSE;
533 for (i = 0; i < length; i++)
534 g_network_monitor_base_add_network (monitor, networks[i]);