tests: only chmod autorun.exe on UNIX
[glib.git] / gio / gnetworkmonitorbase.c
blobf0af80405da0d5a2c3976b2c0e41115ca63a61e7
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 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_CONNECTIVITY
45 struct _GNetworkMonitorBasePrivate
47 GPtrArray *networks;
48 gboolean have_ipv4_default_route;
49 gboolean have_ipv6_default_route;
50 gboolean is_available;
52 GMainContext *context;
53 GSource *network_changed_source;
54 gboolean initializing;
57 static guint network_changed_signal = 0;
59 static void queue_network_changed (GNetworkMonitorBase *monitor);
61 G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorBase, g_network_monitor_base, G_TYPE_OBJECT,
62 G_ADD_PRIVATE (GNetworkMonitorBase)
63 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
64 g_network_monitor_base_initable_iface_init)
65 G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
66 g_network_monitor_base_iface_init)
67 _g_io_modules_ensure_extension_points_registered ();
68 g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
69 g_define_type_id,
70 "base",
71 0))
73 static void
74 g_network_monitor_base_init (GNetworkMonitorBase *monitor)
76 monitor->priv = g_network_monitor_base_get_instance_private (monitor);
77 monitor->priv->networks = g_ptr_array_new_with_free_func (g_object_unref);
78 monitor->priv->context = g_main_context_get_thread_default ();
79 if (monitor->priv->context)
80 g_main_context_ref (monitor->priv->context);
82 monitor->priv->initializing = TRUE;
83 queue_network_changed (monitor);
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 g_network_monitor_base_add_network (monitor, mask);
104 g_object_unref (mask);
108 static void
109 g_network_monitor_base_get_property (GObject *object,
110 guint prop_id,
111 GValue *value,
112 GParamSpec *pspec)
114 GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
116 switch (prop_id)
118 case PROP_NETWORK_AVAILABLE:
119 g_value_set_boolean (value, monitor->priv->is_available);
120 break;
122 case PROP_CONNECTIVITY:
123 g_value_set_enum (value,
124 monitor->priv->is_available ?
125 G_NETWORK_CONNECTIVITY_FULL :
126 G_NETWORK_CONNECTIVITY_LOCAL);
127 break;
129 default:
130 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
131 break;
136 static void
137 g_network_monitor_base_finalize (GObject *object)
139 GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
141 g_ptr_array_free (monitor->priv->networks, TRUE);
142 if (monitor->priv->network_changed_source)
144 g_source_destroy (monitor->priv->network_changed_source);
145 g_source_unref (monitor->priv->network_changed_source);
147 if (monitor->priv->context)
148 g_main_context_unref (monitor->priv->context);
150 G_OBJECT_CLASS (g_network_monitor_base_parent_class)->finalize (object);
153 static void
154 g_network_monitor_base_class_init (GNetworkMonitorBaseClass *monitor_class)
156 GObjectClass *gobject_class = G_OBJECT_CLASS (monitor_class);
158 gobject_class->constructed = g_network_monitor_base_constructed;
159 gobject_class->get_property = g_network_monitor_base_get_property;
160 gobject_class->finalize = g_network_monitor_base_finalize;
162 g_object_class_override_property (gobject_class, PROP_NETWORK_AVAILABLE, "network-available");
163 g_object_class_override_property (gobject_class, PROP_CONNECTIVITY, "connectivity");
166 static gboolean
167 g_network_monitor_base_can_reach_sockaddr (GNetworkMonitorBase *base,
168 GSocketAddress *sockaddr)
170 GInetAddress *iaddr;
171 int i;
173 if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
174 return FALSE;
176 iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (sockaddr));
177 for (i = 0; i < base->priv->networks->len; i++)
179 if (g_inet_address_mask_matches (base->priv->networks->pdata[i], iaddr))
180 return TRUE;
183 return FALSE;
186 static gboolean
187 g_network_monitor_base_can_reach (GNetworkMonitor *monitor,
188 GSocketConnectable *connectable,
189 GCancellable *cancellable,
190 GError **error)
192 GNetworkMonitorBase *base = G_NETWORK_MONITOR_BASE (monitor);
193 GSocketAddressEnumerator *enumerator;
194 GSocketAddress *addr;
196 if (base->priv->networks->len == 0)
198 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
199 _("Network unreachable"));
200 return FALSE;
203 enumerator = g_socket_connectable_proxy_enumerate (connectable);
204 addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
205 if (!addr)
207 /* Either the user cancelled, or DNS resolution failed */
208 g_object_unref (enumerator);
209 return FALSE;
212 if (base->priv->have_ipv4_default_route &&
213 base->priv->have_ipv6_default_route)
215 g_object_unref (enumerator);
216 g_object_unref (addr);
217 return TRUE;
220 while (addr)
222 if (g_network_monitor_base_can_reach_sockaddr (base, addr))
224 g_object_unref (addr);
225 g_object_unref (enumerator);
226 return TRUE;
229 g_object_unref (addr);
230 addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
232 g_object_unref (enumerator);
234 if (error && !*error)
236 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
237 _("Host unreachable"));
239 return FALSE;
242 static void
243 can_reach_async_got_address (GObject *object,
244 GAsyncResult *result,
245 gpointer user_data)
247 GSocketAddressEnumerator *enumerator = G_SOCKET_ADDRESS_ENUMERATOR (object);
248 GTask *task = user_data;
249 GNetworkMonitorBase *base = g_task_get_source_object (task);
250 GSocketAddress *addr;
251 GError *error = NULL;
253 addr = g_socket_address_enumerator_next_finish (enumerator, result, &error);
254 if (!addr)
256 if (error)
258 /* Either the user cancelled, or DNS resolution failed */
259 g_task_return_error (task, error);
260 g_object_unref (task);
261 return;
263 else
265 /* Resolved all addresses, none matched */
266 g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
267 _("Host unreachable"));
268 g_object_unref (task);
269 return;
273 if (g_network_monitor_base_can_reach_sockaddr (base, addr))
275 g_object_unref (addr);
276 g_task_return_boolean (task, TRUE);
277 g_object_unref (task);
278 return;
280 g_object_unref (addr);
282 g_socket_address_enumerator_next_async (enumerator,
283 g_task_get_cancellable (task),
284 can_reach_async_got_address, task);
287 static void
288 g_network_monitor_base_can_reach_async (GNetworkMonitor *monitor,
289 GSocketConnectable *connectable,
290 GCancellable *cancellable,
291 GAsyncReadyCallback callback,
292 gpointer user_data)
294 GTask *task;
295 GSocketAddressEnumerator *enumerator;
297 task = g_task_new (monitor, cancellable, callback, user_data);
299 if (G_NETWORK_MONITOR_BASE (monitor)->priv->networks->len == 0)
301 g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
302 _("Network unreachable"));
303 g_object_unref (task);
304 return;
307 enumerator = g_socket_connectable_proxy_enumerate (connectable);
308 g_socket_address_enumerator_next_async (enumerator, cancellable,
309 can_reach_async_got_address, task);
310 g_object_unref (enumerator);
313 static gboolean
314 g_network_monitor_base_can_reach_finish (GNetworkMonitor *monitor,
315 GAsyncResult *result,
316 GError **error)
318 g_return_val_if_fail (g_task_is_valid (result, monitor), FALSE);
320 return g_task_propagate_boolean (G_TASK (result), error);
323 static void
324 g_network_monitor_base_iface_init (GNetworkMonitorInterface *monitor_iface)
326 monitor_iface->can_reach = g_network_monitor_base_can_reach;
327 monitor_iface->can_reach_async = g_network_monitor_base_can_reach_async;
328 monitor_iface->can_reach_finish = g_network_monitor_base_can_reach_finish;
330 network_changed_signal = g_signal_lookup ("network-changed", G_TYPE_NETWORK_MONITOR);
333 static gboolean
334 g_network_monitor_base_initable_init (GInitable *initable,
335 GCancellable *cancellable,
336 GError **error)
338 return TRUE;
341 static void
342 g_network_monitor_base_initable_iface_init (GInitableIface *iface)
344 iface->init = g_network_monitor_base_initable_init;
347 static gboolean
348 emit_network_changed (gpointer user_data)
350 GNetworkMonitorBase *monitor = user_data;
351 gboolean is_available;
353 g_object_ref (monitor);
355 if (monitor->priv->initializing)
356 monitor->priv->initializing = FALSE;
357 else
359 is_available = (monitor->priv->have_ipv4_default_route ||
360 monitor->priv->have_ipv6_default_route);
361 if (monitor->priv->is_available != is_available)
363 monitor->priv->is_available = is_available;
364 g_object_notify (G_OBJECT (monitor), "network-available");
367 g_signal_emit (monitor, network_changed_signal, 0, is_available);
370 g_source_unref (monitor->priv->network_changed_source);
371 monitor->priv->network_changed_source = NULL;
373 g_object_unref (monitor);
374 return FALSE;
377 static void
378 queue_network_changed (GNetworkMonitorBase *monitor)
380 if (!monitor->priv->network_changed_source)
382 GSource *source;
384 source = g_idle_source_new ();
385 /* Use G_PRIORITY_HIGH_IDLE priority so that multiple
386 * network-change-related notifications coming in at
387 * G_PRIORITY_DEFAULT will get coalesced into one signal
388 * emission.
390 g_source_set_priority (source, G_PRIORITY_HIGH_IDLE);
391 g_source_set_callback (source, emit_network_changed, monitor, NULL);
392 g_source_set_name (source, "[gio] emit_network_changed");
393 g_source_attach (source, monitor->priv->context);
394 monitor->priv->network_changed_source = source;
397 /* Normally we wait to update is_available until we emit the signal,
398 * to keep things consistent. But when we're first creating the
399 * object, we want it to be correct right away.
401 if (monitor->priv->initializing)
403 monitor->priv->is_available = (monitor->priv->have_ipv4_default_route ||
404 monitor->priv->have_ipv6_default_route);
409 * g_network_monitor_base_add_network:
410 * @monitor: the #GNetworkMonitorBase
411 * @network: a #GInetAddressMask
413 * Adds @network to @monitor's list of available networks.
415 * Since: 2.32
417 void
418 g_network_monitor_base_add_network (GNetworkMonitorBase *monitor,
419 GInetAddressMask *network)
421 int i;
423 for (i = 0; i < monitor->priv->networks->len; i++)
425 if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
426 return;
429 g_ptr_array_add (monitor->priv->networks, g_object_ref (network));
430 if (g_inet_address_mask_get_length (network) == 0)
432 switch (g_inet_address_mask_get_family (network))
434 case G_SOCKET_FAMILY_IPV4:
435 monitor->priv->have_ipv4_default_route = TRUE;
436 break;
437 case G_SOCKET_FAMILY_IPV6:
438 monitor->priv->have_ipv6_default_route = TRUE;
439 break;
440 default:
441 break;
445 /* Don't emit network-changed when multicast-link-local routing
446 * changes. This rather arbitrary decision is mostly because it
447 * seems to change quite often...
449 if (g_inet_address_get_is_mc_link_local (g_inet_address_mask_get_address (network)))
450 return;
452 queue_network_changed (monitor);
456 * g_network_monitor_base_remove_network:
457 * @monitor: the #GNetworkMonitorBase
458 * @network: a #GInetAddressMask
460 * Removes @network from @monitor's list of available networks.
462 * Since: 2.32
464 void
465 g_network_monitor_base_remove_network (GNetworkMonitorBase *monitor,
466 GInetAddressMask *network)
468 int i;
470 for (i = 0; i < monitor->priv->networks->len; i++)
472 if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
474 g_ptr_array_remove_index_fast (monitor->priv->networks, i);
476 if (g_inet_address_mask_get_length (network) == 0)
478 switch (g_inet_address_mask_get_family (network))
480 case G_SOCKET_FAMILY_IPV4:
481 monitor->priv->have_ipv4_default_route = FALSE;
482 break;
483 case G_SOCKET_FAMILY_IPV6:
484 monitor->priv->have_ipv6_default_route = FALSE;
485 break;
486 default:
487 break;
491 queue_network_changed (monitor);
492 return;
498 * g_network_monitor_base_set_networks:
499 * @monitor: the #GNetworkMonitorBase
500 * @networks: (array length=length): an array of #GInetAddressMask
501 * @length: length of @networks
503 * Drops @monitor's current list of available networks and replaces
504 * it with @networks.
506 void
507 g_network_monitor_base_set_networks (GNetworkMonitorBase *monitor,
508 GInetAddressMask **networks,
509 gint length)
511 int i;
513 g_ptr_array_set_size (monitor->priv->networks, 0);
514 monitor->priv->have_ipv4_default_route = FALSE;
515 monitor->priv->have_ipv6_default_route = FALSE;
517 for (i = 0; i < length; i++)
518 g_network_monitor_base_add_network (monitor, networks[i]);