/*
-
- * resourced
+ * resourced-headless
*
- * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * 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
+ * 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.
- *
*/
-/*
- * @file proc-monitor.c
- *
- * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
- *
- */
-
-#include <sys/types.h>
-#include <unistd.h>
-#include <sys/mount.h>
-#include <sys/time.h>
-#include <sys/resource.h>
+#include <glib.h>
+#include <stdbool.h>
#include <stdlib.h>
-#include "proc-main.h"
-#include "resourced.h"
-#include "macro.h"
-#include "trace.h"
-#include "dbus-handler.h"
-#include "freezer.h"
-#include "notifier.h"
+#include <config-parser.h>
+#include <dbus.h>
+#include <log.h>
+#include <macro.h>
+#include <module.h>
+#include <procfs.h>
-static GSList *watchdog_app_list;
+#define RDHL_DBUS_PATH_PROCESS RDHL_DBUS_PATH(Process)
+#define RDHL_DBUS_INTERFACE_PROCESS RDHL_DBUS_INTERFACE(process)
+
+#define RDHL_DBUS_PATH_WATCHDOG RDHL_DBUS_PATH(Watchdog)
+#define RDHL_DBUS_INTERFACE_WATCHDOG RDHL_DBUS_INTERFACE(watchdog)
+
+#define SIGNAL_PROC_WATCHDOG "ProcWatchdog"
+
+#define WATCHDOG_CONFIG_FILE "watchdog"
+#define WATCHDOG_EXCLUDE_LIST "watchdog_excluded_processes"
+static GHashTable *watchdog_exclude_map = NULL;
+
+static GSource *watchdog_check_timer;
+#define WATCHDOG_TIMER_INTERVAL 10
+
+typedef void (*d_bus_method_callback)(GDBusMethodInvocation *invocation, GVariant *params);
+typedef void (*d_bus_signal_callback)(GVariant *params);
+
+struct d_bus_method {
+ const char *name;
+ d_bus_method_callback callback;
+};
+
+struct d_bus_signal {
+ const char *path;
+ const char *interface;
+ const char *name;
+ d_bus_signal_callback callback;
+ void *user_data;
+ guint subscription_id;
+};
struct watchdog_app_info {
pid_t pid;
GSource *watchdog_timer;
};
-struct watchdog_app_info *find_watchdog_info(const pid_t pid)
-{
- GSList *iter = NULL;
- struct watchdog_app_info *wai = NULL;
+enum watchdog_control_type {
+ SET_FOREGRD,
+ SET_BACKGRD,
+};
- if (!watchdog_app_list)
- return NULL;
+static struct watchdog_info {
+ pid_t pid;
+ int signum;
+} watchdog_info = { -1, -1 };
- gslist_for_each_item(iter, watchdog_app_list) {
- wai = (struct watchdog_app_info *)iter->data;
- if (wai->pid == pid)
- return wai;
- }
- return NULL;
-}
+static GSList *watchdog_app_list;
static gboolean watchdog_cb(gpointer data)
{
return false;
_I("detect watchdog for pid %d with timeout %u", wai->pid, wai->watchdog_timeout);
- g_dbus_connection_emit_signal(conn, NULL, RESOURCED_PATH_PROCESS,
- RESOURCED_INTERFACE_PROCESS, SIGNAL_PROC_WATCHDOG,
+ g_dbus_connection_emit_signal(conn, NULL, RDHL_DBUS_PATH_PROCESS,
+ RDHL_DBUS_INTERFACE_PROCESS, SIGNAL_PROC_WATCHDOG,
g_variant_new("(ii)", wai->pid, SIGKILL), &err);
return false;
}
+struct watchdog_app_info *find_watchdog_info(const pid_t pid)
+{
+ struct watchdog_app_info *wai = NULL;
+
+ if (!watchdog_app_list)
+ return NULL;
+
+ G_SLIST_FOREACH(watchdog_app_list, wai) {
+ if (wai->pid == pid)
+ return wai;
+ }
+ return NULL;
+}
+
static void proc_update_watchdog(struct watchdog_app_info *wai, unsigned int timeout)
{
if (wai->watchdog_timer) {
wai = find_watchdog_info(pid);
if (!wai) {
wai = calloc(sizeof(struct watchdog_app_info), 1);
- if (!wai)
+ if (!wai) {
+ g_dbus_method_invocation_return_error(invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_NO_MEMORY,
+ "Out of memory");
return;
+ }
wai->pid = pid;
watchdog_app_list = g_slist_prepend(watchdog_app_list, wai);
}
proc_update_watchdog(wai, timeout);
+ g_dbus_method_invocation_return_value(invocation, NULL);
}
-static void proc_watchdog_wakeup(GVariant *params)
+static gboolean watchdog_check_cb(gpointer data)
{
- GSList *iter = NULL;
- struct watchdog_app_info *wai = NULL;
-
- if (!watchdog_app_list)
- return;
+ int oom_score_adj = 0, ret;
+ pid_t pid = watchdog_info.pid;
- gslist_for_each_item(iter, watchdog_app_list) {
- wai = (struct watchdog_app_info *)iter->data;
- proc_update_watchdog(wai, wai->watchdog_timeout);
+ ret = procfs_get_oom_score_adj(pid, &oom_score_adj);
+ if (!ret) {
+ _E("watchdog pid %d not terminated, kill again\n", pid);
+ kill(pid, SIGKILL);
}
+ watchdog_check_timer = NULL;
+ watchdog_info.pid = -1;
+ watchdog_info.signum = -1;
+
+ return FALSE;
}
-static void proc_watchdog_suspend(GVariant *params)
+static void watchdog_add_predefined(const char *key, const void *data)
{
- int pid = -1, type = -1;
- unsigned int timeout = 0;
- struct watchdog_app_info *wai;
+ gchar *basename;
+ gchar *value = (gchar*) data;
- g_variant_get(params, "(ii)", &type, &pid);
- if (pid < 0 || type < 0)
+ if (strcmp("exclude", value) != 0)
return;
- wai = find_watchdog_info(pid);
- if (!wai)
- return;
+ basename = g_strndup(key, strlen(key));
+ g_assert(basename);
+ g_hash_table_add(watchdog_exclude_map, basename);
+ _D("%s : exclude", basename);
+}
- timeout = (type == SET_BACKGRD) ? 0 : wai->watchdog_timeout;
- proc_update_watchdog(wai, timeout);
+int watchdog_app_excluded(const char *app_name)
+{
+ gboolean ret = FALSE;
+ if (watchdog_exclude_map)
+ ret = g_hash_table_contains(watchdog_exclude_map, (gpointer)app_name);
+
+ return ret ? TRUE : FALSE;
}
-static void proc_watchdog_freeze(GVariant *params)
+static void watchdog_dbus_handler(GVariant *params)
{
- GSList *iter = NULL;
- struct watchdog_app_info *wai = NULL;
+ int ret;
+ int pid = -1;
+ int signum = -1;
+ char appname[NAME_MAX + 1];
- if (!watchdog_app_list)
+ g_variant_get(params, "(ii)", &pid, &signum);
+ if (pid < 0 || signum < 0) {
+ _D("there is no message");
return;
+ }
- gslist_for_each_item(iter, watchdog_app_list) {
- wai = (struct watchdog_app_info *)iter->data;
- proc_update_watchdog(wai, 0);
+ ret = procfs_get_pid_basename(pid, sizeof(appname), appname);
+ if (ret < 0) {
+ _E("ERROR : invalid pid(%d)", pid);
+ return;
}
-}
-static int watchdog_app_terminated_cb(void *data)
-{
- struct proc_status *ps = (struct proc_status *)data;
- struct watchdog_app_info *wai;
+ ret = watchdog_app_excluded(appname);
+ if (ret)
+ return;
- if (!ps || !ps->pid)
- return RESOURCED_ERROR_FAIL;
+ _D("Receive watchdog signal to app %s, pid %d\n", appname, pid);
- wai = find_watchdog_info(ps->pid);
- if (!wai)
- return RESOURCED_ERROR_NO_DATA;
+ if (watchdog_check_timer) {
+ if (watchdog_info.pid == pid) {
+ _E("app %s, pid %d has already received watchdog siganl but not terminated", appname, pid);
+ kill(pid, SIGKILL);
+ watchdog_info.pid = -1;
+ watchdog_info.signum = -1;
+ return;
+ }
+ }
- proc_update_watchdog(wai, 0);
- watchdog_app_list = g_slist_remove(watchdog_app_list, wai);
- free(wai);
+ kill(pid, SIGABRT);
+ if (!watchdog_check_timer) {
+ watchdog_check_timer = g_timeout_source_new_seconds(WATCHDOG_TIMER_INTERVAL);
+ g_source_set_callback(watchdog_check_timer, watchdog_check_cb, NULL, NULL);
+ g_source_attach(watchdog_check_timer, NULL);
- return RESOURCED_ERROR_NONE;
+ watchdog_info.pid = pid;
+ watchdog_info.signum = signum;
+ }
}
-static const char dbus_methods_xml[] =
-"<node>"
-" <interface name='"RESOURCED_INTERFACE_WATCHDOG"'>"
-" <method name='Start'>"
-" <arg type='i' name='Pid' direction='in'/>"
-" <arg type='u' name='Timeout' direction='in'/>"
-" </method>"
-" </interface>"
-"</node>";
-
-static struct d_bus_method dbus_methods[] = {
- { "Start", proc_argos_watchdog_handler },
- /* Add methods here */
+static struct dbus_signal watchdog_dbus_signals[] = {
+ { SIGNAL_PROC_WATCHDOG, watchdog_dbus_handler },
};
-static const struct d_bus_signal dbus_signals[] = {
- /* RESOURCED DBUS */
- {DEVICED_PATH_DISPLAY, DEVICED_INTERFACE_DISPLAY,
- SIGNAL_DEVICED_WAKEUP, proc_watchdog_wakeup, NULL},
- {DEVICED_PATH_DISPLAY, DEVICED_INTERFACE_DISPLAY,
- SIGNAL_DEVICED_SUSPEND, proc_watchdog_suspend, NULL},
- {RESOURCED_PATH_FREEZER, RESOURCED_INTERFACE_FREEZER,
- SIGNAL_FREEZER_STATE, proc_watchdog_freeze, NULL},
+static struct dbus_method watchdog_dbus_methods[] = {
+ { "Start", "iu", NULL, proc_argos_watchdog_handler},
+ /* Add methods here */
};
-static int proc_watchdog_init(void *data)
-{
- register_notifier(RESOURCED_NOTIFIER_APP_TERMINATED,
- watchdog_app_terminated_cb);
+int __INIT__ watchdog_init() {
+
+ int ret = 0;
+
+ ret = config_parser_open(WATCHDOG_CONFIG_FILE);
+ if (ret < 0) {
+ _E("Failed to open memory config file (%d)", ret);
+ return ret;
+ }
+
+ watchdog_exclude_map = g_hash_table_new_full(
+ g_str_hash,
+ g_str_equal,
+ free, /* if (data) free(data); XXX */
+ NULL);
- d_bus_register_signals(dbus_signals, ARRAY_SIZE(dbus_signals));
- return d_bus_register_methods(RESOURCED_PATH_WATCHDOG, dbus_methods_xml,
- dbus_methods, ARRAY_SIZE(dbus_methods));
+ if (watchdog_exclude_map == NULL) {
+ /* XXX */
+ return -1;
+ }
+
+ _D("========== Watchdog exclude list ==========");
+ config_parser_foreach(WATCHDOG_CONFIG_FILE, WATCHDOG_EXCLUDE_LIST,
+ watchdog_add_predefined);
+ _D("========== Total %d process(es) ==========", g_hash_table_size(watchdog_exclude_map));
+
+
+ watchdog_check_timer = g_timeout_source_new_seconds(WATCHDOG_TIMER_INTERVAL);
+ g_source_set_callback(watchdog_check_timer, watchdog_check_cb, NULL, NULL);
+ g_source_attach(watchdog_check_timer, NULL);
+
+ ret = dbus_register_signals(RDHL_DBUS_PATH_PROCESS,
+ RDHL_DBUS_INTERFACE_PROCESS,
+ watchdog_dbus_signals,
+ ARRAY_SIZE(watchdog_dbus_signals));
+ if (ret < 0) {
+ _E("Failed to register watchdog signals (%d)", ret);
+ return ret;
+ }
+
+ ret = dbus_register_methods(RDHL_DBUS_PATH_WATCHDOG,
+ RDHL_DBUS_INTERFACE_WATCHDOG,
+ watchdog_dbus_methods,
+ ARRAY_SIZE(watchdog_dbus_methods));
+ if (ret < 0) {
+ _E("Failed to register proc-usage D-Bus methods (%d)", ret);
+ return ret;
+ }
+
+ ret = 0;
+ return ret;
}
-static int proc_watchdog_exit(void *data)
-{
- unregister_notifier(RESOURCED_NOTIFIER_APP_TERMINATED,
- watchdog_app_terminated_cb);
- return RESOURCED_ERROR_NONE;
+static int watchdog_exit() {
+ config_parser_close(WATCHDOG_CONFIG_FILE);
+ g_hash_table_destroy(watchdog_exclude_map);
+ if (watchdog_check_timer)
+ g_source_destroy(watchdog_check_timer);
+ return 0;
}
-static const struct proc_module_ops proc_watchdog_ops = {
- .name = "PROC_WATCHDOG",
- .init = proc_watchdog_init,
- .exit = proc_watchdog_exit,
+static struct module watchdog_module = {
+ .name = "watchdog",
+ .priority = MODULE_PRIORITY_HIGH,
+ .init = watchdog_init,
+ .exit = watchdog_exit,
+ .event_handler = NULL,
};
-PROC_MODULE_REGISTER(&proc_watchdog_ops)
+MODULE_REGISTER(&watchdog_module)