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 <libgdbus/dbus-system.h>
38 #include "config-parser.h"
39 #include "module-intf.h"
41 #define MEMORY_STATUS_TMP_PATH "/tmp"
42 #define MEMORY_STATUS_OPT_PATH "/opt"
43 #define MEMNOTI_TMP_CRITICAL_VALUE (20)
45 #define MEMORY_MEGABYTE_VALUE 1048576
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 */
51 #define SIGNAL_NEED_CLEANUP "NeedCleanup"
52 #define MEMNOTI_TIMER_INTERVAL 5000 /* milliseconds */
54 #define SIGNAL_POWEROFF_STATE "ChangeState"
56 #define STORAGE_CONF_FILE "/etc/storaged/storage.conf"
58 #define STORAGED_DIR_PATH "/run/storaged"
59 #define NEED_CLEANUP_DIR_PATH "/run/storaged/needcleanup"
60 #define NEED_CLEANUP_FILE_PATH "/run/storaged/needcleanup/trigger"
62 #define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0]))
65 MEMNOTI_LEVEL_FULL = 0,
66 MEMNOTI_LEVEL_CRITICAL,
67 MEMNOTI_LEVEL_WARNING,
82 struct storage_config_info {
83 enum memory_id mem_id;
84 enum memnoti_level current_noti_level;
86 double critical_level;
90 static guint memnoti_timer;
92 static guint id_booting_done;
93 static guint id_storage_poweroff;
95 static struct storage_config_info storage_internal_info = {
96 .mem_id = MEMORY_INTERNAL,
97 .current_noti_level = MEMNOTI_LEVEL_NORMAL,
98 .warning_level = MEMNOTI_WARNING_VALUE,
99 .critical_level = MEMNOTI_CRITICAL_VALUE,
100 .full_level = MEMNOTI_FULL_VALUE,
103 static struct storage_config_info storage_tmp_info = {
104 .mem_id = MEMORY_TMP,
105 .current_noti_level = MEMNOTI_LEVEL_NORMAL,
106 .warning_level = MEMNOTI_TMP_CRITICAL_VALUE,
107 .critical_level = MEMNOTI_TMP_CRITICAL_VALUE,
108 .full_level = MEMNOTI_FULL_VALUE,
111 static struct storage_config_info storage_opt_info = {
112 .mem_id = MEMORY_OPT,
113 .current_noti_level = MEMNOTI_LEVEL_NORMAL,
114 .warning_level = MEMNOTI_WARNING_VALUE,
115 .critical_level = MEMNOTI_CRITICAL_VALUE,
116 .full_level = MEMNOTI_FULL_VALUE,
119 static void write_file()
123 fp = fopen(NEED_CLEANUP_FILE_PATH, "w+");
125 fprintf(fp, "needcleanup\n");
128 _E("Fail to open %s", NEED_CLEANUP_FILE_PATH);
131 static void memcleanup_send_broadcast(struct storage_config_info *info, enum memnoti_level level, enum memnoti_level prev_level)
135 enum tzplatform_variable path;
139 if (info->mem_id == MEMORY_INTERNAL)
141 else if (info->mem_id == MEMORY_TMP)
143 else if (info->mem_id == MEMORY_OPT)
148 if (prev_level <= level)
151 if (level == MEMNOTI_LEVEL_WARNING)
153 else if (level == MEMNOTI_LEVEL_CRITICAL)
155 else if (level == MEMNOTI_LEVEL_FULL)
163 if (localtime_r(&t, &timeinfo) == NULL) {
164 _E("Failed to localtime_r");
168 strftime(buf, sizeof(buf), "%b %d %T", &timeinfo);
169 _D("time: %s path: %d level: %s", buf, path, value);
172 dbus_handle_broadcast_dbus_signal_var(STORAGED_PATH_LOWMEM, STORAGED_INTERFACE_LOWMEM,
173 SIGNAL_NEED_CLEANUP, g_variant_new("(is)", path, value));
177 static void __cb(GVariant *var, void *user_data, GError *err)
182 _E("no message [%s]", err->message);
186 if (!dh_get_param_from_var(var, "(i)", &ret)) {
187 _E("no message [%s]", g_variant_get_type_string(var));
191 _D("reply value : %d", ret);
194 g_variant_unref(var);
197 static int launch_memory_popup(int num, ...)
204 ret = dbus_handle_method_async_pairs_with_reply(POPUP_BUS_NAME,
206 POPUP_INTERFACE_SYSTEM,
219 static int memnoti_popup(enum memnoti_level level)
225 if (level != MEMNOTI_LEVEL_WARNING && level != MEMNOTI_LEVEL_CRITICAL) {
226 _E("level check error : %d", level);
230 if (level == MEMNOTI_LEVEL_WARNING)
231 value = "lowstorage_warning";
232 else if (level == MEMNOTI_LEVEL_CRITICAL)
233 value = "lowstorage_critical";
235 ret = vconf_get_int(VCONFKEY_STARTER_SEQUENCE, &val);
236 if (val == 0 || ret != 0)
242 return launch_memory_popup(2, "_SYSPOPUP_CONTENT_", value);
245 static void storage_status_broadcast(struct storage_config_info *info, double total, double avail)
247 double level = (avail/total)*100;
248 enum memnoti_level prev_noti_level;
250 prev_noti_level = info->current_noti_level;
251 if (level <= info->full_level) {
252 if (info->current_noti_level == MEMNOTI_LEVEL_FULL)
254 info->current_noti_level = MEMNOTI_LEVEL_FULL;
256 memcleanup_send_broadcast(info, info->current_noti_level, prev_noti_level);
258 _I("%d current level %4.4lf w:%4.4lf c:%4.4lf f:%4.4lf",
259 info->mem_id, level, info->warning_level, info->critical_level, info->full_level);
263 if (level <= info->critical_level) {
264 if (info->current_noti_level == MEMNOTI_LEVEL_CRITICAL)
266 info->current_noti_level = MEMNOTI_LEVEL_CRITICAL;
268 memcleanup_send_broadcast(info, info->current_noti_level, prev_noti_level);
270 _I("%d current level %4.4lf w:%4.4lf c:%4.4lf f:%4.4lf",
271 info->mem_id, level, info->warning_level, info->critical_level, info->full_level);
275 if (level <= info->warning_level) {
276 info->current_noti_level = MEMNOTI_LEVEL_WARNING;
277 _I("%d current level %4.4lf w:%4.4lf c:%4.4lf f:%4.4lf",
278 info->mem_id, level, info->warning_level, info->critical_level, info->full_level);
280 memcleanup_send_broadcast(info, info->current_noti_level, prev_noti_level);
282 info->current_noti_level = MEMNOTI_LEVEL_NORMAL;
286 static int storage_get_memory_size(const char *path, struct statvfs *s)
291 _E("input param error");
295 ret = statvfs(path, s);
297 _E("fail to get storage size");
304 static void get_storage_status(const char *path, struct statvfs *s)
306 if (strcmp(path, tzplatform_getenv(TZ_SYS_USER)) == 0)
307 storage_get_internal_memory_size(s);
309 storage_get_memory_size(path, s);
312 static void init_storage_config_info(const char *path, struct storage_config_info *info)
318 get_storage_status(path, &s);
320 dTotal = (double)s.f_frsize * s.f_blocks;
321 dAvail = (double)s.f_bsize * s.f_bavail;
323 info->full_level += (MEMORY_MEGABYTE_VALUE/dTotal)*100;
325 _I("%s t: %4.0lf a: %4.0lf(%4.2lf) w:%4.4lf c:%4.4lf f:%4.4lf",
326 path, dTotal, dAvail, (dAvail*100/dTotal), info->warning_level, info->critical_level, info->full_level);
329 static void check_internal_storage_popup(struct storage_config_info *info)
331 static enum memnoti_level old = MEMNOTI_LEVEL_NORMAL;
334 if (info->current_noti_level < MEMNOTI_LEVEL_NORMAL && info->current_noti_level < old) {
335 ret = memnoti_popup(info->current_noti_level);
337 info->current_noti_level = MEMNOTI_LEVEL_NORMAL;
339 old = info->current_noti_level;
342 static gboolean check_storage_status(gpointer data)
349 storage_get_internal_memory_size(&s);
350 dTotal = (double)s.f_frsize * s.f_blocks;
351 dAvail = (double)s.f_bsize * s.f_bavail;
352 storage_status_broadcast(&storage_internal_info, dTotal, dAvail);
353 check_internal_storage_popup(&storage_internal_info);
355 storage_get_memory_size(MEMORY_STATUS_TMP_PATH, &s);
356 dTotal = (double)s.f_frsize * s.f_blocks;
357 dAvail = (double)s.f_bsize * s.f_bavail;
358 storage_status_broadcast(&storage_tmp_info, dTotal, dAvail);
360 storage_get_memory_size(MEMORY_STATUS_OPT_PATH, &s);
361 dTotal = (double)s.f_frsize * s.f_blocks;
362 dAvail = (double)s.f_bsize * s.f_bavail;
363 storage_status_broadcast(&storage_opt_info, dTotal, dAvail);
366 g_source_remove(memnoti_timer);
367 memnoti_timer = g_timeout_add(MEMNOTI_TIMER_INTERVAL,
368 check_storage_status, NULL);
371 return G_SOURCE_REMOVE;
374 static int init_storage_config_info_all(void)
376 init_storage_config_info(tzplatform_getenv(TZ_SYS_USER), &storage_internal_info);
377 init_storage_config_info(MEMORY_STATUS_TMP_PATH, &storage_tmp_info);
378 init_storage_config_info(MEMORY_STATUS_OPT_PATH, &storage_opt_info);
379 memnoti_timer = g_timeout_add(MEMNOTI_TIMER_INTERVAL,
380 check_storage_status, NULL);
381 if (memnoti_timer == 0)
382 _E("fail mem available noti timer add");
386 static GVariant *dbus_getstatus(GDBusConnection *conn,
387 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
388 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
391 unsigned long long dAvail = 0.0;
392 unsigned long long dTotal = 0.0;
394 storage_get_internal_memory_size(&s);
395 dTotal = (unsigned long long)s.f_frsize * s.f_blocks;
396 dAvail = (unsigned long long)s.f_bsize * s.f_bavail;
398 return g_variant_new("(tt)", dTotal, dAvail);
401 static GVariant *dbus_get_storage_status(GDBusConnection *conn,
402 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
403 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
410 unsigned long long dAvail = 0.0;
411 unsigned long long dTotal = 0.0;
415 g_variant_get(param, "(s)", &str_path);
417 temp = tzplatform_getenv(TZ_SYS_USER);
418 if (!strncmp(str_path, temp, strlen(temp))) {
419 ret = stat(str_path, &buf);
421 ret = storage_get_internal_memory_size(&s);
427 ret = storage_get_memory_size(str_path, &s);
433 dTotal = (unsigned long long)s.f_frsize * s.f_blocks;
434 dAvail = (unsigned long long)s.f_bsize * s.f_bavail;
437 pid = dbus_handle_get_sender_pid(NULL, sender);
439 _D("[request %d] path %s total %4.0llu avail %4.0llu", pid, str_path, dTotal, dAvail);
442 return g_variant_new("(tt)", dTotal, dAvail);
445 static GVariant *dbus_getstatvfs(GDBusConnection *conn,
446 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
447 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
453 g_variant_get(param, "(s)", &str_path);
455 storage_get_memory_size(str_path, &s);
457 pid = dbus_handle_get_sender_pid(NULL, sender);
459 _D("[request %d] path %s", pid, str_path);
463 return g_variant_new("(ttttttttttt)", (guint64)(s.f_bsize), (guint64)(s.f_frsize),
464 (guint64)(s.f_blocks), (guint64)(s.f_bfree), (guint64)(s.f_bavail),
465 (guint64)(s.f_files), (guint64)(s.f_ffree), (guint64)(s.f_favail),
466 (guint64)(s.f_fsid), (guint64)(s.f_flag), (guint64)(s.f_namemax));
470 static GVariant *dbus_get_storage_level(GDBusConnection *conn,
471 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
472 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
475 enum memnoti_level level;
478 g_variant_get(param, "(i)", &path_id);
480 if (path_id == TZ_SYS_USER)
481 level = storage_internal_info.current_noti_level;
482 else if (path_id == TZ_SYS_OPT)
483 level = storage_opt_info.current_noti_level;
484 else if (path_id == TZ_SYS_TMP)
485 level = storage_tmp_info.current_noti_level;
489 if (level == MEMNOTI_LEVEL_WARNING)
491 else if (level == MEMNOTI_LEVEL_CRITICAL)
493 else if (level == MEMNOTI_LEVEL_FULL)
495 else if (level == MEMNOTI_LEVEL_NORMAL)
498 value = "Not supported path";
500 return g_variant_new("(s)", value);
503 static const dbus_method_s storage_methods[] = {
504 { "getstorage", NULL, "tt", dbus_getstatus },
505 { "GetStatus", "s", "tt", dbus_get_storage_status },
506 { "GetStatvfs", "s", "ttttttttttt", dbus_getstatvfs },
507 { "GetStorageLevel", "i", "s", dbus_get_storage_level },
508 /* Add methods here */
511 static dbus_interface_u storage_interface = {
512 .name = STORAGED_INTERFACE_STORAGE,
513 .methods = storage_methods,
514 .nr_methods = ARRAY_SIZE(storage_methods),
517 static void booting_done(GDBusConnection *conn,
532 if (init_storage_config_info_all() == -1)
533 _E("fail remain mem noti control fd init");
536 static void storage_poweroff(GDBusConnection *conn,
545 g_source_remove(memnoti_timer);
550 static int load_config(struct parse_result *result, void *user_data)
552 struct storage_config_info *info = (struct storage_config_info *)user_data;
559 if (!MATCH(result->section, "LOWSTORAGE"))
562 _D("%s,%s,%s", result->section, result->name, result->value);
565 value = result->value;
567 if (MATCH(name, "WARNING_LEVEL"))
568 info->warning_level = (double)atof(value);
569 else if (MATCH(name, "CRITICAL_LEVEL"))
570 info->critical_level = (double)atof(value);
571 else if (MATCH(name, "FULL_LEVEL"))
572 info->full_level = (double)atof(value);
577 static void storage_config_load(struct storage_config_info *info)
581 ret = config_parse(STORAGE_CONF_FILE, load_config, info);
583 _E("Failed to load %s, %d Use default value!", STORAGE_CONF_FILE, ret);
586 static void storage_init(void *data)
591 storage_config_load(&storage_internal_info);
593 /* System Session is loaded completely */
594 id_booting_done = subscribe_dbus_signal(NULL, SYSTEMD_DBUS_PATH,
595 SYSTEMD_DBUS_IFACE_MANAGER,
596 SYSTEMD_DBUS_SIGNAL_SYSTEM_STARTUP_FINISHED,
597 booting_done, NULL, NULL);
599 id_storage_poweroff = subscribe_dbus_signal(NULL, DEVICED_PATH_POWEROFF,
600 DEVICED_INTERFACE_POWEROFF,
601 SIGNAL_POWEROFF_STATE,
602 storage_poweroff, NULL, NULL);
604 ret = dbus_handle_register_dbus_object(NULL, STORAGED_PATH_STORAGE,
607 _E("Failed to register dbus interface and methods(%d)", ret);
609 ret = stat(STORAGED_DIR_PATH, &buf);
611 ret = mkdir(STORAGED_DIR_PATH, 0644);
613 _E("Failed to make directory: %d", errno);
614 } else if (!S_ISDIR(buf.st_mode)) {
615 ret = remove(STORAGED_DIR_PATH);
617 _E("Fail to remove %s. errno: %d", STORAGED_DIR_PATH, errno);
618 ret = mkdir(STORAGED_DIR_PATH, 0644);
620 _E("Failed to make directory: %d", errno);
622 ret = chmod(STORAGED_DIR_PATH, 0644);
624 _E("Fail to change permissions of a file");
627 ret = stat(NEED_CLEANUP_DIR_PATH, &buf);
629 ret = mkdir(NEED_CLEANUP_DIR_PATH, 0644);
631 _E("Failed to make directory: %d", errno);
632 } else if (!S_ISDIR(buf.st_mode)) {
633 ret = remove(NEED_CLEANUP_DIR_PATH);
635 _E("Fail to remove %s. errno: %d", NEED_CLEANUP_DIR_PATH, errno);
636 ret = mkdir(NEED_CLEANUP_DIR_PATH, 0644);
638 _E("Failed to make directory: %d", errno);
640 ret = chmod(NEED_CLEANUP_DIR_PATH, 0644);
642 _E("Fail to change permissions of a file");
646 static void storage_exit(void *data)
648 /* unregister notifier for below each event */
649 unsubscribe_dbus_signal(NULL, id_booting_done);
650 unsubscribe_dbus_signal(NULL, id_storage_poweroff);
653 static storaged_module_interface storage_module = {
655 .init = storage_init,
656 .exit = storage_exit,
659 __attribute__ ((visibility("default")))storaged_module_interface *
660 storaged_get_module_interface(void)
662 return &storage_module;