2 * Copyright (c) 2019, Samsung Electronics Co., Ltd. All rights reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
24 #include <json-c/json.h>
26 #include <sys/types.h>
35 #include "cleanup_config.h"
37 enum cleanup_running_type {
38 CLEANUP_TYPE_NONE = 0,
43 struct cleanup_request {
48 struct cleanup_history {
49 enum cleanup_running_type type;
54 #define REMOVE(path) { \
55 if(remove(path) != 0) { \
56 _E("Failed to remove(%s): %d", path, errno); \
59 _D("Remove (%s)", path); \
62 #define ASSERT(ret, format, arg...) {\
69 #define CLEANUP_INTERVAL_SEC (10 * 60)
71 #define CLEANUP_STORAGE_FULL_LOG_PATH "/var/log/storage"
72 #define CLEANUP_STORAGE_FULL_SYSTEM_FILE CLEANUP_STORAGE_FULL_LOG_PATH"/opt_full.log"
73 #define CLEANUP_STORAGE_FULL_USER_FILE CLEANUP_STORAGE_FULL_LOG_PATH"/opt_usr_full.log"
75 static pthread_mutex_t mutex_cancel;
76 static pthread_mutex_t mutex_lock;
77 static pthread_t cleanup_th = 0;
78 static GList *request_queue = NULL;
79 static int cleanup_canceled = 0;
80 static GSList *history = NULL;
82 static int is_cleanup_canceled()
85 ASSERT((pthread_mutex_lock(&mutex_cancel) == 0), "Assert: Failed to pthread_mutex_lock for cancel.");
86 ret = cleanup_canceled;
87 pthread_mutex_unlock(&mutex_cancel);
92 static void cleanup_cancel()
94 ASSERT((pthread_mutex_lock(&mutex_cancel) == 0), "Assert: Failed to pthread_mutex_lock for cancel.");
96 pthread_mutex_unlock(&mutex_cancel);
99 static int find_sub_except_item(const char *item, const char *path)
101 if (strstr(item, path)) {
102 _D("Find except item: %s, path: %s", item, path);
109 static int remove_dir(const char *path, GList *except)
116 else if (except && !g_list_find_custom(except, path, (GCompareFunc)find_sub_except_item))
122 static int remove_oldfile(const char *path)
130 ret = regcomp(®ex, "\\.[0-9]+$", REG_EXTENDED);
134 if (!regexec(®ex, path, 0, NULL, 0)) {
135 if(remove(path) != 0) {
136 _E("Failed to remove(%s): %d", path, errno);
139 _D("Remove (%s)", path);
146 static int cleanup_recursive(const char *path, GList *except, int target)
151 char sub_path[PATH_MAX] = {0,};
154 if (except && g_list_find_custom(except, path, (GCompareFunc)strcmp))
157 if (is_cleanup_canceled())
160 ret = lstat(path, &fstat);
164 if ((fstat.st_mode & S_IFMT) == S_IFDIR) {
165 if (access(path, W_OK) != 0) {
166 _E("Failed to access file status(%s).", path);
172 _E("Failed to open dir(%s).", path);
176 while((dent = readdir(dir))) {
177 if(strcmp(dent->d_name, ".") == 0 ||
178 strcmp(dent->d_name, "..") == 0)
181 if (is_cleanup_canceled()) {
186 if (PATH_MAX <= (strlen(path) + strlen(dent->d_name) + 1)) {
187 _E("File path sould be shorter than %d. But %zu.", PATH_MAX, strlen(path) + strlen(dent->d_name) + 1);
191 snprintf(sub_path, PATH_MAX, "%s/%s", path, dent->d_name);
192 ret = cleanup_recursive(sub_path, except, (target == CLEANUP_TARGET_OLDFILE)? target: CLEANUP_TARGET_ALL);
197 if (!ret && (target == CLEANUP_TARGET_ALL))
198 ret = remove_dir(path, except);
199 } else if (target == CLEANUP_TARGET_OLDFILE)
200 remove_oldfile(path);
207 static bool check_history(enum cleanup_running_type type, int level)
211 struct cleanup_history *item = NULL;
215 * When requesting the same or lower level of cleanup again
216 (warning->warning, full->critical or critical->warning),
217 do it at a specific interval (10 minutes).
219 * When requesting the higher level of cleanup again
220 (warning->critical or critical->full),
223 for (list = history; list != NULL; list = g_slist_next(list)) {
225 if (item->type == type) {
226 if (item->level > level)
229 interval = time(NULL) - item->tm;
230 if (interval < CLEANUP_INTERVAL_SEC) {
231 //_D("Cleanup(type:%d, level:%d) is already requested before %u seconds.", type, level, (unsigned int)interval);
242 static void update_history(enum cleanup_running_type type, int level)
245 struct cleanup_history *item = NULL, *find = NULL;
247 for (list = history; list != NULL; list = g_slist_next(list)) {
249 if (item->type == type) {
256 find = malloc(sizeof(struct cleanup_history));
258 _E("Failed to call malloc function.");
263 find->tm = time(NULL);
264 history = g_slist_prepend(history, find);
267 find->tm = time(NULL);
271 static void save_log_file(enum cleanup_running_type type, int path_id)
276 struct group *group_entry;
277 const char *cleanup_path;
279 if (type == CLEANUP_TYPE_SYSTEM)
280 logpath = CLEANUP_STORAGE_FULL_SYSTEM_FILE;
281 else if (type == CLEANUP_TYPE_USER)
282 logpath = CLEANUP_STORAGE_FULL_USER_FILE;
284 _E("Invalied cleanup type : %u", type);
288 cleanup_path = tzplatform_getenv(path_id);
290 _E("Failed to get cleanup root path.");
295 ret = mkdir(CLEANUP_STORAGE_FULL_LOG_PATH, 0755);
296 if ((ret < 0) && (errno != EEXIST)) {
297 _E("Failed to mkdir: %m");
301 ret = asprintf(&du_cmd, "du -ah %s/>%s 2>&1", cleanup_path, logpath);
303 _E("Failed to allocate memory.");
307 ret = system(du_cmd);
308 if ((ret == -1) || (ret == 127))
309 _E("Failed to run '%s' command: %d", du_cmd, ret);
311 _D("Save log: %s", du_cmd);
316 group_entry = getgrnam("system_share");
318 _E("Failed to getgrnam: %m");
322 if (chown(logpath, -1, group_entry->gr_gid) < 0) {
323 _E("Failed to chown: %m");
327 if (chmod(logpath, 0666) < 0) {
328 _E("Failed to chmod: %m");
333 static int add_request_queue(int type, int level)
336 struct cleanup_request *request = NULL, *item = NULL;
339 ASSERT((pthread_mutex_lock(&mutex_lock) == 0), "Assert: Failed to pthread_mutex_lock.");
341 for (list = g_list_first(request_queue); list != NULL; list = g_list_next(list)) {
344 if ((item->cleanup_mode == type) && (item->level == level)) {
345 _D("cleanup request is already added.");
350 request = malloc(sizeof(struct cleanup_request));
356 request->cleanup_mode = type;
357 request->level = level;
358 request_queue = g_list_append(request_queue, request);
361 pthread_mutex_unlock(&mutex_lock);
365 static void remove_request_queue(struct cleanup_request *item)
367 ASSERT((pthread_mutex_lock(&mutex_lock) == 0), "Assert: Failed to pthread_mutex_lock.");
369 request_queue = g_list_remove(request_queue, item);
371 pthread_mutex_unlock(&mutex_lock);
374 static struct cleanup_request *get_request_queue()
378 ASSERT((pthread_mutex_lock(&mutex_lock) == 0), "Assert: Failed to pthread_mutex_lock.");
380 list = g_list_first(request_queue);
381 pthread_mutex_unlock(&mutex_lock);
383 return (list)? list->data :NULL;
386 static GList *get_cleanup_config(int type)
388 if (type == CLEANUP_TYPE_SYSTEM)
389 return get_cleanup_config_system();
390 else if (type == CLEANUP_TYPE_USER)
391 return get_cleanup_config_user();
397 void *cleanup_storage_start(void *arg)
399 GList *list_config = NULL;
401 struct cleanup_config *config = NULL;
402 struct cleanup_request *request = NULL;
404 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
406 request = get_request_queue();
408 _D("There is no more cleanup request.");
412 _D("Cleanup: mode=%d, level=%d", request->cleanup_mode, request->level);
413 list_config = get_cleanup_config(request->cleanup_mode);
414 for (list= g_list_first(list_config); NULL != list; list = g_list_next(list)) {
416 _D("config path:%s, target:%d", config->path, config->target);
417 if (request->level <= config->level)
418 cleanup_recursive(config->path, config->exclusion_list, config->target);
420 remove_request_queue(request);
423 _D("Cleanup thread exit.");
427 void cleanup_storage(enum tzplatform_variable path_id, int level)
429 bool bth = (get_request_queue() == NULL);
430 enum cleanup_running_type type = CLEANUP_TYPE_NONE;
433 if (path_id == TZ_SYS_OPT)
434 type = CLEANUP_TYPE_SYSTEM;
435 else if (path_id == TZ_SYS_USER)
436 type = CLEANUP_TYPE_USER;
438 _D("Not supported path type: %s", tzplatform_getname(path_id));
442 if (!get_cleanup_config(type)) {
443 _D("There is no list for cleanup.");
447 if (!check_history(type, level))
450 if (add_request_queue(type, level) != 0) {
451 _E("Failed to add request.");
454 //_D("Add cleanup request.(type:%d, level:%d)", type, level);
456 update_history(type, level);
457 save_log_file(type, path_id);
460 ret = pthread_create(&cleanup_th, NULL, cleanup_storage_start, NULL);
462 _E("Failed to start pthread: %d", ret);
466 void init_cleanup_storage()
468 load_cleanup_config();
470 pthread_mutex_init(&mutex_lock, NULL);
471 pthread_mutex_init(&mutex_cancel, NULL);
474 void free_cleanup_storage()
476 ASSERT((pthread_mutex_lock(&mutex_lock) == 0), "Assert: Failed to pthread_mutex_lock.");
478 g_list_free_full(request_queue, free);
479 request_queue = NULL;
480 pthread_mutex_unlock(&mutex_lock);
483 g_slist_free_full(history, free);
487 _D("Cancel cleanup thread %d.", (int)cleanup_th);
489 //pthread_cancel(cleanup_th);
490 pthread_join(cleanup_th, NULL);
491 _D("Exit cleanup thread %d.", (int)cleanup_th);
494 free_cleanup_config();