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