Initialize Tizen 2.3
[framework/system/deviced.git] / src / storage / storage.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 <fcntl.h>
21 #include <assert.h>
22 #include <limits.h>
23 #include <vconf.h>
24 #include <sys/types.h>
25 #include <sys/statvfs.h>
26 #include <sys/stat.h>
27 #include <sys/shm.h>
28 #include <time.h>
29 #include <storage.h>
30
31 #include "device-node.h"
32 #include "core/log.h"
33 #include "core/devices.h"
34 #include "core/common.h"
35 #include "core/edbus-handler.h"
36 #include "core/device-notifier.h"
37 #include "core/config-parser.h"
38
39 #define MEMNOTIFY_NORMAL        0x0000
40 #define MEMNOTIFY_LOW           0xfaac
41 #define MEMNOTIFY_CRITICAL      0xdead
42 #define MEMNOTIFY_REBOOT        0xb00f
43
44 #define MEMORY_STATUS_USR_PATH  "/opt/usr"
45 #define MEMORY_MEGABYTE_VALUE   1048576
46
47 #define MEMNOTI_WARNING_VALUE  (5) // 5% under
48 #define MEMNOTI_CRITICAL_VALUE (0.1) // 0.1% under
49 #define MEMNOTI_FULL_VALUE     (0.0) // 0.0% under
50
51 #define SIGNAL_LOWMEM_STATE     "ChangeState"
52 #define SIGNAL_LOWMEM_FULL      "Full"
53
54 #define POPUP_KEY_MEMNOTI       "_MEM_NOTI_"
55 #define POPUP_KEY_APPNAME       "_APP_NAME_"
56
57 #define LOWMEM_POPUP_NAME       "lowmem-syspopup"
58
59 #define MEMNOTI_TIMER_INTERVAL  5
60 #define MEM_TRIM_TIMER_INTERVAL 86400 /* 24 hour */
61 #define MEM_FSTRIM_PATH         "/sbin/fstrim"
62
63 #define MEM_TRIM_START_TIME     2 // AM 02:00:00
64 #define MIN_SEC                 (60)
65 #define HOUR_SEC                (MIN_SEC * MIN_SEC)
66
67 #define BUF_MAX                 1024
68
69 #define STORAGE_CONF_FILE       "/etc/deviced/storage.conf"
70
71 enum memnoti_level {
72         MEMNOTI_LEVEL_CRITICAL = 0,
73         MEMNOTI_LEVEL_WARNING,
74         MEMNOTI_LEVEL_NORMAL,
75 } ;
76
77 struct popup_data {
78         char *name;
79         char *key;
80         char *value;
81 };
82
83 struct storage_config_info {
84         double warning_level;
85         double critical_level;
86         double full_level;
87 };
88
89 static Ecore_Fd_Handler *lowmem_efd = NULL;
90 static int lowmem_fd;
91 static int cur_mem_state = MEMNOTIFY_NORMAL;
92
93 static Ecore_Timer *memnoti_timer = NULL;
94 static Ecore_Timer *mem_trim_timer = NULL;
95
96 static struct storage_config_info storage_info = {
97         .warning_level   = MEMNOTI_WARNING_VALUE,
98         .critical_level = MEMNOTI_CRITICAL_VALUE,
99         .full_level      = MEMNOTI_FULL_VALUE,
100 };
101
102 static void memnoti_send_broadcast(int status)
103 {
104         static int old = 0;
105         char *arr[1];
106         char str_status[32];
107
108         if (old == status)
109                 return;
110
111         old = status;
112         snprintf(str_status, sizeof(str_status), "%d", status);
113         arr[0] = str_status;
114         broadcast_edbus_signal(DEVICED_PATH_LOWMEM, DEVICED_INTERFACE_LOWMEM,
115                         SIGNAL_LOWMEM_STATE, "i", arr);
116 }
117
118 static void memnoti_level_broadcast(enum memnoti_level level)
119 {
120         static int status = 0;
121         if (level == MEMNOTI_LEVEL_CRITICAL && status == 0)
122                 status = 1;
123         else if (level != MEMNOTI_LEVEL_CRITICAL && status == 1)
124                 status = 0;
125         else
126                 return;
127         _D("send user mem noti : %d %d", level, status);
128         memnoti_send_broadcast(status);
129 }
130
131 static int memnoti_popup(enum memnoti_level level)
132 {
133         int ret = -1;
134         int val = -1;
135         char *value = NULL;
136         struct popup_data *params;
137         static const struct device_ops *apps = NULL;
138
139         if (level != MEMNOTI_LEVEL_WARNING && level != MEMNOTI_LEVEL_CRITICAL) {
140                 _E("level check error : %d",level);
141                 return 0;
142         }
143
144         if (level == MEMNOTI_LEVEL_WARNING) {
145                 value = "warning";
146         } else if (level == MEMNOTI_LEVEL_CRITICAL) {
147                 value = "critical";
148         }
149
150         ret = vconf_get_int(VCONFKEY_STARTER_SEQUENCE, &val);
151         if (val == 0 || ret != 0)
152                 goto out;
153
154         FIND_DEVICE_INT(apps, "apps");
155
156         params = malloc(sizeof(struct popup_data));
157         if (params == NULL) {
158                 _E("Malloc failed");
159                 return -1;
160         }
161         params->name = LOWMEM_POPUP_NAME;
162         params->key = POPUP_KEY_MEMNOTI;
163         params->value = strdup(value);
164         apps->init((void *)params);
165         free(params);
166         return 0;
167 out:
168         return -1;
169 }
170
171 static enum memnoti_level check_memnoti_level(double total, double avail)
172 {
173         double tmp_size = (avail/total)*100;
174
175         if (tmp_size > storage_info.warning_level)
176                 return MEMNOTI_LEVEL_NORMAL;
177         if (tmp_size > storage_info.critical_level)
178                 return MEMNOTI_LEVEL_WARNING;
179         return MEMNOTI_LEVEL_CRITICAL;
180 }
181
182 static void memnoti_full_broadcast(double total, double avail)
183 {
184         static int status = 0;
185         int tmp = 0;
186         double tmp_size = (avail/total)*100;
187         char *arr[1];
188         char str_status[32];
189
190         tmp = status;
191         if (tmp_size <= storage_info.full_level && status == 0)
192                 status = 1;
193         else if (tmp_size > storage_info.full_level && status == 1)
194                 status = 0;
195         if (status == tmp)
196                 return;
197
198         _D("send memory full noti : %d (total: %4.4lf avail: %4.4lf)", status, total, avail);
199         snprintf(str_status, sizeof(str_status), "%d", status);
200         arr[0] = str_status;
201         broadcast_edbus_signal(DEVICED_PATH_LOWMEM, DEVICED_INTERFACE_LOWMEM,
202                         SIGNAL_LOWMEM_FULL, "i", arr);
203 }
204
205 static void memory_status_set_full_mem_size(void)
206 {
207         struct statvfs s;
208         double dTotal = 0.0;
209         double dAvail = 0.0;
210
211         storage_get_internal_memory_size(&s);
212         dTotal = (double)s.f_frsize * s.f_blocks;
213         dAvail = (double)s.f_bsize * s.f_bavail;
214
215         storage_info.full_level += (MEMORY_MEGABYTE_VALUE/dTotal)*100;
216         _I("full : %4.4lf avail : %4.4lf warning : %4.4lf critical : %4.4lf",
217                 storage_info.full_level, (dAvail*100/dTotal),
218                 storage_info.warning_level, storage_info.critical_level);
219 }
220
221 static Eina_Bool memory_status_get_available_size(void *data)
222 {
223         static enum memnoti_level old = MEMNOTI_LEVEL_NORMAL;
224         enum memnoti_level now;
225         int ret;
226         struct statvfs s;
227         double dAvail = 0.0;
228         double dTotal = 0.0;
229
230         storage_get_internal_memory_size(&s);
231         dTotal = (double)s.f_frsize * s.f_blocks;
232         dAvail = (double)s.f_bsize * s.f_bavail;
233
234         memnoti_full_broadcast(dTotal, dAvail);
235
236         now = check_memnoti_level(dTotal, dAvail);
237
238         memnoti_level_broadcast(now);
239
240         if (now < MEMNOTI_LEVEL_NORMAL && now < old) {
241                 ret = memnoti_popup(now);
242                 if (ret != 0)
243                         now = MEMNOTI_LEVEL_NORMAL;
244         }
245         old = now;
246         if (memnoti_timer)
247                 ecore_timer_interval_set(memnoti_timer, MEMNOTI_TIMER_INTERVAL);
248 out:
249         return EINA_TRUE;
250 }
251
252 static int __memnoti_fd_init(void)
253 {
254         memory_status_set_full_mem_size();
255         memory_status_get_available_size(NULL);
256         memnoti_timer = ecore_timer_add(MEMNOTI_TIMER_INTERVAL,
257                                 memory_status_get_available_size, NULL);
258         if (memnoti_timer == NULL)
259             _E("fail mem available noti timer add");
260         return 0;
261 }
262
263 static Eina_Bool memory_trim_cb(void *data)
264 {
265         ecore_timer_interval_set(memnoti_timer, MEM_TRIM_TIMER_INTERVAL);
266         if (launch_if_noexist(MEM_FSTRIM_PATH, MEMORY_STATUS_USR_PATH) == -1) {
267                 _E("fail to launch fstrim");
268         } else {
269                 _D("fs memory trim is operated");
270         }
271         return EINA_TRUE;
272 }
273
274 static int __mem_trim_delta(struct tm *cur_tm)
275 {
276         int delta = 0;
277         int sign_val;
278
279         if (cur_tm->tm_hour < MEM_TRIM_START_TIME)
280                 sign_val = 1;
281         else
282                 sign_val = -1;
283         delta += ((sign_val) * (MEM_TRIM_START_TIME - cur_tm->tm_hour) * HOUR_SEC);
284         delta -= ((sign_val) * (cur_tm->tm_min * MIN_SEC + cur_tm->tm_sec));
285         return delta;
286 }
287
288 static int __run_mem_trim(void)
289 {
290         time_t now;
291         struct tm *cur_tm;
292         int mem_trim_time;
293
294         now = time(NULL);
295         cur_tm = (struct tm *)malloc(sizeof(struct tm));
296         if (cur_tm == NULL) {
297                 _E("Fail to memory allocation");
298                 return -1;
299         }
300
301         if (localtime_r(&now, cur_tm) == NULL) {
302                 _E("Fail to get localtime");
303                 free(cur_tm);
304                 return -1;
305         }
306
307         mem_trim_time = MEM_TRIM_TIMER_INTERVAL + __mem_trim_delta(cur_tm);
308         _D("start mem trim timer", mem_trim_time);
309         mem_trim_timer = ecore_timer_add(mem_trim_time, memory_trim_cb, NULL);
310         if (mem_trim_timer == NULL) {
311                 _E("Fail to add mem trim timer");
312                 free(cur_tm);
313                 return -1;
314         }
315         free(cur_tm);
316         return 0;
317 }
318
319 static DBusMessage *edbus_getstatus(E_DBus_Object *obj, DBusMessage *msg)
320 {
321         DBusMessageIter iter;
322         DBusMessage *reply;
323         int ret;
324         struct statvfs s;
325         double dAvail = 0.0;
326         double dTotal = 0.0;
327
328         storage_get_internal_memory_size(&s);
329         dTotal = (double)s.f_frsize * s.f_blocks;
330         dAvail = (double)s.f_bsize * s.f_bavail;
331
332         reply = dbus_message_new_method_return(msg);
333         dbus_message_iter_init_append(reply, &iter);
334         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT64, &dTotal);
335         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT64, &dAvail);
336         return reply;
337 }
338
339 static DBusMessage *edbus_memtrim(E_DBus_Object *obj, DBusMessage *msg)
340 {
341         DBusMessageIter iter;
342         DBusMessage *reply;
343         int ret;
344
345         ret = launch_if_noexist(MEM_FSTRIM_PATH, MEMORY_STATUS_USR_PATH);
346         if (ret == -1) {
347                 _E("fail to launch fstrim");
348         } else {
349                 _D("fs memory trim is operated");
350         }
351
352         reply = dbus_message_new_method_return(msg);
353         dbus_message_iter_init_append(reply, &iter);
354         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
355         return reply;
356 }
357
358 static const struct edbus_method edbus_methods[] = {
359         { "getstorage",       NULL,   "i", edbus_getstatus },
360         { "MemTrim",       NULL,   "i", edbus_memtrim },
361         /* Add methods here */
362 };
363
364 static int booting_done(void *data)
365 {
366         static int done = 0;
367
368         if (data != NULL) {
369                 done = (int)data;
370                 if (done)
371                         _I("booting done");
372                 if (__memnoti_fd_init() == -1)
373                         _E("fail remain mem noti control fd init");
374         }
375         return done;
376 }
377
378 static int lowmem_poweroff(void *data)
379 {
380         if (memnoti_timer) {
381                 ecore_timer_del(memnoti_timer);
382                 memnoti_timer = NULL;
383         }
384         if (mem_trim_timer) {
385                 ecore_timer_del(mem_trim_timer);
386                 mem_trim_timer = NULL;
387         }
388         return 0;
389 }
390
391 static int load_config(struct parse_result *result, void *user_data)
392 {
393         struct storage_config_info *info = (struct storage_config_info *)user_data;
394         char *name;
395         char *value;
396
397         if (!info)
398                 return -EINVAL;
399
400         if (!MATCH(result->section, "LOWSTORAGE"))
401                 return -EINVAL;
402
403         name = result->name;
404         value = result->value;
405
406         if (MATCH(name, "WARNING_LEVEL"))
407                 info->warning_level = (double)atof(value);
408         else if (MATCH(name, "CRITICAL_LEVEL"))
409                 info->critical_level = (double)atof(value);
410         else if (MATCH(name, "FULL_LEVEL"))
411                 info->full_level = (double)atof(value);
412
413         return 0;
414 }
415
416 static void storage_config_load(struct storage_config_info *info)
417 {
418         int ret;
419
420         ret = config_parse(STORAGE_CONF_FILE, load_config, info);
421         if (ret < 0)
422                 _E("Failed to load %s, %d Use default value!", STORAGE_CONF_FILE, ret);
423 }
424
425 static void lowmem_init(void *data)
426 {
427         int ret;
428
429         storage_config_load(&storage_info);
430         register_notifier(DEVICE_NOTIFIER_BOOTING_DONE, booting_done);
431         register_notifier(DEVICE_NOTIFIER_POWEROFF, lowmem_poweroff);
432         ret = register_edbus_method(DEVICED_PATH_STORAGE, edbus_methods, ARRAY_SIZE(edbus_methods));
433         if (ret < 0)
434                 _E("fail to init edbus method(%d)", ret);
435
436         if (__run_mem_trim() < 0) {
437                 _E("fail mem trim timer start");
438         }
439 }
440
441 static void lowmem_exit(void *data)
442 {
443         unregister_notifier(DEVICE_NOTIFIER_BOOTING_DONE, booting_done);
444         unregister_notifier(DEVICE_NOTIFIER_POWEROFF, lowmem_poweroff);
445 }
446
447 static const struct device_ops lowmem_device_ops = {
448         .priority = DEVICE_PRIORITY_NORMAL,
449         .name     = "lowmem",
450         .init     = lowmem_init,
451         .exit     = lowmem_exit,
452 };
453
454 DEVICE_OPS_REGISTER(&lowmem_device_ops)