gio/tests/gsubprocess.c: Fix on Windows
[glib.git] / gio / gnetworkmonitorbase.c
blob85b7f5341c26a53161f3fd0d7060ba9d3a8a00b2
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, write to the
17 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18 * Boston, MA 02111-1307, USA.
21 #include "config.h"
23 #include "gnetworkmonitorbase.h"
24 #include "ginetaddress.h"
25 #include "ginetaddressmask.h"
26 #include "ginetsocketaddress.h"
27 #include "ginitable.h"
28 #include "gioerror.h"
29 #include "giomodule-priv.h"
30 #include "gnetworkmonitor.h"
31 #include "gsocketaddressenumerator.h"
32 #include "gsocketconnectable.h"
33 #include "gtask.h"
34 #include "glibintl.h"
36 static void g_network_monitor_base_iface_init (GNetworkMonitorInterface *iface);
37 static void g_network_monitor_base_initable_iface_init (GInitableIface *iface);
39 enum
41 PROP_0,
43 PROP_NETWORK_AVAILABLE
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;
84 queue_network_changed (monitor);
87 static void
88 g_network_monitor_base_constructed (GObject *object)
90 GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
92 if (G_OBJECT_TYPE (monitor) == G_TYPE_NETWORK_MONITOR_BASE)
94 GInetAddressMask *mask;
96 /* We're the dumb base class, not a smarter subclass. So just
97 * assume that the network is available.
99 mask = g_inet_address_mask_new_from_string ("0.0.0.0/0", NULL);
100 g_network_monitor_base_add_network (monitor, mask);
101 g_object_unref (mask);
103 mask = g_inet_address_mask_new_from_string ("::/0", NULL);
104 g_network_monitor_base_add_network (monitor, mask);
105 g_object_unref (mask);
109 static void
110 g_network_monitor_base_get_property (GObject *object,
111 guint prop_id,
112 GValue *value,
113 GParamSpec *pspec)
115 GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
117 switch (prop_id)
119 case PROP_NETWORK_AVAILABLE:
120 g_value_set_boolean (value, monitor->priv->is_available);
121 break;
123 default:
124 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
129 static void
130 g_network_monitor_base_finalize (GObject *object)
132 GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
134 g_ptr_array_free (monitor->priv->networks, TRUE);
135 if (monitor->priv->network_changed_source)
137 g_source_destroy (monitor->priv->network_changed_source);
138 g_source_unref (monitor->priv->network_changed_source);
140 if (monitor->priv->context)
141 g_main_context_unref (monitor->priv->context);
143 G_OBJECT_CLASS (g_network_monitor_base_parent_class)->finalize (object);
146 static void
147 g_network_monitor_base_class_init (GNetworkMonitorBaseClass *monitor_class)
149 GObjectClass *gobject_class = G_OBJECT_CLASS (monitor_class);
151 gobject_class->constructed = g_network_monitor_base_constructed;
152 gobject_class->get_property = g_network_monitor_base_get_property;
153 gobject_class->finalize = g_network_monitor_base_finalize;
155 g_object_class_override_property (gobject_class, PROP_NETWORK_AVAILABLE, "network-available");
158 static gboolean
159 g_network_monitor_base_can_reach_sockaddr (GNetworkMonitorBase *base,
160 GSocketAddress *sockaddr)
162 GInetAddress *iaddr;
163 int i;
165 if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
166 return FALSE;
168 iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (sockaddr));
169 for (i = 0; i < base->priv->networks->len; i++)
171 if (g_inet_address_mask_matches (base->priv->networks->pdata[i], iaddr))
172 return TRUE;
175 return FALSE;
178 static gboolean
179 g_network_monitor_base_can_reach (GNetworkMonitor *monitor,
180 GSocketConnectable *connectable,
181 GCancellable *cancellable,
182 GError **error)
184 GNetworkMonitorBase *base = G_NETWORK_MONITOR_BASE (monitor);
185 GSocketAddressEnumerator *enumerator;
186 GSocketAddress *addr;
188 if (base->priv->networks->len == 0)
190 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
191 _("Network unreachable"));
192 return FALSE;
195 enumerator = g_socket_connectable_proxy_enumerate (connectable);
196 addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
197 if (!addr)
199 /* Either the user cancelled, or DNS resolution failed */
200 g_object_unref (enumerator);
201 return FALSE;
204 if (base->priv->have_ipv4_default_route &&
205 base->priv->have_ipv6_default_route)
207 g_object_unref (enumerator);
208 g_object_unref (addr);
209 return TRUE;
212 while (addr)
214 if (g_network_monitor_base_can_reach_sockaddr (base, addr))
216 g_object_unref (addr);
217 g_object_unref (enumerator);
218 return TRUE;
221 g_object_unref (addr);
222 addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
224 g_object_unref (enumerator);
226 if (error && !*error)
228 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
229 _("Host unreachable"));
231 return FALSE;
234 static void
235 can_reach_async_got_address (GObject *object,
236 GAsyncResult *result,
237 gpointer user_data)
239 GSocketAddressEnumerator *enumerator = G_SOCKET_ADDRESS_ENUMERATOR (object);
240 GTask *task = user_data;
241 GNetworkMonitorBase *base = g_task_get_source_object (task);
242 GSocketAddress *addr;
243 GError *error = NULL;
245 addr = g_socket_address_enumerator_next_finish (enumerator, result, &error);
246 if (!addr)
248 if (error)
250 /* Either the user cancelled, or DNS resolution failed */
251 g_task_return_error (task, error);
252 g_object_unref (task);
253 return;
255 else
257 /* Resolved all addresses, none matched */
258 g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
259 _("Host unreachable"));
260 g_object_unref (task);
261 return;
265 if (g_network_monitor_base_can_reach_sockaddr (base, addr))
267 g_object_unref (addr);
268 g_task_return_boolean (task, TRUE);
269 g_object_unref (task);
270 return;
272 g_object_unref (addr);
274 g_socket_address_enumerator_next_async (enumerator,
275 g_task_get_cancellable (task),
276 can_reach_async_got_address, task);
279 static void
280 g_network_monitor_base_can_reach_async (GNetworkMonitor *monitor,
281 GSocketConnectable *connectable,
282 GCancellable *cancellable,
283 GAsyncReadyCallback callback,
284 gpointer user_data)
286 GTask *task;
287 GSocketAddressEnumerator *enumerator;
289 task = g_task_new (monitor, cancellable, callback, user_data);
291 if (G_NETWORK_MONITOR_BASE (monitor)->priv->networks->len == 0)
293 g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
294 _("Network unreachable"));
295 g_object_unref (task);
296 return;
299 enumerator = g_socket_connectable_proxy_enumerate (connectable);
300 g_socket_address_enumerator_next_async (enumerator, cancellable,
301 can_reach_async_got_address, task);
302 g_object_unref (enumerator);
305 static gboolean
306 g_network_monitor_base_can_reach_finish (GNetworkMonitor *monitor,
307 GAsyncResult *result,
308 GError **error)
310 g_return_val_if_fail (g_task_is_valid (result, monitor), FALSE);
312 return g_task_propagate_boolean (G_TASK (result), error);
315 static void
316 g_network_monitor_base_iface_init (GNetworkMonitorInterface *monitor_iface)
318 monitor_iface->can_reach = g_network_monitor_base_can_reach;
319 monitor_iface->can_reach_async = g_network_monitor_base_can_reach_async;
320 monitor_iface->can_reach_finish = g_network_monitor_base_can_reach_finish;
322 network_changed_signal = g_signal_lookup ("network-changed", G_TYPE_NETWORK_MONITOR);
325 static gboolean
326 g_network_monitor_base_initable_init (GInitable *initable,
327 GCancellable *cancellable,
328 GError **error)
330 return TRUE;
333 static void
334 g_network_monitor_base_initable_iface_init (GInitableIface *iface)
336 iface->init = g_network_monitor_base_initable_init;
339 static gboolean
340 emit_network_changed (gpointer user_data)
342 GNetworkMonitorBase *monitor = user_data;
343 gboolean is_available;
345 g_object_ref (monitor);
347 if (monitor->priv->initializing)
348 monitor->priv->initializing = FALSE;
349 else
351 is_available = (monitor->priv->have_ipv4_default_route ||
352 monitor->priv->have_ipv6_default_route);
353 if (monitor->priv->is_available != is_available)
355 monitor->priv->is_available = is_available;
356 g_object_notify (G_OBJECT (monitor), "network-available");
359 g_signal_emit (monitor, network_changed_signal, 0, is_available);
362 g_source_unref (monitor->priv->network_changed_source);
363 monitor->priv->network_changed_source = NULL;
365 g_object_unref (monitor);
366 return FALSE;
369 static void
370 queue_network_changed (GNetworkMonitorBase *monitor)
372 if (!monitor->priv->network_changed_source)
374 GSource *source;
376 source = g_idle_source_new ();
377 /* Use G_PRIORITY_HIGH_IDLE priority so that multiple
378 * network-change-related notifications coming in at
379 * G_PRIORITY_DEFAULT will get coalesced into one signal
380 * emission.
382 g_source_set_priority (source, G_PRIORITY_HIGH_IDLE);
383 g_source_set_callback (source, emit_network_changed, monitor, NULL);
384 g_source_attach (source, monitor->priv->context);
385 monitor->priv->network_changed_source = source;
388 /* Normally we wait to update is_available until we emit the signal,
389 * to keep things consistent. But when we're first creating the
390 * object, we want it to be correct right away.
392 if (monitor->priv->initializing)
394 monitor->priv->is_available = (monitor->priv->have_ipv4_default_route ||
395 monitor->priv->have_ipv6_default_route);
400 * g_network_monitor_base_add_network:
401 * @monitor: the #GNetworkMonitorBase
402 * @network: a #GInetAddressMask
404 * Adds @network to @monitor's list of available networks.
406 * Since: 2.32
408 void
409 g_network_monitor_base_add_network (GNetworkMonitorBase *monitor,
410 GInetAddressMask *network)
412 int i;
414 for (i = 0; i < monitor->priv->networks->len; i++)
416 if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
417 return;
420 g_ptr_array_add (monitor->priv->networks, g_object_ref (network));
421 if (g_inet_address_mask_get_length (network) == 0)
423 switch (g_inet_address_mask_get_family (network))
425 case G_SOCKET_FAMILY_IPV4:
426 monitor->priv->have_ipv4_default_route = TRUE;
427 break;
428 case G_SOCKET_FAMILY_IPV6:
429 monitor->priv->have_ipv6_default_route = TRUE;
430 break;
431 default:
432 break;
436 /* Don't emit network-changed when multicast-link-local routing
437 * changes. This rather arbitrary decision is mostly because it
438 * seems to change quite often...
440 if (g_inet_address_get_is_mc_link_local (g_inet_address_mask_get_address (network)))
441 return;
443 queue_network_changed (monitor);
447 * g_network_monitor_base_remove_network:
448 * @monitor: the #GNetworkMonitorBase
449 * @network: a #GInetAddressMask
451 * Removes @network from @monitor's list of available networks.
453 * Since: 2.32
455 void
456 g_network_monitor_base_remove_network (GNetworkMonitorBase *monitor,
457 GInetAddressMask *network)
459 int i;
461 for (i = 0; i < monitor->priv->networks->len; i++)
463 if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
465 g_ptr_array_remove_index_fast (monitor->priv->networks, i);
467 if (g_inet_address_mask_get_length (network) == 0)
469 switch (g_inet_address_mask_get_family (network))
471 case G_SOCKET_FAMILY_IPV4:
472 monitor->priv->have_ipv4_default_route = FALSE;
473 break;
474 case G_SOCKET_FAMILY_IPV6:
475 monitor->priv->have_ipv6_default_route = FALSE;
476 break;
477 default:
478 break;
482 queue_network_changed (monitor);
483 return;
489 * g_network_monitor_base_set_networks:
490 * @monitor: the #GNetworkMonitorBase
491 * @networks: (array length=length): an array of #GInetAddressMask
492 * @length: length of @networks
494 * Drops @monitor's current list of available networks and replaces
495 * it with @networks.
497 void
498 g_network_monitor_base_set_networks (GNetworkMonitorBase *monitor,
499 GInetAddressMask **networks,
500 gint length)
502 int i;
504 g_ptr_array_set_size (monitor->priv->networks, 0);
505 monitor->priv->have_ipv4_default_route = FALSE;
506 monitor->priv->have_ipv6_default_route = FALSE;
508 for (i = 0; i < length; i++)
509 g_network_monitor_base_add_network (monitor, networks[i]);