4 * Copyright (c) 2012 - 2014 Samsung Electronics Co., Ltd. All rights reserved.
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.
22 * @desc start logging system for resourced
24 * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
35 #include <sys/sysinfo.h>
39 #include <sys/resource.h>
43 #include "logging-common.h"
44 #include "resourced.h"
47 #include "proc-process.h"
48 #include "proc-main.h"
49 #include "proc_stat.h"
51 #include "edbus-handler.h"
54 #define WRITE_INFO_MAX 10
55 #define MAX_PROC_LIST 200
57 #define WEBPROCESS_NAME "WebProcess"
58 #define MAX_PROC_ITEM 200
59 #define INC_PROC_ITEM 10
60 #define COMMIT_INTERVAL 10*60 /* 10 min */
61 #define LOGGING_PTIORITY -20
63 #define SIGNAL_LOGGING_INIT "LoggingInit"
64 #define SIGNAL_LOGGING_GET "LoggingGet"
65 #define SIGNAL_LOGGING_UPDATED "LoggingUpdated"
66 #define PROC_OOM_SCORE_ADJ_PATH "/proc/%d/oom_score_adj"
68 struct logging_sub_sys {
70 time_t commit_interval;
72 struct logging_info_ops *ops;
75 static const struct module_ops logging_modules_ops;
76 static const struct module_ops *logging_ops;
78 static int num_log_infos;
79 static bool need_to_update;
80 static GHashTable *logging_proc_list;
81 static GArray *logging_ss_list;
82 static pthread_t logging_thread = 0;
83 static pthread_mutex_t logging_mutex = PTHREAD_MUTEX_INITIALIZER;
84 static pthread_mutex_t proc_list_mutex = PTHREAD_MUTEX_INITIALIZER;
85 static pthread_cond_t logging_cond = PTHREAD_COND_INITIALIZER;
87 static int init_logging_infos(struct logging_infos *info, const char *key,
88 pid_t pid, int oom, time_t now)
95 return RESOURCED_ERROR_FAIL;
100 info->running = true;
102 for (i = 0; i < logging_ss_list->len; i++) {
103 struct logging_sub_sys *ss = &g_array_index(logging_ss_list,
104 struct logging_sub_sys, i);
105 ret = ss->ops->init(&(info->stats[i]), pid, oom, now);
106 if (ret != RESOURCED_ERROR_NONE) {
107 _E("init logging at %lu", now);
108 /* not return error, just continue updating */
112 return RESOURCED_ERROR_NONE;
115 static void update_logging_infos(struct logging_infos *info,
116 time_t now, unsigned first)
120 for (i = 0; i < logging_ss_list->len; i++) {
121 struct logging_sub_sys *ss = &g_array_index(logging_ss_list,
122 struct logging_sub_sys, i);
123 ret = ss->ops->update(info->stats[i], info->pid, info->oom, now, first);
124 if (ret != RESOURCED_ERROR_NONE) {
126 * when update failed, this is because there is no
127 * running process. So, just update processes running
130 info->running = false;
137 static void insert_hash_table(char *key, pid_t pid, int oom)
139 struct logging_infos *info;
146 name = malloc(strlen(key) + 1);
149 _D("memory allocation for name failed");
153 info = (struct logging_infos *)malloc(sizeof(struct logging_infos));
156 _D("memory allocation for logging_infos failed");
161 stats = (void **)malloc(sizeof(void *) * num_log_infos);
164 _D("memory allocation for log infos fails");
171 init_logging_infos(info, name, pid, oom, now);
173 g_hash_table_insert(logging_proc_list, (gpointer) name, (gpointer) info);
174 update_logging_infos(info, now, true);
178 static int write_journal(struct logging_sub_sys *pss, int ss_index)
182 int ret = RESOURCED_ERROR_NONE;
185 struct logging_infos *infos;
186 g_hash_table_iter_init(&iter, logging_proc_list);
189 ret = pthread_mutex_lock(&proc_list_mutex);
191 _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
195 if (!g_hash_table_iter_next(&iter, &key, &value)) {
196 ret = pthread_mutex_unlock(&proc_list_mutex);
198 _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
205 infos = (struct logging_infos *)value;
206 pss->ops->write(name, infos, ss_index);
207 ret = pthread_mutex_unlock(&proc_list_mutex);
209 _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
214 return RESOURCED_ERROR_NONE;
217 static int write_logging_subsys_info(struct logging_sub_sys *pss, int sindex,
218 time_t now, bool force)
221 if (!force && now < pss->last_commit + pss->commit_interval)
222 return RESOURCED_ERROR_NONE;
224 _D("start write %s subsys, now %lu, last:%lu, interval:%lu",
225 pss->name, now, pss->last_commit, pss->commit_interval);
227 write_journal(pss, sindex);
229 pss->last_commit = now;
230 return RESOURCED_ERROR_NONE;
234 static int write_logging_infos(bool force)
242 for (i = 0; i < logging_ss_list->len; i++) {
243 struct logging_sub_sys *ss = &g_array_index(logging_ss_list,
244 struct logging_sub_sys, i);
245 ret = write_logging_subsys_info(ss, i, now, force);
246 if (ret != RESOURCED_ERROR_NONE) {
247 _E("write logging at %lu", now);
248 /* not return error, just continue updating */
252 return RESOURCED_ERROR_NONE;
255 int register_logging_subsystem(const char*name, struct logging_info_ops *ops)
257 struct logging_sub_sys ss;
261 ss_name = malloc(strlen(name)+1);
264 _E("memory allocation for name is failed");
265 return RESOURCED_ERROR_FAIL;
270 strcpy(ss_name, name);
272 ss.commit_interval = COMMIT_INTERVAL;
273 ss.last_commit = now;
276 g_array_append_val(logging_ss_list, ss);
279 return RESOURCED_ERROR_NONE;
282 int update_commit_interval(const char *name, time_t commit_interval)
285 for (i = 0; i < logging_ss_list->len; i++) {
286 struct logging_sub_sys *ss = &g_array_index(logging_ss_list,
287 struct logging_sub_sys, i);
288 if (!strcmp(ss->name, name)) {
289 ss->commit_interval = commit_interval;
290 _D("%s logging subsystem commit interval updated to %lu",
291 ss->name, ss->commit_interval);
292 return RESOURCED_ERROR_NONE;
296 _D("%s subsystem update fail, not exist", name);
297 return RESOURCED_ERROR_FAIL;
300 static inline int is_webprocess(char *name)
302 return !strcmp(name, WEBPROCESS_NAME);
305 static int rename_webprocess(pid_t pgid, char *name)
307 char webui_name[PROC_NAME_MAX];
310 if ((ret = proc_get_cmdline(pgid, webui_name)) != RESOURCED_ERROR_NONE)
311 return RESOURCED_ERROR_FAIL;
314 strcat(name, webui_name);
316 return RESOURCED_ERROR_NONE;
319 static int get_cmdline(pid_t pid, char *cmdline)
321 char buf[PROC_BUF_MAX];
324 sprintf(buf, "/proc/%d/cmdline", pid);
325 fp = fopen(buf, "r");
327 return RESOURCED_ERROR_FAIL;
329 if (fgets(cmdline, PROC_NAME_MAX-1, fp) == NULL) {
331 return RESOURCED_ERROR_FAIL;
335 return RESOURCED_ERROR_NONE;
338 static void insert_proc_list(pid_t pid, pid_t pgid, int oom)
340 int ret = RESOURCED_ERROR_NONE;
341 char name[PROC_NAME_MAX];
342 struct logging_infos *info;
344 ret = get_cmdline(pid, name);
346 * if cmdline does not exist, remove item from queue
347 * and continue logging remaining items
349 if (ret != RESOURCED_ERROR_NONE) {
353 if (is_webprocess(name)) {
354 ret = rename_webprocess(pgid, name);
355 if (ret != RESOURCED_ERROR_NONE)
360 ret = pthread_mutex_lock(&proc_list_mutex);
362 _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
366 info = (struct logging_infos *)
367 g_hash_table_lookup(logging_proc_list, name);
369 /* To Do: handle multiple daemons with the same name */
371 insert_hash_table(name, pid, oom);
373 info->running = true;
378 ret = pthread_mutex_unlock(&proc_list_mutex);
380 _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
386 static bool is_running_process(GArray *garray, pid_t pid)
391 for (i = 0 ; i < garray->len; i++) {
392 tpid = g_array_index(garray, pid_t, i);
399 static void update_proc_state(void)
402 struct dirent *entry;
403 GArray *running_procs = NULL;
408 running_procs = g_array_new(false, false, sizeof(pid_t));
410 if (!running_procs) {
411 _E("fail to create garray for pids");
415 dirp = opendir("/proc");
418 _E("/proc open is failed, and cannot updated running procs");
422 while ((entry = readdir(dirp)) != NULL) {
425 if (!isdigit(entry->d_name[0]))
427 pid = atoi(entry->d_name);
428 g_array_append_val(running_procs, pid);
433 g_hash_table_iter_init(&iter, logging_proc_list);
435 ret = pthread_mutex_lock(&proc_list_mutex);
437 _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
438 g_array_free(running_procs, true);
442 while (g_hash_table_iter_next(&iter, &key, &value)) {
443 struct logging_infos *info = (struct logging_infos *)value;
444 info->running = is_running_process(running_procs, info->pid);
447 g_array_free(running_procs, true);
449 ret = pthread_mutex_unlock(&proc_list_mutex);
451 _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
455 need_to_update = false;
459 static void update_proc_list(void)
464 struct logging_infos *infos;
472 g_hash_table_iter_init(&iter, logging_proc_list);
475 ret = pthread_mutex_lock(&proc_list_mutex);
477 _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
481 if (!g_hash_table_iter_next(&iter, &key, &value)) {
482 ret = pthread_mutex_unlock(&proc_list_mutex);
484 _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
487 _D("finish proc list update");
490 infos = (struct logging_infos *)value;
493 update_logging_infos(infos, now, false);
494 ret = pthread_mutex_unlock(&proc_list_mutex);
496 _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
504 static void logging_update_state(void)
506 need_to_update = true;
509 static int check_running(gpointer key, gpointer value, gpointer user_data)
511 struct logging_infos *infos = (struct logging_infos *)value;
513 return !(infos->running);
516 static void reclaim_proc_list(void)
520 ret = pthread_mutex_lock(&proc_list_mutex);
522 _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
526 g_hash_table_foreach_remove(logging_proc_list, check_running, NULL);
527 ret = pthread_mutex_unlock(&proc_list_mutex);
529 _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
534 static void logging(void)
538 if (g_hash_table_size(logging_proc_list) > MAX_PROC_LIST) {
539 write_logging_infos(true);
542 write_logging_infos(false);
545 static void *logging_pthread(void *arg)
549 setpriority(PRIO_PROCESS, 0, LOGGING_PTIORITY);
553 * When signalled by main thread,
554 * it starts logging_pthread().
556 ret = pthread_mutex_lock(&logging_mutex);
558 _E("logging thread::pthread_mutex_lock() failed, %d", ret);
562 ret = pthread_cond_wait(&logging_cond, &logging_mutex);
564 _E("logging thread::pthread_cond_wait() failed, %d", ret);
565 ret = pthread_mutex_unlock(&logging_mutex);
567 _E("logging thread::pthread_mutex_lock() failed, %d", ret);
573 ret = pthread_mutex_unlock(&logging_mutex);
575 _E("logging thread::pthread_mutex_unlock() failed, %d", ret);
580 /* Now our thread finishes - cleanup TID */
587 static int logging_thread_create(void)
589 int ret = RESOURCED_ERROR_NONE;
591 if (logging_thread) {
592 _I("logging thread %u already created", (unsigned)logging_thread);
594 /* initialize logging_thread */
595 ret = pthread_create(&logging_thread, NULL, (void *)logging_pthread, (void *)NULL);
597 _E("pthread creation for logging_pthread failed, %d\n", ret);
600 _D("pthread creation for logging success");
601 pthread_detach(logging_thread);
608 static void free_key(gpointer key)
614 static void free_value(gpointer value)
617 struct logging_infos * info = (struct logging_infos *)value;
622 for (i = 0; i < num_log_infos; i++) {
624 free(info->stats[i]);
633 static void initialize_logging_proc_list(void)
636 struct dirent *entry;
637 char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
641 dirp = opendir("/proc");
644 _E("/proc open is failed, and cannot updated running procs");
648 while ((entry = readdir(dirp)) != NULL) {
651 if (!isdigit(entry->d_name[0]))
653 pid = atoi(entry->d_name);
657 snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
658 fp = fopen(buf, "r+");
661 if (fgets(buf, sizeof(buf), fp) == NULL) {
667 insert_proc_list(pid, pgid, cur_oom);
671 write_logging_infos(true);
675 static void logging_update_start(void)
678 /* signal to logging_pthread to start */
679 ret = pthread_mutex_lock(&logging_mutex);
681 _E("logging_update_start::pthread_mutex_lock() failed, %d", ret);
685 ret = pthread_cond_signal(&logging_cond);
687 _E("logging_update_start::pthread_cond_wait() failed, %d", ret);
688 ret = pthread_mutex_unlock(&logging_mutex);
690 _E("logging_update_start::pthread_mutex_unlock() failed, %d", ret);
694 _D("send signal logging_pthread");
695 ret = pthread_mutex_unlock(&logging_mutex);
697 _E("logging_update_start::pthread_mutex_unlock() failed, %d", ret);
702 static void broadcast_logging_data_updated_signal(void)
706 r = broadcast_edbus_signal_str(RESOURCED_PATH_LOGGING, RESOURCED_INTERFACE_LOGGING,
707 SIGNAL_LOGGING_UPDATED, NULL, NULL);
708 _I("broadcast logging_data updated signal!");
711 _E("Failed: broadcast logging_data_updated signal");
714 static void logging_init_booting_done_edbus_signal_handler(void *data, DBusMessage *msg)
718 ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_LOGGING,
719 SIGNAL_LOGGING_INIT);
721 _D("there is booting done signal");
724 initialize_logging_proc_list();
725 _D("logging_init_booting_done_edbus_signal_handler");
728 static void logging_get_edbus_signal_handler(void *data, DBusMessage *msg)
732 ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_LOGGING,
735 _D("there is logging get signal");
738 write_logging_infos(true);
739 _D("logging_get_edbus_signal_handler");
740 broadcast_logging_data_updated_signal();
743 static int logging_init(void)
745 int ret = RESOURCED_ERROR_NONE;
747 _D("logging_init start");
749 logging_proc_list = g_hash_table_new_full(
755 if (!logging_proc_list) {
756 _E("fail g_hash_table_new_full() for logging_proc_list");
757 return RESOURCED_ERROR_FAIL;
760 logging_ss_list = g_array_new(false, false,
761 sizeof(struct logging_sub_sys));
763 if (logging_ss_list == NULL)
764 return RESOURCED_ERROR_FAIL;
766 ret = logging_thread_create();
768 _E("logging thread create failed");
769 return RESOURCED_ERROR_FAIL;
772 register_edbus_signal_handler(RESOURCED_PATH_LOGGING,
773 RESOURCED_INTERFACE_LOGGING, SIGNAL_LOGGING_INIT,
774 (void *)logging_init_booting_done_edbus_signal_handler);
776 register_edbus_signal_handler(RESOURCED_PATH_LOGGING,
777 RESOURCED_INTERFACE_LOGGING, SIGNAL_LOGGING_GET,
778 (void *)logging_get_edbus_signal_handler);
780 return RESOURCED_ERROR_NONE;
783 static int resourced_logging_control(void *data)
785 int ret = RESOURCED_ERROR_NONE;
786 struct logging_data_type *l_data;
791 l_data = (struct logging_data_type *)data;
793 switch(l_data->control_type) {
794 case LOGGING_INSERT_PROC_LIST:
796 insert_proc_list((pid_t)l_data->args[0],
797 (pid_t)l_data->args[1], (int)l_data->args[2]);
799 case LOGGING_UPDATE_PROC_INFO:
800 logging_update_start();
802 case LOGGING_UPDATE_STATE:
803 logging_update_state();
809 static int resourced_logging_init(void *data)
811 logging_ops = &logging_modules_ops;
813 return logging_init();
816 static int resourced_logging_exit(void *data)
819 g_array_free(logging_ss_list, TRUE);
820 if (logging_proc_list)
821 g_hash_table_destroy(logging_proc_list);
822 return RESOURCED_ERROR_NONE;
825 int logging_control(enum logging_control_type type, unsigned long *args)
827 struct logging_data_type l_data;
830 l_data.control_type = type;
832 return logging_ops->control(&l_data);
835 return RESOURCED_ERROR_NONE;
838 static const struct module_ops logging_modules_ops = {
839 .priority = MODULE_PRIORITY_HIGH,
841 .init = resourced_logging_init,
842 .exit = resourced_logging_exit,
843 .control = resourced_logging_control,
846 MODULE_REGISTER(&logging_modules_ops)