99dc51668259f8085158f356eea8edc96faeff5c
[platform/core/system/storaged.git] / src / storage / storage.c
1 /*
2  * storaged
3  *
4  * Copyright (c) 2012 - 2017 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 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <stdarg.h>
23 #include <fcntl.h>
24 #include <assert.h>
25 #include <limits.h>
26 #include <vconf.h>
27 #include <sys/types.h>
28 #include <sys/statvfs.h>
29 #include <sys/stat.h>
30 #include <sys/shm.h>
31 #include <time.h>
32 #include <storage.h>
33 #include <tzplatform_config.h>
34 #include <glib.h>
35 #include <libsyscommon/libgdbus.h>
36 #include <libsyscommon/ini-parser.h>
37 #include <device/board-internal.h>
38 #include <ctype.h>
39
40 #include "log.h"
41 #include "module-intf.h"
42 #include "storaged_common.h"
43 #include "cleanup.h"
44 #include "udev.h"
45
46 #define MEMORY_STATUS_TMP_PATH  "/tmp"
47 #define MEMORY_STATUS_OPT_PATH  "/opt"
48 #define MEMNOTI_TMP_CRITICAL_VALUE (20)
49
50 #define MEMORY_MEGABYTE_VALUE   1048576
51
52 #define MEMNOTI_WARNING_VALUE  (5) /* 5% under */
53 #define MEMNOTI_CRITICAL_VALUE (0.1) /* 0.1% under */
54 #define MEMNOTI_FULL_VALUE     (0.0) /* 0.0% under */
55
56 #define SIGNAL_NEED_CLEANUP     "NeedCleanup"
57 #define MEMNOTI_TIMER_INTERVAL  5000 /* milliseconds */
58
59 #define SIGNAL_POWEROFF_STATE   "ChangeState"
60
61 #define STORAGE_CONF_FILE       "/etc/storaged/storage.conf"
62
63 #define NEED_CLEANUP_DIR_PATH   "/run/storaged/needcleanup"
64 #define NEED_CLEANUP_FILE_PATH  "/run/storaged/needcleanup/trigger"
65
66 #define DM_ROOT_NODE_NAME       "rootfs"
67
68 #define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0]))
69
70 #define LOW_STORAGE_WARNING                     "lowstorage_warning"
71 #define LOW_STORAGE_CRITICAL                    "lowstorage_critical"
72 #define LOW_STORAGE_FULL                        "lowstorage_full"
73 #define LOW_STORAGE_REMOVE                      "lowstorage_remove"
74
75 #define INTERNAL_STORAGE_NOTION                 "InternalStorageNotiOn"
76 #define INTERNAL_STORAGE_NOTIOFF                "InternalStorageNotiOff"
77
78 #define RECOVERY_ACTION_IGNORE_STR                "ignore"
79 #define RECOVERY_ACTION_REBOOT_RECOVERY_STR       "reboot,recovery"
80 #define ROOTFS_DMVERITY_CORRUPTED_COUNT_DEFUALT   (1)
81 #define ROOTFS_DMVERITY_CORRUPTED_TIMEOUT_DEFUALT (0)
82
83 #define PARTITION_STATUS_OK "ok"
84 #define PARTITION_STATUS_BUFFER_LEN 16
85
86 enum memnoti_level {
87         MEMNOTI_LEVEL_FULL = 0,
88         MEMNOTI_LEVEL_CRITICAL,
89         MEMNOTI_LEVEL_WARNING,
90         MEMNOTI_LEVEL_NORMAL,
91 };
92
93 enum memnoti_status {
94         MEMNOTI_DISABLE,
95         MEMNOTI_ENABLE,
96 };
97
98 enum memory_id {
99         MEMORY_INTERNAL = 0,
100         MEMORY_TMP,
101         MEMORY_OPT,
102 };
103
104 struct storage_config_info {
105         enum memory_id mem_id;
106         enum memnoti_level current_noti_level;
107         double warning_level;
108         double critical_level;
109         double full_level;
110 };
111
112 enum recovery_action {
113         RECOVERY_ACTION_IGNORE = 0,
114         RECOVERY_ACTION_REBOOT_RECOVERY,
115 };
116
117 struct storage_config_rootfs_recovery {
118         enum recovery_action action;
119         int dm_verity_corrupted_count;
120         int dm_verity_corrupted_timeout;
121 };
122
123 static guint memnoti_timer;
124 static int noti_id;
125
126 static guint id_storage_poweroff;
127
128 static struct storage_config_info storage_internal_info = {
129         .mem_id             = MEMORY_INTERNAL,
130         .current_noti_level = MEMNOTI_LEVEL_NORMAL,
131         .warning_level      = MEMNOTI_WARNING_VALUE,
132         .critical_level     = MEMNOTI_CRITICAL_VALUE,
133         .full_level         = MEMNOTI_FULL_VALUE,
134 };
135
136 static struct storage_config_info storage_tmp_info = {
137         .mem_id             = MEMORY_TMP,
138         .current_noti_level = MEMNOTI_LEVEL_NORMAL,
139         .warning_level      = MEMNOTI_TMP_CRITICAL_VALUE,
140         .critical_level     = MEMNOTI_TMP_CRITICAL_VALUE,
141         .full_level         = MEMNOTI_FULL_VALUE,
142 };
143
144 static struct storage_config_info storage_opt_info = {
145         .mem_id             = MEMORY_OPT,
146         .current_noti_level = MEMNOTI_LEVEL_NORMAL,
147         .warning_level      = MEMNOTI_WARNING_VALUE,
148         .critical_level     = MEMNOTI_CRITICAL_VALUE,
149         .full_level         = MEMNOTI_FULL_VALUE,
150 };
151
152 static char *recovery_action_str[] = {
153         [RECOVERY_ACTION_IGNORE] = RECOVERY_ACTION_IGNORE_STR,
154         [RECOVERY_ACTION_REBOOT_RECOVERY] = RECOVERY_ACTION_REBOOT_RECOVERY_STR,
155 };
156 static struct storage_config_rootfs_recovery storage_rootfs_recovery_info = {
157         .action = RECOVERY_ACTION_IGNORE,
158         .dm_verity_corrupted_count = ROOTFS_DMVERITY_CORRUPTED_COUNT_DEFUALT,
159         .dm_verity_corrupted_timeout = ROOTFS_DMVERITY_CORRUPTED_TIMEOUT_DEFUALT,
160 };
161 static guint poweroff_g_timeout_event_source_id = 0;
162
163 static void dm_verity_uevent_block_handler(struct udev_device *dev);
164 static struct uevent_handler dm_verity_uh = {
165         .subsystem = BLOCK_SUBSYSTEM,
166         .uevent_func = dm_verity_uevent_block_handler,
167 };
168
169 static void write_file(void)
170 {
171         FILE *fp;
172
173         fp = fopen(NEED_CLEANUP_FILE_PATH, "w+");
174         if (fp) {
175                 fprintf(fp, "needcleanup\n");
176                 fclose(fp);
177         } else
178                 _E("Failed to open '%s'.", NEED_CLEANUP_FILE_PATH);
179 }
180
181 static void memcleanup_send_broadcast(struct storage_config_info *info, enum memnoti_level level, enum memnoti_level prev_level)
182 {
183         time_t t;
184         struct tm timeinfo;
185         enum tzplatform_variable path;
186         char *value;
187         char buf[20];
188         int ret_dbus;
189
190         if (info->mem_id == MEMORY_INTERNAL)
191                 path = TZ_SYS_USER;
192         else if (info->mem_id == MEMORY_TMP)
193                 path = TZ_SYS_TMP;
194         else if (info->mem_id == MEMORY_OPT)
195                 path = TZ_SYS_OPT;
196         else
197                 return;
198
199         if (prev_level <= level)
200                 return;
201
202         if (level == MEMNOTI_LEVEL_WARNING)
203                 value = "Warning";
204         else if (level == MEMNOTI_LEVEL_CRITICAL)
205                 value = "Critical";
206         else if (level == MEMNOTI_LEVEL_FULL)
207                 value = "Full";
208         else
209                 return;
210
211         write_file();
212
213         t = time(NULL);
214         if (localtime_r(&t, &timeinfo) == NULL) {
215                 _E("Failed to localtime_r.");
216                 goto out;
217         }
218
219         strftime(buf, sizeof(buf), "%b %d %T", &timeinfo);
220         _D("time=%s path=%d level=%s", buf, path, value);
221
222 out:
223         ret_dbus = gdbus_signal_emit(NULL,
224                                 STORAGED_PATH_LOWMEM,
225                                 STORAGED_INTERFACE_LOWMEM,
226                                 SIGNAL_NEED_CLEANUP,
227                                 g_variant_new("(is)", path, value));
228         if (ret_dbus < 0)
229                 _E("Failed to send dbus signal");
230
231         cleanup_storage(path, level);
232 }
233
234 static void _popup_cb(GVariant *var, void *user_data, GError *err)
235 {
236          int ret_val;
237
238         if (!var) {
239                 _E("No message: %s", err->message);
240                 return;
241         }
242
243         if (!g_variant_get_safe(var, "(i)", &ret_val)) {
244                 _E("No message: %s", g_variant_get_type_string(var));
245                 goto out;
246         }
247
248         _D("Reply value: %d", ret_val);
249
250 out:
251         g_variant_unref(var);
252 }
253
254 static int launch_memory_popup(int num, ...)
255 {
256         int ret;
257         va_list args;
258
259         va_start(args, num);
260
261         ret = gdbus_call_pairs_async_with_reply(POPUP_BUS_NAME,
262                         POPUP_PATH_SYSTEM,
263                         POPUP_INTERFACE_SYSTEM,
264                         "PopupLaunch",
265                         num,
266                         args,
267                         _popup_cb,
268                         -1,
269                         NULL);
270
271         va_end(args);
272
273         return ret;
274 }
275
276 static void _noti_cb(GVariant *var, void *user_data, GError *err)
277 {
278          int ret_val;
279
280         if (!var) {
281                 _E("No message: %s", err->message);
282                 return;
283         }
284
285         if (!g_variant_get_safe(var, "(i)", &ret_val)) {
286                 _E("No message: %s", g_variant_get_type_string(var));
287                 goto out;
288         }
289
290         noti_id = ret_val;
291         _D("Reply value: %d", ret_val);
292
293 out:
294         g_variant_unref(var);
295 }
296
297 static int remove_notification(void)
298 {
299         int ret;
300
301         ret = gdbus_call_sync_with_reply_int(POPUP_BUS_NAME,
302                 POPUP_PATH_NOTI,
303                 POPUP_INTERFACE_NOTI,
304                 INTERNAL_STORAGE_NOTIOFF,
305                 g_variant_new("(i)", noti_id),
306                 NULL);
307         if (ret < 0)
308                 _E("Failed to remove noti(%d).", noti_id);
309
310         return ret;
311 }
312
313 static int create_notification(void)
314 {
315         int ret;
316
317         ret = gdbus_call_async_with_reply(POPUP_BUS_NAME,
318                 POPUP_PATH_NOTI,
319                 POPUP_INTERFACE_NOTI,
320                 INTERNAL_STORAGE_NOTION,
321                 NULL,
322                 _noti_cb,
323                 -1,
324                 NULL);
325         if (ret < 0)
326                 _E("Failed to create noti.");
327
328         return ret;
329 }
330
331 static int launch_memory_notification(char *type)
332 {
333         int ret = -1;
334
335         if (!type)
336                 return -EINVAL;
337
338         if (!strncmp(type, INTERNAL_STORAGE_NOTION, sizeof(INTERNAL_STORAGE_NOTION))) {
339                 if (noti_id) {
340                         ret = remove_notification();
341                         if (ret < 0)
342                                 return ret;
343                         noti_id = 0;
344                 }
345
346                 ret = create_notification();
347         } else if (!strncmp(type, INTERNAL_STORAGE_NOTIOFF, sizeof(INTERNAL_STORAGE_NOTIOFF))) {
348                 if (!noti_id)
349                         return -1;
350
351                 ret = remove_notification();
352         } else
353                 _E("Invalid noti type(%s).", type);
354
355         return ret;
356 }
357
358 static int process_memory_info(enum memnoti_level level)
359 {
360         int ret = -1;
361         int val = -1;
362         char *popup_value = NULL;
363         char *noti_value = NULL;
364
365         if (level < 0 || level > MEMNOTI_LEVEL_NORMAL) {
366                 _E("Failed to check level(%d).", level);
367                 return 0;
368         }
369
370         if (level == MEMNOTI_LEVEL_WARNING) {
371                 popup_value = LOW_STORAGE_WARNING;
372                 noti_value = INTERNAL_STORAGE_NOTION;
373         } else if (level == MEMNOTI_LEVEL_CRITICAL) {
374                 popup_value = LOW_STORAGE_CRITICAL;
375                 noti_value = INTERNAL_STORAGE_NOTION;
376         } else if (level == MEMNOTI_LEVEL_FULL) {
377                 popup_value = LOW_STORAGE_FULL;
378                 noti_value = INTERNAL_STORAGE_NOTION;
379         } else if (level == MEMNOTI_LEVEL_NORMAL) {
380                 popup_value = LOW_STORAGE_REMOVE;
381                 noti_value = INTERNAL_STORAGE_NOTIOFF;
382         }
383
384         ret = vconf_get_int(VCONFKEY_STARTER_SEQUENCE, &val);
385         if (val == 0 || ret != 0)
386                 return -1;
387
388         if (!popup_value && !noti_value) {
389                 _E("Invalid memnoti level(%d).", level);
390                 return 0;
391         }
392
393         ret = launch_memory_popup(2, "_SYSPOPUP_CONTENT_", popup_value);
394         if (ret < 0)
395                 _E("Failed to launch momory popup: %d", ret);
396
397         ret = launch_memory_notification(noti_value);
398         if (ret < 0)
399                 _E("Failed to launch momory notification: %d", ret);
400
401         return ret;
402 }
403
404 static void storage_status_broadcast(struct storage_config_info *info, double total, double avail)
405 {
406         double level = (avail/total)*100;
407         enum memnoti_level prev_noti_level;
408
409         prev_noti_level = info->current_noti_level;
410         if (level <= info->full_level) {
411                 if (info->current_noti_level == MEMNOTI_LEVEL_FULL)
412                         return;
413                 info->current_noti_level = MEMNOTI_LEVEL_FULL;
414
415                 memcleanup_send_broadcast(info, info->current_noti_level, prev_noti_level);
416
417                 _I("Id=%d current level=%4.4lf warning=%4.4lf critical=%4.4lf full=%4.4lf",
418                         info->mem_id, level, info->warning_level, info->critical_level, info->full_level);
419                 return;
420         }
421
422         if (level <= info->critical_level) {
423                 if (info->current_noti_level == MEMNOTI_LEVEL_CRITICAL)
424                         return;
425                 info->current_noti_level = MEMNOTI_LEVEL_CRITICAL;
426
427                 memcleanup_send_broadcast(info, info->current_noti_level, prev_noti_level);
428
429                 _I("Id=%d current level=%4.4lf warning=%4.4lf critical=%4.4lf full=:%4.4lf",
430                         info->mem_id, level, info->warning_level, info->critical_level, info->full_level);
431                 return;
432         }
433
434         if (level <= info->warning_level) {
435                 info->current_noti_level = MEMNOTI_LEVEL_WARNING;
436                 _I("Id=%d current level=%4.4lf warning=%4.4lf critical=%4.4lf full=%4.4lf",
437                         info->mem_id, level, info->warning_level, info->critical_level, info->full_level);
438
439                 memcleanup_send_broadcast(info, info->current_noti_level, prev_noti_level);
440         } else
441                 info->current_noti_level = MEMNOTI_LEVEL_NORMAL;
442 }
443
444 static int storage_get_memory_size(const char *path, struct statvfs *s)
445 {
446         int ret_val;
447
448         if (!path) {
449                 _E("Input param error.");
450                 return -EINVAL;
451         }
452
453         ret_val = statvfs(path, s);
454         if (ret_val) {
455                 _E("Failed to get storage size.");
456                 return -errno;
457         }
458
459         return 0;
460 }
461
462 static void get_storage_status(const char *path, struct statvfs *s)
463 {
464         if (strcmp(path, tzplatform_getenv(TZ_SYS_USER)) == 0)
465                 storage_get_internal_memory_size(s);
466         else
467                 storage_get_memory_size(path, s);
468 }
469
470 static void init_storage_config_info(const char *path, struct storage_config_info *info)
471 {
472         struct statvfs s;
473         double dAvail = 0.0;
474         double dTotal = 0.0;
475
476         get_storage_status(path, &s);
477
478         dTotal = (double)s.f_frsize * s.f_blocks;
479         dAvail = (double)s.f_bsize * s.f_bavail;
480
481         info->full_level += (MEMORY_MEGABYTE_VALUE/dTotal)*100;
482
483         _I("'%s' t=%4.0lf a=%4.0lf(%4.2lf) w=%4.4lf c=%4.4lf f=%4.4lf",
484                 path, dTotal, dAvail, (dAvail*100/dTotal), info->warning_level, info->critical_level, info->full_level);
485 }
486
487 static void check_internal_storage(struct storage_config_info *info)
488 {
489         static enum memnoti_level old = MEMNOTI_LEVEL_NORMAL;
490         int ret_val;
491
492         if (info->current_noti_level < MEMNOTI_LEVEL_NORMAL && info->current_noti_level < old) {
493                 ret_val = process_memory_info(info->current_noti_level);
494                 if (ret_val != 0)
495                         info->current_noti_level = MEMNOTI_LEVEL_NORMAL;
496         }
497
498         if (info->current_noti_level == MEMNOTI_LEVEL_NORMAL && info->current_noti_level > old) {
499                 ret_val = process_memory_info(info->current_noti_level);
500                 if (ret_val != 0)
501                         info->current_noti_level = MEMNOTI_LEVEL_NORMAL;
502         }
503         old = info->current_noti_level;
504 }
505
506 static gboolean check_storage_status(gpointer data)
507 {
508         struct statvfs s;
509         double dAvail = 0.0;
510         double dTotal = 0.0;
511
512         /* check internal */
513         storage_get_internal_memory_size(&s);
514         dTotal = (double)s.f_frsize * s.f_blocks;
515         dAvail = (double)s.f_bsize * s.f_bavail;
516         storage_status_broadcast(&storage_internal_info, dTotal, dAvail);
517         check_internal_storage(&storage_internal_info);
518         /* check tmp */
519         storage_get_memory_size(MEMORY_STATUS_TMP_PATH, &s);
520         dTotal = (double)s.f_frsize * s.f_blocks;
521         dAvail = (double)s.f_bsize * s.f_bavail;
522         storage_status_broadcast(&storage_tmp_info, dTotal, dAvail);
523         /* check opt */
524         storage_get_memory_size(MEMORY_STATUS_OPT_PATH, &s);
525         dTotal = (double)s.f_frsize * s.f_blocks;
526         dAvail = (double)s.f_bsize * s.f_bavail;
527         storage_status_broadcast(&storage_opt_info, dTotal, dAvail);
528
529         return G_SOURCE_CONTINUE;
530 }
531
532 static int init_storage_config_info_all(void)
533 {
534         init_storage_config_info(tzplatform_getenv(TZ_SYS_USER), &storage_internal_info);
535         init_storage_config_info(MEMORY_STATUS_TMP_PATH, &storage_tmp_info);
536         init_storage_config_info(MEMORY_STATUS_OPT_PATH, &storage_opt_info);
537         memnoti_timer = g_timeout_add(MEMNOTI_TIMER_INTERVAL,
538                                 check_storage_status, NULL);
539         if (memnoti_timer == 0)
540                 _E("Failed mem available noti timer add.");
541         return 0;
542 }
543
544 static GVariant *dbus_getstatus(GDBusConnection *conn,
545                 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
546                 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
547 {
548         struct statvfs s;
549         unsigned long long dAvail = 0.0;
550         unsigned long long dTotal = 0.0;
551
552         storage_get_internal_memory_size(&s);
553         dTotal = (unsigned long long)s.f_frsize * s.f_blocks;
554         dAvail = (unsigned long long)s.f_bsize * s.f_bavail;
555
556         return g_variant_new("(tt)", dTotal, dAvail);
557 }
558
559 static GVariant *dbus_get_storage_status(GDBusConnection *conn,
560                 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
561                 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
562 {
563         const char *temp;
564         char *str_path;
565         struct statvfs s;
566         struct stat buf;
567         pid_t pid;
568         unsigned long long dAvail = 0.0;
569         unsigned long long dTotal = 0.0;
570         int ret_val;
571         bool success = true;
572
573         g_variant_get(param, "(s)", &str_path);
574
575         temp = tzplatform_getenv(TZ_SYS_USER);
576         if (!strncmp(str_path, temp, strlen(temp))) {
577                 ret_val = stat(str_path, &buf);
578                 if (ret_val == 0) {
579                         ret_val = storage_get_internal_memory_size(&s);
580                         if (ret_val < 0)
581                                 success = false;
582                 } else
583                         success = false;
584         } else {
585                 ret_val = storage_get_memory_size(str_path, &s);
586                 if (ret_val < 0)
587                         success = false;
588         }
589
590         if (success) {
591                 dTotal = (unsigned long long)s.f_frsize * s.f_blocks;
592                 dAvail = (unsigned long long)s.f_bsize * s.f_bavail;
593         }
594
595         pid = gdbus_connection_get_sender_pid(NULL, sender);
596
597         _D("PID=%d path='%s' total=%4.0llu avail=%4.0llu", pid, str_path, dTotal, dAvail);
598
599         g_free(str_path);
600         return g_variant_new("(tt)", dTotal, dAvail);
601 }
602
603 static GVariant *dbus_getstatvfs(GDBusConnection *conn,
604                 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
605                 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
606 {
607         char *str_path;
608         pid_t pid;
609         struct statvfs s;
610
611         g_variant_get(param, "(s)", &str_path);
612
613         storage_get_memory_size(str_path, &s);
614
615         pid = gdbus_connection_get_sender_pid(NULL, sender);
616
617         _D("PID=%d path='%s'", pid, str_path);
618
619         g_free(str_path);
620
621         return g_variant_new("(ttttttttttt)", (guint64)(s.f_bsize), (guint64)(s.f_frsize),
622                         (guint64)(s.f_blocks), (guint64)(s.f_bfree), (guint64)(s.f_bavail),
623                         (guint64)(s.f_files), (guint64)(s.f_ffree), (guint64)(s.f_favail),
624                         (guint64)(s.f_fsid), (guint64)(s.f_flag), (guint64)(s.f_namemax));
625
626 }
627
628 static GVariant *dbus_get_storage_level(GDBusConnection *conn,
629                 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
630                 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
631 {
632         int path_id;
633         enum memnoti_level level;
634         char *value;
635
636         g_variant_get(param, "(i)", &path_id);
637
638         if (path_id == TZ_SYS_USER)
639                 level = storage_internal_info.current_noti_level;
640         else if (path_id == TZ_SYS_OPT)
641                 level = storage_opt_info.current_noti_level;
642         else if (path_id == TZ_SYS_TMP)
643                 level = storage_tmp_info.current_noti_level;
644         else
645                 level = -1;
646
647         if (level == MEMNOTI_LEVEL_WARNING)
648                 value = "Warning";
649         else if (level == MEMNOTI_LEVEL_CRITICAL)
650                 value = "Critical";
651         else if (level == MEMNOTI_LEVEL_FULL)
652                 value = "Full";
653         else if (level == MEMNOTI_LEVEL_NORMAL)
654                 value = "Normal";
655         else
656                 value = "Not supported path";
657
658         return g_variant_new("(s)", value);
659 }
660
661 static const dbus_method_s storage_methods[] = {
662         { "getstorage",     NULL,          "tt", dbus_getstatus },
663         { "GetStatus",       "s",          "tt", dbus_get_storage_status },
664         { "GetStatvfs",      "s", "ttttttttttt", dbus_getstatvfs },
665         { "GetStorageLevel", "i",           "s", dbus_get_storage_level },
666         /* Add methods here */
667 };
668
669 static dbus_interface_u storage_interface = {
670         .name = STORAGED_INTERFACE_STORAGE,
671         .methods = storage_methods,
672         .nr_methods = ARRAY_SIZE(storage_methods),
673 };
674
675 static void booting_done(void)
676 {
677         static int done;
678         int ret_val;
679
680         if (done > 0)
681                 return;
682         done = 1;
683         _I("Booting done.");
684
685         /* UDEV register_udev_uevent_control */
686         ret_val = register_udev_uevent_control(&dm_verity_uh);
687         if (ret_val < 0) {
688                 _E("Failed to register dm_verity uevent: %d", ret_val);
689         }
690
691         if (init_storage_config_info_all() == -1)
692                 _E("Failed to remain mem noti control fd init.");
693 }
694
695 static void storage_poweroff(GDBusConnection  *conn,
696                 const gchar *sender,
697                 const gchar *path,
698                 const gchar *iface,
699                 const gchar *name,
700                 GVariant *param,
701                 gpointer data)
702 {
703         if (memnoti_timer) {
704                 g_source_remove(memnoti_timer);
705                 memnoti_timer = 0;
706         }
707
708         /* UDEV unregister_udev_uevent_control */
709         unregister_udev_uevent_control(&dm_verity_uh);
710 }
711
712 static int is_recovery_available()
713 {
714         int ret_board_api;
715         int cloned;
716
717         /* check if a/b partitions are cloned */
718         ret_board_api = device_board_get_partition_ab_cloned(&cloned);
719         if (ret_board_api != 0) {
720                 CRITICAL_LOG("device_board_get_partition_ab_cloned failed: %d", ret_board_api);
721                 return 0;
722         }
723         if(cloned != 1) {
724                 CRITICAL_LOG("Partition a/b are not cloned");
725                 return 0;
726         }
727
728         return 1;
729 }
730
731 static gboolean reboot_recovery_handler(void *data)
732 {
733         int ret_board_api;
734         int ret_dbus;
735         int recovery_available;
736         gint poweroff_retval;
737         GVariant *poweroff_param = NULL;
738
739         recovery_available = is_recovery_available();
740
741         /* set current partition status as "corrupted" */
742         ret_board_api = device_board_set_partition_status('\0', "corrupted");
743         CRITICAL_LOG("device_board_set_partition_status: (%d)", ret_board_api);
744         /* clear partition ab cloned */
745         ret_board_api = device_board_clear_partition_ab_cloned();
746         CRITICAL_LOG("device_board_clear_partition_ab_cloned: (%d)", ret_board_api);
747
748         if (!recovery_available) {
749                 CRITICAL_LOG("Recovery is not available, do not reboot/recovery");
750                 return G_SOURCE_REMOVE;
751         }
752
753         /* toggle partition */
754         ret_board_api = device_board_switch_partition('\0');
755         CRITICAL_LOG("device_board_switch_partition: (%d)", ret_board_api);
756
757         /* call method for reboot recovery */
758         poweroff_param = g_variant_new("(ss)", "reboot", "recovery");
759
760         ret_dbus = gdbus_call_sync_with_reply_int("org.tizen.system.deviced",
761                         "/Org/Tizen/System/DeviceD/PowerOff",
762                         "org.tizen.system.deviced.PowerOff",
763                         "PowerOffWithOption",
764                         poweroff_param, &poweroff_retval);
765         g_variant_unref(poweroff_param);
766
767         if (ret_dbus < 0) {
768                 CRITICAL_LOG("Failed to call PowerOffWithOption(reboot recovery): (%d)", ret_dbus);
769                 return G_SOURCE_CONTINUE;
770         }
771
772         CRITICAL_LOG("org.tizen.system.deviced.PowerOff.PowerOffWithOption reboot recovery: (%d)",
773                         poweroff_retval);
774
775         return G_SOURCE_REMOVE;
776 }
777
778 static void dm_verity_uevent_block_handler(struct udev_device *dev)
779 {
780         static int dm_verity_corrupted_cnt = 0;
781         const char *dm_name = NULL;
782         const char *dm_verity_err_block_nr = NULL;
783
784         /* if dm_name is rootfs and DM_VERITY_ERR_BLOCK_NR exist, then reboot recovery */
785         dm_name = udev_device_get_property_value(dev, "DM_NAME");
786         dm_verity_err_block_nr = udev_device_get_property_value(dev, "DM_VERITY_ERR_BLOCK_NR");
787
788         if (!dm_name || !dm_verity_err_block_nr) {
789                 return;
790         }
791
792         if(strncmp(DM_ROOT_NODE_NAME, dm_name, strlen(DM_ROOT_NODE_NAME))) {
793                 return;
794         }
795
796         ++dm_verity_corrupted_cnt;
797         if (dm_verity_corrupted_cnt < storage_rootfs_recovery_info.dm_verity_corrupted_count) {
798                 CRITICAL_LOG("dm_verity_corrupted_cnt: %d(<%d)",
799                                 dm_verity_corrupted_cnt,
800                                 storage_rootfs_recovery_info.dm_verity_corrupted_count);
801                 return;
802         }
803         /* prevent overflow */
804         dm_verity_corrupted_cnt = storage_rootfs_recovery_info.dm_verity_corrupted_count;
805
806         CRITICAL_LOG("Dmverity corrupted: action=(%s), count=(%d), timeout=(%d)ms",
807                         recovery_action_str[storage_rootfs_recovery_info.action],
808                         storage_rootfs_recovery_info.dm_verity_corrupted_count,
809                         storage_rootfs_recovery_info.dm_verity_corrupted_timeout);
810
811         /* action: ignore */
812         if (storage_rootfs_recovery_info.action == RECOVERY_ACTION_IGNORE) {
813                 /* ignore, do nothing */
814                 return;
815         }
816         /* action: reboot,recovery */
817         if (storage_rootfs_recovery_info.action == RECOVERY_ACTION_REBOOT_RECOVERY &&
818                         poweroff_g_timeout_event_source_id <= 0) {
819                 poweroff_g_timeout_event_source_id =
820                         g_timeout_add(storage_rootfs_recovery_info.dm_verity_corrupted_timeout,
821                                         reboot_recovery_handler, NULL);
822                 _I("g_timeout_add event source id: (%u)",
823                                 poweroff_g_timeout_event_source_id);
824         }
825 }
826
827 static int config_parse_time_ms(const char *value, int *parsed_ms)
828 {
829         char prefix;
830         char *unit_ptr = strchr(value, 's');
831         if (unit_ptr == NULL) {
832                 _E("Cannot find 's' in the string (%s)", value);
833                 return -EINVAL;
834         }
835
836         if (value > (unit_ptr - 1)) {
837                 _E("Size of string should be larger than 1");
838                 return -EINVAL;
839         }
840
841         prefix = *(unit_ptr - 1);
842
843         if (isdigit(prefix)) {
844                 *parsed_ms = atoi(value) * 1000;
845                 return 0;
846         }
847
848         *(unit_ptr - 1) = '\0';
849         if (prefix == 'm') {
850                 *parsed_ms = atoi(value);
851                 return 0;
852         }
853
854         _E("Unknown unit of time");
855         return -EINVAL;
856 }
857
858 static int load_config(struct parse_result *result, void *user_data)
859 {
860         char *name;
861         char *value;
862
863         if (MATCH(result->section, "StorageUsageLevelThreshold")) {
864                 _D("%s,%s,%s", result->section, result->name, result->value);
865
866                 name = result->name;
867                 value = result->value;
868
869                 if (MATCH(name, "WarningLevel")) {
870                         storage_internal_info.warning_level = (double)atof(value);
871                         _I("WarningLevel: (%f)", storage_internal_info.warning_level);
872                 } else if (MATCH(name, "CriticalLevel")) {
873                         storage_internal_info.critical_level = (double)atof(value);
874                         _I("CriticalLevel: (%f)", storage_internal_info.critical_level);
875                 } else if (MATCH(name, "FullLevel")) {
876                         storage_internal_info.full_level = (double)atof(value);
877                         _I("FullLevel: (%f)", storage_internal_info.full_level);
878                 }
879
880         } else if (MATCH(result->section, "StorageRootfsRecovery")) {
881                 _D("%s,%s,%s", result->section, result->name, result->value);
882
883                 name = result->name;
884                 value = result->value;
885
886                 if (MATCH(name, "DmverityCorruptedAction")) {
887                         if (!value || !(*value)) {
888                                 return -EINVAL;
889                         }
890
891                         if (!strncmp(value, RECOVERY_ACTION_IGNORE_STR, strlen(RECOVERY_ACTION_IGNORE_STR))) {
892                                 storage_rootfs_recovery_info.action = RECOVERY_ACTION_IGNORE;
893                         } else if (!strncmp(value, RECOVERY_ACTION_REBOOT_RECOVERY_STR, strlen(RECOVERY_ACTION_REBOOT_RECOVERY_STR))) {
894                                 storage_rootfs_recovery_info.action = RECOVERY_ACTION_REBOOT_RECOVERY;
895                         } else {
896                                 _E("Unknown action: (%s)", value);
897                                 /* do nothing, use previous value */
898                         }
899
900                         _I("Dmverity Corrupted Action: (%s)", recovery_action_str[storage_rootfs_recovery_info.action]);
901
902                 } else if (MATCH(name, "DmverityCorruptedCount")) {
903                         storage_rootfs_recovery_info.dm_verity_corrupted_count = atoi(value);
904                         _I("DmverityCorruptedCount: (%d)",
905                                         storage_rootfs_recovery_info.dm_verity_corrupted_count);
906
907                 } else if (MATCH(name, "DmverityCorruptedTimeout")) {
908                         int ms = 0;
909                         if (!config_parse_time_ms(value, &ms)) {
910                                 storage_rootfs_recovery_info.dm_verity_corrupted_timeout = ms;
911                         }
912                         _I("DmverityCorruptedTimeout: (%d)ms",
913                                         storage_rootfs_recovery_info.dm_verity_corrupted_timeout);
914                 }
915
916         } else {
917                 return -EINVAL;
918         }
919
920         return 0;
921 }
922
923 static void storage_config_load(void)
924 {
925         int ret_val;
926
927         ret_val = config_parse(STORAGE_CONF_FILE, load_config, NULL);
928         if (ret_val < 0)
929                 _E("Failed to load %s, %d Use default value.", STORAGE_CONF_FILE, ret_val);
930 }
931
932 static void storage_init(void *data)
933 {
934         int ret_val;
935
936         /* UDEV udev_init */
937         udev_init(NULL);
938
939         storage_config_load();
940
941         ret_val = gdbus_register_object(NULL, STORAGED_PATH_STORAGE,
942                         &storage_interface);
943         if (ret_val < 0)
944                 _E("Failed to register dbus interface and methods: %d", ret_val);
945
946         ret_val = remove_directory(NEED_CLEANUP_DIR_PATH);
947         if (ret_val < 0)
948                 _E("Failed to remove directory.");
949         ret_val = mkdir(NEED_CLEANUP_DIR_PATH, 0644);
950         if (ret_val < 0)
951                 _E("Failed to make directory: %d", errno);
952
953         init_cleanup_storage();
954
955         id_storage_poweroff = gdbus_signal_subscribe(NULL, DEVICED_PATH_POWEROFF,
956                         DEVICED_INTERFACE_POWEROFF,
957                         SIGNAL_POWEROFF_STATE,
958                         storage_poweroff, NULL, NULL);
959
960         booting_done();
961 }
962
963 static void storage_exit(void *data)
964 {
965         int ret_val;
966
967         udev_exit(NULL);
968
969         free_cleanup_storage();
970         /* unregister notifier for below each event */
971         gdbus_signal_unsubscribe(NULL, id_storage_poweroff);
972
973         /* UDEV unregister_udev_uevent_control */
974         ret_val = unregister_udev_uevent_control(&dm_verity_uh);
975         if (ret_val < 0) {
976                 _E("Failed to unregister block uevent: %d", ret_val);
977         }
978 }
979
980 static storaged_module_interface storage_module = {
981         .name = "storage",
982         .init = storage_init,
983         .exit = storage_exit,
984 };
985
986 __attribute__ ((visibility("default")))storaged_module_interface *
987 storaged_get_module_interface(void)
988 {
989         return &storage_module;
990 }