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