clock: Export information about time update method
[platform/upstream/connman.git] / src / clock.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2012  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 enum time_updates __connman_clock_timeupdates(void)
92 {
93         return time_updates_config;
94 }
95
96 static void append_timeservers(DBusMessageIter *iter, void *user_data)
97 {
98         int i;
99         char **timeservers = __connman_timeserver_system_get();
100
101         if (timeservers == NULL)
102                 return;
103
104         for (i = 0; timeservers[i] != NULL; i++) {
105                 dbus_message_iter_append_basic(iter,
106                                 DBUS_TYPE_STRING, &timeservers[i]);
107         }
108
109         g_strfreev(timeservers);
110 }
111
112 static DBusMessage *get_properties(DBusConnection *conn,
113                                         DBusMessage *msg, void *data)
114 {
115         DBusMessage *reply;
116         DBusMessageIter array, dict;
117         struct timeval tv;
118         const char *str;
119
120         DBG("conn %p", conn);
121
122         reply = dbus_message_new_method_return(msg);
123         if (reply == NULL)
124                 return NULL;
125
126         dbus_message_iter_init_append(reply, &array);
127
128         connman_dbus_dict_open(&array, &dict);
129
130         if (gettimeofday(&tv, NULL) == 0) {
131                 dbus_uint64_t val = tv.tv_sec;
132
133                 connman_dbus_dict_append_basic(&dict, "Time",
134                                                 DBUS_TYPE_UINT64, &val);
135         }
136
137         str = time_updates2string(time_updates_config);
138         if (str != NULL)
139                 connman_dbus_dict_append_basic(&dict, "TimeUpdates",
140                                                 DBUS_TYPE_STRING, &str);
141
142         if (timezone_config != NULL)
143                 connman_dbus_dict_append_basic(&dict, "Timezone",
144                                         DBUS_TYPE_STRING, &timezone_config);
145
146         str = timezone_updates2string(timezone_updates_config);
147         if (str != NULL)
148                 connman_dbus_dict_append_basic(&dict, "TimezoneUpdates",
149                                                 DBUS_TYPE_STRING, &str);
150
151         connman_dbus_dict_append_array(&dict, "Timeservers",
152                                 DBUS_TYPE_STRING, append_timeservers, NULL);
153
154         connman_dbus_dict_close(&array, &dict);
155
156         return reply;
157 }
158
159 static DBusMessage *set_property(DBusConnection *conn,
160                                         DBusMessage *msg, void *data)
161 {
162         DBusMessageIter iter, value;
163         const char *name;
164         int type;
165
166         DBG("conn %p", conn);
167
168         if (dbus_message_iter_init(msg, &iter) == FALSE)
169                 return __connman_error_invalid_arguments(msg);
170
171         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
172                 return __connman_error_invalid_arguments(msg);
173
174         dbus_message_iter_get_basic(&iter, &name);
175         dbus_message_iter_next(&iter);
176
177         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
178                 return __connman_error_invalid_arguments(msg);
179
180         dbus_message_iter_recurse(&iter, &value);
181
182         type = dbus_message_iter_get_arg_type(&value);
183
184         if (g_str_equal(name, "Time") == TRUE) {
185                 struct timeval tv;
186                 dbus_uint64_t newval;
187
188                 if (type != DBUS_TYPE_UINT64)
189                         return __connman_error_invalid_arguments(msg);
190
191                 if (time_updates_config != TIME_UPDATES_MANUAL)
192                         return __connman_error_permission_denied(msg);
193
194                 dbus_message_iter_get_basic(&value, &newval);
195
196                 tv.tv_sec = newval;
197                 tv.tv_usec = 0;
198
199                 if (settimeofday(&tv, NULL) < 0)
200                         return __connman_error_invalid_arguments(msg);
201
202                 connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
203                                 CONNMAN_CLOCK_INTERFACE, "Time",
204                                 DBUS_TYPE_UINT64, &newval);
205         } else if (g_str_equal(name, "TimeUpdates") == TRUE) {
206                 const char *strval;
207                 enum time_updates newval;
208
209                 if (type != DBUS_TYPE_STRING)
210                         return __connman_error_invalid_arguments(msg);
211
212                 dbus_message_iter_get_basic(&value, &strval);
213                 newval = string2time_updates(strval);
214
215                 if (newval == TIME_UPDATES_UNKNOWN)
216                         return __connman_error_invalid_arguments(msg);
217
218                 if (newval == time_updates_config)
219                         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
220
221                 time_updates_config = newval;
222
223                 connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
224                                 CONNMAN_CLOCK_INTERFACE, "TimeUpdates",
225                                 DBUS_TYPE_STRING, &strval);
226         } else if (g_str_equal(name, "Timezone") == TRUE) {
227                 const char *strval;
228
229                 if (type != DBUS_TYPE_STRING)
230                         return __connman_error_invalid_arguments(msg);
231
232                 if (timezone_updates_config != TIMEZONE_UPDATES_MANUAL)
233                         return __connman_error_permission_denied(msg);
234
235                 dbus_message_iter_get_basic(&value, &strval);
236
237                 if (__connman_timezone_change(strval) < 0)
238                         return __connman_error_invalid_arguments(msg);
239         } else if (g_str_equal(name, "TimezoneUpdates") == TRUE) {
240                 const char *strval;
241                 enum timezone_updates newval;
242
243                 if (type != DBUS_TYPE_STRING)
244                         return __connman_error_invalid_arguments(msg);
245
246                 dbus_message_iter_get_basic(&value, &strval);
247                 newval = string2timezone_updates(strval);
248
249                 if (newval == TIMEZONE_UPDATES_UNKNOWN)
250                         return __connman_error_invalid_arguments(msg);
251
252                 if (newval == timezone_updates_config)
253                         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
254
255                 timezone_updates_config = newval;
256
257                 connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
258                                 CONNMAN_CLOCK_INTERFACE, "TimezoneUpdates",
259                                 DBUS_TYPE_STRING, &strval);
260         } else if (g_str_equal(name, "Timeservers") == TRUE) {
261                 DBusMessageIter entry;
262                 char **str = NULL;
263                 GSList *list = NULL;
264                 int count = 0;
265
266                 if (type != DBUS_TYPE_ARRAY)
267                         return __connman_error_invalid_arguments(msg);
268
269                 dbus_message_iter_recurse(&value, &entry);
270
271                 while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
272                         const char *val;
273                         GSList *new_head;
274
275                         dbus_message_iter_get_basic(&entry, &val);
276
277                         new_head = __connman_timeserver_add_list(list, val);
278                         if (list != new_head) {
279                                 count++;
280                                 list = new_head;
281                         }
282
283                         dbus_message_iter_next(&entry);
284                 }
285
286                 if (list != NULL) {
287                         str = g_new0(char *, count+1);
288
289                         while (list != NULL) {
290                                 count--;
291                                 str[count] = list->data;
292                                 list = g_slist_delete_link(list, list);
293                         };
294                 }
295
296                 __connman_timeserver_system_set(str);
297
298                 if (str != NULL)
299                         g_strfreev(str);
300
301                 connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
302                                 CONNMAN_CLOCK_INTERFACE, "Timeservers",
303                                 DBUS_TYPE_STRING, append_timeservers, NULL);
304         } else
305                 return __connman_error_invalid_property(msg);
306
307         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
308 }
309
310 static const GDBusMethodTable clock_methods[] = {
311         { GDBUS_METHOD("GetProperties",
312                         NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
313                         get_properties) },
314         { GDBUS_METHOD("SetProperty",
315                         GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
316                         set_property)   },
317         { },
318 };
319
320 static const GDBusSignalTable clock_signals[] = {
321         { GDBUS_SIGNAL("PropertyChanged",
322                         GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
323         { },
324 };
325
326 static DBusConnection *connection = NULL;
327
328 void __connman_clock_update_timezone(void)
329 {
330         DBG("");
331
332         g_free(timezone_config);
333         timezone_config = __connman_timezone_lookup();
334
335         if (timezone_config == NULL)
336                 return;
337
338         connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
339                                 CONNMAN_CLOCK_INTERFACE, "Timezone",
340                                 DBUS_TYPE_STRING, &timezone_config);
341 }
342
343 int __connman_clock_init(void)
344 {
345         DBG("");
346
347         connection = connman_dbus_get_connection();
348         if (connection == NULL)
349                 return -1;
350
351         __connman_timezone_init();
352
353         timezone_config = __connman_timezone_lookup();
354
355         g_dbus_register_interface(connection, CONNMAN_MANAGER_PATH,
356                                                 CONNMAN_CLOCK_INTERFACE,
357                                                 clock_methods, clock_signals,
358                                                 NULL, NULL, NULL);
359
360         return 0;
361 }
362
363 void __connman_clock_cleanup(void)
364 {
365         DBG("");
366
367         if (connection == NULL)
368                 return;
369
370         g_dbus_unregister_interface(connection, CONNMAN_MANAGER_PATH,
371                                                 CONNMAN_CLOCK_INTERFACE);
372
373         dbus_connection_unref(connection);
374
375         __connman_timezone_cleanup();
376
377         g_free(timezone_config);
378 }