ntp: Add timeserver switch logic
[platform/upstream/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         __sync_fetch_and_add(&count, 1);
234
235         if (status == G_RESOLV_RESULT_STATUS_SUCCESS) {
236                 if (results != NULL) {
237                         for (i = 0; results[i]; i++)
238                                 DBG("result: %s", results[i]);
239
240                         __connman_ntp_start(results[0]);
241
242                         return;
243                 }
244         }
245
246         __connman_timeserver_sync_next();
247 }
248
249 void __connman_timeserver_sync_next()
250 {
251         if (system_timeservers == NULL ||
252         system_timeservers[count] == NULL)
253                 return;
254
255         DBG("Trying timeserver %s", system_timeservers[count]);
256
257         if (resolv)
258                 resolv_id = g_resolv_lookup_hostname(resolv,
259                         system_timeservers[count], resolv_result,
260                                                 NULL);
261 }
262
263 int __connman_timeserver_sync(struct connman_service *service)
264 {
265         char **nameservers = NULL;
266         int i;
267
268         DBG("service %p", service);
269
270         i = __connman_service_get_index(service);
271         if (i < 0)
272                 return -EINVAL;
273
274         nameservers = connman_service_get_nameservers(service);
275         if (nameservers == NULL)
276                 return -EINVAL;
277
278         resolv = g_resolv_new(i);
279         if (resolv == NULL)
280                 return -ENOMEM;
281
282         if (getenv("CONNMAN_RESOLV_DEBUG"))
283                 g_resolv_set_debug(resolv, resolv_debug, "RESOLV");
284
285         for (i = 0; nameservers[i] != NULL; i++)
286                 g_resolv_add_nameserver(resolv, nameservers[i], 53, 0);
287
288         count = 0;
289
290         system_timeservers = load_timeservers();
291
292         if (system_timeservers == NULL || system_timeservers[count] == NULL) {
293                 DBG("No timeservers set.");
294                 return 0;
295         }
296
297         DBG("Trying server %s", system_timeservers[count]);
298
299         resolv_id = g_resolv_lookup_hostname(resolv, system_timeservers[count],
300                                                 resolv_result, NULL);
301         return 0;
302 }
303
304 void __connman_timeserver_stop()
305 {
306         DBG(" ");
307
308         if (resolv != NULL) {
309                 g_resolv_unref(resolv);
310                 resolv = NULL;
311         }
312
313         if (system_timeservers != NULL) {
314                 g_strfreev(system_timeservers);
315                 system_timeservers = NULL;
316         }
317
318         count = 0;
319
320         __connman_ntp_stop();
321 }
322
323 int __connman_timeserver_system_append(const char *server)
324 {
325         int len;
326         char **servers = NULL;
327
328         if (server == NULL) {
329                 save_timeservers(servers);
330                 goto restart;
331         }
332
333         DBG("server %s", server);
334
335         servers = load_timeservers();
336
337         if (servers != NULL) {
338                 int i;
339
340                 for (i = 0; servers[i] != NULL; i++)
341                         if (g_strcmp0(servers[i], server) == 0) {
342                                 g_strfreev(servers);
343                                 return -EEXIST;
344                         }
345
346                 len = g_strv_length(servers);
347                 servers = g_try_renew(char *, servers, len + 2);
348         } else {
349                 len = 0;
350                 servers = g_try_new0(char *, len + 2);
351         }
352
353         if (servers == NULL)
354                 return -ENOMEM;
355
356         servers[len] = g_strdup(server);
357         servers[len + 1] = NULL;
358
359         save_timeservers(servers);
360
361         g_strfreev(servers);
362 restart:
363         connman_timeserver_restart();
364
365         return 0;
366 }
367
368 int __connman_timeserver_system_remove(const char *server)
369 {
370         char **servers;
371         char **temp;
372         int len, i, j;
373
374         if (server == NULL)
375                 return -EINVAL;
376
377         DBG("server %s", server);
378
379         servers = load_timeservers();
380
381         if (servers == NULL)
382                 return 0;
383
384         len = g_strv_length(servers);
385         if (len == 1) {
386                 if (g_strcmp0(servers[0], server) != 0) {
387                         g_strfreev(servers);
388                         return 0;
389                 }
390
391                 g_strfreev(servers);
392                 servers = NULL;
393                 save_timeservers(servers);
394                 return 0;
395         }
396
397         temp = g_try_new0(char *, len - 1);
398         if (temp == NULL) {
399                         g_strfreev(servers);
400                         return -ENOMEM;
401         }
402
403         for (i = 0, j = 0; i < len; i++) {
404                 if (g_strcmp0(servers[i], server) != 0) {
405                         temp[j] = g_strdup(servers[i]);
406                         j++;
407                 }
408         }
409         temp[len - 1] = NULL;
410
411         g_strfreev(servers);
412         servers = g_strdupv(temp);
413         g_strfreev(temp);
414
415         save_timeservers(servers);
416         g_strfreev(servers);
417
418         connman_timeserver_restart();
419
420         return 0;
421 }
422
423 char **__connman_timeserver_system_get()
424 {
425         char **servers;
426
427         servers = load_timeservers();
428         return servers;
429 }
430
431 int __connman_timeserver_init(void)
432 {
433         DBG("");
434
435         server_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
436                                                 g_free, NULL);
437
438         return 0;
439 }
440
441 void __connman_timeserver_cleanup(void)
442 {
443         DBG("");
444
445         g_hash_table_destroy(server_hash);
446 }