X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Ftimeserver.c;h=ac99f76d2c605dc475d4e93ba6ac8e378da3ef59;hb=5bf80017321e03103f588306d2888d22f3f67ffa;hp=2af173d84c91cd8842ad7c64504e7dd1675af018;hpb=8f3c51386929c432af4d78c1e8ab1d82a1f6ac8b;p=platform%2Fupstream%2Fconnman.git diff --git a/src/timeserver.c b/src/timeserver.c old mode 100644 new mode 100755 index 2af173d..ac99f76 --- a/src/timeserver.c +++ b/src/timeserver.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2007-2010 Intel Corporation. All rights reserved. + * Copyright (C) 2007-2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -28,32 +28,47 @@ #include #include #include +#include #include "connman.h" -static GSList *driver_list = NULL; -static GHashTable *server_hash = NULL; +#define TS_RECHECK_INTERVAL 7200 -static char **system_timeservers = NULL; +static struct connman_service *ts_service; +static GSList *timeservers_list = NULL; +static GSList *ts_list = NULL; +static char *ts_current = NULL; +static int ts_recheck_id = 0; +static int ts_backoff_id = 0; static GResolv *resolv = NULL; static int resolv_id = 0; -static volatile int count; + +static void sync_next(void); static void resolv_debug(const char *str, void *data) { connman_info("%s: %s\n", (const char *) data, str); } + +static void ntp_callback(bool success, void *user_data) +{ + DBG("success %d", success); + + if (!success) + sync_next(); +} + static void save_timeservers(char **servers) { GKeyFile *keyfile; int cnt; keyfile = __connman_storage_load_global(); - if (keyfile == NULL) + if (!keyfile) keyfile = g_key_file_new(); - for (cnt = 0; servers != NULL && servers[cnt] != NULL; cnt++); + for (cnt = 0; servers && servers[cnt]; cnt++); g_key_file_set_string_list(keyfile, "global", "Timeservers", (const gchar **)servers, cnt); @@ -61,359 +76,411 @@ static void save_timeservers(char **servers) __connman_storage_save_global(keyfile); g_key_file_free(keyfile); - - return; } -static char **load_timeservers() +static char **load_timeservers(void) { GKeyFile *keyfile; - GError *error = NULL; char **servers = NULL; keyfile = __connman_storage_load_global(); - if (keyfile == NULL) + if (!keyfile) return NULL; servers = g_key_file_get_string_list(keyfile, "global", - "Timeservers", NULL, &error); - if (error) { - DBG("Error loading timeservers: %s", error->message); - g_error_free(error); - } + "Timeservers", NULL, NULL); g_key_file_free(keyfile); return servers; } -static gint compare_priority(gconstpointer a, gconstpointer b) +static void resolv_result(GResolvResultStatus status, char **results, + gpointer user_data) { - const struct connman_timeserver_driver *driver1 = a; - const struct connman_timeserver_driver *driver2 = b; + int i; + +#if defined TIZEN_EXT + gchar *server = NULL; + + server = g_strdup((gchar *)user_data); + ts_list = g_slist_append(ts_list, server); + + if (!simplified_log) + DBG("added server %s", server); + + if (!simplified_log) +#endif + DBG("status %d", status); + + if (status == G_RESOLV_RESULT_STATUS_SUCCESS) { + if (results) { + /* prepend the results in reverse order */ + + for (i = 0; results[i]; i++) + /* count */; + i--; + + for (; i >= 0; i--) { + DBG("result[%d]: %s", i, results[i]); - return driver2->priority - driver1->priority; + ts_list = __connman_timeserver_add_list( + ts_list, results[i]); + } + } + } + + sync_next(); } -/** - * connman_timeserver_driver_register: - * @driver: timeserver driver definition - * - * Register a new timeserver driver - * - * Returns: %0 on success +/* + * Once the timeserver list (timeserver_list) is created, we start + * querying the servers one by one. If resolving fails on one of them, + * we move to the next one. The user can enter either an IP address or + * a URL for the timeserver. We only resolve the URLs. Once we have an + * IP for the NTP server, we start querying it for time corrections. */ -int connman_timeserver_driver_register(struct connman_timeserver_driver *driver) +static void timeserver_sync_start(void) { - DBG("driver %p name %s", driver, driver->name); + GSList *list; - driver_list = g_slist_insert_sorted(driver_list, driver, - compare_priority); + for (list = timeservers_list; list; list = list->next) { + char *timeserver = list->data; - return 0; + ts_list = g_slist_prepend(ts_list, g_strdup(timeserver)); + } + ts_list = g_slist_reverse(ts_list); + + sync_next(); } -/** - * connman_timeserver_driver_unregister: - * @driver: timeserver driver definition - * - * Remove a previously registered timeserver driver - */ -void connman_timeserver_driver_unregister(struct connman_timeserver_driver *driver) +static gboolean timeserver_sync_restart(gpointer user_data) { - DBG("driver %p name %s", driver, driver->name); + timeserver_sync_start(); + ts_backoff_id = 0; - driver_list = g_slist_remove(driver_list, driver); + return FALSE; } -/** - * connman_timeserver_append: - * @server: server address - * - * Append time server server address to current list +/* + * Select the next time server from the working list (ts_list) because + * for some reason the first time server in the list didn't work. If + * none of the server did work we start over with the first server + * with a backoff. */ -int connman_timeserver_append(const char *server) +static void sync_next(void) { - GSList *list; + if (ts_current) { + g_free(ts_current); + ts_current = NULL; + } - DBG("server %s", server); + __connman_ntp_stop(); - if (server == NULL) - return -EINVAL; + while (ts_list) { + ts_current = ts_list->data; + ts_list = g_slist_delete_link(ts_list, ts_list); - /* This server is already handled by a driver */ - if (g_hash_table_lookup(server_hash, server)) - return 0; + /* if it's an IP, directly query it. */ + if (connman_inet_check_ipaddress(ts_current) > 0) { + DBG("Using timeserver %s", ts_current); + __connman_ntp_start(ts_current, ntp_callback, NULL); + return; + } +#if defined TIZEN_EXT + if (!simplified_log) +#endif + DBG("Resolving timeserver %s", ts_current); +#if defined TIZEN_EXT + resolv_id = g_resolv_lookup_hostname(resolv, ts_current, + resolv_result, ts_current); +#else + resolv_id = g_resolv_lookup_hostname(resolv, ts_current, + resolv_result, NULL); +#endif + return; + } - for (list = driver_list; list; list = list->next) { - struct connman_timeserver_driver *driver = list->data; - char *new_server; + DBG("No timeserver could be used, restart probing in 5 seconds"); + ts_backoff_id = g_timeout_add_seconds(5, timeserver_sync_restart, NULL); +} - if (driver->append == NULL) - continue; +GSList *__connman_timeserver_add_list(GSList *server_list, + const char *timeserver) +{ + GSList *list = server_list; - new_server = g_strdup(server); - if (new_server == NULL) - return -ENOMEM; + if (!timeserver) + return server_list; - if (driver->append(server) == 0) { - g_hash_table_insert(server_hash, new_server, driver); - return 0; - } else { - g_free(new_server); - } + while (list) { + char *existing_server = list->data; + if (strcmp(timeserver, existing_server) == 0) + return server_list; + list = g_slist_next(list); } - - return -ENOENT; + return g_slist_prepend(server_list, g_strdup(timeserver)); } -/** - * connman_timeserver_remove: - * @server: server address - * - * Remover time server server address from current list +/* + * __connman_timeserver_get_all function creates the timeserver + * list which will be used to determine NTP server for time corrections. + * The service settings take priority over the global timeservers. */ -int connman_timeserver_remove(const char *server) +GSList *__connman_timeserver_get_all(struct connman_service *service) { - struct connman_timeserver_driver *driver; + GSList *list = NULL; + struct connman_network *network; + char **timeservers; + char **service_ts; + char **service_ts_config; + const char *service_gw; + char **fallback_ts; + int index, i; + + if (__connman_clock_timeupdates() == TIME_UPDATES_MANUAL) + return NULL; + + service_ts_config = connman_service_get_timeservers_config(service); + + /* First add Service Timeservers.Configuration to the list */ + for (i = 0; service_ts_config && service_ts_config[i]; + i++) + list = __connman_timeserver_add_list(list, + service_ts_config[i]); + + service_ts = connman_service_get_timeservers(service); + + /* Then add Service Timeservers via DHCP to the list */ + for (i = 0; service_ts && service_ts[i]; i++) + list = __connman_timeserver_add_list(list, service_ts[i]); + + /* + * Then add Service Gateway to the list, if UseGatewaysAsTimeservers + * configuration option is set to true. + */ + if (connman_setting_get_bool("UseGatewaysAsTimeservers")) { + network = __connman_service_get_network(service); + if (network) { + index = connman_network_get_index(network); + service_gw = __connman_ipconfig_get_gateway_from_index(index, + CONNMAN_IPCONFIG_TYPE_ALL); + + if (service_gw) + list = __connman_timeserver_add_list(list, service_gw); + } + } - DBG("server %s", server); + /* Then add Global Timeservers to the list */ + timeservers = load_timeservers(); - if (server == NULL) - return -EINVAL; + for (i = 0; timeservers && timeservers[i]; i++) + list = __connman_timeserver_add_list(list, timeservers[i]); - driver = g_hash_table_lookup(server_hash, server); - if (driver == NULL) - return -EINVAL; + g_strfreev(timeservers); - g_hash_table_remove(server_hash, server); + fallback_ts = connman_setting_get_string_list("FallbackTimeservers"); - if (driver->remove == NULL) - return -ENOENT; + /* Lastly add the fallback servers */ + for (i = 0; fallback_ts && fallback_ts[i]; i++) + list = __connman_timeserver_add_list(list, fallback_ts[i]); - return driver->remove(server); + return g_slist_reverse(list); } -/* Restart NTP procedure */ -static void connman_timeserver_restart() +static gboolean ts_recheck(gpointer user_data) { - if (resolv == NULL) { - DBG("No online service."); - return; - } + GSList *ts; - /* Cancel current lookup */ - if(resolv_id > 0) - g_resolv_cancel_lookup(resolv, resolv_id); + ts = __connman_timeserver_get_all(connman_service_get_default()); + + if (!ts) { + DBG("timeservers disabled"); - /* Reload system timeserver list */ - if (system_timeservers != NULL) { - g_strfreev(system_timeservers); - system_timeservers = NULL; + return TRUE; } - system_timeservers = load_timeservers(); + if (g_strcmp0(ts_current, ts->data) != 0) { + DBG("current %s preferred %s", ts_current, (char *)ts->data); - if (system_timeservers == NULL) - return; + g_slist_free_full(ts, g_free); - __connman_ntp_stop(); + __connman_timeserver_sync(NULL); - count = 0; + return FALSE; + } + + DBG(""); - __connman_timeserver_sync_next(); + g_slist_free_full(ts, g_free); + + return TRUE; } -static void resolv_result(GResolvResultStatus status, char **results, gpointer user_data) +static void ts_recheck_disable(void) { - int i; - - DBG("status %d", status); + if (ts_recheck_id == 0) + return; - if (status == G_RESOLV_RESULT_STATUS_SUCCESS) { - if (results != NULL) { - for (i = 0; results[i]; i++) - DBG("result: %s", results[i]); + g_source_remove(ts_recheck_id); + ts_recheck_id = 0; - __connman_ntp_start(results[0]); + if (ts_backoff_id) { + g_source_remove(ts_backoff_id); + ts_backoff_id = 0; + } - return; - } + if (ts_current) { + g_free(ts_current); + ts_current = NULL; } - __sync_fetch_and_add(&count, 1); - __connman_timeserver_sync_next(); } -void __connman_timeserver_sync_next() +static void ts_recheck_enable(void) { - if (system_timeservers == NULL || - system_timeservers[count] == NULL) + if (ts_recheck_id > 0) return; - DBG("Trying timeserver %s", system_timeservers[count]); - - if (resolv) - resolv_id = g_resolv_lookup_hostname(resolv, - system_timeservers[count], resolv_result, - NULL); + ts_recheck_id = g_timeout_add_seconds(TS_RECHECK_INTERVAL, ts_recheck, + NULL); } -int __connman_timeserver_sync(struct connman_service *service) +/* + * This function must be called everytime the default service changes, the + * service timeserver(s) or gateway changes or the global timeserver(s) changes. + */ +int __connman_timeserver_sync(struct connman_service *default_service) { - char **nameservers = NULL; + struct connman_service *service; + char **nameservers; int i; - DBG("service %p", service); + if (default_service) + service = default_service; + else + service = connman_service_get_default(); - i = __connman_service_get_index(service); - if (i < 0) + if (!service) return -EINVAL; - nameservers = connman_service_get_nameservers(service); - if (nameservers == NULL) - return -EINVAL; +#if !defined TIZEN_EXT + if (service == ts_service) + return -EALREADY; +#endif - resolv = g_resolv_new(i); - if (resolv == NULL) - return -ENOMEM; + if (!resolv) + return 0; + /* + * Before we start creating the new timeserver list we must stop + * any ongoing ntp query and server resolution. + */ - if (getenv("CONNMAN_RESOLV_DEBUG")) - g_resolv_set_debug(resolv, resolv_debug, "RESOLV"); + __connman_ntp_stop(); - for (i = 0; nameservers[i] != NULL; i++) - g_resolv_add_nameserver(resolv, nameservers[i], 53, 0); + ts_recheck_disable(); - count = 0; + if (resolv_id > 0) + g_resolv_cancel_lookup(resolv, resolv_id); - system_timeservers = load_timeservers(); + g_resolv_flush_nameservers(resolv); - if (system_timeservers == NULL || system_timeservers[count] == NULL) { - DBG("No timeservers set."); - return 0; - } + nameservers = connman_service_get_nameservers(service); + if (!nameservers) + return -EINVAL; - DBG("Trying server %s", system_timeservers[count]); + for (i = 0; nameservers[i]; i++) + g_resolv_add_nameserver(resolv, nameservers[i], 53, 0); - resolv_id = g_resolv_lookup_hostname(resolv, system_timeservers[count], - resolv_result, NULL); - return 0; -} + g_strfreev(nameservers); -void __connman_timeserver_stop() -{ - DBG(" "); + g_slist_free_full(timeservers_list, g_free); - if (resolv != NULL) { - g_resolv_unref(resolv); - resolv = NULL; - } + timeservers_list = __connman_timeserver_get_all(service); + + __connman_service_timeserver_changed(service, timeservers_list); - if (system_timeservers != NULL) { - g_strfreev(system_timeservers); - system_timeservers = NULL; + if (!timeservers_list) { + DBG("No timeservers set."); + return 0; } - count = 0; + ts_recheck_enable(); - __connman_ntp_stop(); + ts_service = service; + timeserver_sync_start(); + + return 0; } -int __connman_timeserver_system_append(const char *server) +static int timeserver_start(struct connman_service *service) { - int len; - char **servers = NULL; - - if (server == NULL) { - save_timeservers(servers); - goto restart; - } + char **nameservers; + int i; - DBG("server %s", server); + DBG("service %p", service); - servers = load_timeservers(); + i = __connman_service_get_index(service); + if (i < 0) + return -EINVAL; - if (servers != NULL) { - int i; + nameservers = connman_service_get_nameservers(service); - for (i = 0; servers[i] != NULL; i++) - if (g_strcmp0(servers[i], server) == 0) { - g_strfreev(servers); - return -EEXIST; - } + /* Stop an already ongoing resolution, if there is one */ + if (resolv && resolv_id > 0) + g_resolv_cancel_lookup(resolv, resolv_id); - len = g_strv_length(servers); - servers = g_try_renew(char *, servers, len + 2); - } else { - len = 0; - servers = g_try_new0(char *, len + 2); + /* get rid of the old resolver */ + if (resolv) { + g_resolv_unref(resolv); + resolv = NULL; } - if (servers == NULL) + resolv = g_resolv_new(i); + if (!resolv) { + g_strfreev(nameservers); return -ENOMEM; + } - servers[len] = g_strdup(server); - servers[len + 1] = NULL; + if (getenv("CONNMAN_RESOLV_DEBUG")) + g_resolv_set_debug(resolv, resolv_debug, "RESOLV"); - save_timeservers(servers); + if (nameservers) { + for (i = 0; nameservers[i]; i++) + g_resolv_add_nameserver(resolv, nameservers[i], 53, 0); - g_strfreev(servers); -restart: - connman_timeserver_restart(); + g_strfreev(nameservers); + } - return 0; + return __connman_timeserver_sync(service); } -int __connman_timeserver_system_remove(const char *server) +static void timeserver_stop(void) { - char **servers; - char **temp; - int len, i, j; - - if (server == NULL) - return -EINVAL; - - DBG("server %s", server); - - servers = load_timeservers(); - - if (servers == NULL) - return 0; + DBG(" "); - len = g_strv_length(servers); - if (len == 1) { - if (g_strcmp0(servers[0], server) != 0) { - g_strfreev(servers); - return 0; - } + ts_service = NULL; - g_strfreev(servers); - servers = NULL; - save_timeservers(servers); - return 0; + if (resolv) { + g_resolv_unref(resolv); + resolv = NULL; } - temp = g_try_new0(char *, len - 1); - if (temp == NULL) { - g_strfreev(servers); - return -ENOMEM; - } + g_slist_free_full(timeservers_list, g_free); + timeservers_list = NULL; - for (i = 0, j = 0; i < len; i++) { - if (g_strcmp0(servers[i], server) != 0) { - temp[j] = g_strdup(servers[i]); - j++; - } - } - temp[len - 1] = NULL; + g_slist_free_full(ts_list, g_free); + ts_list = NULL; - g_strfreev(servers); - servers = g_strdupv(temp); - g_strfreev(temp); + __connman_ntp_stop(); + + ts_recheck_disable(); +} +int __connman_timeserver_system_set(char **servers) +{ save_timeservers(servers); - g_strfreev(servers); - connman_timeserver_restart(); + __connman_timeserver_sync(NULL); return 0; } @@ -426,12 +493,24 @@ char **__connman_timeserver_system_get() return servers; } +static void default_changed(struct connman_service *default_service) +{ + if (default_service) + timeserver_start(default_service); + else + timeserver_stop(); +} + +static const struct connman_notifier timeserver_notifier = { + .name = "timeserver", + .default_changed = default_changed, +}; + int __connman_timeserver_init(void) { DBG(""); - server_hash = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, NULL); + connman_notifier_register(×erver_notifier); return 0; } @@ -440,5 +519,5 @@ void __connman_timeserver_cleanup(void) { DBG(""); - g_hash_table_destroy(server_hash); + connman_notifier_unregister(×erver_notifier); }