clock: Add support for timezone change notifications
[platform/upstream/connman.git] / src / clock.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 <sys/time.h>
27
28 #include <gdbus.h>
29
30 #include "connman.h"
31
32 enum time_updates {
33         TIME_UPDATES_UNKNOWN = 0,
34         TIME_UPDATES_MANUAL  = 1,
35         TIME_UPDATES_AUTO    = 2,
36 };
37
38 enum timezone_updates {
39         TIMEZONE_UPDATES_UNKNOWN = 0,
40         TIMEZONE_UPDATES_MANUAL  = 1,
41         TIMEZONE_UPDATES_AUTO    = 2,
42 };
43
44 static enum time_updates time_updates_config = TIME_UPDATES_AUTO;
45 static enum timezone_updates timezone_updates_config = TIMEZONE_UPDATES_AUTO;
46
47 static char *timezone_config = NULL;
48 static char **timeservers_config = NULL;
49
50 static const char *time_updates2string(enum time_updates value)
51 {
52         switch (value) {
53         case TIME_UPDATES_UNKNOWN:
54                 break;
55         case TIME_UPDATES_MANUAL:
56                 return "manual";
57         case TIME_UPDATES_AUTO:
58                 return "auto";
59         }
60
61         return NULL;
62 }
63
64 static enum time_updates string2time_updates(const char *value)
65 {
66         if (g_strcmp0(value, "manual") == 0)
67                 return TIME_UPDATES_MANUAL;
68         else if (g_strcmp0(value, "auto") == 0)
69                 return TIME_UPDATES_AUTO;
70
71         return TIME_UPDATES_UNKNOWN;
72 }
73
74 static const char *timezone_updates2string(enum timezone_updates value)
75 {
76         switch (value) {
77         case TIMEZONE_UPDATES_UNKNOWN:
78                 break;
79         case TIMEZONE_UPDATES_MANUAL:
80                 return "manual";
81         case TIMEZONE_UPDATES_AUTO:
82                 return "auto";
83         }
84
85         return NULL;
86 }
87
88 static enum timezone_updates string2timezone_updates(const char *value)
89 {
90         if (g_strcmp0(value, "manual") == 0)
91                 return TIMEZONE_UPDATES_MANUAL;
92         else if (g_strcmp0(value, "auto") == 0)
93                 return TIMEZONE_UPDATES_AUTO;
94
95         return TIMEZONE_UPDATES_UNKNOWN;
96 }
97
98 static void append_timeservers(DBusMessageIter *iter, void *user_data)
99 {
100         int i;
101
102         if (timeservers_config == NULL)
103                 return;
104
105         for (i = 0; timeservers_config[i] != NULL; i++) {
106                 dbus_message_iter_append_basic(iter,
107                                 DBUS_TYPE_STRING, &timeservers_config[i]);
108         }
109 }
110
111 static DBusMessage *get_properties(DBusConnection *conn,
112                                         DBusMessage *msg, void *data)
113 {
114         DBusMessage *reply;
115         DBusMessageIter array, dict;
116         struct timeval tv;
117         const char *str;
118
119         DBG("conn %p", conn);
120
121         reply = dbus_message_new_method_return(msg);
122         if (reply == NULL)
123                 return NULL;
124
125         dbus_message_iter_init_append(reply, &array);
126
127         connman_dbus_dict_open(&array, &dict);
128
129         if (gettimeofday(&tv, NULL) == 0) {
130                 dbus_uint64_t val = tv.tv_sec;
131
132                 connman_dbus_dict_append_basic(&dict, "Time",
133                                                 DBUS_TYPE_UINT64, &val);
134         }
135
136         str = time_updates2string(time_updates_config);
137         if (str != NULL)
138                 connman_dbus_dict_append_basic(&dict, "TimeUpdates",
139                                                 DBUS_TYPE_STRING, &str);
140
141         if (timezone_config != NULL)
142                 connman_dbus_dict_append_basic(&dict, "Timezone",
143                                         DBUS_TYPE_STRING, &timezone_config);
144
145         str = timezone_updates2string(timezone_updates_config);
146         if (str != NULL)
147                 connman_dbus_dict_append_basic(&dict, "TimezoneUpdates",
148                                                 DBUS_TYPE_STRING, &str);
149
150         connman_dbus_dict_append_array(&dict, "Timeservers",
151                                 DBUS_TYPE_STRING, append_timeservers, NULL);
152
153         connman_dbus_dict_close(&array, &dict);
154
155         return reply;
156 }
157
158 static DBusMessage *set_property(DBusConnection *conn,
159                                         DBusMessage *msg, void *data)
160 {
161         DBusMessageIter iter, value;
162         const char *name;
163         int type;
164
165         DBG("conn %p", conn);
166
167         if (dbus_message_iter_init(msg, &iter) == FALSE)
168                 return __connman_error_invalid_arguments(msg);
169
170         dbus_message_iter_get_basic(&iter, &name);
171         dbus_message_iter_next(&iter);
172         dbus_message_iter_recurse(&iter, &value);
173
174         type = dbus_message_iter_get_arg_type(&value);
175
176         if (g_str_equal(name, "Time") == TRUE) {
177                 struct timeval tv;
178                 dbus_uint64_t newval;
179
180                 if (type != DBUS_TYPE_UINT64)
181                         return __connman_error_invalid_arguments(msg);
182
183                 if (time_updates_config != TIME_UPDATES_MANUAL)
184                         return __connman_error_permission_denied(msg);
185
186                 dbus_message_iter_get_basic(&value, &newval);
187
188                 tv.tv_sec = newval;
189                 tv.tv_usec = 0;
190
191                 if (settimeofday(&tv, NULL) < 0)
192                         return __connman_error_invalid_arguments(msg);
193         } else if (g_str_equal(name, "TimeUpdates") == TRUE) {
194                 const char *strval;
195                 enum time_updates newval;
196
197                 if (type != DBUS_TYPE_STRING)
198                         return __connman_error_invalid_arguments(msg);
199
200                 dbus_message_iter_get_basic(&value, &strval);
201                 newval = string2time_updates(strval);
202
203                 if (newval == TIME_UPDATES_UNKNOWN)
204                         return __connman_error_invalid_arguments(msg);
205
206                 if (newval == time_updates_config)
207                         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
208
209                 time_updates_config = newval;
210
211                 connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
212                                 CONNMAN_CLOCK_INTERFACE, "TimeUpdates",
213                                 DBUS_TYPE_STRING, &strval);
214         } else if (g_str_equal(name, "TimezoneUpdates") == TRUE) {
215                 const char *strval;
216                 enum timezone_updates newval;
217
218                 if (type != DBUS_TYPE_STRING)
219                         return __connman_error_invalid_arguments(msg);
220
221                 dbus_message_iter_get_basic(&value, &strval);
222                 newval = string2timezone_updates(strval);
223
224                 if (newval == TIMEZONE_UPDATES_UNKNOWN)
225                         return __connman_error_invalid_arguments(msg);
226
227                 if (newval == timezone_updates_config)
228                         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
229
230                 timezone_updates_config = newval;
231
232                 connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
233                                 CONNMAN_CLOCK_INTERFACE, "TimezoneUpdates",
234                                 DBUS_TYPE_STRING, &strval);
235         } else if (g_str_equal(name, "Timeservers") == TRUE) {
236                 DBusMessageIter entry;
237                 GString *str;
238
239                 if (type != DBUS_TYPE_ARRAY)
240                         return __connman_error_invalid_arguments(msg);
241
242                 str = g_string_new(NULL);
243                 if (str == NULL)
244                         return __connman_error_invalid_arguments(msg);
245
246                 dbus_message_iter_recurse(&value, &entry);
247
248                 while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
249                         const char *val;
250
251                         dbus_message_iter_get_basic(&entry, &val);
252                         dbus_message_iter_next(&entry);
253
254                         if (str->len > 0)
255                                 g_string_append_printf(str, " %s", val);
256                         else
257                                 g_string_append(str, val);
258                 }
259
260                 g_strfreev(timeservers_config);
261
262                 if (str->len > 0)
263                         timeservers_config = g_strsplit_set(str->str, " ", 0);
264                 else
265                         timeservers_config = NULL;
266
267                 g_string_free(str, TRUE);
268
269                 connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
270                                 CONNMAN_CLOCK_INTERFACE, "Timeservers",
271                                 DBUS_TYPE_STRING, append_timeservers, NULL);
272         } else
273                 return __connman_error_invalid_property(msg);
274
275         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
276 }
277
278 static GDBusMethodTable clock_methods[] = {
279         { "GetProperties", "",   "a{sv}", get_properties },
280         { "SetProperty",   "sv", "",      set_property   },
281         { },
282 };
283
284 static GDBusSignalTable clock_signals[] = {
285         { "PropertyChanged", "sv" },
286         { },
287 };
288
289 static DBusConnection *connection = NULL;
290
291 void __connman_clock_update_timezone(void)
292 {
293         DBG("");
294
295         g_free(timezone_config);
296         timezone_config = __connman_timezone_lookup();
297
298         if (timezone_config == NULL)
299                 return;
300
301         connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
302                                 CONNMAN_CLOCK_INTERFACE, "Timezone",
303                                 DBUS_TYPE_STRING, &timezone_config);
304 }
305
306 int __connman_clock_init(void)
307 {
308         DBG("");
309
310         connection = connman_dbus_get_connection();
311         if (connection == NULL)
312                 return -1;
313
314         __connman_timezone_init();
315
316         timezone_config = __connman_timezone_lookup();
317
318         g_dbus_register_interface(connection, CONNMAN_MANAGER_PATH,
319                                                 CONNMAN_CLOCK_INTERFACE,
320                                                 clock_methods, clock_signals,
321                                                 NULL, NULL, NULL);
322
323         return 0;
324 }
325
326 void __connman_clock_cleanup(void)
327 {
328         DBG("");
329
330         if (connection == NULL)
331                 return;
332
333         g_dbus_unregister_interface(connection, CONNMAN_MANAGER_PATH,
334                                                 CONNMAN_CLOCK_INTERFACE);
335
336         dbus_connection_unref(connection);
337
338         __connman_timezone_cleanup();
339
340         g_free(timezone_config);
341         g_strfreev(timeservers_config);
342 }