BuildRequires: pkgconfig(blkid)
BuildRequires: pkgconfig(mount)
BuildRequires: pkgconfig(libsyscommon)
+BuildRequires: pkgconfig(json-c)
BuildRequires: pkgconfig(capi-ui-efl-util)
BuildRequires: pkgconfig(capi-appfw-application)
dlog
gio-2.0
glib-2.0
+ json-c
storage
vconf
libsyscommon
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIC")
SET(CMAKE_C_FLAGS_DEBUG "-O0 -g")
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/storage)
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/shared)
FILE(GLOB ALL_SRCS "*.c")
--- /dev/null
+/*
+ * Copyright (c) 2019, Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <pthread.h>
+#include <assert.h>
+#include <json-c/json.h>
+#include <sys/stat.h>
+#include <gmodule.h>
+#include <limits.h>
+
+#include "log.h"
+#include "cleanup.h"
+#include "cleanup_config.h"
+
+enum cleanup_running_type {
+ CLEANUP_TYPE_NONE = 0,
+ CLEANUP_TYPE_SYSTEM,
+ CLEANUP_TYPE_USER
+};
+
+struct cleanup_request {
+ int cleanup_mode;
+ int level;
+};
+
+#define REMOVE(path) { \
+ if(remove(path) != 0) { \
+ _E("Failed to remove(%s): %d", path, errno); \
+ return -errno; \
+ } \
+ _D("Remove (%s)", path); \
+}
+
+#define ASSERT(ret, format, arg...) {\
+ if (!ret) { \
+ _E(format, ##arg); \
+ assert(ret); \
+ } \
+}
+
+static pthread_mutex_t mutex_cancel;
+static pthread_mutex_t mutex_lock;
+static pthread_t cleanup_th = 0;
+static GList *request_queue = NULL;
+static int cleanup_canceled = 0;
+
+static int is_cleanup_canceled()
+{
+ int ret = 0;
+ ASSERT((pthread_mutex_lock(&mutex_cancel) == 0), "Assert: Failed to pthread_mutex_lock for cancel.");
+ ret = cleanup_canceled;
+ pthread_mutex_unlock(&mutex_cancel);
+
+ return ret;
+}
+
+static void cleanup_cancel()
+{
+ ASSERT((pthread_mutex_lock(&mutex_cancel) == 0), "Assert: Failed to pthread_mutex_lock for cancel.");
+ cleanup_canceled = 1;
+ pthread_mutex_unlock(&mutex_cancel);
+}
+
+static int find_sub_except_item(const char *item, const char *path)
+{
+ if (strstr(item, path)) {
+ _D("Find except item: %s, path: %s", item, path);
+ return 0;
+ }
+
+ return -1;
+}
+
+static int remove_path(const char *path, GList *except)
+{
+ if (!path)
+ return -EINVAL;
+
+ if (!except)
+ REMOVE(path)
+ else if (except && !g_list_find_custom(except, path, (GCompareFunc)find_sub_except_item))
+ REMOVE(path);
+
+ return 0;
+}
+
+static int cleanup_recursive(const char *path, GList *except)
+{
+ DIR *dir = NULL;
+ struct dirent *dent;
+ struct stat fstat;
+ char sub_path[PATH_MAX] = {0,};
+ int ret;
+
+ if (except && g_list_find_custom(except, path, (GCompareFunc)strcmp))
+ return 0;
+
+ if (is_cleanup_canceled())
+ return 0;
+
+ ret = lstat(path, &fstat);
+ if (ret)
+ return -EINVAL;
+
+ if ((fstat.st_mode & S_IFMT) == S_IFDIR) {
+ if (access(path, W_OK) != 0) {
+ _E("Failed to access file status(%s).", path);
+ return -EINVAL;
+ }
+
+ dir = opendir(path);
+ if (!dir) {
+ _E("Failed to open dir(%s).", path);
+ return -EINVAL;
+ }
+
+ while((dent = readdir(dir))) {
+ if(strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0)
+ continue;
+
+ if (is_cleanup_canceled()) {
+ ret = -EINVAL;
+ break;
+ }
+
+ if (PATH_MAX <= (strlen(path) + strlen(dent->d_name) + 1)) {
+ _E("File path sould be shorter than %d. But %d.", PATH_MAX, strlen(path) + strlen(dent->d_name) + 1);
+ continue;
+ }
+
+ snprintf(sub_path, PATH_MAX, "%s/%s", path, dent->d_name);
+ ret = cleanup_recursive(sub_path, except);
+ if (ret != 0)
+ break;
+ }
+ closedir(dir);
+ if (!ret)
+ ret = remove_path(path, except);
+ } else
+ REMOVE(path);
+
+ return ret;
+}
+
+static int add_request_queue(int type, int level)
+{
+ int ret = 0;
+ struct cleanup_request *request = NULL, *item = NULL;
+ GList *list = NULL;
+
+ ASSERT((pthread_mutex_lock(&mutex_lock) == 0), "Assert: Failed to pthread_mutex_lock.");
+
+ for (list = g_list_first(request_queue); list != NULL; list = g_list_next(list)) {
+ item = list->data;
+
+ if ((item->cleanup_mode == type) && (item->level == level)) {
+ _D("cleanup request is already added.");
+ goto cleanup;
+ }
+ }
+
+ request = malloc(sizeof(struct cleanup_request));
+ if (!request) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ request->cleanup_mode = type;
+ request->level = level;
+ request_queue = g_list_append(request_queue, request);
+
+cleanup:
+ pthread_mutex_unlock(&mutex_lock);
+ return ret;
+}
+
+static void remove_request_queue(struct cleanup_request *item)
+{
+ ASSERT((pthread_mutex_lock(&mutex_lock) == 0), "Assert: Failed to pthread_mutex_lock.");
+
+ request_queue = g_list_remove(request_queue, item);
+ free(item);
+ pthread_mutex_unlock(&mutex_lock);
+}
+
+static struct cleanup_request *get_request_queue()
+{
+ GList *list = NULL;
+
+ ASSERT((pthread_mutex_lock(&mutex_lock) == 0), "Assert: Failed to pthread_mutex_lock.");
+
+ list = g_list_first(request_queue);
+ pthread_mutex_unlock(&mutex_lock);
+
+ return (list)? list->data :NULL;
+}
+
+static GList *get_cleanup_config(int type)
+{
+ if (type == CLEANUP_TYPE_SYSTEM)
+ return get_cleanup_config_system();
+ else if (type == CLEANUP_TYPE_USER)
+ return get_cleanup_config_user();
+
+ return NULL;
+}
+
+// Called by thread
+void *cleanup_storage_start(void *arg)
+{
+ GList *list_config = NULL;
+ GList *list = NULL;
+ struct cleanup_config *config = NULL;
+ struct cleanup_request *request = NULL;
+
+ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+ while(true) {
+ request = get_request_queue();
+ if (!request) {
+ _D("There is no more cleanup request.");
+ break;
+ }
+
+ _D("Cleanup: mode=%d, level=%d", request->cleanup_mode, request->level);
+ list_config = get_cleanup_config(request->cleanup_mode);
+ for (list= g_list_first(list_config); NULL != list; list = g_list_next(list)) {
+ config = list->data;
+ if (request->level <= config->level)
+ cleanup_recursive(config->path, config->exclusion_list);
+ }
+
+ remove_request_queue(request);
+ }
+ cleanup_th = 0;
+ _D("Cleanup thread exit.");
+ return NULL;
+}
+
+void cleanup_storage(enum tzplatform_variable path_id, int level)
+{
+ bool bth = (get_request_queue() == NULL);
+ int type = CLEANUP_TYPE_NONE;
+ int ret;
+
+ if (path_id == TZ_SYS_OPT)
+ type = CLEANUP_TYPE_SYSTEM;
+ else if (path_id == TZ_SYS_USER)
+ type = CLEANUP_TYPE_USER;
+ else {
+ _D("Not supported path type: %d", path_id);
+ return ;
+ }
+
+ if (!get_cleanup_config(type)) {
+ _D("There is no list for cleanup.");
+ return ;
+ }
+
+ if (add_request_queue(type, level) != 0) {
+ _E("Failed to add request.");
+ return ;
+ }
+ //_D("Add cleanup request.(type:%d, level:%d)", type, level);
+
+ if (bth) {
+ ret = pthread_create(&cleanup_th, NULL, cleanup_storage_start, NULL);
+ if (ret != 0)
+ _E("Failed to start pthread: %d", ret);
+ }
+}
+
+void init_cleanup_storage()
+{
+ load_cleanup_config();
+
+ pthread_mutex_init(&mutex_lock, NULL);
+ pthread_mutex_init(&mutex_cancel, NULL);
+}
+
+void free_cleanup_storage()
+{
+ ASSERT((pthread_mutex_lock(&mutex_lock) == 0), "Assert: Failed to pthread_mutex_lock.");
+ if (request_queue)
+ g_list_free_full(request_queue, free);
+ request_queue = NULL;
+ pthread_mutex_unlock(&mutex_lock);
+
+ if (cleanup_th) {
+ _D("Cancel cleanup thread %d.", (int)cleanup_th);
+ cleanup_cancel();
+ //pthread_cancel(cleanup_th);
+ pthread_join(cleanup_th, NULL);
+ _D("Exit cleanup thread %d.", (int)cleanup_th);
+ }
+
+ free_cleanup_config();
+}
--- /dev/null
+/*
+ * storaged
+ *
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __STORAGED_CLEANUP_H__
+#define __STORAGED_CLEANUP_H__
+
+#include <stdio.h>
+#include <tzplatform_config.h>
+
+void init_cleanup_storage();
+void free_cleanup_storage();
+
+void cleanup_storage(enum tzplatform_variable path_id, int level);
+
+#endif /* #define __STORAGED_CLEANUP_H___ */
--- /dev/null
+/*
+ * Copyright (c) 2019, Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <json-c/json.h>
+#include <tzplatform_config.h>
+
+#include "log.h"
+#include "cleanup_config.h"
+
+char *cleanup_level_str[] = {
+ "full",
+ "critical",
+ "warning",
+ "normal"
+};
+
+#define DEF_CLEANUP_CONFIG_FILE "/etc/storaged/cleanup-storage.conf"
+#define RESULT(ret, val) \
+{ \
+ if (ret) \
+ *ret = val; \
+}
+
+static GList *cleanup_list_system = NULL;
+static GList *cleanup_list_user = NULL;
+
+static void free_config_item(void *data)
+{
+ struct cleanup_config *item = data;
+
+ if (item->path) {
+ free(item->path);
+ item->path = NULL;
+ }
+
+ if (item->exclusion_list) {
+ g_list_free_full(item->exclusion_list, free);
+ item->exclusion_list = NULL;
+ }
+
+ free(item);
+}
+
+void free_cleanup_config()
+{
+ if (cleanup_list_system) {
+ g_list_free_full(cleanup_list_system, free_config_item);
+ cleanup_list_system = NULL;
+ }
+
+ if (cleanup_list_user) {
+ g_list_free_full(cleanup_list_user, free_config_item);
+ cleanup_list_user = NULL;
+ }
+}
+
+static int get_config_string_field(struct json_object *root, const char *key, char** value)
+{
+ struct json_object *node = NULL;
+ const char *node_value;
+
+ if (!json_object_object_get_ex(root, key, &node)) {
+ _D("Config doesn't contain path param.");
+ return 0;
+ }
+
+ if (!json_object_is_type(node, json_type_string)) {
+ _E("'%s' type should be string. but it isn't.", key);
+ return -EINVAL;
+ }
+
+ node_value = json_object_get_string(node);
+ *value = strdup(node_value);
+ if (*value == NULL) {
+ _E("Failed to duplicate string.");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static GList *get_config_array_field(struct json_object *root, const char *key, int *ret)
+{
+ struct json_object *node = NULL, *obj;
+ int i, len;
+ const char *str = NULL;
+ char *temp;
+ GList *list = NULL;
+
+ if (!json_object_object_get_ex(root, key, &node)) {
+ _D("Config doesn't contain '%s' param.", key);
+ RESULT(ret, 0);
+ return NULL;
+ }
+
+ if (!json_object_is_type(node, json_type_array)) {
+ _E("'%s' type should be array. but it isn't.", key);
+ RESULT(ret, -EINVAL);
+ return NULL;
+ }
+
+ len = json_object_array_length(node);
+ for (i = 0; i < len; i++) {
+ obj = json_object_array_get_idx(node, i);
+ str = json_object_get_string(obj);
+ if (!str)
+ {
+ _E("Failed to get stirng value from json object.");
+ RESULT(ret, -EINVAL);
+ goto cleanup;
+ }
+
+ temp = strdup(str);
+ if (!temp) {
+ _E("Failed to duplicate array item string");
+ RESULT(ret, -ENOMEM);
+ goto cleanup;
+ }
+
+ list = g_list_append(list, temp);
+ }
+
+ RESULT(ret, 0);
+ return list;
+
+cleanup:
+ g_list_free_full(list, free);
+ return NULL;
+}
+
+static int get_cleanup_level(const char *level_str)
+{
+ int ret = -EINVAL;
+ int i;
+
+ if (level_str == NULL)
+ return -EINVAL;
+
+ for (i = 0; i< CLEANUP_LEVEL_NONE; i++) {
+ if (strcmp(level_str, cleanup_level_str[i]) == 0)
+ break;
+ }
+
+ if (i < CLEANUP_LEVEL_NONE)
+ ret = i;
+
+ return ret;
+}
+
+static int add_config_item(struct json_object *obj, struct cleanup_config *item)
+{
+ char *temp = NULL;
+ int ret;
+
+ if (!item) {
+ _E("Parameter is invalid.");
+ return -EINVAL;
+ }
+
+ if (get_config_string_field(obj, "level", &temp) != 0) {
+ _E("There is no level node.");
+ return -EINVAL;
+ }
+
+ item->level = get_cleanup_level(temp);
+ free(temp);
+
+ if (item->level < 0) {
+ _E("Invalid level value.");
+ return -EINVAL;
+ }
+
+ if (get_config_string_field(obj, "path", &item->path) != 0)
+ return -EINVAL;
+
+ //todo.
+ //check duplicated path
+
+ item->exclusion_list = get_config_array_field(obj, "except", &ret);
+ if (ret != 0)
+ return -EINVAL;
+
+ //_D("Get config item: level=%d, path=%s, except count=%d", item->level, item->path, g_list_length(item->exclusion_list));
+ return 0;
+}
+
+static GList *parsing_json_array_object_to_list(struct json_object *array, int *ret)
+{
+ int i, len, item_size;
+ GList *list = NULL;
+ struct cleanup_config *item = NULL;
+ struct json_object *obj = NULL;
+
+ if (!json_object_is_type(array, json_type_array)) {
+ _E("Config value is not an array.");
+ RESULT(ret, -EINVAL);
+ return NULL;
+ }
+
+ len = json_object_array_length(array);
+ for (i = 0; i < len; ++i) {
+ obj = json_object_array_get_idx(array, i);
+ if (!json_object_is_type(obj, json_type_object)) {
+ _E("Array item is not an object.");
+ RESULT(ret, -EINVAL);
+ goto cleanup;
+ }
+
+ item_size = sizeof(struct cleanup_config);
+ item = malloc(item_size);
+ if (!item) {
+ _E("Memory allocation fail. size=%d", item_size);
+ RESULT(ret, -ENOMEM);
+ goto cleanup;
+ }
+ memset(item, '\0', item_size);
+
+ if (add_config_item(json_object_get(obj), item) != 0) {
+ RESULT(ret, -EINVAL);
+ goto cleanup;
+ }
+ list = g_list_append(list, item);
+ }
+ RESULT(ret, 0);
+ return list;
+cleanup:
+ if (item)
+ free(item);
+
+ g_list_free_full(list, free_config_item);
+ return NULL;
+}
+
+int load_json_object_with_file(const char *file)
+{
+ struct json_object *root_node, *array;
+ const char *root = NULL;
+ int ret = 0;
+
+ root_node = json_object_from_file(file);
+ if (root_node == NULL) {
+ _E("Could not create object from json file '%s'", file);
+ return -EINVAL;
+ }
+
+ root = tzplatform_getenv(TZ_SYS_OPT);
+ if (json_object_object_get_ex(root_node, root, &array)) {
+ cleanup_list_system = parsing_json_array_object_to_list(array, &ret);
+ if(ret != 0)
+ goto cleanup;
+ _D("'%s' config has %d items.", root, g_list_length(cleanup_list_system));
+ }
+
+ root = tzplatform_getenv(TZ_SYS_USER);
+ if (json_object_object_get_ex(root_node, root, &array)) {
+ cleanup_list_user = parsing_json_array_object_to_list(array, &ret);
+ if (ret != 0)
+ goto cleanup;
+ _D("'%s' config has %d items.", root, g_list_length(cleanup_list_user));
+ }
+
+cleanup:
+ json_object_put(root_node);
+ return ret;
+}
+
+int load_cleanup_config()
+{
+ int ret = 0;
+
+ ret = access(DEF_CLEANUP_CONFIG_FILE, F_OK|R_OK);
+ if (ret) {
+ if (errno == ENOENT) {
+ _D("Config file doesn't exist '%s'", DEF_CLEANUP_CONFIG_FILE);
+ return -ENOENT;
+ }
+ _E("Could not access config file '%s'", DEF_CLEANUP_CONFIG_FILE);
+ return -errno;
+ }
+
+ _D("Using cleanup config file: '%s'", DEF_CLEANUP_CONFIG_FILE);
+
+ ret = load_json_object_with_file(DEF_CLEANUP_CONFIG_FILE);
+
+ return ret;
+}
+
+GList *get_cleanup_config_system()
+{
+ return cleanup_list_system;
+}
+
+GList *get_cleanup_config_user()
+{
+ return cleanup_list_user;
+}
--- /dev/null
+/*
+ * storaged
+ *
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __STORAGED_CLEANUP_CONFIG_H__
+#define __STORAGED_CLEANUP_CONFIG_H__
+
+#include <stdio.h>
+#include <gmodule.h>
+
+enum cleanup_level {
+ CLEANUP_LEVEL_FULL = 0,
+ CLEANUP_LEVEL_CRITICAL,
+ CLEANUP_LEVEL_WARNING,
+ CLEANUP_LEVEL_NORMAL,
+ CLEANUP_LEVEL_NONE
+};
+
+struct cleanup_config {
+ int level;
+ char *path;
+ GList *exclusion_list;
+};
+
+int load_cleanup_config();
+void free_cleanup_config();
+
+GList *get_cleanup_config_system();
+GList *get_cleanup_config_user();
+
+#endif /* #define __STORAGED_CLEANUP_CONFIG_H___ */
--- /dev/null
+{
+ "/opt":[
+ {
+ "level":"critical",
+ "path":"/opt/val/test",
+ "except":[
+ "/opt/val/test/except1",
+ "/opt/val/test/except2"
+ ]
+ }
+ ],
+ "/opt/usr":[
+ {
+ "level":"full",
+ "path":"/opt/usr/logtest"
+ },
+ {
+ "level":"full",
+ "path":"/opt/usr/etc/test"
+ },
+ {
+ "level":"full",
+ "path":"/opt/usr/etc/test_crash"
+ },
+ {
+ "level":"critical",
+ "path":"/opt/usr/data/test_GL"
+ }
+ ]
+}
#include "config-parser.h"
#include "module-intf.h"
#include "storaged_common.h"
+#include "cleanup.h"
#define MEMORY_STATUS_TMP_PATH "/tmp"
#define MEMORY_STATUS_OPT_PATH "/opt"
g_variant_new("(is)", path, value));
if (ret < 0)
_E("Failed to send dbus signal");
+
+ cleanup_storage(path, level);
}
static void _popup_cb(GVariant *var, void *user_data, GError *err)
if (ret < 0)
_E("Failed to make directory: %d", errno);
+ init_cleanup_storage();
+
id_storage_poweroff = subscribe_dbus_signal(NULL, DEVICED_PATH_POWEROFF,
DEVICED_INTERFACE_POWEROFF,
SIGNAL_POWEROFF_STATE,
static void storage_exit(void *data)
{
+ free_cleanup_storage();
/* unregister notifier for below each event */
unsubscribe_dbus_signal(NULL, id_storage_poweroff);
}