4 * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd.
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
27 #include <sys/types.h>
28 #include <sys/statvfs.h>
33 #include <tzplatform_config.h>
35 #include <libsyscommon/dbus-system.h>
36 #include <libsyscommon/systemd-state.h>
39 #include "config-parser.h"
40 #include "module-intf.h"
41 #include "storaged_common.h"
43 #define MEMORY_STATUS_TMP_PATH "/tmp"
44 #define MEMORY_STATUS_OPT_PATH "/opt"
45 #define MEMNOTI_TMP_CRITICAL_VALUE (20)
47 #define MEMORY_MEGABYTE_VALUE 1048576
49 #define MEMNOTI_WARNING_VALUE (5) /* 5% under */
50 #define MEMNOTI_CRITICAL_VALUE (0.1) /* 0.1% under */
51 #define MEMNOTI_FULL_VALUE (0.0) /* 0.0% under */
53 #define SIGNAL_NEED_CLEANUP "NeedCleanup"
54 #define MEMNOTI_TIMER_INTERVAL 5000 /* milliseconds */
56 #define SIGNAL_POWEROFF_STATE "ChangeState"
58 #define STORAGE_CONF_FILE "/etc/storaged/storage.conf"
60 #define NEED_CLEANUP_DIR_PATH "/run/storaged/needcleanup"
61 #define NEED_CLEANUP_FILE_PATH "/run/storaged/needcleanup/trigger"
63 #define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0]))
65 #define LOW_STORAGE_WARNING "lowstorage_warning"
66 #define LOW_STORAGE_CRITICAL "lowstorage_critical"
67 #define LOW_STORAGE_FULL "lowstorage_full"
68 #define LOW_STORAGE_REMOVE "lowstorage_remove"
70 #define INTERNAL_STORAGE_NOTION "InternalStorageNotiOn"
71 #define INTERNAL_STORAGE_NOTIOFF "InternalStorageNotiOff"
74 MEMNOTI_LEVEL_FULL = 0,
75 MEMNOTI_LEVEL_CRITICAL,
76 MEMNOTI_LEVEL_WARNING,
91 struct storage_config_info {
92 enum memory_id mem_id;
93 enum memnoti_level current_noti_level;
95 double critical_level;
99 static guint memnoti_timer;
102 static guint id_booting_done;
103 static guint id_storage_poweroff;
105 static struct storage_config_info storage_internal_info = {
106 .mem_id = MEMORY_INTERNAL,
107 .current_noti_level = MEMNOTI_LEVEL_NORMAL,
108 .warning_level = MEMNOTI_WARNING_VALUE,
109 .critical_level = MEMNOTI_CRITICAL_VALUE,
110 .full_level = MEMNOTI_FULL_VALUE,
113 static struct storage_config_info storage_tmp_info = {
114 .mem_id = MEMORY_TMP,
115 .current_noti_level = MEMNOTI_LEVEL_NORMAL,
116 .warning_level = MEMNOTI_TMP_CRITICAL_VALUE,
117 .critical_level = MEMNOTI_TMP_CRITICAL_VALUE,
118 .full_level = MEMNOTI_FULL_VALUE,
121 static struct storage_config_info storage_opt_info = {
122 .mem_id = MEMORY_OPT,
123 .current_noti_level = MEMNOTI_LEVEL_NORMAL,
124 .warning_level = MEMNOTI_WARNING_VALUE,
125 .critical_level = MEMNOTI_CRITICAL_VALUE,
126 .full_level = MEMNOTI_FULL_VALUE,
129 static void write_file()
133 fp = fopen(NEED_CLEANUP_FILE_PATH, "w+");
135 fprintf(fp, "needcleanup\n");
138 _E("Failed to open '%s'.", NEED_CLEANUP_FILE_PATH);
141 static void memcleanup_send_broadcast(struct storage_config_info *info, enum memnoti_level level, enum memnoti_level prev_level)
145 enum tzplatform_variable path;
149 if (info->mem_id == MEMORY_INTERNAL)
151 else if (info->mem_id == MEMORY_TMP)
153 else if (info->mem_id == MEMORY_OPT)
158 if (prev_level <= level)
161 if (level == MEMNOTI_LEVEL_WARNING)
163 else if (level == MEMNOTI_LEVEL_CRITICAL)
165 else if (level == MEMNOTI_LEVEL_FULL)
173 if (localtime_r(&t, &timeinfo) == NULL) {
174 _E("Failed to localtime_r.");
178 strftime(buf, sizeof(buf), "%b %d %T", &timeinfo);
179 _D("time=%s path=%d level=%s", buf, path, value);
182 dbus_handle_broadcast_dbus_signal_var(STORAGED_PATH_LOWMEM, STORAGED_INTERFACE_LOWMEM,
183 SIGNAL_NEED_CLEANUP, g_variant_new("(is)", path, value));
187 static void _popup_cb(GVariant *var, void *user_data, GError *err)
192 _E("No message: %s", err->message);
196 if (!dh_get_param_from_var(var, "(i)", &ret)) {
197 _E("No message: %s", g_variant_get_type_string(var));
201 _D("Reply value: %d", ret);
204 g_variant_unref(var);
207 static int launch_memory_popup(int num, ...)
214 ret = dbus_handle_method_async_pairs_with_reply(POPUP_BUS_NAME,
216 POPUP_INTERFACE_SYSTEM,
229 static void _noti_cb(GVariant *var, void *user_data, GError *err)
234 _E("No message: %s", err->message);
238 if (!dh_get_param_from_var(var, "(i)", &ret)) {
239 _E("No message: %s", g_variant_get_type_string(var));
244 _D("Reply value: %d", ret);
247 g_variant_unref(var);
250 static int remove_notification(void)
252 const char *param[1];
256 snprintf(id_str, sizeof(id_str), "%d", noti_id);
259 ret = dbus_handle_method_sync(POPUP_BUS_NAME,
261 POPUP_INTERFACE_NOTI,
262 INTERNAL_STORAGE_NOTIOFF,
266 _E("Failed to remove noti(%d).", noti_id);
271 static int create_notification(void)
275 ret = dbus_handle_method_async_with_reply(POPUP_BUS_NAME,
277 POPUP_INTERFACE_NOTI,
278 INTERNAL_STORAGE_NOTION,
285 _E("Failed to create noti.");
290 static int launch_memory_notification(char *type)
297 if (!strncmp(type, INTERNAL_STORAGE_NOTION, sizeof(INTERNAL_STORAGE_NOTION))) {
299 ret = remove_notification();
305 ret = create_notification();
306 } else if (!strncmp(type, INTERNAL_STORAGE_NOTIOFF, sizeof(INTERNAL_STORAGE_NOTIOFF))) {
310 ret = remove_notification();
312 _E("Invalid noti type(%s).", type);
317 static int process_memory_info(enum memnoti_level level)
321 char *popup_value = NULL;
322 char *noti_value = NULL;
324 if (level < 0 || level > MEMNOTI_LEVEL_NORMAL) {
325 _E("Failed to check level(%d).", level);
329 if (level == MEMNOTI_LEVEL_WARNING) {
330 popup_value = LOW_STORAGE_WARNING;
331 noti_value = INTERNAL_STORAGE_NOTION;
332 } else if (level == MEMNOTI_LEVEL_CRITICAL) {
333 popup_value = LOW_STORAGE_CRITICAL;
334 noti_value = INTERNAL_STORAGE_NOTION;
335 } else if (level == MEMNOTI_LEVEL_FULL) {
336 popup_value = LOW_STORAGE_FULL;
337 noti_value = INTERNAL_STORAGE_NOTION;
338 } else if (level == MEMNOTI_LEVEL_NORMAL) {
339 popup_value = LOW_STORAGE_REMOVE;
340 noti_value = INTERNAL_STORAGE_NOTIOFF;
343 ret = vconf_get_int(VCONFKEY_STARTER_SEQUENCE, &val);
344 if (val == 0 || ret != 0)
347 if (!popup_value && !noti_value) {
348 _E("Invalid memnoti level(%d).", level);
352 ret = launch_memory_popup(2, "_SYSPOPUP_CONTENT_", popup_value);
354 _E("Failed to launch momory popup: %d", ret);
356 ret = launch_memory_notification(noti_value);
358 _E("Failed to launch momory notification: %d", ret);
363 static void storage_status_broadcast(struct storage_config_info *info, double total, double avail)
365 double level = (avail/total)*100;
366 enum memnoti_level prev_noti_level;
368 prev_noti_level = info->current_noti_level;
369 if (level <= info->full_level) {
370 if (info->current_noti_level == MEMNOTI_LEVEL_FULL)
372 info->current_noti_level = MEMNOTI_LEVEL_FULL;
374 memcleanup_send_broadcast(info, info->current_noti_level, prev_noti_level);
376 _I("Id=%d current level=%4.4lf warning=%4.4lf critical=%4.4lf full=%4.4lf",
377 info->mem_id, level, info->warning_level, info->critical_level, info->full_level);
381 if (level <= info->critical_level) {
382 if (info->current_noti_level == MEMNOTI_LEVEL_CRITICAL)
384 info->current_noti_level = MEMNOTI_LEVEL_CRITICAL;
386 memcleanup_send_broadcast(info, info->current_noti_level, prev_noti_level);
388 _I("Id=%d current level=%4.4lf warning=%4.4lf critical=%4.4lf full=:%4.4lf",
389 info->mem_id, level, info->warning_level, info->critical_level, info->full_level);
393 if (level <= info->warning_level) {
394 info->current_noti_level = MEMNOTI_LEVEL_WARNING;
395 _I("Id=%d current level=%4.4lf warning=%4.4lf critical=%4.4lf full=%4.4lf",
396 info->mem_id, level, info->warning_level, info->critical_level, info->full_level);
398 memcleanup_send_broadcast(info, info->current_noti_level, prev_noti_level);
400 info->current_noti_level = MEMNOTI_LEVEL_NORMAL;
403 static int storage_get_memory_size(const char *path, struct statvfs *s)
408 _E("Input param error.");
412 ret = statvfs(path, s);
414 _E("Failed to get storage size.");
421 static void get_storage_status(const char *path, struct statvfs *s)
423 if (strcmp(path, tzplatform_getenv(TZ_SYS_USER)) == 0)
424 storage_get_internal_memory_size(s);
426 storage_get_memory_size(path, s);
429 static void init_storage_config_info(const char *path, struct storage_config_info *info)
435 get_storage_status(path, &s);
437 dTotal = (double)s.f_frsize * s.f_blocks;
438 dAvail = (double)s.f_bsize * s.f_bavail;
440 info->full_level += (MEMORY_MEGABYTE_VALUE/dTotal)*100;
442 _I("'%s' t=%4.0lf a=%4.0lf(%4.2lf) w=%4.4lf c=%4.4lf f=%4.4lf",
443 path, dTotal, dAvail, (dAvail*100/dTotal), info->warning_level, info->critical_level, info->full_level);
446 static void check_internal_storage(struct storage_config_info *info)
448 static enum memnoti_level old = MEMNOTI_LEVEL_NORMAL;
451 if (info->current_noti_level < MEMNOTI_LEVEL_NORMAL && info->current_noti_level < old) {
452 ret = process_memory_info(info->current_noti_level);
454 info->current_noti_level = MEMNOTI_LEVEL_NORMAL;
457 if (info->current_noti_level == MEMNOTI_LEVEL_NORMAL && info->current_noti_level > old) {
458 ret = process_memory_info(info->current_noti_level);
460 info->current_noti_level = MEMNOTI_LEVEL_NORMAL;
462 old = info->current_noti_level;
465 static gboolean check_storage_status(gpointer data)
472 storage_get_internal_memory_size(&s);
473 dTotal = (double)s.f_frsize * s.f_blocks;
474 dAvail = (double)s.f_bsize * s.f_bavail;
475 storage_status_broadcast(&storage_internal_info, dTotal, dAvail);
476 check_internal_storage(&storage_internal_info);
478 storage_get_memory_size(MEMORY_STATUS_TMP_PATH, &s);
479 dTotal = (double)s.f_frsize * s.f_blocks;
480 dAvail = (double)s.f_bsize * s.f_bavail;
481 storage_status_broadcast(&storage_tmp_info, dTotal, dAvail);
483 storage_get_memory_size(MEMORY_STATUS_OPT_PATH, &s);
484 dTotal = (double)s.f_frsize * s.f_blocks;
485 dAvail = (double)s.f_bsize * s.f_bavail;
486 storage_status_broadcast(&storage_opt_info, dTotal, dAvail);
489 g_source_remove(memnoti_timer);
490 memnoti_timer = g_timeout_add(MEMNOTI_TIMER_INTERVAL,
491 check_storage_status, NULL);
494 return G_SOURCE_REMOVE;
497 static int init_storage_config_info_all(void)
499 init_storage_config_info(tzplatform_getenv(TZ_SYS_USER), &storage_internal_info);
500 init_storage_config_info(MEMORY_STATUS_TMP_PATH, &storage_tmp_info);
501 init_storage_config_info(MEMORY_STATUS_OPT_PATH, &storage_opt_info);
502 memnoti_timer = g_timeout_add(MEMNOTI_TIMER_INTERVAL,
503 check_storage_status, NULL);
504 if (memnoti_timer == 0)
505 _E("Failed mem available noti timer add.");
509 static GVariant *dbus_getstatus(GDBusConnection *conn,
510 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
511 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
514 unsigned long long dAvail = 0.0;
515 unsigned long long dTotal = 0.0;
517 storage_get_internal_memory_size(&s);
518 dTotal = (unsigned long long)s.f_frsize * s.f_blocks;
519 dAvail = (unsigned long long)s.f_bsize * s.f_bavail;
521 return g_variant_new("(tt)", dTotal, dAvail);
524 static GVariant *dbus_get_storage_status(GDBusConnection *conn,
525 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
526 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
533 unsigned long long dAvail = 0.0;
534 unsigned long long dTotal = 0.0;
538 g_variant_get(param, "(s)", &str_path);
540 temp = tzplatform_getenv(TZ_SYS_USER);
541 if (!strncmp(str_path, temp, strlen(temp))) {
542 ret = stat(str_path, &buf);
544 ret = storage_get_internal_memory_size(&s);
550 ret = storage_get_memory_size(str_path, &s);
556 dTotal = (unsigned long long)s.f_frsize * s.f_blocks;
557 dAvail = (unsigned long long)s.f_bsize * s.f_bavail;
560 pid = dbus_handle_get_sender_pid(NULL, sender);
562 _D("PID=%d path='%s' total=%4.0llu avail=%4.0llu", pid, str_path, dTotal, dAvail);
565 return g_variant_new("(tt)", dTotal, dAvail);
568 static GVariant *dbus_getstatvfs(GDBusConnection *conn,
569 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
570 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
576 g_variant_get(param, "(s)", &str_path);
578 storage_get_memory_size(str_path, &s);
580 pid = dbus_handle_get_sender_pid(NULL, sender);
582 _D("PID=%d path='%s'", pid, str_path);
586 return g_variant_new("(ttttttttttt)", (guint64)(s.f_bsize), (guint64)(s.f_frsize),
587 (guint64)(s.f_blocks), (guint64)(s.f_bfree), (guint64)(s.f_bavail),
588 (guint64)(s.f_files), (guint64)(s.f_ffree), (guint64)(s.f_favail),
589 (guint64)(s.f_fsid), (guint64)(s.f_flag), (guint64)(s.f_namemax));
593 static GVariant *dbus_get_storage_level(GDBusConnection *conn,
594 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
595 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
598 enum memnoti_level level;
601 g_variant_get(param, "(i)", &path_id);
603 if (path_id == TZ_SYS_USER)
604 level = storage_internal_info.current_noti_level;
605 else if (path_id == TZ_SYS_OPT)
606 level = storage_opt_info.current_noti_level;
607 else if (path_id == TZ_SYS_TMP)
608 level = storage_tmp_info.current_noti_level;
612 if (level == MEMNOTI_LEVEL_WARNING)
614 else if (level == MEMNOTI_LEVEL_CRITICAL)
616 else if (level == MEMNOTI_LEVEL_FULL)
618 else if (level == MEMNOTI_LEVEL_NORMAL)
621 value = "Not supported path";
623 return g_variant_new("(s)", value);
626 static const dbus_method_s storage_methods[] = {
627 { "getstorage", NULL, "tt", dbus_getstatus },
628 { "GetStatus", "s", "tt", dbus_get_storage_status },
629 { "GetStatvfs", "s", "ttttttttttt", dbus_getstatvfs },
630 { "GetStorageLevel", "i", "s", dbus_get_storage_level },
631 /* Add methods here */
634 static dbus_interface_u storage_interface = {
635 .name = STORAGED_INTERFACE_STORAGE,
636 .methods = storage_methods,
637 .nr_methods = ARRAY_SIZE(storage_methods),
640 static void booting_done(void)
649 if (init_storage_config_info_all() == -1)
650 _E("Failed to remain mem noti control fd init.");
653 static void booting_done_received(GDBusConnection *conn,
661 _I("Signal received: %s", SYSTEMD_DBUS_SIGNAL_SYSTEM_STARTUP_FINISHED);
665 static void storage_poweroff(GDBusConnection *conn,
674 g_source_remove(memnoti_timer);
679 static int load_config(struct parse_result *result, void *user_data)
681 struct storage_config_info *info = (struct storage_config_info *)user_data;
688 if (!MATCH(result->section, "LOWSTORAGE"))
691 _D("%s,%s,%s", result->section, result->name, result->value);
694 value = result->value;
696 if (MATCH(name, "WARNING_LEVEL"))
697 info->warning_level = (double)atof(value);
698 else if (MATCH(name, "CRITICAL_LEVEL"))
699 info->critical_level = (double)atof(value);
700 else if (MATCH(name, "FULL_LEVEL"))
701 info->full_level = (double)atof(value);
706 static void storage_config_load(struct storage_config_info *info)
710 ret = config_parse(STORAGE_CONF_FILE, load_config, info);
712 _E("Failed to load %s, %d Use default value.", STORAGE_CONF_FILE, ret);
715 static void storage_init(void *data)
719 storage_config_load(&storage_internal_info);
721 ret = dbus_handle_register_dbus_object(NULL, STORAGED_PATH_STORAGE,
724 _E("Failed to register dbus interface and methods: %d", ret);
726 ret = remove_directory(NEED_CLEANUP_DIR_PATH);
728 _E("Failed to remove directory.");
729 ret = mkdir(NEED_CLEANUP_DIR_PATH, 0644);
731 _E("Failed to make directory: %d", errno);
733 if (check_system_boot_finished() != 0) {
734 _I("System session is already loaded.");
738 /* System Session is loaded completely */
739 id_booting_done = subscribe_dbus_signal(NULL, SYSTEMD_DBUS_PATH,
740 SYSTEMD_DBUS_IFACE_MANAGER,
741 SYSTEMD_DBUS_SIGNAL_SYSTEM_STARTUP_FINISHED,
742 booting_done_received, NULL, NULL);
745 id_storage_poweroff = subscribe_dbus_signal(NULL, DEVICED_PATH_POWEROFF,
746 DEVICED_INTERFACE_POWEROFF,
747 SIGNAL_POWEROFF_STATE,
748 storage_poweroff, NULL, NULL);
751 static void storage_exit(void *data)
753 /* unregister notifier for below each event */
754 unsubscribe_dbus_signal(NULL, id_booting_done);
755 unsubscribe_dbus_signal(NULL, id_storage_poweroff);
758 static storaged_module_interface storage_module = {
760 .init = storage_init,
761 .exit = storage_exit,
764 __attribute__ ((visibility("default")))storaged_module_interface *
765 storaged_get_module_interface(void)
767 return &storage_module;