diff options
author | Youngjae Cho <y0.cho@samsung.com> | 2020-08-11 15:54:18 +0900 |
---|---|---|
committer | Hyotaek Shim <hyotaek.shim@samsung.com> | 2020-08-25 01:15:44 +0000 |
commit | 6e4f55e7b51f0e33c9dd976c6bd982e23c958cfd (patch) | |
tree | 3e324e17c8303843be5af402ce19a039094e5bbb | |
parent | 7d2a08b3f6c2445b766ecade848a04b80023797e (diff) | |
download | deviced-6e4f55e7b51f0e33c9dd976c6bd982e23c958cfd.tar.gz deviced-6e4f55e7b51f0e33c9dd976c6bd982e23c958cfd.tar.bz2 deviced-6e4f55e7b51f0e33c9dd976c6bd982e23c958cfd.zip |
Enhance display/power lock managemenentsubmit/tizen_5.5/20200825.012738accepted/tizen/5.5/unified/20200827.041647
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.c | 239 |
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(); |