diff options
author | Youngjae Cho <y0.cho@samsung.com> | 2020-08-31 11:38:10 +0900 |
---|---|---|
committer | Hyotaek Shim <hyotaek.shim@samsung.com> | 2020-09-01 05:56:21 +0000 |
commit | b8087749b614f5460bec2a321756c661cbd86310 (patch) | |
tree | c4a607398512a66b7bb7e5fa04f1f9767c3c7479 | |
parent | e9731fde6488fd49dbeb937f280d5769886eef76 (diff) | |
download | deviced-b8087749b614f5460bec2a321756c661cbd86310.tar.gz deviced-b8087749b614f5460bec2a321756c661cbd86310.tar.bz2 deviced-b8087749b614f5460bec2a321756c661cbd86310.zip |
Enhance display/power lock managemenentsubmit/tizen/20200902.021453accepted/tizen/unified/20200902.145534
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.c | 231 | ||||
-rw-r--r-- | src/display/core.h | 7 | ||||
-rw-r--r-- | src/display/display-lock.c | 62 | ||||
-rw-r--r-- | src/display/display-lock.h | 13 |
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); |