summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYoungjae Cho <y0.cho@samsung.com>2020-08-31 11:38:10 +0900
committerHyotaek Shim <hyotaek.shim@samsung.com>2020-09-01 05:56:21 +0000
commitb8087749b614f5460bec2a321756c661cbd86310 (patch)
treec4a607398512a66b7bb7e5fa04f1f9767c3c7479
parente9731fde6488fd49dbeb937f280d5769886eef76 (diff)
downloaddeviced-b8087749b614f5460bec2a321756c661cbd86310.tar.gz
deviced-b8087749b614f5460bec2a321756c661cbd86310.tar.bz2
deviced-b8087749b614f5460bec2a321756c661cbd86310.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: Ia0d433facdcc7814e19278619a58a111ff7ff7c3 Signed-off-by: Youngjae Cho <y0.cho@samsung.com>
-rw-r--r--plugins/wearable/display/core.c231
-rw-r--r--src/display/core.h7
-rw-r--r--src/display/display-lock.c62
-rw-r--r--src/display/display-lock.h13
4 files changed, 279 insertions, 34 deletions
diff --git a/plugins/wearable/display/core.c b/plugins/wearable/display/core.c
index 4a5c8e08..6f3bb434 100644
--- a/plugins/wearable/display/core.c
+++ b/plugins/wearable/display/core.c
@@ -69,6 +69,7 @@
#include "shared/plugin.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"
@@ -189,6 +190,19 @@ static int trans_table[S_END][EVENT_END] = {
(b.tv_sec * 1000000 + b.tv_usec)) \
/ 1000)
+#define KILLABLE_DAEMON_LOCK_LIMIT 1800 /* seconds, 30min */
+#define FORCE_RELEASE_LOCK_INTERVAL 5 /* seconds */
+
+static void get_comm(pid_t pid, char *comm);
+static bool is_killable_daemon(pid_t pid);
+static gboolean pmlock_terminate_daemon_to_release_lock(gpointer data);
+static int pmlock_check(void *data);
+static int powerlock_load_config(struct parse_result *result, void *user_data);
+static void free_killable_daemon_list(void);
+
+static dd_list *display_lock_killable_daemon;
+static bool initialized_killable_daemon_list;
+
static struct display_config display_conf = {
.lock_wait_time = LOCK_SCREEN_WATING_TIME,
.longpress_interval = LONG_PRESS_INTERVAL,
@@ -206,6 +220,7 @@ static struct display_config display_conf = {
.timeout_enable = true,
.input_support = true,
.lockcheck_timeout = 600,
+ .pmlock_check = pmlock_check,
.aod_enter_level = 40,
.aod_tsp = true,
.touch_wakeup = false,
@@ -319,6 +334,181 @@ static const char* __device_flags_to_string(enum device_flags flags)
return UNKNOWN_STR;
}
+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);
+}
+
+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[get_pm_cur_state()].trans(EVENT_TIMEOUT);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static int pmlock_check(void *data)
+{
+ PmLockNode *node;
+ int ret;
+
+ assert(data);
+ node = (PmLockNode *) data;
+
+ if (!initialized_killable_daemon_list) {
+ ret = config_parse(POWERLOCK_CONF_FILE, powerlock_load_config, NULL);
+ /* config file may not exist */
+ if (ret < 0 && ret != -ENOENT)
+ _W("Failed to load %s, %d.", POWERLOCK_CONF_FILE, ret);
+
+ if (status == DEVICE_OPS_STATUS_UNINIT) {
+ _W("Lock request before display init. Preloaded killable list.");
+ initialized_killable_daemon_list = true;
+ } else if (status == DEVICE_OPS_STATUS_STOP) {
+ _W("Lock request after display stop. Loaded list will be freed immediately.");
+ node->killable_daemon = is_killable_daemon(node->pid);
+ free_killable_daemon_list();
+ goto killable_marked;
+ }
+ }
+
+ node->killable_daemon = is_killable_daemon(node->pid);
+
+killable_marked:
+ /* use default lock checker */
+ if (!node->killable_daemon)
+ return 0;
+
+ return g_timeout_add_seconds(KILLABLE_DAEMON_LOCK_LIMIT,
+ pmlock_terminate_daemon_to_release_lock, (gpointer)node);
+}
+
+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;
+ initialized_killable_daemon_list = false;
+}
+
static unsigned long get_lcd_on_flags(void)
{
unsigned long flags = NORMAL_MODE;
@@ -1089,9 +1279,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;
}
@@ -1122,8 +1316,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);
@@ -2172,6 +2366,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;
@@ -2331,6 +2545,12 @@ 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);
+ /* config file may not exist */
+ if (ret < 0 && ret != -ENOENT)
+ _W("Failed to load %s, %d.", POWERLOCK_CONF_FILE, ret);
+ initialized_killable_daemon_list = true;
+
register_kernel_uevent_control(&lcd_uevent_ops);
register_kernel_uevent_control(&sec_dsim_uevent_ops);
@@ -2489,6 +2709,7 @@ static void display_exit(void *data)
exit_lcd_operation();
free_lock_info_list();
+ free_killable_daemon_list();
/* free display service */
display_service_free();
diff --git a/src/display/core.h b/src/display/core.h
index caa97dc4..5bfb1d17 100644
--- a/src/display/core.h
+++ b/src/display/core.h
@@ -126,6 +126,13 @@ struct display_config {
int accel_sensor_on;
int continuous_sampling;
int lockcheck_timeout;
+
+ /* Define pmlock checker.
+ * Return id of the lock checker.
+ *
+ * Returning 0 will use default lock checker */
+ int (*pmlock_check) (void *data);
+
int aod_enter_level;
bool aod_tsp;
bool timeout_enable;
diff --git a/src/display/display-lock.c b/src/display/display-lock.c
index 4450a76a..b1e08d4e 100644
--- a/src/display/display-lock.c
+++ b/src/display/display-lock.c
@@ -37,6 +37,8 @@ static struct _backlight_ops *backlight_ops;
static dd_list *cond_head[S_END];
static int trans_condition;
+static struct display_config *display_conf;
+
bool check_lock_state(int state)
{
dd_list *elem;
@@ -62,7 +64,7 @@ static void refresh_app_cond()
trans_condition |= MASK_OFF;
}
-static void pmlock_check_cb(GVariant *var, void *user_data, GError *err)
+static void default_pmlock_check_cb(GVariant *var, void *user_data, GError *err)
{
pid_t pid = 0;
int ret, detected = 0;
@@ -97,7 +99,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");
@@ -107,12 +109,11 @@ out:
g_variant_unref(var);
}
-static gboolean pmlock_check(void *data)
+static gboolean default_pmlock_check(void *data)
{
const char *arr[2];
char chr_pid[PID_MAX];
PmLockNode *node;
- GVariant *v;
enum state_t state;
pid_t pid;
int ret;
@@ -122,8 +123,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");
@@ -152,7 +154,6 @@ static gboolean pmlock_check(void *data)
break;
default:
_E("Invalid state.");
- g_variant_unref(v);
return G_SOURCE_REMOVE;
}
@@ -160,7 +161,7 @@ static gboolean pmlock_check(void *data)
RESOURCED_PATH_PROCESS,
RESOURCED_INTERFACE_PROCESS,
METHOD_APP_STATUS,
- "is", arr, pmlock_check_cb, -1, (void *)(intptr_t)state);
+ "is", arr, default_pmlock_check_cb, -1, (void *)(intptr_t)state);
if (ret < 0)
_E("Failed to call dbus method");
@@ -184,41 +185,39 @@ PmLockNode *find_node(enum state_t s_index, pid_t pid)
PmLockNode *add_node(enum state_t s_index, pid_t pid, guint timeout_id,
bool holdkey_block)
{
- guint warning_id = 0;
PmLockNode *n;
- GVariant *v = NULL;
time_t now;
- struct display_config *display_conf = get_display_config();
- if (!display_conf) {
- _E("Failed to get display configuration.");
- return NULL;
- }
- n = (PmLockNode *) malloc(sizeof(PmLockNode));
+ assert(display_conf);
+
+ n = (PmLockNode *) calloc(1, sizeof(PmLockNode));
if (n == NULL) {
- _E("Not enough memory, add cond. fail");
+ _E("Not enough memory, failed to alloc lock node.");
return NULL;
}
- if (pid < INTERNAL_LOCK_BASE) {
- v = g_variant_new("(ii)", s_index, pid);
- if (v) {
- warning_id = g_timeout_add_seconds(display_conf->lockcheck_timeout,
- pmlock_check, (void *)v);
- } else {
- _E("Failed to make GVariant.");
- }
- }
-
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;
+
+ if (pid < INTERNAL_LOCK_BASE) {
+ /* check if this lock node needs custom-defined lock checker.
+ * n->warning_id would be 0 if fails to register the checker,
+ * or there is no need to use that checker */
+ if (display_conf->pmlock_check)
+ n->warning_id = display_conf->pmlock_check(n);
+
+ /* use default lock checker */
+ if (!n->warning_id)
+ n->warning_id = g_timeout_add_seconds(display_conf->lockcheck_timeout,
+ default_pmlock_check, (gpointer)n);
+ }
+
DD_LIST_APPEND(cond_head[s_index], n);
refresh_app_cond();
@@ -403,4 +402,9 @@ static void __CONSTRUCTOR__ initialize(void)
backlight_ops = get_backlight_ops();
if (!backlight_ops)
_E("Failed to get backlight operator.");
+
+ display_conf = get_display_config();
+ if (!display_conf) {
+ _E("Failed to get display config.");
+ }
}
diff --git a/src/display/display-lock.h b/src/display/display-lock.h
index bde19db1..c52ac84c 100644
--- a/src/display/display-lock.h
+++ b/src/display/display-lock.h
@@ -27,6 +27,7 @@
#include "core.h"
typedef struct _pm_lock_node {
+ enum state_t state;
pid_t pid;
guint timeout_id;
guint warning_id;
@@ -35,6 +36,18 @@ typedef struct _pm_lock_node {
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;
bool check_lock_state(int state);