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