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