tizen 2.3 release
[framework/system/deviced.git] / src / time / time-handler.c
1 /*
2  * deviced
3  *
4  * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19
20 #include <stdio.h>
21 #include <stdbool.h>
22 #include <unistd.h>
23 #include <sys/types.h>
24 #include <errno.h>
25 #include <sys/stat.h>
26 #include <vconf.h>
27 #include <time.h>
28 #include <sys/ioctl.h>
29 #include <linux/rtc.h>
30 #include <fcntl.h>
31 #include <sys/timerfd.h>
32
33 #include "core/log.h"
34 #include "core/devices.h"
35 #include "display/poll.h"
36 #include "display/core.h"
37 #include "core/edbus-handler.h"
38 #include "core/common.h"
39 #include "core/device-notifier.h"
40
41 #define PREDEF_SET_DATETIME             "set_datetime"
42 #define PREDEF_SET_TIMEZONE             "set_timezone"
43
44 #ifndef TFD_TIMER_CANCELON_SET
45 #define TFD_TIMER_CANCELON_SET (1<<1)
46 #endif
47 #ifndef O_CLOEXEC
48 #define O_CLOEXEC       0x2000000
49 #endif
50
51 #ifndef O_NONBLOCK
52 #define O_NONBLOCK      0x4000
53 #endif
54
55 #ifndef TFD_CLOEXEC
56 #define TFD_CLOEXEC     O_CLOEXEC
57 #endif
58
59 #ifndef TFD_NONBLOCK
60 #define TFD_NONBLOCK    O_NONBLOCK
61 #endif
62
63 #define TIME_CHANGE_SIGNAL     "STimeChanged"
64
65 static const char default_rtc0[] = "/dev/rtc0";
66 static const char default_rtc1[] = "/dev/rtc1";
67 static const char default_localtime[] = "/opt/etc/localtime";
68
69 static const time_t default_time = 2147483645; // max(32bit) -3sec
70 static Ecore_Fd_Handler *tfdh = NULL; // tfd change noti
71
72 static Eina_Bool tfd_cb(void *data, Ecore_Fd_Handler * fd_handler);
73 static int timerfd_check_stop(int fd);
74 static int timerfd_check_start(void);
75
76 char *substring(const char *str, size_t begin, size_t len)
77 {
78         if (str == 0 || strlen(str) == 0 || strlen(str) < begin
79             || strlen(str) < (begin + len))
80                 return 0;
81
82         return strndup(str + begin, len);
83 }
84
85 int handle_timezone(char *str)
86 {
87         int ret;
88         struct stat sts;
89         time_t now;
90         struct tm *ts;
91         const char *sympath = default_localtime;
92
93         if (str == NULL)
94                 return -1;
95         const char *tzpath = str;
96
97         _D("TZPATH = %s", tzpath);
98
99         if (stat(tzpath, &sts) == -1 && errno == ENOENT) {
100                 _E("invalid tzpath(%s)", tzpath);
101                 return -EINVAL;
102         }
103
104         /* FIXME for debugging purpose */
105         time(&now);
106         ts = localtime(&now);
107         _D("cur local time is %s", asctime(ts));
108
109         /* unlink current link
110          * eg. rm /opt/etc/localtime */
111         if (stat(sympath, &sts) == -1 && errno == ENOENT) {
112                 /* DO NOTHING */
113         } else {
114                 ret = unlink(sympath);
115                 if (ret < 0) {
116                         _E("unlink error : [%d]%s", ret,
117                                   strerror(errno));
118                         return -1;
119                 }
120                 _D("unlink success");
121         }
122
123         /* symlink new link
124          * eg. ln -s /usr/share/zoneinfo/Asia/Seoul /opt/etc/localtime */
125         ret = symlink(tzpath, sympath);
126         if (ret < 0) {
127                 _E("symlink error : [%d]%s", ret, strerror(errno));
128                 return -1;
129         }
130         _D("symlink success");
131
132         tzset();
133
134         /* FIXME for debugging purpose */
135         ts = localtime(&now);
136         _D("new local time is %s", asctime(ts));
137         return 0;
138 }
139
140 /*
141  * TODO : error handling code should be added here.
142  */
143 int handle_date(char *str)
144 {
145         long int tmp = 0;
146         time_t timet = 0;
147         time_t before = 0;
148
149         if (str == NULL)
150                 return -1;
151
152         tmp = (long int)atoi(str);
153         timet = (time_t) tmp;
154
155         _D("ctime = %s", ctime(&timet));
156         vconf_set_int(VCONFKEY_SYSTEM_TIMECHANGE, timet);
157
158         return 0;
159 }
160
161 int set_datetime_action(int argc, char **argv)
162 {
163         int ret = 0;
164         unsigned int pm_state;
165         if (argc < 1)
166                 return -1;
167         if (vconf_get_int(VCONFKEY_PM_STATE, &ret) != 0)
168                 _E("Fail to get vconf value for pm state\n");
169         if (ret == 1)
170                 pm_state = 0x1;
171         else if (ret == 2)
172                 pm_state = 0x2;
173         else
174                 pm_state = 0x4;
175
176         pm_lock_internal(INTERNAL_LOCK_TIME, pm_state, STAY_CUR_STATE, 0);
177         ret = handle_date(argv[0]);
178         pm_unlock_internal(INTERNAL_LOCK_TIME, pm_state, STAY_CUR_STATE);
179         return ret;
180 }
181
182 int set_timezone_action(int argc, char **argv)
183 {
184         int ret;
185         unsigned int pm_state;
186         if (argc < 1)
187                 return -1;
188         if (vconf_get_int(VCONFKEY_PM_STATE, &ret) != 0)
189                 _E("Fail to get vconf value for pm state\n");
190         if (ret == 1)
191                 pm_state = 0x1;
192         else if (ret == 2)
193                 pm_state = 0x2;
194         else
195                 pm_state = 0x4;
196
197         pm_lock_internal(INTERNAL_LOCK_TIME, pm_state, STAY_CUR_STATE, 0);
198         ret = handle_timezone(argv[0]);
199         pm_unlock_internal(INTERNAL_LOCK_TIME, pm_state, STAY_CUR_STATE);
200         return ret;
201 }
202
203 static void time_changed_broadcast(void)
204 {
205         broadcast_edbus_signal(DEVICED_PATH_TIME, DEVICED_INTERFACE_TIME,
206                         TIME_CHANGE_SIGNAL, NULL, NULL);
207 }
208
209 static int timerfd_check_start(void)
210 {
211         int tfd;
212         struct itimerspec tmr;
213
214         if ((tfd = timerfd_create(CLOCK_REALTIME,TFD_NONBLOCK|TFD_CLOEXEC)) == -1) {
215                 _E("error timerfd_create() %d", errno);
216                 tfdh = NULL;
217                 return -1;
218         }
219
220         tfdh = ecore_main_fd_handler_add(tfd,ECORE_FD_READ,tfd_cb,NULL,NULL,NULL);
221         if (!tfdh) {
222                 _E("error ecore_main_fd_handler_add");
223                 return -1;
224         }
225         memset(&tmr, 0, sizeof(tmr));
226         tmr.it_value.tv_sec = default_time;
227
228         if (timerfd_settime(tfd,TFD_TIMER_ABSTIME|TFD_TIMER_CANCELON_SET,&tmr,NULL) < 0) {
229                 _E("error timerfd_settime() %d", errno);
230                 return -1;
231         }
232         return 0;
233 }
234
235 static int timerfd_check_stop(int tfd)
236 {
237         if (tfdh) {
238                 ecore_main_fd_handler_del(tfdh);
239                 tfdh = NULL;
240         }
241         if (tfd >=0) {
242                 close(tfd);
243                 tfd = -1;
244         }
245         return 0;
246 }
247
248 static Eina_Bool tfd_cb(void *data, Ecore_Fd_Handler * fd_handler)
249 {
250         int tfd = -1;
251         u_int64_t ticks;
252         int ret = -1;
253
254         if (!ecore_main_fd_handler_active_get(fd_handler,ECORE_FD_READ)) {
255                 _E("error ecore_main_fd_handler_get()");
256                 goto out;
257         }
258
259         if((tfd = ecore_main_fd_handler_fd_get(fd_handler)) == -1) {
260                 _E("error ecore_main_fd_handler_fd_get()");
261                 goto out;
262         }
263
264         ret = read(tfd,&ticks,sizeof(ticks));
265         if (ret < 0 && errno == ECANCELED) {
266                 vconf_set_int(VCONFKEY_SYSMAN_STIME, VCONFKEY_SYSMAN_STIME_CHANGED);
267                 time_changed_broadcast();
268                 timerfd_check_stop(tfd);
269                 _D("NOTIFICATION here");
270                 timerfd_check_start();
271         } else {
272                 _E("unexpected read (err:%d)", errno);
273         }
274 out:
275         return EINA_TRUE;
276 }
277
278 static DBusMessage *dbus_time_handler(E_DBus_Object *obj, DBusMessage *msg)
279 {
280         DBusError err;
281         DBusMessageIter iter;
282         DBusMessage *reply;
283         pid_t pid;
284         int ret;
285         int argc;
286         char *type_str;
287         char *argv;
288
289         dbus_error_init(&err);
290
291         if (!dbus_message_get_args(msg, &err,
292                     DBUS_TYPE_STRING, &type_str,
293                     DBUS_TYPE_INT32, &argc,
294                     DBUS_TYPE_STRING, &argv, DBUS_TYPE_INVALID)) {
295                 _E("there is no message");
296                 ret = -EINVAL;
297                 goto out;
298         }
299
300         if (argc < 0) {
301                 _E("message is invalid!");
302                 ret = -EINVAL;
303                 goto out;
304         }
305
306         pid = get_edbus_sender_pid(msg);
307         if (kill(pid, 0) == -1) {
308                 _E("%d process does not exist, dbus ignored!", pid);
309                 ret = -ESRCH;
310                 goto out;
311         }
312
313         if (strncmp(type_str, PREDEF_SET_DATETIME, strlen(PREDEF_SET_DATETIME)) == 0)
314                 ret = set_datetime_action(argc, (char **)&argv);
315         else if (strncmp(type_str, PREDEF_SET_TIMEZONE, strlen(PREDEF_SET_TIMEZONE)) == 0)
316                 ret = set_timezone_action(argc, (char **)&argv);
317 out:
318         reply = dbus_message_new_method_return(msg);
319         dbus_message_iter_init_append(reply, &iter);
320         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
321
322         return reply;
323 }
324
325 static const struct edbus_method edbus_methods[] = {
326         { PREDEF_SET_DATETIME, "sis", "i", dbus_time_handler },
327         { PREDEF_SET_TIMEZONE, "sis", "i", dbus_time_handler },
328
329 };
330
331 static int time_lcd_changed_cb(void *data)
332 {
333         int lcd_state = (int)data;
334         int tfd = -1;
335
336         if (lcd_state < S_LCDOFF)
337                 goto restart;
338
339         lcd_state = check_lcdoff_lock_state();
340         if (lcd_state || !tfdh)
341                 goto out;
342         tfd = ecore_main_fd_handler_fd_get(tfdh);
343         if (tfd == -1)
344                 goto out;
345
346         _D("stop tfd");
347         timerfd_check_stop(tfd);
348         goto out;
349 restart:
350         if (tfdh)
351                 return 0;
352         _D("restart tfd");
353         timerfd_check_start();
354 out:
355         return 0;
356 }
357
358 static void time_init(void *data)
359 {
360         int ret;
361
362         ret = register_edbus_method(DEVICED_PATH_SYSNOTI, edbus_methods, ARRAY_SIZE(edbus_methods));
363         if (ret < 0)
364                 _E("fail to init edbus method(%d)", ret);
365
366         if (timerfd_check_start() == -1) {
367                 _E("fail system time change detector init");
368         }
369         register_notifier(DEVICE_NOTIFIER_LCD, time_lcd_changed_cb);
370 }
371
372 static const struct device_ops time_device_ops = {
373         .priority = DEVICE_PRIORITY_NORMAL,
374         .name     = "time",
375         .init     = time_init,
376 };
377
378 DEVICE_OPS_REGISTER(&time_device_ops)