watchdog: Adapt watchdog code for resourced-headless 08/197508/7
authorŁukasz Stelmach <l.stelmach@samsung.com>
Mon, 7 Jan 2019 13:44:51 +0000 (14:44 +0100)
committerŁukasz Stelmach <l.stelmach@samsung.com>
Tue, 5 Mar 2019 12:39:17 +0000 (13:39 +0100)
Change-Id: I9ff7ee5428376a4f657b8047c99e1f5d8fd59540

CMakeLists.txt
cfg/watchdog.conf [new file with mode: 0644]
packaging/resourced-headless.spec
src/watchdog/resourced-headless-watchdog.manifest [new file with mode: 0644]
src/watchdog/watchdog.c

index 0c3fb965b0e313c1a512942c04829ded237abbb6..d99415bc5e0fb425d603fee0344de793223cb3a1 100644 (file)
@@ -89,6 +89,11 @@ IF("${PROC_USAGE_MODULE}" STREQUAL "ON")
        BUILD_MODULE("${PKGNAME}-proc-usage" "proc-usage" "library")
 ENDIF()
 
+IF("${WATCHDOG_MODULE}" STREQUAL "ON")
+       BUILD_MODULE("${PKGNAME}-watchdog" "watchdog" "library")
+       ADD_CONFIG("watchdog")
+ENDIF()
+
 IF("${TEST_MODULE}" STREQUAL "ON")
        BUILD_MODULE("${PKGNAME}-test" "test" "library")
 ENDIF()
diff --git a/cfg/watchdog.conf b/cfg/watchdog.conf
new file mode 100644 (file)
index 0000000..92d5e4b
--- /dev/null
@@ -0,0 +1,8 @@
+[watchdog_excluded_processes]
+# Exclude BASENAME from being victim
+# BASENAME=exclude
+volume=exclude
+indicator-win=exclude
+lockscreen=exclude
+quickpanel=exclude
+data-provider-master=exclude
index 31628c2f1ba4439f83487ccd887e8ebf21847183..b0865e290cbbc8c259c2216be04d31c66c089d85 100644 (file)
@@ -12,6 +12,7 @@ Source102:  %{name}.conf
 %define memory_module ON
 %define proc_manager_module ON
 %define proc_usage_module ON
+%define watchdog_module ON
 %define test_module    ON
 
 %define config_install_dir /etc/resourced
@@ -63,6 +64,15 @@ Requires:   %{name}-common = %{version}-%{release}
 Provide information related to files in the /proc
 %endif
 
+%if %{watchdog_module} == ON
+%package watchdog
+Summary:       Provide process usage information
+Requires:   %{name} = %{version}-%{release}
+Requires:   %{name}-common = %{version}-%{release}
+%description watchdog
+Application watchdog
+%endif
+
 %if %{test_module} == ON
 %package test
 Summary:       Test module
@@ -85,6 +95,7 @@ MAJORVER=`echo %{version} | awk 'BEGIN {FS="."}{print $1}'`
        -DMEMORY_MODULE=%{memory_module}        \
        -DPROC_MANAGER_MODULE=%{proc_manager_module}    \
        -DPROC_USAGE_MODULE=%{proc_usage_module}        \
+       -DWATCHDOG_MODULE=%{watchdog_module}    \
        -DTEST_MODULE=%{test_module}
 
 make %{?jobs:-j%jobs}
@@ -138,6 +149,14 @@ install -m 0644 %{SOURCE102} %{buildroot}%{_sysconfdir}/dbus-1/system.d/resource
 %{_libdir}/libresourced-headless-proc-usage.so*
 %endif
 
+%if %{watchdog_module} == ON
+%files watchdog
+%license LICENSE
+%manifest ./src/watchdog/%{name}-watchdog.manifest
+%{_libdir}/libresourced-headless-watchdog.so*
+%config %{config_install_dir}/watchdog.conf
+%endif
+
 %if %{test_module} == ON
 %files test
 %license LICENSE
diff --git a/src/watchdog/resourced-headless-watchdog.manifest b/src/watchdog/resourced-headless-watchdog.manifest
new file mode 100644 (file)
index 0000000..a76fdba
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+       <request>
+               <domain name="_" />
+       </request>
+</manifest>
index e7dfea1779850fbcb000a959b41e0ffbb4f7f743..d04134a0625ebf3822796afe25ea281feac132ef 100644 (file)
@@ -1,46 +1,63 @@
 /*
-
- * 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;
@@ -48,21 +65,17 @@ struct watchdog_app_info {
        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)
 {
@@ -89,13 +102,27 @@ 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) {
@@ -129,8 +156,13 @@ static void proc_argos_watchdog_handler(GDBusMethodInvocation *invocation, GVari
        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);
@@ -138,119 +170,171 @@ static void proc_argos_watchdog_handler(GDBusMethodInvocation *invocation, GVari
        }
 
        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)