Imported Upstream version 1.40
[platform/upstream/connman.git] / src / clock.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2013  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 <sys/time.h>
27
28 #include <gdbus.h>
29
30 #include "connman.h"
31
32 enum timezone_updates {
33         TIMEZONE_UPDATES_UNKNOWN = 0,
34         TIMEZONE_UPDATES_MANUAL  = 1,
35         TIMEZONE_UPDATES_AUTO    = 2,
36 };
37
38 static enum time_updates time_updates_config = TIME_UPDATES_AUTO;
39 static enum timezone_updates timezone_updates_config = TIMEZONE_UPDATES_AUTO;
40
41 static char *timezone_config = NULL;
42
43 static const char *time_updates2string(enum time_updates value)
44 {
45         switch (value) {
46         case TIME_UPDATES_UNKNOWN:
47                 break;
48         case TIME_UPDATES_MANUAL:
49                 return "manual";
50         case TIME_UPDATES_AUTO:
51                 return "auto";
52         }
53
54         return NULL;
55 }
56
57 static enum time_updates string2time_updates(const char *value)
58 {
59         if (g_strcmp0(value, "manual") == 0)
60                 return TIME_UPDATES_MANUAL;
61         else if (g_strcmp0(value, "auto") == 0)
62                 return TIME_UPDATES_AUTO;
63
64         return TIME_UPDATES_UNKNOWN;
65 }
66
67 static const char *timezone_updates2string(enum timezone_updates value)
68 {
69         switch (value) {
70         case TIMEZONE_UPDATES_UNKNOWN:
71                 break;
72         case TIMEZONE_UPDATES_MANUAL:
73                 return "manual";
74         case TIMEZONE_UPDATES_AUTO:
75                 return "auto";
76         }
77
78         return NULL;
79 }
80
81 static enum timezone_updates string2timezone_updates(const char *value)
82 {
83         if (g_strcmp0(value, "manual") == 0)
84                 return TIMEZONE_UPDATES_MANUAL;
85         else if (g_strcmp0(value, "auto") == 0)
86                 return TIMEZONE_UPDATES_AUTO;
87
88         return TIMEZONE_UPDATES_UNKNOWN;
89 }
90
91 static void clock_properties_load(void)
92 {
93         GKeyFile *keyfile;
94         char *str;
95         enum time_updates time_value;
96         enum timezone_updates timezone_value;
97
98         keyfile = __connman_storage_load_global();
99         if (!keyfile)
100                 return;
101
102         str = g_key_file_get_string(keyfile, "global", "TimeUpdates", NULL);
103
104         time_value = string2time_updates(str);
105         if (time_value != TIME_UPDATES_UNKNOWN)
106                 time_updates_config = time_value;
107
108         g_free(str);
109
110         str = g_key_file_get_string(keyfile, "global", "TimezoneUpdates",
111                         NULL);
112
113         timezone_value = string2timezone_updates(str);
114         if (timezone_value != TIMEZONE_UPDATES_UNKNOWN)
115                 timezone_updates_config = timezone_value;
116
117         g_free(str);
118
119         g_key_file_free(keyfile);
120 }
121
122 static void clock_properties_save(void)
123 {
124         GKeyFile *keyfile;
125         const char *str;
126
127         keyfile = __connman_storage_load_global();
128         if (!keyfile)
129                 keyfile = g_key_file_new();
130
131         str = time_updates2string(time_updates_config);
132         if (str)
133                 g_key_file_set_string(keyfile, "global", "TimeUpdates", str);
134         else
135                 g_key_file_remove_key(keyfile, "global", "TimeUpdates", NULL);
136
137         str = timezone_updates2string(timezone_updates_config);
138         if (str)
139                 g_key_file_set_string(keyfile, "global", "TimezoneUpdates",
140                                 str);
141         else
142                 g_key_file_remove_key(keyfile, "global", "TimezoneUpdates",
143                                 NULL);
144
145         __connman_storage_save_global(keyfile);
146
147         g_key_file_free(keyfile);
148 }
149
150 enum time_updates __connman_clock_timeupdates(void)
151 {
152         return time_updates_config;
153 }
154
155 static void append_timeservers(DBusMessageIter *iter, void *user_data)
156 {
157         int i;
158         char **timeservers = __connman_timeserver_system_get();
159
160         if (!timeservers)
161                 return;
162
163         for (i = 0; timeservers[i]; i++) {
164                 dbus_message_iter_append_basic(iter,
165                                 DBUS_TYPE_STRING, &timeservers[i]);
166         }
167
168         g_strfreev(timeservers);
169 }
170
171 static DBusMessage *get_properties(DBusConnection *conn,
172                                         DBusMessage *msg, void *data)
173 {
174         DBusMessage *reply;
175         DBusMessageIter array, dict;
176         dbus_bool_t is_synced;
177         struct timeval tv;
178         const char *str;
179
180         DBG("conn %p", conn);
181
182         reply = dbus_message_new_method_return(msg);
183         if (!reply)
184                 return NULL;
185
186         dbus_message_iter_init_append(reply, &array);
187
188         connman_dbus_dict_open(&array, &dict);
189
190         if (gettimeofday(&tv, NULL) == 0) {
191                 dbus_uint64_t val = tv.tv_sec;
192
193                 connman_dbus_dict_append_basic(&dict, "Time",
194                                                 DBUS_TYPE_UINT64, &val);
195         }
196
197         str = time_updates2string(time_updates_config);
198         if (str)
199                 connman_dbus_dict_append_basic(&dict, "TimeUpdates",
200                                                 DBUS_TYPE_STRING, &str);
201
202         if (timezone_config)
203                 connman_dbus_dict_append_basic(&dict, "Timezone",
204                                         DBUS_TYPE_STRING, &timezone_config);
205
206         str = timezone_updates2string(timezone_updates_config);
207         if (str)
208                 connman_dbus_dict_append_basic(&dict, "TimezoneUpdates",
209                                                 DBUS_TYPE_STRING, &str);
210
211         connman_dbus_dict_append_array(&dict, "Timeservers",
212                                 DBUS_TYPE_STRING, append_timeservers, NULL);
213
214         is_synced = __connman_timeserver_is_synced();
215         connman_dbus_dict_append_basic(&dict, "TimeserverSynced",
216                                         DBUS_TYPE_BOOLEAN, &is_synced);
217
218         connman_dbus_dict_close(&array, &dict);
219
220         return reply;
221 }
222
223 static DBusMessage *set_property(DBusConnection *conn,
224                                         DBusMessage *msg, void *data)
225 {
226         DBusMessageIter iter, value;
227         const char *name;
228         int type;
229
230         DBG("conn %p", conn);
231
232         if (!dbus_message_iter_init(msg, &iter))
233                 return __connman_error_invalid_arguments(msg);
234
235         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
236                 return __connman_error_invalid_arguments(msg);
237
238         dbus_message_iter_get_basic(&iter, &name);
239         dbus_message_iter_next(&iter);
240
241         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
242                 return __connman_error_invalid_arguments(msg);
243
244         dbus_message_iter_recurse(&iter, &value);
245
246         type = dbus_message_iter_get_arg_type(&value);
247
248         if (g_str_equal(name, "Time")) {
249                 struct timeval tv;
250                 dbus_uint64_t newval;
251
252                 if (type != DBUS_TYPE_UINT64)
253                         return __connman_error_invalid_arguments(msg);
254
255                 if (time_updates_config != TIME_UPDATES_MANUAL)
256                         return __connman_error_permission_denied(msg);
257
258                 dbus_message_iter_get_basic(&value, &newval);
259
260                 tv.tv_sec = newval;
261                 tv.tv_usec = 0;
262
263                 if (settimeofday(&tv, NULL) < 0)
264                         return __connman_error_invalid_arguments(msg);
265
266                 __connman_timeserver_set_synced(false);
267                 connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
268                                 CONNMAN_CLOCK_INTERFACE, "Time",
269                                 DBUS_TYPE_UINT64, &newval);
270         } else if (g_str_equal(name, "TimeUpdates")) {
271                 const char *strval;
272                 enum time_updates newval;
273
274                 if (type != DBUS_TYPE_STRING)
275                         return __connman_error_invalid_arguments(msg);
276
277                 dbus_message_iter_get_basic(&value, &strval);
278                 newval = string2time_updates(strval);
279
280                 if (newval == TIME_UPDATES_UNKNOWN)
281                         return __connman_error_invalid_arguments(msg);
282
283                 if (newval == time_updates_config)
284                         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
285
286                 time_updates_config = newval;
287
288                 clock_properties_save();
289                 connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
290                                 CONNMAN_CLOCK_INTERFACE, "TimeUpdates",
291                                 DBUS_TYPE_STRING, &strval);
292
293                 if (newval == TIME_UPDATES_AUTO) {
294                         struct connman_service *service;
295
296                         service = connman_service_get_default();
297                         __connman_timeserver_conf_update(service);
298                 }
299         } else if (g_str_equal(name, "Timezone")) {
300                 const char *strval;
301
302                 if (type != DBUS_TYPE_STRING)
303                         return __connman_error_invalid_arguments(msg);
304
305                 if (timezone_updates_config != TIMEZONE_UPDATES_MANUAL)
306                         return __connman_error_permission_denied(msg);
307
308                 dbus_message_iter_get_basic(&value, &strval);
309
310                 if (__connman_timezone_change(strval) < 0)
311                         return __connman_error_invalid_arguments(msg);
312         } else if (g_str_equal(name, "TimezoneUpdates")) {
313                 const char *strval;
314                 enum timezone_updates newval;
315
316                 if (type != DBUS_TYPE_STRING)
317                         return __connman_error_invalid_arguments(msg);
318
319                 dbus_message_iter_get_basic(&value, &strval);
320                 newval = string2timezone_updates(strval);
321
322                 if (newval == TIMEZONE_UPDATES_UNKNOWN)
323                         return __connman_error_invalid_arguments(msg);
324
325                 if (newval == timezone_updates_config)
326                         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
327
328                 timezone_updates_config = newval;
329
330                 clock_properties_save();
331                 connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
332                                 CONNMAN_CLOCK_INTERFACE, "TimezoneUpdates",
333                                 DBUS_TYPE_STRING, &strval);
334         } else if (g_str_equal(name, "Timeservers")) {
335                 DBusMessageIter entry;
336                 char **str = NULL;
337                 GSList *list = NULL;
338                 int count = 0;
339
340                 if (type != DBUS_TYPE_ARRAY)
341                         return __connman_error_invalid_arguments(msg);
342
343                 dbus_message_iter_recurse(&value, &entry);
344
345                 while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
346                         const char *val;
347                         GSList *new_head;
348
349                         dbus_message_iter_get_basic(&entry, &val);
350
351                         new_head = __connman_timeserver_add_list(list, val);
352                         if (list != new_head) {
353                                 count++;
354                                 list = new_head;
355                         }
356
357                         dbus_message_iter_next(&entry);
358                 }
359
360                 if (list) {
361                         str = g_new0(char *, count+1);
362
363                         while (list) {
364                                 count--;
365                                 str[count] = list->data;
366                                 list = g_slist_delete_link(list, list);
367                         };
368                 }
369
370                 __connman_timeserver_system_set(str);
371
372                 if (str)
373                         g_strfreev(str);
374
375                 connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
376                                 CONNMAN_CLOCK_INTERFACE, "Timeservers",
377                                 DBUS_TYPE_STRING, append_timeservers, NULL);
378         } else if (g_str_equal(name, "TimeserverSynced")) {
379                 return __connman_error_permission_denied(msg);
380         } else
381                 return __connman_error_invalid_property(msg);
382
383         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
384 }
385
386 static const GDBusMethodTable clock_methods[] = {
387         { GDBUS_METHOD("GetProperties",
388                         NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
389                         get_properties) },
390         { GDBUS_METHOD("SetProperty",
391                         GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
392                         set_property)   },
393         { },
394 };
395
396 static const GDBusSignalTable clock_signals[] = {
397         { GDBUS_SIGNAL("PropertyChanged",
398                         GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
399         { },
400 };
401
402 static DBusConnection *connection = NULL;
403
404 void __connman_clock_update_timezone(void)
405 {
406         DBG("");
407
408         g_free(timezone_config);
409         timezone_config = __connman_timezone_lookup();
410
411         if (!timezone_config)
412                 return;
413
414         connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
415                                 CONNMAN_CLOCK_INTERFACE, "Timezone",
416                                 DBUS_TYPE_STRING, &timezone_config);
417 }
418
419 int __connman_clock_init(void)
420 {
421         DBG("");
422
423         connection = connman_dbus_get_connection();
424         if (!connection)
425                 return -1;
426
427         __connman_timezone_init();
428
429         timezone_config = __connman_timezone_lookup();
430
431         g_dbus_register_interface(connection, CONNMAN_MANAGER_PATH,
432                                                 CONNMAN_CLOCK_INTERFACE,
433                                                 clock_methods, clock_signals,
434                                                 NULL, NULL, NULL);
435
436         clock_properties_load();
437         return 0;
438 }
439
440 void __connman_clock_cleanup(void)
441 {
442         DBG("");
443
444         if (!connection)
445                 return;
446
447         g_dbus_unregister_interface(connection, CONNMAN_MANAGER_PATH,
448                                                 CONNMAN_CLOCK_INTERFACE);
449
450         dbus_connection_unref(connection);
451
452         __connman_timezone_cleanup();
453
454         g_free(timezone_config);
455 }