diff --git a/include/NetworkManager.h b/include/NetworkManager.h index 27c20f6000..4aa6e9e72d 100644 --- a/include/NetworkManager.h +++ b/include/NetworkManager.h @@ -101,6 +101,27 @@ typedef enum { /* For backwards compat */ #define NM_STATE_CONNECTED NM_STATE_CONNECTED_GLOBAL +/** + * NMConnectivityState: + * @NM_CONNECTIVITY_UNKNOWN: Network connectivity is unknown. + * @NM_CONNECTIVITY_NONE: The host is not connected to any network. + * @NM_CONNECTIVITY_PORTAL: The host is behind a captive portal and + * cannot reach the full Internet. + * @NM_CONNECTIVITY_LIMITED: The host is connected to a network, but + * does not appear to be able to reach the full Internet. + * @NM_CONNECTIVITY_FULL: The host is connected to a network, and + * appears to be able to reach the full Internet. + * + * Since: 0.9.10 + */ +typedef enum { + NM_CONNECTIVITY_UNKNOWN, + NM_CONNECTIVITY_NONE, + NM_CONNECTIVITY_PORTAL, + NM_CONNECTIVITY_LIMITED, + NM_CONNECTIVITY_FULL +} NMConnectivityState; + /** * NMDeviceType: * @NM_DEVICE_TYPE_UNKNOWN: unknown device diff --git a/introspection/nm-manager.xml b/introspection/nm-manager.xml index d9f6a7c3d2..4fc7692cc4 100644 --- a/introspection/nm-manager.xml +++ b/introspection/nm-manager.xml @@ -244,6 +244,19 @@ + + + + + Re-check the network connectivity state. + + + + The current connectivity state. + + + + The overall networking state as determined by the NetworkManager daemon, @@ -333,6 +346,12 @@ + + + The network connectivity state. + + + NetworkManager's properties changed. @@ -413,5 +432,39 @@ + + + Describes the network-connectivity state. + + + + Network connectivity is unknown. + + + + + The host is not connected to any network. + + + + + The host is behind a captive portal and cannot reach the + full Internet. + + + + + The host is connected to a network, but does not appear to + be able to reach the full Internet. + + + + + The host is connected to a network, and appears to be able + to reach the full Internet + + + + diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c index 2b6c25944b..16d8e26688 100644 --- a/src/nm-connectivity.c +++ b/src/nm-connectivity.c @@ -48,7 +48,7 @@ typedef struct { guint check_id; #endif - gboolean connected; + NMConnectivityState state; } NMConnectivityPrivate; enum { @@ -56,28 +56,28 @@ enum { PROP_URI, PROP_INTERVAL, PROP_RESPONSE, - PROP_CONNECTED, + PROP_STATE, LAST_PROP }; -gboolean -nm_connectivity_get_connected (NMConnectivity *connectivity) +NMConnectivityState +nm_connectivity_get_state (NMConnectivity *connectivity) { - g_return_val_if_fail (NM_IS_CONNECTIVITY (connectivity), FALSE); + g_return_val_if_fail (NM_IS_CONNECTIVITY (connectivity), NM_CONNECTIVITY_UNKNOWN); - return NM_CONNECTIVITY_GET_PRIVATE (connectivity)->connected; + return NM_CONNECTIVITY_GET_PRIVATE (connectivity)->state; } static void -update_connected (NMConnectivity *self, gboolean connected) +update_state (NMConnectivity *self, NMConnectivityState state) { NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - gboolean old_connected = priv->connected; - priv->connected = connected; - if (priv->connected != old_connected) - g_object_notify (G_OBJECT (self), NM_CONNECTIVITY_CONNECTED); + if (priv->state != state) { + priv->state = state; + g_object_notify (G_OBJECT (self), NM_CONNECTIVITY_STATE); + } } #if WITH_CONCHECK @@ -87,37 +87,47 @@ nm_connectivity_check_cb (SoupSession *session, SoupMessage *msg, gpointer user_ GSimpleAsyncResult *simple = user_data; NMConnectivity *self; NMConnectivityPrivate *priv; - gboolean connected_new = FALSE; + NMConnectivityState new_state; const char *nm_header; self = NM_CONNECTIVITY (g_async_result_get_source_object (G_ASYNC_RESULT (simple))); g_object_unref (self); priv = NM_CONNECTIVITY_GET_PRIVATE (self); + if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) { + nm_log_info (LOGD_CONCHECK, "Connectivity check for uri '%s' failed with '%s'.", + priv->uri, msg->reason_phrase); + new_state = NM_CONNECTIVITY_LIMITED; + goto done; + } + /* Check headers; if we find the NM-specific one we're done */ nm_header = soup_message_headers_get_one (msg->response_headers, "X-NetworkManager-Status"); if (g_strcmp0 (nm_header, "online") == 0) { nm_log_dbg (LOGD_CONCHECK, "Connectivity check for uri '%s' with Status header successful.", priv->uri); - connected_new = TRUE; + new_state = NM_CONNECTIVITY_FULL; } else if (msg->status_code == SOUP_STATUS_OK) { /* check response */ if (msg->response_body->data && (g_str_has_prefix (msg->response_body->data, priv->response))) { nm_log_dbg (LOGD_CONCHECK, "Connectivity check for uri '%s' successful.", priv->uri); - connected_new = TRUE; + new_state = NM_CONNECTIVITY_FULL; } else { - nm_log_info (LOGD_CONCHECK, "Connectivity check for uri '%s' did not match expected response '%s'.", + nm_log_info (LOGD_CONCHECK, "Connectivity check for uri '%s' did not match expected response '%s'; assuming captive portal.", priv->uri, priv->response); + new_state = NM_CONNECTIVITY_PORTAL; } } else { - nm_log_info (LOGD_CONCHECK, "Connectivity check for uri '%s' returned status '%d %s'.", + nm_log_info (LOGD_CONCHECK, "Connectivity check for uri '%s' returned status '%d %s'; assuming captive portal.", priv->uri, msg->status_code, msg->reason_phrase); + new_state = NM_CONNECTIVITY_PORTAL; } - g_simple_async_result_set_op_res_gboolean (simple, connected_new); + done: + g_simple_async_result_set_op_res_gssize (simple, new_state); g_simple_async_result_complete (simple); - update_connected (self, connected_new); + update_state (self, new_state); } static void @@ -174,7 +184,7 @@ nm_connectivity_set_online (NMConnectivity *self, /* Either @online is %TRUE but we aren't checking connectivity, or * @online is %FALSE. Either way we can update our status immediately. */ - update_connected (self, online); + update_state (self, online ? NM_CONNECTIVITY_FULL : NM_CONNECTIVITY_NONE); } void @@ -207,23 +217,23 @@ nm_connectivity_check_async (NMConnectivity *self, } #endif - g_simple_async_result_set_op_res_gboolean (simple, TRUE); + g_simple_async_result_set_op_res_gssize (simple, priv->state); g_simple_async_result_complete_in_idle (simple); } -gboolean +NMConnectivityState nm_connectivity_check_finish (NMConnectivity *self, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple; - g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), nm_connectivity_check_async), FALSE); + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), nm_connectivity_check_async), NM_CONNECTIVITY_UNKNOWN); simple = G_SIMPLE_ASYNC_RESULT (result); if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - return g_simple_async_result_get_op_res_gboolean (simple); + return NM_CONNECTIVITY_UNKNOWN; + return (NMConnectivityState) g_simple_async_result_get_op_res_gssize (simple); } @@ -243,7 +253,7 @@ nm_connectivity_new (void) NM_CONNECTIVITY_RESPONSE, check_response ? check_response : DEFAULT_RESPONSE, NULL); g_return_val_if_fail (self != NULL, NULL); - update_connected (self, FALSE); + update_state (self, NM_CONNECTIVITY_NONE); return self; } @@ -314,8 +324,8 @@ get_property (GObject *object, guint property_id, case PROP_RESPONSE: g_value_set_string (value, priv->response); break; - case PROP_CONNECTED: - g_value_set_boolean (value, priv->connected); + case PROP_STATE: + g_value_set_uint (value, priv->state); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -395,11 +405,11 @@ nm_connectivity_class_init (NMConnectivityClass *klass) G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property - (object_class, PROP_CONNECTED, - g_param_spec_boolean (NM_CONNECTIVITY_CONNECTED, - "Connected", - "Is connected", - FALSE, - G_PARAM_READABLE)); + (object_class, PROP_STATE, + g_param_spec_uint (NM_CONNECTIVITY_STATE, + "State", + "Connectivity state", + NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN, + G_PARAM_READABLE)); } diff --git a/src/nm-connectivity.h b/src/nm-connectivity.h index 36d6e8d12a..04d0f2a309 100644 --- a/src/nm-connectivity.h +++ b/src/nm-connectivity.h @@ -38,7 +38,7 @@ #define NM_CONNECTIVITY_URI "uri" #define NM_CONNECTIVITY_INTERVAL "interval" #define NM_CONNECTIVITY_RESPONSE "response" -#define NM_CONNECTIVITY_CONNECTED "connected" +#define NM_CONNECTIVITY_STATE "state" typedef struct { GObject parent; @@ -50,18 +50,18 @@ typedef struct { GType nm_connectivity_get_type (void); -NMConnectivity *nm_connectivity_new (void); +NMConnectivity *nm_connectivity_new (void); -void nm_connectivity_set_online (NMConnectivity *self, - gboolean online); +void nm_connectivity_set_online (NMConnectivity *self, + gboolean online); -gboolean nm_connectivity_get_connected (NMConnectivity *self); +NMConnectivityState nm_connectivity_get_state (NMConnectivity *self); -void nm_connectivity_check_async (NMConnectivity *self, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean nm_connectivity_check_finish (NMConnectivity *self, - GAsyncResult *result, - GError **error); +void nm_connectivity_check_async (NMConnectivity *self, + GAsyncReadyCallback callback, + gpointer user_data); +NMConnectivityState nm_connectivity_check_finish (NMConnectivity *self, + GAsyncResult *result, + GError **error); #endif /* NM_CONNECTIVITY_H */ diff --git a/src/nm-manager.c b/src/nm-manager.c index df7d8cfb67..b799d3e204 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -132,6 +132,9 @@ static void impl_manager_get_logging (NMManager *manager, char **level, char **domains); +static void impl_manager_check_connectivity (NMManager *manager, + DBusGMethodInvocation *context); + #include "nm-manager-glue.h" static void bluez_manager_bdaddr_added_cb (NMBluezManager *bluez_mgr, @@ -291,6 +294,7 @@ enum { PROP_WIMAX_ENABLED, PROP_WIMAX_HARDWARE_ENABLED, PROP_ACTIVE_CONNECTIONS, + PROP_CONNECTIVITY, /* Not exported */ PROP_HOSTNAME, @@ -577,11 +581,15 @@ checked_connectivity (GObject *object, GAsyncResult *result, gpointer user_data) { NMManager *manager = user_data; NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + NMConnectivityState connectivity; if (priv->state == NM_STATE_CONNECTING || priv->state == NM_STATE_CONNECTED_SITE) { - if (nm_connectivity_check_finish (priv->connectivity, result, NULL)) + connectivity = nm_connectivity_check_finish (priv->connectivity, result, NULL); + + if (connectivity == NM_CONNECTIVITY_FULL) set_state (manager, NM_STATE_CONNECTED_GLOBAL); - else + else if ( connectivity == NM_CONNECTIVITY_PORTAL + || connectivity == NM_CONNECTIVITY_LIMITED) set_state (manager, NM_STATE_CONNECTED_SITE); } @@ -609,7 +617,7 @@ nm_manager_update_state (NMManager *manager) if (state == NM_DEVICE_STATE_ACTIVATED) { nm_connectivity_set_online (priv->connectivity, TRUE); - if (!nm_connectivity_get_connected (priv->connectivity)) { + if (nm_connectivity_get_state (priv->connectivity) != NM_CONNECTIVITY_FULL) { new_state = NM_STATE_CONNECTING; want_connectivity_check = TRUE; } else { @@ -3953,6 +3961,87 @@ impl_manager_get_logging (NMManager *manager, *domains = g_strdup (nm_logging_domains_to_string ()); } +static void +connectivity_check_done (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + DBusGMethodInvocation *context = user_data; + NMConnectivityState state; + GError *error = NULL; + + state = nm_connectivity_check_finish (NM_CONNECTIVITY (object), result, &error); + if (error) { + dbus_g_method_return_error (context, error); + g_error_free (error); + } else + dbus_g_method_return (context, state); +} + + +static void +check_connectivity_auth_done_cb (NMAuthChain *chain, + GError *auth_error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GError *error = NULL; + NMAuthCallResult result; + + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + + result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL); + + if (auth_error) { + nm_log_dbg (LOGD_CORE, "CheckConnectivity request failed: %s", auth_error->message); + error = g_error_new (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Connectivity check request failed: %s", + auth_error->message); + } else if (result != NM_AUTH_CALL_RESULT_YES) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Not authorized to recheck connectivity"); + } else { + /* it's allowed */ + nm_connectivity_check_async (priv->connectivity, + connectivity_check_done, + context); + } + + if (error) { + dbus_g_method_return_error (context, error); + g_error_free (error); + } + nm_auth_chain_unref (chain); +} + +static void +impl_manager_check_connectivity (NMManager *manager, + DBusGMethodInvocation *context) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + NMAuthChain *chain; + const char *error_desc = NULL; + GError *error; + + /* Validate the user request */ + chain = nm_auth_chain_new (context, check_connectivity_auth_done_cb, manager, &error_desc); + if (chain) { + priv->auth_chains = g_slist_append (priv->auth_chains, chain); + + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); + } else { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + error_desc); + dbus_g_method_return_error (context, error); + g_error_free (error); + } +} + void nm_manager_start (NMManager *self) { @@ -4050,11 +4139,12 @@ connectivity_changed (NMConnectivity *connectivity, gpointer user_data) { NMManager *self = NM_MANAGER (user_data); - gboolean connected; + NMConnectivityState state; + static const char *connectivity_states[] = { "UNKNOWN", "NONE", "PORTAL", "LIMITED", "FULL" }; - connected = nm_connectivity_get_connected (connectivity); + state = nm_connectivity_get_state (connectivity); nm_log_dbg (LOGD_CORE, "connectivity checking indicates %s", - connected ? "CONNECTED" : "NOT CONNECTED"); + connectivity_states[state]); nm_manager_update_state (self); } @@ -4267,7 +4357,7 @@ nm_manager_new (NMSettings *settings, priv = NM_MANAGER_GET_PRIVATE (singleton); priv->connectivity = nm_connectivity_new (); - g_signal_connect (priv->connectivity, "notify::" NM_CONNECTIVITY_CONNECTED, + g_signal_connect (priv->connectivity, "notify::" NM_CONNECTIVITY_STATE, G_CALLBACK (connectivity_changed), singleton); bus = nm_dbus_manager_get_connection (priv->dbus_mgr); @@ -4639,6 +4729,9 @@ get_property (GObject *object, guint prop_id, } g_value_take_boxed (value, active); break; + case PROP_CONNECTIVITY: + g_value_set_uint (value, nm_connectivity_get_state (priv->connectivity)); + break; case PROP_HOSTNAME: g_value_set_string (value, priv->hostname); break; @@ -4903,6 +4996,14 @@ nm_manager_class_init (NMManagerClass *manager_class) DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_CONNECTIVITY, + g_param_spec_uint (NM_MANAGER_CONNECTIVITY, + "Connectivity", + "Connectivity state", + NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN, + G_PARAM_READABLE)); + /* Hostname is not exported over D-Bus */ g_object_class_install_property (object_class, PROP_HOSTNAME, diff --git a/src/nm-manager.h b/src/nm-manager.h index 6e376787e1..79165f0f4c 100644 --- a/src/nm-manager.h +++ b/src/nm-manager.h @@ -60,6 +60,7 @@ typedef enum { #define NM_MANAGER_WIMAX_ENABLED "wimax-enabled" #define NM_MANAGER_WIMAX_HARDWARE_ENABLED "wimax-hardware-enabled" #define NM_MANAGER_ACTIVE_CONNECTIONS "active-connections" +#define NM_MANAGER_CONNECTIVITY "connectivity" /* Not exported */ #define NM_MANAGER_HOSTNAME "hostname"