diff options
| author | Michael Biebl <biebl@debian.org> | 2017-05-11 14:55:55 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2017-05-11 14:55:55 +0200 |
| commit | c333f062ddcba9b35330647bf6cbd0a07f2d786e (patch) | |
| tree | 257c3a0c74c09f4ad2328eab5b932806405f0c1c /src/nm-connectivity.c | |
| parent | a222e56e103f949b148a6942e385ccca2c26d9f3 (diff) | |
New upstream version 1.8.0 upstream/1.8.0
Diffstat (limited to 'src/nm-connectivity.c')
| -rw-r--r-- | src/nm-connectivity.c | 658 |
1 files changed, 345 insertions, 313 deletions
diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c index a33ec680..75bb7b63 100644 --- a/src/nm-connectivity.c +++ b/src/nm-connectivity.c @@ -17,6 +17,7 @@ * * Copyright (C) 2011 Thomas Bechtold <thomasbechtold@jpberlin.de> * Copyright (C) 2011 Dan Williams <dcbw@redhat.com> + * Copyright (C) 2016,2017 Red Hat, Inc. */ #include "nm-default.h" @@ -24,36 +25,21 @@ #include "nm-connectivity.h" #include <string.h> -#if WITH_CONCHECK -#include <libsoup/soup.h> -#endif +#include <curl/curl.h> #include "nm-config.h" -#include "nm-dispatcher.h" #include "NetworkManagerUtils.h" /*****************************************************************************/ -NM_GOBJECT_PROPERTIES_DEFINE (NMConnectivity, - PROP_URI, - PROP_INTERVAL, - PROP_RESPONSE, - PROP_STATE, -); - typedef struct { char *uri; char *response; guint interval; - gboolean online; /* whether periodic connectivity checking is enabled. */ - -#if WITH_CONCHECK - SoupSession *soup_session; - gboolean initial_check_obsoleted; - guint check_id; -#endif - - NMConnectivityState state; + NMConfig *config; + guint periodic_check_id; + CURLM *curl_mhandle; + guint curl_timer; } NMConnectivityPrivate; struct _NMConnectivity { @@ -69,20 +55,37 @@ G_DEFINE_TYPE (NMConnectivity, nm_connectivity, G_TYPE_OBJECT) #define NM_CONNECTIVITY_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMConnectivity, NM_IS_CONNECTIVITY) +NM_DEFINE_SINGLETON_GETTER (NMConnectivity, nm_connectivity_get, NM_TYPE_CONNECTIVITY); + +enum { + PERIODIC_CHECK, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + /*****************************************************************************/ #define _NMLOG_DOMAIN LOGD_CONCHECK #define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "connectivity", __VA_ARGS__) -/*****************************************************************************/ - -NMConnectivityState -nm_connectivity_get_state (NMConnectivity *connectivity) -{ - g_return_val_if_fail (NM_IS_CONNECTIVITY (connectivity), NM_CONNECTIVITY_UNKNOWN); +#define _NMLOG2_DOMAIN LOGD_CONCHECK +#define _NMLOG2(level, ...) \ + G_STMT_START { \ + const NMLogLevel __level = (level); \ + \ + if (nm_logging_enabled (__level, _NMLOG2_DOMAIN)) { \ + _nm_log (__level, _NMLOG2_DOMAIN, 0, \ + &cb_data->ifspec[3], NULL, \ + "connectivity: (%s) " \ + _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + &cb_data->ifspec[3] \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + } \ + } G_STMT_END - return NM_CONNECTIVITY_GET_PRIVATE (connectivity)->state; -} +/*****************************************************************************/ NM_UTILS_LOOKUP_STR_DEFINE (nm_connectivity_state_to_string, NMConnectivityState, NM_UTILS_LOOKUP_DEFAULT_WARN ("???"), @@ -93,190 +96,245 @@ NM_UTILS_LOOKUP_STR_DEFINE (nm_connectivity_state_to_string, NMConnectivityState NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_FULL, "FULL"), ); -static void -update_state (NMConnectivity *self, NMConnectivityState state) -{ - NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - - if (priv->state != state) { - _LOGD ("state changed from %s to %s", - nm_connectivity_state_to_string (priv->state), - nm_connectivity_state_to_string (state)); - priv->state = state; - _notify (self, PROP_STATE); - - /* Notify dispatcher scripts of a connectivity state change */ - nm_dispatcher_call_connectivity (DISPATCHER_ACTION_CONNECTIVITY_CHANGE, state); - } -} +/*****************************************************************************/ -#if WITH_CONCHECK typedef struct { GSimpleAsyncResult *simple; - char *uri; char *response; - guint check_id_when_scheduled; + CURL *curl_ehandle; + size_t msg_size; + char *msg; + struct curl_slist *request_headers; + guint timeout_id; + char *ifspec; } ConCheckCbData; static void -nm_connectivity_check_cb (SoupSession *session, SoupMessage *msg, gpointer user_data) +finish_cb_data (ConCheckCbData *cb_data, NMConnectivityState new_state) { - NMConnectivity *self; - NMConnectivityPrivate *priv; - ConCheckCbData *cb_data = user_data; - GSimpleAsyncResult *simple = cb_data->simple; - NMConnectivityState new_state; - const char *nm_header; - const char *uri = cb_data->uri; - const char *response = cb_data->response ? cb_data->response : NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE; - - self = NM_CONNECTIVITY (g_async_result_get_source_object (G_ASYNC_RESULT (simple))); - /* it is safe to unref @self here, @simple holds yet another reference. */ - g_object_unref (self); - priv = NM_CONNECTIVITY_GET_PRIVATE (self); + /* Contrary to what cURL manual claim it is *not* safe to remove + * the easy handle "at any moment"; specifically not from the + * write function. Thus here we just dissociate the cb_data from + * the easy handle and the easy handle will be cleaned up when the + * message goes to CURLMSG_DONE in curl_check_connectivity(). */ + curl_easy_setopt (cb_data->curl_ehandle, CURLOPT_PRIVATE, NULL); + + g_simple_async_result_set_op_res_gssize (cb_data->simple, new_state); + g_simple_async_result_complete (cb_data->simple); + g_object_unref (cb_data->simple); + curl_slist_free_all (cb_data->request_headers); + g_free (cb_data->response); + g_source_remove (cb_data->timeout_id); + g_slice_free (ConCheckCbData, cb_data); +} - if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) { - _LOGI ("check for uri '%s' failed with '%s'", uri, msg->reason_phrase); - new_state = NM_CONNECTIVITY_LIMITED; - goto done; - } +static void +curl_check_connectivity (CURLM *mhandle, CURLMcode ret) +{ + ConCheckCbData *cb_data; + CURLMsg *msg; + CURLcode eret; + gint m_left; + + if (ret != CURLM_OK) + _LOGW ("connectivity check failed"); + + while ((msg = curl_multi_info_read (mhandle, &m_left))) { + if (msg->msg != CURLMSG_DONE) + continue; + + /* Here we have completed a session. Check easy session result. */ + eret = curl_easy_getinfo (msg->easy_handle, CURLINFO_PRIVATE, &cb_data); + if (eret != CURLE_OK) { + _LOG2E ("curl cannot extract cb_data for easy handle %p, skipping msg", msg->easy_handle); + continue; + } - if (msg->status_code == 511) { - _LOGD ("check for uri '%s' returned status '%d %s'; captive portal present.", - uri, msg->status_code, msg->reason_phrase); - new_state = NM_CONNECTIVITY_PORTAL; - } else { - /* 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) { - _LOGD ("check for uri '%s' with Status header successful.", uri); - 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, response)) { - _LOGD ("check for uri '%s' successful.", uri); - new_state = NM_CONNECTIVITY_FULL; + if (cb_data) { + /* If cb_data is still there this message hasn't been + * taken care of. Do so now. */ + if (msg->data.result == CURLE_OK) { + /* If we get here, it means that easy_write_cb() didn't read enough + * bytes to be able to do a match. */ + _LOG2I ("response shorter than expected '%s'; assuming captive portal.", + cb_data->response); + finish_cb_data (cb_data, NM_CONNECTIVITY_PORTAL); } else { - _LOGI ("check for uri '%s' did not match expected response '%s'; assuming captive portal.", - uri, response); - new_state = NM_CONNECTIVITY_PORTAL; + _LOG2D ("check failed (%d)", msg->data.result); + finish_cb_data (cb_data, NM_CONNECTIVITY_LIMITED); } - } else { - _LOGI ("check for uri '%s' returned status '%d %s'; assuming captive portal.", - uri, msg->status_code, msg->reason_phrase); - new_state = NM_CONNECTIVITY_PORTAL; } - } - done: - /* Only update the state, if the call was done from external, or if the periodic check - * is still the one that called this async check. */ - if (!cb_data->check_id_when_scheduled || cb_data->check_id_when_scheduled == priv->check_id) { - /* Only update the state, if the URI and response parameters did not change - * since invocation. - * The interval does not matter for exernal calls, and for internal calls - * we don't reach this line if the interval changed. */ - if ( !g_strcmp0 (cb_data->uri, priv->uri) - && !g_strcmp0 (cb_data->response, priv->response)) - update_state (self, new_state); + curl_multi_remove_handle (mhandle, msg->easy_handle); + curl_easy_cleanup (msg->easy_handle); } +} - g_simple_async_result_set_op_res_gssize (simple, new_state); - g_simple_async_result_complete (simple); - g_object_unref (simple); +static gboolean +curl_timeout_cb (gpointer user_data) +{ + NMConnectivity *self = NM_CONNECTIVITY (user_data); + NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); + CURLMcode ret; + int pending_conn; - g_free (cb_data->uri); - g_free (cb_data->response); - g_slice_free (ConCheckCbData, cb_data); -} + priv->curl_timer = 0; -#define IS_PERIODIC_CHECK(callback) (callback == run_check_complete) + ret = curl_multi_socket_action (priv->curl_mhandle, CURL_SOCKET_TIMEOUT, 0, &pending_conn); + _LOGT ("timeout elapsed - multi_socket_action (%d conn remaining)", pending_conn); -static void -run_check_complete (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - NMConnectivity *self = NM_CONNECTIVITY (object); - GError *error = NULL; + curl_check_connectivity (priv->curl_mhandle, ret); - nm_connectivity_check_finish (self, result, &error); - if (error) { - _LOGE ("check failed: %s", error->message); - g_error_free (error); - } + return G_SOURCE_REMOVE; } -static gboolean -run_check (gpointer user_data) +static int +multi_timer_cb (CURLM *multi, long timeout_ms, void *userdata) { - NMConnectivity *self = NM_CONNECTIVITY (user_data); + NMConnectivity *self = NM_CONNECTIVITY (userdata); + NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - nm_connectivity_check_async (self, run_check_complete, NULL); - return TRUE; + nm_clear_g_source (&priv->curl_timer); + if (timeout_ms != -1) + priv->curl_timer = g_timeout_add (timeout_ms * 1000, curl_timeout_cb, self); + + return 0; } static gboolean -idle_start_periodic_checks (gpointer user_data) +curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer data) { - NMConnectivity *self = user_data; + NMConnectivity *self = NM_CONNECTIVITY (data); NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); + CURLMcode ret; + int pending_conn = 0; + gboolean bret = TRUE; + int fd = g_io_channel_unix_get_fd (ch); + int action = 0; + + if (condition & G_IO_IN) + action |= CURL_CSELECT_IN; + if (condition & G_IO_OUT) + action |= CURL_CSELECT_OUT; - priv->check_id = g_timeout_add_seconds (priv->interval, run_check, self); - if (!priv->initial_check_obsoleted) - run_check (self); + ret = curl_multi_socket_action (priv->curl_mhandle, fd, 0, &pending_conn); - return FALSE; + curl_check_connectivity (priv->curl_mhandle, ret); + + if (pending_conn == 0) { + nm_clear_g_source (&priv->curl_timer); + bret = FALSE; + } + return bret; } -#endif -static void -_reschedule_periodic_checks (NMConnectivity *self, gboolean force_reschedule) +typedef struct { + GIOChannel *ch; + guint ev; +} CurlSockData; + +static int +multi_socket_cb (CURL *e_handle, curl_socket_t s, int what, void *userdata, void *socketp) { + NMConnectivity *self = NM_CONNECTIVITY (userdata); NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - -#if WITH_CONCHECK - if (priv->online && priv->uri && priv->interval) { - if (force_reschedule || !priv->check_id) { - if (priv->check_id) - g_source_remove (priv->check_id); - priv->check_id = g_timeout_add (0, idle_start_periodic_checks, self); - priv->initial_check_obsoleted = FALSE; + CurlSockData *fdp = (CurlSockData *) socketp; + GIOCondition condition = 0; + + if (what == CURL_POLL_REMOVE) { + if (fdp) { + nm_clear_g_source (&fdp->ev); + g_io_channel_unref (fdp->ch); + g_slice_free (CurlSockData, fdp); } } else { - nm_clear_g_source (&priv->check_id); + if (!fdp) { + fdp = g_slice_new0 (CurlSockData); + fdp->ch = g_io_channel_unix_new (s); + } else + nm_clear_g_source (&fdp->ev); + + if (what & CURL_POLL_IN) + condition |= G_IO_IN; + if (what & CURL_POLL_OUT) + condition |= G_IO_OUT; + + fdp->ev = g_io_add_watch (fdp->ch, condition, curl_socketevent_cb, self); + curl_multi_assign (priv->curl_mhandle, s, fdp); } - if (priv->check_id) - return; -#endif - /* Either @online is %TRUE but we aren't checking connectivity, or - * @online is %FALSE. Either way we can update our status immediately. - */ - update_state (self, priv->online ? NM_CONNECTIVITY_FULL : NM_CONNECTIVITY_NONE); + return CURLM_OK; } -void -nm_connectivity_set_online (NMConnectivity *self, - gboolean online) +#define HEADER_STATUS_ONLINE "X-NetworkManager-Status: online\r\n" + +static size_t +easy_header_cb (char *buffer, size_t size, size_t nitems, void *userdata) { - NMConnectivityPrivate *priv= NM_CONNECTIVITY_GET_PRIVATE (self); + ConCheckCbData *cb_data = userdata; + size_t len = size * nitems; + + if ( len >= sizeof (HEADER_STATUS_ONLINE) - 1 + && !g_ascii_strncasecmp (buffer, HEADER_STATUS_ONLINE, sizeof (HEADER_STATUS_ONLINE) - 1)) { + _LOG2D ("status header found, check successful"); + finish_cb_data (cb_data, NM_CONNECTIVITY_FULL); + return 0; + } + + return len; +} - online = !!online; - if (priv->online != online) { - _LOGD ("set %s", online ? "online" : "offline"); - priv->online = online; - _reschedule_periodic_checks (self, FALSE); +static size_t +easy_write_cb (void *buffer, size_t size, size_t nmemb, void *userdata) +{ + ConCheckCbData *cb_data = userdata; + size_t len = size * nmemb; + + cb_data->msg = g_realloc (cb_data->msg, cb_data->msg_size + len); + memcpy (cb_data->msg + cb_data->msg_size, buffer, len); + cb_data->msg_size += len; + + if (cb_data->msg_size >= strlen (cb_data->response)) { + /* We already have enough data -- check response */ + if (g_str_has_prefix (cb_data->msg, cb_data->response)) { + _LOG2D ("check successful."); + finish_cb_data (cb_data, NM_CONNECTIVITY_FULL); + } else { + _LOG2I ("response did not match expected response '%s'; assuming captive portal.", + cb_data->response); + finish_cb_data (cb_data, NM_CONNECTIVITY_PORTAL); + } + return 0; } + + return len; +} + +static gboolean +timeout_cb (gpointer user_data) +{ + ConCheckCbData *cb_data = user_data; + NMConnectivity *self = NM_CONNECTIVITY (g_async_result_get_source_object (G_ASYNC_RESULT (cb_data->simple))); + NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); + CURL *ehandle = cb_data->curl_ehandle; + + _LOG2I ("timed out"); + finish_cb_data (cb_data, NM_CONNECTIVITY_LIMITED); + curl_multi_remove_handle (priv->curl_mhandle, ehandle); + curl_easy_cleanup (ehandle); + + return G_SOURCE_REMOVE; } void nm_connectivity_check_async (NMConnectivity *self, + const char *iface, GAsyncReadyCallback callback, gpointer user_data) { NMConnectivityPrivate *priv; GSimpleAsyncResult *simple; + CURL *ehandle = NULL; g_return_if_fail (NM_IS_CONNECTIVITY (self)); priv = NM_CONNECTIVITY_GET_PRIVATE (self); @@ -284,39 +342,40 @@ nm_connectivity_check_async (NMConnectivity *self, simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, nm_connectivity_check_async); -#if WITH_CONCHECK - if (priv->uri && priv->interval) { - SoupMessage *msg; - ConCheckCbData *cb_data = g_slice_new (ConCheckCbData); + if (priv->uri && priv->interval && priv->curl_mhandle) + ehandle = curl_easy_init (); + + if (ehandle) { + ConCheckCbData *cb_data = g_slice_new0 (ConCheckCbData); - msg = soup_message_new ("GET", priv->uri); - soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT); - /* Disable HTTP/1.1 keepalive; the connection should not persist */ - soup_message_headers_append (msg->request_headers, "Connection", "close"); + cb_data->curl_ehandle = ehandle; + cb_data->request_headers = curl_slist_append (NULL, "Connection: close"); + cb_data->ifspec = g_strdup_printf ("if!%s", iface); cb_data->simple = simple; - cb_data->uri = g_strdup (priv->uri); - cb_data->response = g_strdup (priv->response); + if (priv->response) + cb_data->response = g_strdup (priv->response); + else + cb_data->response = g_strdup (NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE); - /* For internal calls (periodic), remember the check-id at time of scheduling. */ - cb_data->check_id_when_scheduled = IS_PERIODIC_CHECK (callback) ? priv->check_id : 0; + curl_easy_setopt (ehandle, CURLOPT_URL, priv->uri); + curl_easy_setopt (ehandle, CURLOPT_WRITEFUNCTION, easy_write_cb); + curl_easy_setopt (ehandle, CURLOPT_WRITEDATA, cb_data); + curl_easy_setopt (ehandle, CURLOPT_HEADERFUNCTION, easy_header_cb); + curl_easy_setopt (ehandle, CURLOPT_HEADERDATA, cb_data); + curl_easy_setopt (ehandle, CURLOPT_PRIVATE, cb_data); + curl_easy_setopt (ehandle, CURLOPT_HTTPHEADER, cb_data->request_headers); + curl_easy_setopt (ehandle, CURLOPT_INTERFACE, cb_data->ifspec); + curl_multi_add_handle (priv->curl_mhandle, ehandle); - soup_session_queue_message (priv->soup_session, - msg, - nm_connectivity_check_cb, - cb_data); - priv->initial_check_obsoleted = TRUE; + cb_data->timeout_id = g_timeout_add_seconds (30, timeout_cb, cb_data); - _LOGD ("check: send %srequest to '%s'", IS_PERIODIC_CHECK (callback) ? "periodic " : "", priv->uri); + _LOG2D ("sending request to '%s'", priv->uri); return; } else { - g_warn_if_fail (!IS_PERIODIC_CHECK (callback)); - _LOGD ("check: faking request. Connectivity check disabled"); + _LOGD ("(%s) faking request. Connectivity check disabled", iface); } -#else - _LOGD ("check: faking request. Compiled without connectivity-check support"); -#endif - g_simple_async_result_set_op_res_gssize (simple, priv->state); + g_simple_async_result_set_op_res_gssize (simple, NM_CONNECTIVITY_UNKNOWN); g_simple_async_result_complete_in_idle (simple); g_object_unref (simple); } @@ -336,120 +395,118 @@ nm_connectivity_check_finish (NMConnectivity *self, return (NMConnectivityState) g_simple_async_result_get_op_res_gssize (simple); } -/*****************************************************************************/ - -static void -get_property (GObject *object, guint property_id, - GValue *value, GParamSpec *pspec) +gboolean +nm_connectivity_check_enabled (NMConnectivity *self) { - NMConnectivity *self = NM_CONNECTIVITY (object); NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - switch (property_id) { - case PROP_URI: - g_value_set_string (value, priv->uri); - break; - case PROP_INTERVAL: - g_value_set_uint (value, priv->interval); - break; - case PROP_RESPONSE: - if (priv->response) - g_value_set_string (value, priv->response); - else - g_value_set_static_string (value, NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE); - break; - case PROP_STATE: - g_value_set_uint (value, priv->state); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } + return (priv->uri && priv->interval && priv->curl_mhandle); +} + +/*****************************************************************************/ + +static gboolean +periodic_check (gpointer user_data) +{ + g_signal_emit (NM_CONNECTIVITY (user_data), signals[PERIODIC_CHECK], 0); + return G_SOURCE_CONTINUE; } static void -set_property (GObject *object, guint property_id, - const GValue *value, GParamSpec *pspec) +update_config (NMConnectivity *self, NMConfigData *config_data) { - NMConnectivity *self = NM_CONNECTIVITY (object); NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); const char *uri, *response; guint interval; - gboolean changed; - - switch (property_id) { - case PROP_URI: - uri = g_value_get_string (value); - if (uri && !*uri) + gboolean changed = FALSE; + + /* Set the URI. */ + uri = nm_config_data_get_connectivity_uri (config_data); + if (uri && !*uri) + uri = NULL; + changed = g_strcmp0 (uri, priv->uri) != 0; + if (uri) { + char *scheme = g_uri_parse_scheme (uri); + + if (!scheme) { + _LOGE ("invalid URI '%s' for connectivity check.", uri); + uri = NULL; + } else if (strcasecmp (scheme, "https") == 0) { + _LOGW ("use of HTTPS for connectivity checking is not reliable and is discouraged (URI: %s)", uri); + } else if (strcasecmp (scheme, "http") != 0) { + _LOGE ("scheme of '%s' uri does't use a scheme that is allowed for connectivity check.", uri); uri = NULL; - changed = g_strcmp0 (uri, priv->uri) != 0; -#if WITH_CONCHECK - if (uri) { - SoupURI *soup_uri = soup_uri_new (uri); - - if (!soup_uri || !SOUP_URI_VALID_FOR_HTTP (soup_uri)) { - _LOGE ("invalid uri '%s' for connectivity check.", uri); - uri = NULL; - } - if (uri && soup_uri && changed && - soup_uri_get_scheme(soup_uri) == SOUP_URI_SCHEME_HTTPS) - _LOGW ("use of HTTPS for connectivity checking is not reliable and is discouraged (URI: %s)", uri); - if (soup_uri) - soup_uri_free (soup_uri); - } -#endif - if (changed) { - g_free (priv->uri); - priv->uri = g_strdup (uri); - _reschedule_periodic_checks (self, TRUE); - } - break; - case PROP_INTERVAL: - interval = g_value_get_uint (value); - if (priv->interval != interval) { - priv->interval = interval; - _reschedule_periodic_checks (self, TRUE); - } - break; - case PROP_RESPONSE: - response = g_value_get_string (value); - if (g_strcmp0 (response, priv->response) != 0) { - /* a response %NULL means, NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE. Any other response - * (including "") is accepted. */ - g_free (priv->response); - priv->response = g_strdup (response); - _reschedule_periodic_checks (self, TRUE); } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; + + if (scheme) + g_free (scheme); + } + if (changed) { + g_free (priv->uri); + priv->uri = g_strdup (uri); + } + + /* Set the interval. */ + interval = nm_config_data_get_connectivity_interval (config_data); + if (priv->interval != interval) { + priv->interval = interval; + changed = TRUE; + } + + /* Set the response. */ + response = nm_config_data_get_connectivity_response (config_data); + if (g_strcmp0 (response, priv->response) != 0) { + /* a response %NULL means, NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE. Any other response + * (including "") is accepted. */ + g_free (priv->response); + priv->response = g_strdup (response); + changed = TRUE; + } + + if (changed) { + nm_clear_g_source (&priv->periodic_check_id); + if (nm_connectivity_check_enabled (self)) + priv->periodic_check_id = g_timeout_add_seconds (priv->interval, periodic_check, self); } } -/*****************************************************************************/ +static void +config_changed_cb (NMConfig *config, + NMConfigData *config_data, + NMConfigChangeFlags changes, + NMConfigData *old_data, + NMConnectivity *self) +{ + update_config (self, config_data); +} static void nm_connectivity_init (NMConnectivity *self) { NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); + CURLcode retv; -#if WITH_CONCHECK - priv->soup_session = soup_session_async_new_with_options (SOUP_SESSION_TIMEOUT, 15, NULL); -#endif - priv->state = NM_CONNECTIVITY_NONE; -} + priv->config = g_object_ref (nm_config_get ()); + update_config (self, nm_config_get_data (priv->config)); + g_signal_connect (G_OBJECT (priv->config), + NM_CONFIG_SIGNAL_CONFIG_CHANGED, + G_CALLBACK (config_changed_cb), + self); -NMConnectivity * -nm_connectivity_new (const char *uri, - guint interval, - const char *response) -{ - return g_object_new (NM_TYPE_CONNECTIVITY, - NM_CONNECTIVITY_URI, uri, - NM_CONNECTIVITY_INTERVAL, interval, - NM_CONNECTIVITY_RESPONSE, response, - NULL); + retv = curl_global_init (CURL_GLOBAL_ALL); + if (retv == CURLE_OK) + priv->curl_mhandle = curl_multi_init (); + + if (priv->curl_mhandle == NULL) { + _LOGE ("cnable to init cURL, connectivity check will not work"); + return; + } + + curl_multi_setopt (priv->curl_mhandle, CURLMOPT_SOCKETFUNCTION, multi_socket_cb); + curl_multi_setopt (priv->curl_mhandle, CURLMOPT_SOCKETDATA, self); + curl_multi_setopt (priv->curl_mhandle, CURLMOPT_TIMERFUNCTION, multi_timer_cb); + curl_multi_setopt (priv->curl_mhandle, CURLMOPT_TIMERDATA, self); + curl_multi_setopt (priv->curl_mhandle, CURLOPT_VERBOSE, 1); } static void @@ -461,14 +518,14 @@ dispose (GObject *object) g_clear_pointer (&priv->uri, g_free); g_clear_pointer (&priv->response, g_free); -#if WITH_CONCHECK - if (priv->soup_session) { - soup_session_abort (priv->soup_session); - g_clear_object (&priv->soup_session); + if (priv->config) { + g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self); + g_clear_object (&priv->config); } - nm_clear_g_source (&priv->check_id); -#endif + curl_multi_cleanup (priv->curl_mhandle); + curl_global_cleanup (); + nm_clear_g_source (&priv->periodic_check_id); G_OBJECT_CLASS (nm_connectivity_parent_class)->dispose (object); } @@ -478,37 +535,12 @@ nm_connectivity_class_init (NMConnectivityClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->set_property = set_property; - object_class->get_property = get_property; - object_class->dispose = dispose; + signals[PERIODIC_CHECK] = + g_signal_new (NM_CONNECTIVITY_PERIODIC_CHECK, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); - obj_properties[PROP_URI] = - g_param_spec_string (NM_CONNECTIVITY_URI, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_INTERVAL] = - g_param_spec_uint (NM_CONNECTIVITY_INTERVAL, "", "", - 0, G_MAXUINT, NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_RESPONSE] = - g_param_spec_string (NM_CONNECTIVITY_RESPONSE, "", "", - NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_STATE] = - g_param_spec_uint (NM_CONNECTIVITY_STATE, "", "", - NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); + object_class->dispose = dispose; } - |