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;
145 _D("input parameter key is NULL");
151 name = strndup(key, strlen(key)+1);
154 _D("memory allocation for name failed");
157 info = (struct logging_infos *)malloc(sizeof(struct logging_infos));
160 _D("memory allocation for logging_infos failed");
165 stats = (void **)malloc(sizeof(void *) * num_log_infos);
168 _D("memory allocation for log infos fails");
175 init_logging_infos(info, name, pid, oom, now);
177 g_hash_table_insert(logging_proc_list, (gpointer) name, (gpointer) info);
178 update_logging_infos(info, now, true);
182 static int write_journal(struct logging_sub_sys *pss, int ss_index)
186 int ret = RESOURCED_ERROR_NONE;
189 struct logging_infos *infos;
190 g_hash_table_iter_init(&iter, logging_proc_list);
193 ret = pthread_mutex_lock(&proc_list_mutex);
195 _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
199 if (!g_hash_table_iter_next(&iter, &key, &value)) {
200 ret = pthread_mutex_unlock(&proc_list_mutex);
202 _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
209 infos = (struct logging_infos *)value;
210 pss->ops->write(name, infos, ss_index);
211 ret = pthread_mutex_unlock(&proc_list_mutex);
213 _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
218 return RESOURCED_ERROR_NONE;
221 static int write_logging_subsys_info(struct logging_sub_sys *pss, int sindex,
222 time_t now, bool force)
225 if (!force && now < pss->last_commit + pss->commit_interval)
226 return RESOURCED_ERROR_NONE;
228 _D("start write %s subsys, now %lu, last:%lu, interval:%lu",
229 pss->name, now, pss->last_commit, pss->commit_interval);
231 write_journal(pss, sindex);
233 pss->last_commit = now;
234 return RESOURCED_ERROR_NONE;
238 static int write_logging_infos(bool force)
246 for (i = 0; i < logging_ss_list->len; i++) {
247 struct logging_sub_sys *ss = &g_array_index(logging_ss_list,
248 struct logging_sub_sys, i);
249 ret = write_logging_subsys_info(ss, i, now, force);
250 if (ret != RESOURCED_ERROR_NONE) {
251 _E("write logging at %lu", now);
252 /* not return error, just continue updating */
256 return RESOURCED_ERROR_NONE;
259 int register_logging_subsystem(const char*name, struct logging_info_ops *ops)
261 struct logging_sub_sys ss;
266 _E("input parameter name is NULL");
267 return RESOURCED_ERROR_INVALID_PARAMETER;
270 ss_name = strndup(name, strlen(name)+1);
273 _E("memory allocation for name is failed");
274 return RESOURCED_ERROR_FAIL;
280 ss.commit_interval = COMMIT_INTERVAL;
281 ss.last_commit = now;
284 g_array_append_val(logging_ss_list, ss);
287 return RESOURCED_ERROR_NONE;
290 int update_commit_interval(const char *name, time_t commit_interval)
293 for (i = 0; i < logging_ss_list->len; i++) {
294 struct logging_sub_sys *ss = &g_array_index(logging_ss_list,
295 struct logging_sub_sys, i);
296 if (!strcmp(ss->name, name)) {
297 ss->commit_interval = commit_interval;
298 _D("%s logging subsystem commit interval updated to %lu",
299 ss->name, ss->commit_interval);
300 return RESOURCED_ERROR_NONE;
304 _D("%s subsystem update fail, not exist", name);
305 return RESOURCED_ERROR_FAIL;
308 static inline int is_webprocess(char *name)
310 return !strcmp(name, WEBPROCESS_NAME);
313 static int rename_webprocess(pid_t pgid, char *name)
315 char webui_name[PROC_NAME_MAX];
318 if ((ret = proc_get_cmdline(pgid, webui_name)) != RESOURCED_ERROR_NONE)
319 return RESOURCED_ERROR_FAIL;
322 strcat(name, webui_name);
324 return RESOURCED_ERROR_NONE;
327 static int get_cmdline(pid_t pid, char *cmdline)
329 char buf[PROC_BUF_MAX];
332 snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
333 fp = fopen(buf, "r");
335 return RESOURCED_ERROR_FAIL;
337 if (fgets(cmdline, PROC_NAME_MAX-1, fp) == NULL) {
339 return RESOURCED_ERROR_FAIL;
343 return RESOURCED_ERROR_NONE;
346 static void insert_proc_list(pid_t pid, pid_t pgid, int oom)
348 int ret = RESOURCED_ERROR_NONE;
349 char name[PROC_NAME_MAX];
350 struct logging_infos *info;
352 ret = get_cmdline(pid, name);
354 * if cmdline does not exist, remove item from queue
355 * and continue logging remaining items
357 if (ret != RESOURCED_ERROR_NONE) {
361 if (is_webprocess(name)) {
362 ret = rename_webprocess(pgid, name);
363 if (ret != RESOURCED_ERROR_NONE)
368 ret = pthread_mutex_lock(&proc_list_mutex);
370 _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
374 info = (struct logging_infos *)
375 g_hash_table_lookup(logging_proc_list, name);
377 /* To Do: handle multiple daemons with the same name */
379 insert_hash_table(name, pid, oom);
381 info->running = true;
386 ret = pthread_mutex_unlock(&proc_list_mutex);
388 _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
394 static bool is_running_process(GArray *garray, pid_t pid)
399 for (i = 0 ; i < garray->len; i++) {
400 tpid = g_array_index(garray, pid_t, i);
407 static void update_proc_state(void)
411 struct dirent *result;
412 GArray *running_procs = NULL;
417 running_procs = g_array_new(false, false, sizeof(pid_t));
419 if (!running_procs) {
420 _E("fail to create garray for pids");
424 dirp = opendir("/proc");
427 _E("/proc open is failed, and cannot updated running procs");
431 while (!(ret = readdir_r(dirp, &entry, &result)) && result != NULL) {
434 if (!isdigit(entry.d_name[0]))
436 pid = atoi(entry.d_name);
437 g_array_append_val(running_procs, pid);
443 _E("readdir_r on /proc directory failed");
444 g_array_free(running_procs, true);
448 g_hash_table_iter_init(&iter, logging_proc_list);
450 ret = pthread_mutex_lock(&proc_list_mutex);
452 _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
453 g_array_free(running_procs, true);
457 while (g_hash_table_iter_next(&iter, &key, &value)) {
458 struct logging_infos *info = (struct logging_infos *)value;
459 info->running = is_running_process(running_procs, info->pid);
462 g_array_free(running_procs, true);
464 ret = pthread_mutex_unlock(&proc_list_mutex);
466 _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
470 need_to_update = false;
474 static void update_proc_list(void)
479 struct logging_infos *infos;
487 g_hash_table_iter_init(&iter, logging_proc_list);
490 ret = pthread_mutex_lock(&proc_list_mutex);
492 _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
496 if (!g_hash_table_iter_next(&iter, &key, &value)) {
497 ret = pthread_mutex_unlock(&proc_list_mutex);
499 _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
502 _D("finish proc list update");
505 infos = (struct logging_infos *)value;
508 update_logging_infos(infos, now, false);
509 ret = pthread_mutex_unlock(&proc_list_mutex);
511 _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
519 static void logging_update_state(void)
521 need_to_update = true;
524 static int check_running(gpointer key, gpointer value, gpointer user_data)
526 struct logging_infos *infos = (struct logging_infos *)value;
528 return !(infos->running);
531 static void reclaim_proc_list(void)
535 ret = pthread_mutex_lock(&proc_list_mutex);
537 _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
541 g_hash_table_foreach_remove(logging_proc_list, check_running, NULL);
542 ret = pthread_mutex_unlock(&proc_list_mutex);
544 _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
549 static void logging(void)
553 if (g_hash_table_size(logging_proc_list) > MAX_PROC_LIST) {
554 write_logging_infos(true);
557 write_logging_infos(false);
560 static void *logging_pthread(void *arg)
564 setpriority(PRIO_PROCESS, 0, LOGGING_PTIORITY);
568 * When signalled by main thread,
569 * it starts logging_pthread().
571 ret = pthread_mutex_lock(&logging_mutex);
573 _E("logging thread::pthread_mutex_lock() failed, %d", ret);
577 ret = pthread_cond_wait(&logging_cond, &logging_mutex);
579 _E("logging thread::pthread_cond_wait() failed, %d", ret);
580 ret = pthread_mutex_unlock(&logging_mutex);
582 _E("logging thread::pthread_mutex_lock() failed, %d", ret);
588 ret = pthread_mutex_unlock(&logging_mutex);
590 _E("logging thread::pthread_mutex_unlock() failed, %d", ret);
595 /* Now our thread finishes - cleanup TID */
602 static int logging_thread_create(void)
604 int ret = RESOURCED_ERROR_NONE;
606 if (logging_thread) {
607 _I("logging thread %u already created", (unsigned)logging_thread);
609 /* initialize logging_thread */
610 ret = pthread_create(&logging_thread, NULL, (void *)logging_pthread, (void *)NULL);
612 _E("pthread creation for logging_pthread failed, %d\n", ret);
615 _D("pthread creation for logging success");
616 pthread_detach(logging_thread);
623 static void free_key(gpointer key)
629 static void free_value(gpointer value)
632 struct logging_infos * info = (struct logging_infos *)value;
637 for (i = 0; i < num_log_infos; i++) {
639 free(info->stats[i]);
648 static void initialize_logging_proc_list(void)
652 struct dirent *result;
653 char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
658 dirp = opendir("/proc");
661 _E("/proc open is failed, and cannot updated running procs");
665 while (!(ret = readdir_r(dirp, &entry, &result)) && result != NULL) {
668 if (!isdigit(entry.d_name[0]))
670 pid = atoi(entry.d_name);
674 snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
675 fp = fopen(buf, "r+");
678 if (fgets(buf, sizeof(buf), fp) == NULL) {
684 insert_proc_list(pid, pgid, cur_oom);
688 write_logging_infos(true);
692 static void logging_update_start(void)
695 /* signal to logging_pthread to start */
696 ret = pthread_mutex_lock(&logging_mutex);
698 _E("logging_update_start::pthread_mutex_lock() failed, %d", ret);
702 ret = pthread_cond_signal(&logging_cond);
704 _E("logging_update_start::pthread_cond_wait() failed, %d", ret);
705 ret = pthread_mutex_unlock(&logging_mutex);
707 _E("logging_update_start::pthread_mutex_unlock() failed, %d", ret);
711 _D("send signal logging_pthread");
712 ret = pthread_mutex_unlock(&logging_mutex);
714 _E("logging_update_start::pthread_mutex_unlock() failed, %d", ret);
719 static void broadcast_logging_data_updated_signal(void)
723 r = broadcast_edbus_signal_str(RESOURCED_PATH_LOGGING, RESOURCED_INTERFACE_LOGGING,
724 SIGNAL_LOGGING_UPDATED, NULL, NULL);
725 _I("broadcast logging_data updated signal!");
728 _E("Failed: broadcast logging_data_updated signal");
731 static void logging_init_booting_done_edbus_signal_handler(void *data, DBusMessage *msg)
735 ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_LOGGING,
736 SIGNAL_LOGGING_INIT);
738 _D("there is booting done signal");
741 initialize_logging_proc_list();
742 _D("logging_init_booting_done_edbus_signal_handler");
745 static void logging_get_edbus_signal_handler(void *data, DBusMessage *msg)
749 ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_LOGGING,
752 _D("there is logging get signal");
755 write_logging_infos(true);
756 _D("logging_get_edbus_signal_handler");
757 broadcast_logging_data_updated_signal();
760 static int logging_init(void)
762 int ret = RESOURCED_ERROR_NONE;
764 _D("logging_init start");
766 logging_proc_list = g_hash_table_new_full(
772 if (!logging_proc_list) {
773 _E("fail g_hash_table_new_full() for logging_proc_list");
774 return RESOURCED_ERROR_FAIL;
777 logging_ss_list = g_array_new(false, false,
778 sizeof(struct logging_sub_sys));
780 if (logging_ss_list == NULL)
781 return RESOURCED_ERROR_FAIL;
783 ret = logging_thread_create();
785 _E("logging thread create failed");
786 return RESOURCED_ERROR_FAIL;
789 register_edbus_signal_handler(RESOURCED_PATH_LOGGING,
790 RESOURCED_INTERFACE_LOGGING, SIGNAL_LOGGING_INIT,
791 (void *)logging_init_booting_done_edbus_signal_handler);
793 register_edbus_signal_handler(RESOURCED_PATH_LOGGING,
794 RESOURCED_INTERFACE_LOGGING, SIGNAL_LOGGING_GET,
795 (void *)logging_get_edbus_signal_handler);
797 return RESOURCED_ERROR_NONE;
800 static int resourced_logging_control(void *data)
802 int ret = RESOURCED_ERROR_NONE;
803 struct logging_data_type *l_data;
808 l_data = (struct logging_data_type *)data;
810 switch(l_data->control_type) {
811 case LOGGING_INSERT_PROC_LIST:
813 insert_proc_list((pid_t)l_data->args[0],
814 (pid_t)l_data->args[1], (int)l_data->args[2]);
816 case LOGGING_UPDATE_PROC_INFO:
817 logging_update_start();
819 case LOGGING_UPDATE_STATE:
820 logging_update_state();
826 static int resourced_logging_init(void *data)
828 logging_ops = &logging_modules_ops;
830 return logging_init();
833 static int resourced_logging_exit(void *data)
836 g_array_free(logging_ss_list, TRUE);
837 if (logging_proc_list)
838 g_hash_table_destroy(logging_proc_list);
839 return RESOURCED_ERROR_NONE;
842 int logging_control(enum logging_control_type type, unsigned long *args)
844 struct logging_data_type l_data;
847 l_data.control_type = type;
849 return logging_ops->control(&l_data);
852 return RESOURCED_ERROR_NONE;
855 static const struct module_ops logging_modules_ops = {
856 .priority = MODULE_PRIORITY_HIGH,
858 .init = resourced_logging_init,
859 .exit = resourced_logging_exit,
860 .control = resourced_logging_control,
863 MODULE_REGISTER(&logging_modules_ops)