summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYoungjae Cho <y0.cho@samsung.com>2020-08-11 15:54:18 +0900
committerHyotaek Shim <hyotaek.shim@samsung.com>2020-08-25 01:15:44 +0000
commit6e4f55e7b51f0e33c9dd976c6bd982e23c958cfd (patch)
tree3e324e17c8303843be5af402ce19a039094e5bbb
parent7d2a08b3f6c2445b766ecade848a04b80023797e (diff)
downloaddeviced-6e4f55e7b51f0e33c9dd976c6bd982e23c958cfd.tar.gz
deviced-6e4f55e7b51f0e33c9dd976c6bd982e23c958cfd.tar.bz2
deviced-6e4f55e7b51f0e33c9dd976c6bd982e23c958cfd.zip
Summary: - Detect someone who holds lock longer than 30 minutes. - If a general application is detected, deviced generates signal with time information in addtion to pid. - If a specific daemon is detected, deviced tries to kill it directly. For general applications: Added an addition information, lock holding time in second, to the signal "pmlock_expired". This makes someone, who is in charge of application lifecycle and notification, possible to determine whether to kill that application or not. For some specific killable daemon: Added list of killable daemon to conf file and deviced loads that list. If a killable daemon holds lock longer than 30 minutes, deviced directly sends SIGTERM to that daemon, releasing lock. After for a while, deviced checks once more, if the daemon is still alive then, sends SIGKILL. Change-Id: Ib399ee2955ea735c122aebfd7c41be5d938cb7d4 Signed-off-by: Youngjae Cho <y0.cho@samsung.com>
-rw-r--r--plugins/wearable/display/core.c239
1 files changed, 212 insertions, 27 deletions
diff --git a/plugins/wearable/display/core.c b/plugins/wearable/display/core.c
index 1b8c67d1..50445347 100644
--- a/plugins/wearable/display/core.c
+++ b/plugins/wearable/display/core.c
@@ -63,6 +63,7 @@
#include "battery-monitor.h"
#define DISPLAY_CONF_FILE "/etc/deviced/display.conf"
+#define POWERLOCK_CONF_FILE "/etc/deviced/powerlock.conf"
#ifndef VCONFKEY_HOMESCREEN_TUTORIAL_OOBE_ENABLED
#define VCONFKEY_HOMESCREEN_TUTORIAL_OOBE_ENABLED "db/private/com.samsung.w-home/tutorial_oobe_enabled"
@@ -143,7 +144,10 @@ static gboolean del_normal_cond(void *data);
static gboolean del_dim_cond(void *data);
static gboolean del_off_cond(void *data);
-static gboolean pmlock_check(void *data);
+static void get_comm(pid_t pid, char *comm);
+static bool is_killable_daemon(pid_t pid);
+static gboolean pmlock_check(gpointer data);
+static gboolean pmlock_terminate_daemon_to_release_lock(gpointer data);
static int default_proc_change_state(unsigned int cond, pid_t pid);
static int (*proc_change_state)(unsigned int cond, pid_t pid) = default_proc_change_state;
@@ -217,6 +221,9 @@ static const char *lcdoff_sig_lookup[SIGNAL_MAX] = {
(b.tv_sec * 1000000 + b.tv_usec)) \
/ 1000)
+#define KILLABLE_DAEMON_LOCK_LIMIT 1800 /* seconds, 30min */
+#define FORCE_RELEASE_LOCK_INTERVAL 5 /* seconds */
+
struct display_config display_conf = {
.lock_wait_time = LOCK_SCREEN_WATING_TIME,
.longpress_interval = LONG_PRESS_INTERVAL,
@@ -248,18 +255,32 @@ struct display_function_info display_info = {
};
typedef struct _pm_lock_node {
+ enum state_t state;
pid_t pid;
guint timeout_id;
guint warning_id;
- GVariant *warning_param;
time_t time;
bool holdkey_block;
bool background;
bool broadcast_warning;
+
+ /* Set true when the lock holder is an entry of
+ * the list display_lock_killable_daemon.
+ * If true, deviced tries to kill this lock holder when
+ * the holder holds lock longer than KILLABLE_DAEMON_LOCK_LIMIT */
+ bool killable_daemon;
+
+ /* Set true when the lock holder holds lock
+ * longer than KILLABLE_DAEMON_LOCK_LIMIT.
+ * After a while, FORCE_RELEASE_LOCK_INTERVAL,
+ * this lock will be released. */
+ bool force_release;
} PmLockNode;
static dd_list *cond_head[S_END];
+static dd_list *display_lock_killable_daemon;
+
static void set_process_active(bool flag, pid_t pid)
{
int ret;
@@ -711,7 +732,6 @@ static PmLockNode *add_node(enum state_t s_index, pid_t pid, guint timeout_id,
{
guint warning_id = 0;
PmLockNode *n;
- GVariant *v = NULL;
time_t now;
n = (PmLockNode *) malloc(sizeof(PmLockNode));
@@ -720,25 +740,29 @@ static PmLockNode *add_node(enum state_t s_index, pid_t pid, guint timeout_id,
return NULL;
}
+ time(&now);
+
if (pid < INTERNAL_LOCK_BASE) {
- v = g_variant_new("(ii)", s_index, pid);
- if (v) {
+ n->killable_daemon = is_killable_daemon(pid);
+ if (n->killable_daemon)
+ warning_id = g_timeout_add_seconds(KILLABLE_DAEMON_LOCK_LIMIT,
+ pmlock_terminate_daemon_to_release_lock, (gpointer)n);
+ else
warning_id = g_timeout_add_seconds(display_conf.lockcheck_timeout,
- pmlock_check, (void *)v);
- } else {
- _E("Failed to make GVariant.");
- }
+ pmlock_check, (gpointer)n);
+ } else {
+ n->killable_daemon = false;
}
- time(&now);
+ n->state = s_index;
n->pid = pid;
n->timeout_id = timeout_id;
n->warning_id = warning_id;
- n->warning_param = v;
n->time = now;
n->holdkey_block = holdkey_block;
n->background = false;
n->broadcast_warning = true;
+ n->force_release = false;
DD_LIST_APPEND(cond_head[s_index], n);
refresh_app_cond();
@@ -761,10 +785,6 @@ static int del_node(enum state_t s_index, PmLockNode *n)
g_source_remove(n->warning_id);
n->warning_id = 0;
}
- if (n->warning_param) {
- g_variant_unref(n->warning_param);
- n->warning_param = NULL;
- }
free(n);
refresh_app_cond();
@@ -806,6 +826,35 @@ static void print_node(int next)
}
}
+static void get_comm(pid_t pid, char *comm)
+{
+ char buf[PATH_MAX];
+ int fd, r;
+
+ if (pid >= INTERNAL_LOCK_BASE)
+ snprintf(buf, PATH_MAX, "/proc/%d/comm", getpid());
+ else
+ snprintf(buf, PATH_MAX, "/proc/%d/comm", pid);
+
+ fd = open(buf, O_RDONLY);
+ if (fd < 0) {
+ comm[0] = '\0';
+ _E("Process(%d) does not exist now(may be dead without unlock).", pid);
+ return;
+ }
+
+ r = read(fd, comm, PATH_MAX);
+ if ((r > 0) && (r < PATH_MAX)) {
+ if (comm[r - 1] == '\n')
+ comm[r - 1] = '\0';
+ else
+ comm[r] = '\0';
+ } else
+ comm[0] = '\0';
+
+ close(fd);
+}
+
void get_pname(pid_t pid, char *pname)
{
char buf[PATH_MAX];
@@ -911,7 +960,7 @@ static void pmlock_check_cb(GVariant *var, void *user_data, GError *err)
DEVICED_PATH_DISPLAY,
DEVICED_INTERFACE_DISPLAY,
"pmlock_expired",
- g_variant_new("(i)", pid));
+ g_variant_new("(ii)", pid, (int)diff));
if (ret < 0)
_E("Failed to send dbus pmlock_expired");
@@ -921,17 +970,110 @@ out:
g_variant_unref(var);
}
+static bool is_killable_daemon(pid_t pid)
+{
+ char pname[PATH_MAX] = {0, };
+ const char *blacklist;
+ dd_list *l;
+
+ get_comm(pid, pname);
+ if (!(*pname))
+ return false;
+
+ DD_LIST_FOREACH(display_lock_killable_daemon, l, blacklist) {
+ if (MATCH(pname, blacklist))
+ return true;
+ }
+
+ return false;
+}
+
+static gboolean pmlock_terminate_daemon_to_release_lock(gpointer data)
+{
+ pid_t pid;
+ PmLockNode *node;
+ enum state_t state;
+ int ret;
+ bool pid_exist;
+ char pname[PATH_MAX] = {0, };
+
+ if (!data) {
+ _E("Invalid parameter.");
+ return G_SOURCE_REMOVE;
+ }
+
+ node = (PmLockNode *) data;
+ state = node->state;
+ pid = node->pid;
+
+ if (pid <= 0) {
+ _E("Invalid lock pid.");
+ del_node(state, node);
+ return G_SOURCE_REMOVE;
+ }
+
+ if (!node->killable_daemon) {
+ _E("Incorrect checker, this is not a killable daemon. Stop checking lock.");
+ return G_SOURCE_REMOVE;
+ }
+
+ pid_exist = (kill(pid, 0) == 0);
+ if (pid_exist)
+ get_comm(pid, pname);
+
+ if (node->force_release == false) {
+ /* Stop checking lock if process had been terminated */
+ if (!pid_exist) {
+ del_node(state, node);
+ _I("Process %d not found. Stop checking lock.", pid);
+ return G_SOURCE_REMOVE;
+ }
+
+ /* KILLABLE_DAEMON_LOCK_LIMIT is expired. Kill the daemon */
+ CRITICAL_LOG("%s(%d) holds %s lock for %ds. kill SIGTERM.",
+ *pname ? pname : "Unknown", pid, states[state].name, KILLABLE_DAEMON_LOCK_LIMIT);
+ ret = kill(pid, SIGTERM);
+ if (ret < 0)
+ CRITICAL_LOG("Failed to send SIGTERM to process %s(%d), %d.",
+ *pname ? pname : "Unknown", pid, errno);
+
+ node->force_release = true;
+ g_timeout_add_seconds(FORCE_RELEASE_LOCK_INTERVAL,
+ pmlock_terminate_daemon_to_release_lock, (gpointer)node);
+ } else if (node->force_release == true) {
+ /* kill confirmation */
+ if (pid_exist) {
+ CRITICAL_LOG("%s(%d) is still alive, kill SIGKILL.",
+ *pname ? pname : "Unknown", pid);
+
+ ret = kill(pid, SIGKILL);
+ if (ret < 0)
+ CRITICAL_LOG("Failed to kill process %s(%d), %d.",
+ *pname ? pname : "Unknown", pid, errno);
+ }
+
+ /* release lock */
+ CRITICAL_LOG("Release %s lock occupied by PID %d.", states[state].name, pid);
+ del_node(state, node);
+ set_unlock_time(pid, state);
+
+ if (!timeout_src_id)
+ states[pm_cur_state].trans(EVENT_TIMEOUT);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
/*
* Any process is possible many time lock, deviced can not know malicious
* or good process. so infinity or more then 10 min lock process, deviced
* will check it through resoured. And then, it will ask the user whether to quit or not.
*/
-static gboolean pmlock_check(void *data)
+static gboolean pmlock_check(gpointer data)
{
const char *arr[2];
char chr_pid[PID_MAX];
PmLockNode *node;
- GVariant *v;
enum state_t state;
pid_t pid;
int ret;
@@ -941,8 +1083,9 @@ static gboolean pmlock_check(void *data)
return G_SOURCE_REMOVE;
}
- v = (GVariant*)data;
- g_variant_get(v, "(ii)", &state, &pid);
+ node = (PmLockNode *) data;
+ state = node->state;
+ pid = node->pid;
if (state == S_LCDOFF && backlight_ops.get_lcd_power() == DPMS_ON) {
_D("Lcd state is PM_LCD_POWER_ON");
@@ -951,7 +1094,6 @@ static gboolean pmlock_check(void *data)
/* Stop checking lock if process had been terminated */
if (kill(pid, 0) == -1) {
- node = find_node(state, pid);
del_node(state, node);
_I("Process %d not found. Stop checking lock.", pid);
return G_SOURCE_REMOVE;
@@ -971,7 +1113,6 @@ static gboolean pmlock_check(void *data)
break;
default:
_E("Invalid state.");
- g_variant_unref(v);
return G_SOURCE_REMOVE;
}
@@ -1535,9 +1676,13 @@ static void proc_condition_lock(PMMsg *data)
holdkey_block = GET_COND_FLAG(data->cond) & PM_FLAG_BLOCK_HOLDKEY;
tmp = find_node(state, pid);
- if (!tmp)
- add_node(state, pid, cond_timeout_id, holdkey_block);
- else {
+ if (!tmp) {
+ tmp = add_node(state, pid, cond_timeout_id, holdkey_block);
+ if (!tmp) {
+ _E("Failed to acquire lock, state: %d, pid: %d.", state, pid);
+ return;
+ }
+ } else {
update_lock_timer(data, tmp, cond_timeout_id);
tmp->holdkey_block = holdkey_block;
}
@@ -1568,8 +1713,8 @@ static void proc_condition_lock(PMMsg *data)
}
}
- _SD("be requested LOCK info pname(%s), holdkeyblock(%d) flags(%d)",
- pname, holdkey_block, flags);
+ _SD("be requested LOCK info pname(%s), holdkeyblock(%d) flags(%d) killable_daemon(%d)",
+ pname, holdkey_block, flags, tmp->killable_daemon);
set_lock_time(pid, pname, state);
device_notify(DEVICE_NOTIFIER_DISPLAY_LOCK, (void *)&value);
@@ -2703,6 +2848,21 @@ static int battery_health_changed(void *data)
return 0;
}
+static void free_killable_daemon_list(void)
+{
+ dd_list *l, *l_next;
+ char *blacklist;
+
+ if (display_lock_killable_daemon)
+ return;
+
+ DD_LIST_FOREACH_SAFE(display_lock_killable_daemon, l, l_next, blacklist) {
+ DD_LIST_REMOVE(display_lock_killable_daemon, blacklist);
+ free(blacklist);
+ }
+ display_lock_killable_daemon = NULL;
+}
+
static int display_load_config(struct parse_result *result, void *user_data)
{
struct display_config *c = user_data;
@@ -2777,6 +2937,26 @@ static int display_load_config(struct parse_result *result, void *user_data)
return 0;
}
+static int powerlock_load_config(struct parse_result *result, void *user_data)
+{
+ char *name = NULL;
+
+ _D("powerlock_load_config: section=%s name=%s value=%s", result->section, result->name, result->value);
+
+ if (MATCH(result->section, "KillableDaemon") && MATCH(result->name, "KillableList")) {
+ name = strndup(result->value, PATH_MAX - 1);
+ if (!name) {
+ _E("Not enough memory.");
+ return -ENOMEM;
+ }
+
+ CRITICAL_LOG("Add %s to killable daemon list.", name);
+ DD_LIST_APPEND(display_lock_killable_daemon, name);
+ }
+
+ return 0;
+}
+
static gboolean delayed_init_dpms(gpointer data)
{
int timeout;
@@ -2931,6 +3111,10 @@ static void display_init(void *data)
_W("Failed to load '%s', use default value: %d",
DISPLAY_CONF_FILE, ret);
+ ret = config_parse(POWERLOCK_CONF_FILE, powerlock_load_config, NULL);
+ if (ret < 0)
+ _W("Failed to load %s, %d.", POWERLOCK_CONF_FILE, ret);
+
register_kernel_uevent_control(&lcd_uevent_ops);
register_kernel_uevent_control(&sec_dsim_uevent_ops);
@@ -3081,6 +3265,7 @@ static void display_exit(void *data)
exit_lcd_operation();
free_lock_info_list();
+ free_killable_daemon_list();
/* free display service */
display_service_free();