Add __connman_timerserver_sync() to resolve a list of timeservers
[framework/connectivity/connman.git] / src / timeserver.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27
28 #include <glib.h>
29 #include <stdlib.h>
30 #include <gweb/gresolv.h>
31
32 #include "connman.h"
33
34 static GSList *driver_list = NULL;
35 static GHashTable *server_hash = NULL;
36
37 static char **system_timeservers = NULL;
38
39 static GResolv *resolv = NULL;
40 static int resolv_id = 0;
41 static volatile int count;
42
43 static void resolv_debug(const char *str, void *data)
44 {
45         connman_info("%s: %s\n", (const char *) data, str);
46 }
47 static void save_timeservers(char **servers)
48 {
49         GKeyFile *keyfile;
50         int cnt;
51
52         keyfile = __connman_storage_load_global();
53         if (keyfile == NULL)
54                 keyfile = g_key_file_new();
55
56         for (cnt = 0; servers != NULL && servers[cnt] != NULL; cnt++);
57
58         g_key_file_set_string_list(keyfile, "global", "Timeservers",
59                            (const gchar **)servers, cnt);
60
61         __connman_storage_save_global(keyfile);
62
63         g_key_file_free(keyfile);
64
65         return;
66 }
67
68 static char **load_timeservers()
69 {
70         GKeyFile *keyfile;
71         GError *error = NULL;
72         char **servers = NULL;
73
74         keyfile = __connman_storage_load_global();
75         if (keyfile == NULL)
76                 return NULL;
77
78         servers = g_key_file_get_string_list(keyfile, "global",
79                                                 "Timeservers", NULL, &error);
80         if (error) {
81                 DBG("Error loading timeservers: %s", error->message);
82                 g_error_free(error);
83         }
84
85         g_key_file_free(keyfile);
86
87         return servers;
88 }
89
90 static gint compare_priority(gconstpointer a, gconstpointer b)
91 {
92         const struct connman_timeserver_driver *driver1 = a;
93         const struct connman_timeserver_driver *driver2 = b;
94
95         return driver2->priority - driver1->priority;
96 }
97
98 /**
99  * connman_timeserver_driver_register:
100  * @driver: timeserver driver definition
101  *
102  * Register a new timeserver driver
103  *
104  * Returns: %0 on success
105  */
106 int connman_timeserver_driver_register(struct connman_timeserver_driver *driver)
107 {
108         DBG("driver %p name %s", driver, driver->name);
109
110         driver_list = g_slist_insert_sorted(driver_list, driver,
111                                                         compare_priority);
112
113         return 0;
114 }
115
116 /**
117  * connman_timeserver_driver_unregister:
118  * @driver: timeserver driver definition
119  *
120  * Remove a previously registered timeserver driver
121  */
122 void connman_timeserver_driver_unregister(struct connman_timeserver_driver *driver)
123 {
124         DBG("driver %p name %s", driver, driver->name);
125
126         driver_list = g_slist_remove(driver_list, driver);
127 }
128
129 /**
130  * connman_timeserver_append:
131  * @server: server address
132  *
133  * Append time server server address to current list
134  */
135 int connman_timeserver_append(const char *server)
136 {
137         GSList *list;
138
139         DBG("server %s", server);
140
141         if (server == NULL)
142                 return -EINVAL;
143
144         /* This server is already handled by a driver */
145         if (g_hash_table_lookup(server_hash, server))
146                 return 0;
147
148         for (list = driver_list; list; list = list->next) {
149                 struct connman_timeserver_driver *driver = list->data;
150                 char *new_server;
151
152                 if (driver->append == NULL)
153                         continue;
154
155                 new_server = g_strdup(server);
156                 if (new_server == NULL)
157                         return -ENOMEM;
158
159                 if (driver->append(server) == 0) {
160                         g_hash_table_insert(server_hash, new_server, driver);
161                         return 0;
162                 } else {
163                         g_free(new_server);
164                 }
165         }
166
167         return -ENOENT;
168 }
169
170 /**
171  * connman_timeserver_remove:
172  * @server: server address
173  *
174  * Remover time server server address from current list
175  */
176 int connman_timeserver_remove(const char *server)
177 {
178         struct connman_timeserver_driver *driver;
179
180         DBG("server %s", server);
181
182         if (server == NULL)
183                 return -EINVAL;
184
185         driver = g_hash_table_lookup(server_hash, server);
186         if (driver == NULL)
187                 return -EINVAL;
188
189         g_hash_table_remove(server_hash, server);
190
191         if (driver->remove == NULL)
192                 return -ENOENT;
193
194         return driver->remove(server);
195 }
196
197 /* Restart NTP procedure */
198 static void connman_timeserver_restart()
199 {
200         if (resolv == NULL) {
201                 DBG("No online service.");
202                 return;
203         }
204
205         /* Cancel current lookup */
206         if(resolv_id > 0)
207                 g_resolv_cancel_lookup(resolv, resolv_id);
208
209         /* Reload system timeserver list */
210         if (system_timeservers != NULL) {
211                 g_strfreev(system_timeservers);
212                 system_timeservers = NULL;
213         }
214
215         system_timeservers = load_timeservers();
216
217         if (system_timeservers == NULL)
218                 return;
219
220         __connman_ntp_stop();
221
222         count = 0;
223
224         __connman_timeserver_sync_next();
225 }
226
227 static void resolv_result(GResolvResultStatus status, char **results, gpointer user_data)
228 {
229         int i;
230
231         DBG("status %d", status);
232
233         if (status == G_RESOLV_RESULT_STATUS_SUCCESS) {
234                 if (results != NULL) {
235                         for (i = 0; results[i]; i++)
236                                 DBG("result: %s", results[i]);
237
238                         __connman_ntp_start(results[0]);
239
240                         return;
241                 }
242         }
243         __sync_fetch_and_add(&count, 1);
244         __connman_timeserver_sync_next();
245 }
246
247 void __connman_timeserver_sync_next()
248 {
249         if (system_timeservers == NULL ||
250         system_timeservers[count] == NULL)
251                 return;
252
253         DBG("Trying timeserver %s", system_timeservers[count]);
254
255         if (resolv)
256                 resolv_id = g_resolv_lookup_hostname(resolv,
257                         system_timeservers[count], resolv_result,
258                                                 NULL);
259 }
260
261 int __connman_timeserver_sync(struct connman_service *service)
262 {
263         char **nameservers = NULL;
264         int i;
265
266         DBG("service %p", service);
267
268         i = __connman_service_get_index(service);
269         if (i < 0)
270                 return -EINVAL;
271
272         nameservers = connman_service_get_nameservers(service);
273         if (nameservers == NULL)
274                 return -EINVAL;
275
276         resolv = g_resolv_new(i);
277         if (resolv == NULL)
278                 return -ENOMEM;
279
280         if (getenv("CONNMAN_RESOLV_DEBUG"))
281                 g_resolv_set_debug(resolv, resolv_debug, "RESOLV");
282
283         for (i = 0; nameservers[i] != NULL; i++)
284                 g_resolv_add_nameserver(resolv, nameservers[i], 53, 0);
285
286         count = 0;
287
288         system_timeservers = load_timeservers();
289
290         if (system_timeservers == NULL || system_timeservers[count] == NULL) {
291                 DBG("No timeservers set.");
292                 return 0;
293         }
294
295         DBG("Trying server %s", system_timeservers[count]);
296
297         resolv_id = g_resolv_lookup_hostname(resolv, system_timeservers[count],
298                                                 resolv_result, NULL);
299         return 0;
300 }
301
302 void __connman_timeserver_stop()
303 {
304         DBG(" ");
305
306         if (resolv != NULL) {
307                 g_resolv_unref(resolv);
308                 resolv = NULL;
309         }
310
311         if (system_timeservers != NULL) {
312                 g_strfreev(system_timeservers);
313                 system_timeservers = NULL;
314         }
315
316         count = 0;
317
318         __connman_ntp_stop();
319 }
320
321 int __connman_timeserver_system_append(const char *server)
322 {
323         int len;
324         char **servers = NULL;
325
326         if (server == NULL) {
327                 save_timeservers(servers);
328                 goto restart;
329         }
330
331         DBG("server %s", server);
332
333         servers = load_timeservers();
334
335         if (servers != NULL) {
336                 int i;
337
338                 for (i = 0; servers[i] != NULL; i++)
339                         if (g_strcmp0(servers[i], server) == 0) {
340                                 g_strfreev(servers);
341                                 return -EEXIST;
342                         }
343
344                 len = g_strv_length(servers);
345                 servers = g_try_renew(char *, servers, len + 2);
346         } else {
347                 len = 0;
348                 servers = g_try_new0(char *, len + 2);
349         }
350
351         if (servers == NULL)
352                 return -ENOMEM;
353
354         servers[len] = g_strdup(server);
355         servers[len + 1] = NULL;
356
357         save_timeservers(servers);
358
359         g_strfreev(servers);
360 restart:
361         connman_timeserver_restart();
362
363         return 0;
364 }
365
366 int __connman_timeserver_system_remove(const char *server)
367 {
368         char **servers;
369         char **temp;
370         int len, i, j;
371
372         if (server == NULL)
373                 return -EINVAL;
374
375         DBG("server %s", server);
376
377         servers = load_timeservers();
378
379         if (servers == NULL)
380                 return 0;
381
382         len = g_strv_length(servers);
383         if (len == 1) {
384                 if (g_strcmp0(servers[0], server) != 0) {
385                         g_strfreev(servers);
386                         return 0;
387                 }
388
389                 g_strfreev(servers);
390                 servers = NULL;
391                 save_timeservers(servers);
392                 return 0;
393         }
394
395         temp = g_try_new0(char *, len - 1);
396         if (temp == NULL) {
397                         g_strfreev(servers);
398                         return -ENOMEM;
399         }
400
401         for (i = 0, j = 0; i < len; i++) {
402                 if (g_strcmp0(servers[i], server) != 0) {
403                         temp[j] = g_strdup(servers[i]);
404                         j++;
405                 }
406         }
407         temp[len - 1] = NULL;
408
409         g_strfreev(servers);
410         servers = g_strdupv(temp);
411         g_strfreev(temp);
412
413         save_timeservers(servers);
414         g_strfreev(servers);
415
416         connman_timeserver_restart();
417
418         return 0;
419 }
420
421 char **__connman_timeserver_system_get()
422 {
423         char **servers;
424
425         servers = load_timeservers();
426         return servers;
427 }
428
429 int __connman_timeserver_init(void)
430 {
431         DBG("");
432
433         server_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
434                                                 g_free, NULL);
435
436         return 0;
437 }
438
439 void __connman_timeserver_cleanup(void)
440 {
441         DBG("");
442
443         g_hash_table_destroy(server_hash);
444 }