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