#include "online-monitor.h"
+#define AOC_DBUS_REPLY_TIMEOUT (15 * 1000)
+
+#define CONNMAN_SERVICE "net.connman"
+#define CONNMAN_SERVICE_INTERFACE CONNMAN_SERVICE ".Service"
+#define CONNMAN_MANAGER_PATH "/"
+#define CONNMAN_MANAGER_INTERFACE CONNMAN_SERVICE ".Manager"
+
+/* Async HTTP Request */
+static CURL *curl_handle = NULL;
+static CURLM *multi_handle = NULL;
+static int still_running = 0;
+static guint timer_id = 0;
+
+static GVariant *__invoke_dbus_method(const char *dest, const char *path,
+ const char *interface_name, const char *method, GVariant *params)
+{
+
+ GError *error = NULL;
+ GVariant *reply = NULL;
+ GDBusConnection *connection;
+
+ connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (connection == NULL) {
+ ERR("[AOC] Failed to get GDBusconnection");
+ return reply;
+ }
+
+ reply = g_dbus_connection_call_sync(
+ connection,
+ dest,
+ path,
+ interface_name,
+ method,
+ params,
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ AOC_DBUS_REPLY_TIMEOUT,
+ NULL,
+ &error);
+
+ g_object_unref(connection);
+
+ if (reply == NULL) {
+ if (error != NULL) {
+ ERR("[AOC] g_dbus_connection_call_sync() failed"
+ "error [%d: %s]", error->code, error->message);
+ g_error_free(error);
+ } else {
+ ERR("[AOC] g_dbus_connection_call_sync() failed");
+ }
+
+ return NULL;
+ }
+
+ return reply;
+}
+
+static void _get_default_service_profile(char **profile)
+{
+ GVariant *message = NULL;
+ GVariantIter *iter;
+ gchar *obj_path;
+
+ message = __invoke_dbus_method(CONNMAN_SERVICE,
+ CONNMAN_MANAGER_PATH, CONNMAN_MANAGER_INTERFACE,
+ "GetDefaultService", NULL);
+
+ if (message == NULL) {
+ ERR("Failed to get services informations");
+ return;
+ }
+
+ if (!g_variant_is_of_type(message, G_VARIANT_TYPE("(oa{sv})"))) {
+ DBG("There is no default service");
+ g_variant_unref(message);
+ return;
+ }
+
+ g_variant_get(message, "(oa{sv})", &obj_path, &iter);
+ *profile = g_strdup(obj_path);
+
+ g_variant_iter_free(iter);
+ g_variant_unref(message);
+}
+
+static void _get_url(char **url)
+{
+ online_monitor_config_t *config = online_monitor_get_configuration();
+
+ /* Generate a random number in range [0, 5] */
+ const int max_url = 6;
+
+ srand(time(0));
+ int index = (rand() % max_url);
+
+ *url = g_strdup(config->url_list[index]);
+}
+
+void _curl_state_cleanup()
+{
+ int rv = 0;
+
+ if (timer_id != 0) {
+ g_source_remove(timer_id);
+ timer_id = 0;
+ }
+
+ /* Clean up easy and multi curl handle which might have been initialized
+ * in previous call to curl_easy_init() and not yet cleaned up */
+
+ if (curl_handle != NULL && multi_handle != NULL) {
+ rv = curl_multi_remove_handle(multi_handle, curl_handle);
+ if(rv != CURLM_OK)
+ ERR("curl_multi_remove_handle Failed [%s]", curl_multi_strerror(rv));
+ }
+
+ if(curl_handle != NULL) {
+ curl_easy_cleanup(curl_handle);
+ curl_handle = NULL;
+ }
+
+ if(multi_handle != NULL) {
+ rv = curl_multi_cleanup(multi_handle);
+ if(rv != CURLM_OK)
+ ERR("curl_multi_cleanup() Failed [%s]", curl_multi_strerror(rv));
+ multi_handle = NULL;
+ }
+}
+
+static gboolean _curl_http_request(gpointer data)
+{
+ int rv;
+ bool flag = false;
+
+ if (0 != still_running) {
+ rv = curl_multi_perform(multi_handle, &still_running);
+ if (rv != CURLM_OK) {
+ ERR("curl multi perform failed %s", curl_multi_strerror(rv));
+ flag = false;
+ goto out;
+ }
+ return true;
+ }
+
+ CURLMsg *m = NULL;
+ int msg_left = 0;
+
+ m = curl_multi_info_read(multi_handle, &msg_left);
+ if (m && (m->msg == CURLMSG_DONE))
+ DBG("HTTP transfer completed with result code[%d: %s]", m->data.result, curl_easy_strerror(m->data.result));
+
+ if (curl_handle != NULL) {
+ long http_status = 0;
+ curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_status);
+ DBG("HTTP Status [%ld]\n", http_status);
+
+ if (http_status == 200 || (http_status >= 300 && http_status < 400))
+ flag = true;
+ else
+ flag = false;
+ }
+
+out:
+ if (!flag) {
+ /* Downgrade service state */
+ char *service_profile = NULL;
+ _get_default_service_profile(&service_profile);
+
+ if (service_profile) {
+ DBG("service_profile: %s", service_profile);
+ GVariant *reply = __invoke_dbus_method(CONNMAN_SERVICE,
+ service_profile, CONNMAN_SERVICE_INTERFACE, "Downgrade",
+ NULL);
+
+ if (reply != NULL)
+ g_variant_unref(reply);
+ else
+ ERR("Failed to Downgrade service\n");
+ g_free(service_profile);
+ }
+ }
+ _curl_state_cleanup();
+ online_monitor_url_check_result(flag);
+ return false;
+}
+
+/*
+ HTTP test to check the online state in order to confirm that there are no exceptions.
+ If online check fails, downgrade the ConnMan state to 'ready'.
+ */
+int start_url_check(void)
+{
+ int result;
+ char *url = NULL;
+
+ if (timer_id != 0) {
+ ERR("Request already in progress.");
+ result = 0;
+ goto out;
+ }
+
+ curl_handle = curl_easy_init();
+
+ if (curl_handle == NULL) {
+ ERR("Failed to curl_easy_init()");
+ result = -1;
+ goto out;
+ }
+
+ _get_url(&url);
+ DBG("HTTP Test for url: %s", url);
+
+ int rv = curl_easy_setopt(curl_handle, CURLOPT_URL, url);
+ if (rv != CURLE_OK)
+ {
+ ERR("Failed to set URL %s", curl_easy_strerror(rv));
+ result = -1;
+ goto out;
+ }
+
+ rv = curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 10);
+ if (rv != CURLE_OK) {
+ ERR("Failed to set connection timeout %s", curl_easy_strerror(rv));
+ result = -1;
+ goto out;
+ }
+
+ rv = curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 12);
+ if (rv != CURLE_OK) {
+ ERR("Failed to set timeout %s", curl_easy_strerror(rv));
+ result = -1;
+ goto out;
+ }
+
+ rv = curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);
+ if (rv != CURLE_OK) {
+ ERR("Failed to set Noprogress option %s", curl_easy_strerror(rv));
+ result = -1;
+ goto out;
+ }
+
+ rv = curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1L);
+ if (rv != CURLE_OK) {
+ ERR("Failed to set Nosignal option %s", curl_easy_strerror(rv));
+ result = -1;
+ goto out;
+ }
+
+ /* Perform curl operation */
+ multi_handle = curl_multi_init();
+ if (multi_handle == NULL) {
+ ERR("Failed to curl_multi_init() %s", curl_multi_strerror(rv));
+ result = -1;
+ goto out;
+ }
+
+ rv = curl_multi_add_handle(multi_handle, curl_handle);
+ if(rv != CURLM_OK) {
+ ERR("curl multi add handle failed %s", curl_multi_strerror(rv));
+ result = -1;
+ goto out;
+ }
+
+ rv = curl_multi_perform(multi_handle, &still_running);
+ if(rv != CURLM_OK) {
+ ERR("curl multi perform failed %s", curl_multi_strerror(rv));
+ result = -1;
+ } else {
+ /* Adding timeout of 100 msec to check whether data is available
+ * for multi handle to read or write or timeout set initially
+ * has elapsed. curl_multi_perform() should be called whenever
+ * data is available to read or write. Smaller time will result
+ * in faster processing
+ * */
+ timer_id = g_timeout_add(100, _curl_http_request, NULL);
+ result = 0;
+ }
+
+out:
+
+ if (result < 0)
+ _curl_state_cleanup();
+
+ g_free(url);
+ return result;
+}
+
static void url_checker_state_chaged_cb(online_monitor_state_e state,
char* ifname, online_monitor_detection_e reason)
{
DBG("state %d, ifname %s, reason %d", state, ifname, reason);
+ if (state == ONLINE_MONITOR_STATE_OFFLINE_DETECTED)
+ start_url_check();
}
int url_checker_init(void)
{
online_monitor_notifier_register(url_checker_state_chaged_cb);
+
+ /* Init curl library */
+ curl_global_init(CURL_GLOBAL_ALL);
+
DBG("url_checker initialized");
return 0;
}
int url_checker_deinit(void)
{
online_monitor_notifier_unregister(url_checker_state_chaged_cb);
+ curl_global_cleanup();
+
DBG("url_checker deinitialized");
return 0;
}