menu threaded test: run the mainloop after export
[glib.git] / gio / gnetworkmonitorbase.c
blob2ac41157db8c676d767d195a4db1df8fa782d0ee
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 "glibintl.h"
35 static void g_network_monitor_base_iface_init (GNetworkMonitorInterface *iface);
36 static void g_network_monitor_base_initable_iface_init (GInitableIface *iface);
38 G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorBase, g_network_monitor_base, G_TYPE_OBJECT,
39 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
40 g_network_monitor_base_initable_iface_init)
41 G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
42 g_network_monitor_base_iface_init)
43 _g_io_modules_ensure_extension_points_registered ();
44 g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
45 g_define_type_id,
46 "base",
47 0))
49 enum
51 PROP_0,
53 PROP_NETWORK_AVAILABLE
56 struct _GNetworkMonitorBasePrivate
58 GPtrArray *networks;
59 gboolean have_ipv4_default_route;
60 gboolean have_ipv6_default_route;
61 gboolean is_available;
63 GMainContext *context;
64 GSource *network_changed_source;
65 gboolean initializing;
68 static guint network_changed_signal = 0;
70 static void queue_network_changed (GNetworkMonitorBase *monitor);
72 static void
73 g_network_monitor_base_init (GNetworkMonitorBase *monitor)
75 monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor,
76 G_TYPE_NETWORK_MONITOR_BASE,
77 GNetworkMonitorBasePrivate);
79 monitor->priv->networks = g_ptr_array_new_with_free_func (g_object_unref);
80 monitor->priv->context = g_main_context_get_thread_default ();
81 if (monitor->priv->context)
82 g_main_context_ref (monitor->priv->context);
84 monitor->priv->initializing = TRUE;
85 queue_network_changed (monitor);
88 static void
89 g_network_monitor_base_constructed (GObject *object)
91 GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
93 if (G_OBJECT_TYPE (monitor) == G_TYPE_NETWORK_MONITOR_BASE)
95 GInetAddressMask *mask;
97 /* We're the dumb base class, not a smarter subclass. So just
98 * assume that the network is available.
100 mask = g_inet_address_mask_new_from_string ("0.0.0.0/0", NULL);
101 g_network_monitor_base_add_network (monitor, mask);
102 g_object_unref (mask);
104 mask = g_inet_address_mask_new_from_string ("::/0", NULL);
105 g_network_monitor_base_add_network (monitor, mask);
106 g_object_unref (mask);
110 static void
111 g_network_monitor_base_get_property (GObject *object,
112 guint prop_id,
113 GValue *value,
114 GParamSpec *pspec)
116 GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
118 switch (prop_id)
120 case PROP_NETWORK_AVAILABLE:
121 g_value_set_boolean (value, monitor->priv->is_available);
122 break;
124 default:
125 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
130 static void
131 g_network_monitor_base_finalize (GObject *object)
133 GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
135 g_ptr_array_free (monitor->priv->networks, TRUE);
136 if (monitor->priv->network_changed_source)
138 g_source_destroy (monitor->priv->network_changed_source);
139 g_source_unref (monitor->priv->network_changed_source);
141 if (monitor->priv->context)
142 g_main_context_unref (monitor->priv->context);
144 G_OBJECT_CLASS (g_network_monitor_base_parent_class)->finalize (object);
147 static void
148 g_network_monitor_base_class_init (GNetworkMonitorBaseClass *monitor_class)
150 GObjectClass *gobject_class = G_OBJECT_CLASS (monitor_class);
152 g_type_class_add_private (monitor_class, sizeof (GNetworkMonitorBasePrivate));
154 gobject_class->constructed = g_network_monitor_base_constructed;
155 gobject_class->get_property = g_network_monitor_base_get_property;
156 gobject_class->finalize = g_network_monitor_base_finalize;
158 g_object_class_override_property (gobject_class, PROP_NETWORK_AVAILABLE, "network-available");
161 static gboolean
162 g_network_monitor_base_can_reach (GNetworkMonitor *monitor,
163 GSocketConnectable *connectable,
164 GCancellable *cancellable,
165 GError **error)
167 GNetworkMonitorBasePrivate *priv = G_NETWORK_MONITOR_BASE (monitor)->priv;
168 GSocketAddressEnumerator *enumerator;
169 GSocketAddress *addr;
171 if (priv->have_ipv4_default_route &&
172 priv->have_ipv6_default_route)
173 return TRUE;
175 if (priv->networks->len == 0)
177 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
178 _("Network unreachable"));
179 return FALSE;
182 enumerator = g_socket_connectable_proxy_enumerate (connectable);
183 addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
184 if (!addr)
186 /* Either the user cancelled, or DNS resolution failed */
187 g_object_unref (enumerator);
188 return FALSE;
191 while (addr)
193 if (G_IS_INET_SOCKET_ADDRESS (addr))
195 GInetAddress *iaddr;
196 int i;
198 iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr));
199 for (i = 0; i < priv->networks->len; i++)
201 if (g_inet_address_mask_matches (priv->networks->pdata[i], iaddr))
203 g_object_unref (addr);
204 g_object_unref (enumerator);
205 return TRUE;
210 g_object_unref (addr);
211 addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
213 g_object_unref (enumerator);
215 if (error && !*error)
217 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
218 _("Host unreachable"));
220 return FALSE;
223 static void
224 g_network_monitor_base_iface_init (GNetworkMonitorInterface *monitor_iface)
226 monitor_iface->can_reach = g_network_monitor_base_can_reach;
228 network_changed_signal = g_signal_lookup ("network-changed", G_TYPE_NETWORK_MONITOR);
231 static gboolean
232 g_network_monitor_base_initable_init (GInitable *initable,
233 GCancellable *cancellable,
234 GError **error)
236 return TRUE;
239 static void
240 g_network_monitor_base_initable_iface_init (GInitableIface *iface)
242 iface->init = g_network_monitor_base_initable_init;
245 static gboolean
246 emit_network_changed (gpointer user_data)
248 GNetworkMonitorBase *monitor = user_data;
249 gboolean is_available;
251 g_object_ref (monitor);
253 if (monitor->priv->initializing)
254 monitor->priv->initializing = FALSE;
255 else
257 is_available = (monitor->priv->have_ipv4_default_route ||
258 monitor->priv->have_ipv6_default_route);
259 if (monitor->priv->is_available != is_available)
261 monitor->priv->is_available = is_available;
262 g_object_notify (G_OBJECT (monitor), "network-available");
265 g_signal_emit (monitor, network_changed_signal, 0, is_available);
268 g_source_unref (monitor->priv->network_changed_source);
269 monitor->priv->network_changed_source = NULL;
271 g_object_unref (monitor);
272 return FALSE;
275 static void
276 queue_network_changed (GNetworkMonitorBase *monitor)
278 if (!monitor->priv->network_changed_source)
280 GSource *source;
282 source = g_idle_source_new ();
283 /* Use G_PRIORITY_HIGH_IDLE priority so that multiple
284 * network-change-related notifications coming in at
285 * G_PRIORITY_DEFAULT will get coalesced into one signal
286 * emission.
288 g_source_set_priority (source, G_PRIORITY_HIGH_IDLE);
289 g_source_set_callback (source, emit_network_changed, monitor, NULL);
290 g_source_attach (source, monitor->priv->context);
291 monitor->priv->network_changed_source = source;
294 /* Normally we wait to update is_available until we emit the signal,
295 * to keep things consistent. But when we're first creating the
296 * object, we want it to be correct right away.
298 if (monitor->priv->initializing)
300 monitor->priv->is_available = (monitor->priv->have_ipv4_default_route ||
301 monitor->priv->have_ipv6_default_route);
306 * g_network_monitor_base_add_network:
307 * @monitor: the #GNetworkMonitorBase
308 * @network: a #GInetAddressMask
310 * Adds @network to @monitor's list of available networks.
312 void
313 g_network_monitor_base_add_network (GNetworkMonitorBase *monitor,
314 GInetAddressMask *network)
316 int i;
318 for (i = 0; i < monitor->priv->networks->len; i++)
320 if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
321 return;
324 g_ptr_array_add (monitor->priv->networks, g_object_ref (network));
325 if (g_inet_address_mask_get_length (network) == 0)
327 switch (g_inet_address_mask_get_family (network))
329 case G_SOCKET_FAMILY_IPV4:
330 monitor->priv->have_ipv4_default_route = TRUE;
331 break;
332 case G_SOCKET_FAMILY_IPV6:
333 monitor->priv->have_ipv6_default_route = TRUE;
334 break;
335 default:
336 break;
340 /* Don't emit network-changed when multicast-link-local routing
341 * changes. This rather arbitrary decision is mostly because it
342 * seems to change quite often...
344 if (g_inet_address_get_is_mc_link_local (g_inet_address_mask_get_address (network)))
345 return;
347 queue_network_changed (monitor);
351 * g_network_monitor_base_remove_network:
352 * @monitor: the #GNetworkMonitorBase
353 * @network: a #GInetAddressMask
355 * Removes @network from @monitor's list of available networks.
357 void
358 g_network_monitor_base_remove_network (GNetworkMonitorBase *monitor,
359 GInetAddressMask *network)
361 int i;
363 for (i = 0; i < monitor->priv->networks->len; i++)
365 if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
367 g_ptr_array_remove_index_fast (monitor->priv->networks, i);
369 if (g_inet_address_mask_get_length (network) == 0)
371 switch (g_inet_address_mask_get_family (network))
373 case G_SOCKET_FAMILY_IPV4:
374 monitor->priv->have_ipv4_default_route = FALSE;
375 break;
376 case G_SOCKET_FAMILY_IPV6:
377 monitor->priv->have_ipv6_default_route = FALSE;
378 break;
379 default:
380 break;
384 queue_network_changed (monitor);
385 return;
391 * g_network_monitor_base_set_networks:
392 * @monitor: the #GNetworkMonitorBase
393 * @networks: (array length=length): an array of #GInetAddressMask
394 * @length: length of @networks
396 * Drops @monitor's current list of available networks and replaces
397 * it with @networks.
399 void
400 g_network_monitor_base_set_networks (GNetworkMonitorBase *monitor,
401 GInetAddressMask **networks,
402 gint length)
404 int i;
406 g_ptr_array_set_size (monitor->priv->networks, 0);
407 monitor->priv->have_ipv4_default_route = FALSE;
408 monitor->priv->have_ipv6_default_route = FALSE;
410 for (i = 0; i < length; i++)
411 g_network_monitor_base_add_network (monitor, networks[i]);