Fix timeserver API
[framework/connectivity/connman.git] / plugins / ntpd.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 #include <stdio.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <arpa/inet.h>
32
33 #define CONNMAN_API_SUBJECT_TO_CHANGE
34 #include <connman/plugin.h>
35 #include <connman/task.h>
36 #include <connman/timeserver.h>
37 #include <connman/driver.h>
38 #include <connman/log.h>
39
40 /*
41  * The peers list are the peers currently added to a running ntpd,
42  * while pending_peers are the one appended but not used by ntpd yet.
43  */
44 static GList *peers = NULL;
45 static GList *pending_peers = NULL;
46
47 #define NTPD_PORT 123
48 #define DEFAULT_NTP_PEER "ntp.meego.com"
49
50 struct ntpd_peer {
51         char *server;
52         gint refcount;
53 };
54
55 struct ntpdate_task {
56         struct connman_task *task;
57         gint conf_fd;
58         char *conf_path;
59 };
60
61 static struct ntpd_peer *find_peer(GList *peer_list, const char* server)
62 {
63         GList *list;
64         struct ntpd_peer *peer;
65
66         for (list = peer_list; list; list = list->next) {
67                 peer = list->data;
68
69                 if (g_str_equal(peer->server, server))
70                         return peer;
71         }
72
73         return NULL;
74 }
75
76 static void remove_peer(GList *peer_list, struct ntpd_peer *peer)
77 {
78         if (!g_atomic_int_dec_and_test(&peer->refcount))
79                 return;
80
81         g_free(peer->server);
82         g_free(peer);
83         peer_list = g_list_remove(peer_list, peer);
84 }
85
86 static connman_bool_t ntpd_running(void)
87 {
88         int sock;
89         connman_bool_t ret;
90         struct sockaddr_in server_addr;
91
92         if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
93                 return FALSE;
94
95         server_addr.sin_family = AF_INET;
96         server_addr.sin_port = htons(NTPD_PORT);
97         server_addr.sin_addr.s_addr = INADDR_ANY;
98         memset(&(server_addr.sin_zero), 0, 8);
99
100         if (bind(sock, (struct sockaddr *)&server_addr,
101                         sizeof(struct sockaddr)) == -1) {
102                 if (errno == EADDRINUSE)
103                         ret = TRUE;
104                 else
105                         ret = FALSE;
106         }
107
108         close(sock);
109
110         return ret;
111 }
112
113 static void ntpdate_died(struct connman_task *task, void *user_data)
114 {
115         struct ntpdate_task *ntpdate = user_data;
116
117         DBG("");
118
119         unlink(ntpdate->conf_path);
120         g_free(ntpdate->conf_path);
121         connman_task_destroy(ntpdate->task);
122 }
123
124 static void ntpdate_add_peer(struct ntpdate_task *ntpdate, char *peer)
125 {
126         FILE *conf_file;
127
128         DBG("%s", peer);
129
130         conf_file = fdopen(ntpdate->conf_fd, "a+");
131         if (conf_file == NULL) {
132                 connman_error("fdopen failed");
133                 return;
134         }
135
136         fprintf(conf_file, "server %s iburst\n", peer);
137
138         fclose(conf_file);
139 }
140
141 static int ntpdate(void)
142 {
143         int err;
144         GError *g_err;
145         GList *list;
146         struct ntpd_peer *peer;
147         struct ntpdate_task *ntpdate;
148
149         DBG("");
150
151         ntpdate = g_try_new0(struct ntpdate_task, 1);
152         if (ntpdate == NULL)
153                 return -ENOMEM;
154
155         /* ntpdate is deprecated, we use ntpd -q instead */
156         ntpdate->task = connman_task_create(NTPD);
157         if (ntpdate->task == NULL) {
158                 err = -ENOMEM;
159                 goto error_task;
160         }
161
162         connman_task_add_argument(ntpdate->task, "-q", NULL);
163
164         /* The servers are added through a temp configuration file */
165         ntpdate->conf_fd = g_file_open_tmp("connman.ntp.conf_XXXXXX",
166                                                 &ntpdate->conf_path, &g_err);
167         if  (ntpdate->conf_fd == -1) {
168                 err = g_err->code;
169                 g_free(g_err);
170                 goto error_open;
171         }
172
173         connman_task_add_argument(ntpdate->task, "-c", ntpdate->conf_path);
174
175         DBG("conf path %s", ntpdate->conf_path);
176
177         if (pending_peers == NULL && peers == NULL)
178                 ntpdate_add_peer(ntpdate, DEFAULT_NTP_PEER);
179
180         for (list = pending_peers; list; list = list->next) {
181                 peer = list->data;
182
183                 ntpdate_add_peer(ntpdate, peer->server);
184         }
185
186         for (list = peers; list; list = list->next) {
187                 peer = list->data;
188
189                 ntpdate_add_peer(ntpdate, peer->server);
190         }
191
192         close(ntpdate->conf_fd);
193
194         return connman_task_run(ntpdate->task, ntpdate_died, ntpdate,
195                                                 NULL, NULL, NULL);
196 error_open:
197         connman_task_destroy(ntpdate->task);
198
199 error_task:
200         g_free(ntpdate);
201
202         return err;
203 }
204
205 static int ntpd_add_peer(char *peer)
206 {
207         DBG("%s", peer);
208
209         return 0;
210 }
211
212 static void ntpd_sync(void)
213 {
214         int err;
215         GList *list;
216
217         DBG("");
218
219         if (!ntpd_running()) {
220                 ntpdate();
221                 return;
222         }
223
224         /* TODO Grab ntp keys path */
225
226         list = g_list_first(pending_peers);
227         while(list) {
228                 struct ntpd_peer *peer = list->data;
229
230                 err = ntpd_add_peer(peer->server);
231                 if (err)
232                         continue;
233
234                 peers = g_list_prepend(peers, peer);
235
236                 list = g_list_next(list);
237
238                 pending_peers = g_list_remove(pending_peers, peer);
239         };
240 }
241
242 static int ntpd_append(const char *server)
243 {
244         struct ntpd_peer *peer;
245
246         DBG("");
247
248         if (server == NULL)
249                 return 0;
250
251         if ((peer = find_peer(pending_peers, server)) ||
252                         (peer = find_peer(peers, server))) {
253                 g_atomic_int_inc(&peer->refcount);
254                 return 0;
255         }
256
257         peer = g_try_new0(struct ntpd_peer, 1);
258         if (peer == NULL)
259                 return -ENOMEM;
260
261         peer->server = g_strdup(server);
262         if (peer->server == NULL) {
263                 g_free(peer);
264                 return -ENOMEM;
265         }
266
267         peer->refcount = 1;
268
269         pending_peers = g_list_prepend(pending_peers, peer);
270
271         return 0;
272 }
273
274 static int ntpd_remove(const char *server)
275 {
276         struct ntpd_peer *peer;
277
278         DBG("");
279
280         if (server == NULL)
281                 return 0;
282
283         peer = find_peer(peers, server);
284         if (peer == NULL)
285                 goto remove;
286
287         remove_peer(peers, peer);
288
289 remove:
290         /* TODO: send ntpd remove command */
291
292         peer = find_peer(pending_peers, server);
293         if (peer == NULL)
294                 return 0;
295
296         remove_peer(pending_peers, peer);
297
298         return 0;
299 }
300
301 static struct connman_timeserver_driver ntpd_driver = {
302         .name           = "ntpd",
303         .priority       = CONNMAN_DRIVER_PRIORITY_DEFAULT,
304         .append         = ntpd_append,
305         .remove         = ntpd_remove,
306         .sync           = ntpd_sync,
307 };
308
309 static int ntpd_init(void)
310 {
311         return connman_timeserver_driver_register(&ntpd_driver);
312 }
313
314 static void ntpd_exit(void)
315 {
316         connman_timeserver_driver_unregister(&ntpd_driver);
317 }
318
319 CONNMAN_PLUGIN_DEFINE(ntpd, "ntpd plugin", VERSION,
320                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, ntpd_init, ntpd_exit)