Merge pull request #12153 from benjarobin/killall-show-not-killed

shutdown/killall: Show in the console the processes not yet killed
This commit is contained in:
Lennart Poettering 2019-04-11 18:58:43 +02:00 committed by GitHub
commit aa46c28418
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 117 additions and 22 deletions

View file

@ -77,19 +77,46 @@ static bool ignore_proc(pid_t pid, bool warn_rootfs) {
return true;
}
static void wait_for_children(Set *pids, sigset_t *mask, usec_t timeout) {
static void log_children_no_yet_killed(Set *pids) {
void *p;
Iterator i;
_cleanup_free_ char *lst_child = NULL;
SET_FOREACH(p, pids, i) {
_cleanup_free_ char *s = NULL;
if (get_process_comm(PTR_TO_PID(p), &s) == 0) {
if (!strextend(&lst_child, ", ", s, NULL))
break;
}
}
if (!isempty(lst_child))
log_notice("Waiting for process:%s", lst_child + 1);
}
static int wait_for_children(Set *pids, sigset_t *mask, usec_t timeout) {
usec_t until;
usec_t date_log_child;
usec_t n;
assert(mask);
if (set_isempty(pids))
return;
/* Return the number of children remaining in the pids set: That correspond to the number
* of processes still "alive" after the timeout */
if (set_isempty(pids))
return 0;
n = now(CLOCK_MONOTONIC);
until = usec_add(n, timeout);
date_log_child = usec_add(n, 10u * USEC_PER_SEC);
if (date_log_child > until)
date_log_child = usec_add(n, timeout / 2u);
until = now(CLOCK_MONOTONIC) + timeout;
for (;;) {
struct timespec ts;
int k;
usec_t n;
void *p;
Iterator i;
@ -107,8 +134,7 @@ static void wait_for_children(Set *pids, sigset_t *mask, usec_t timeout) {
if (errno == ECHILD)
break;
log_error_errno(errno, "waitpid() failed: %m");
return;
return log_error_errno(errno, "waitpid() failed: %m");
}
(void) set_remove(pids, PID_TO_PTR(pid));
@ -130,20 +156,28 @@ static void wait_for_children(Set *pids, sigset_t *mask, usec_t timeout) {
}
if (set_isempty(pids))
return;
return 0;
n = now(CLOCK_MONOTONIC);
if (n >= until)
return;
if (date_log_child > 0 && n >= date_log_child) {
log_children_no_yet_killed(pids);
/* Log the children not yet killed only once */
date_log_child = 0;
}
if (n >= until)
return set_size(pids);
if (date_log_child > 0)
timespec_store(&ts, MIN(until - n, date_log_child - n));
else
timespec_store(&ts, until - n);
timespec_store(&ts, until - n);
k = sigtimedwait(mask, NULL, &ts);
if (k != SIGCHLD) {
if (k < 0 && errno != EAGAIN) {
log_error_errno(errno, "sigtimedwait() failed: %m");
return;
}
if (k < 0 && errno != EAGAIN)
return log_error_errno(errno, "sigtimedwait() failed: %m");
if (k >= 0)
log_warning("sigtimedwait() returned unexpected signal.");
@ -154,10 +188,14 @@ static void wait_for_children(Set *pids, sigset_t *mask, usec_t timeout) {
static int killall(int sig, Set *pids, bool send_sighup) {
_cleanup_closedir_ DIR *dir = NULL;
struct dirent *d;
int n_killed = 0;
/* Send the specified signal to all remaining processes, if not excluded by ignore_proc().
* Returns the number of processes to which the specified signal was sent */
dir = opendir("/proc");
if (!dir)
return -errno;
return log_warning_errno(errno, "opendir(/proc) failed: %m");
FOREACH_DIRENT_ALL(d, dir, break) {
pid_t pid;
@ -180,6 +218,7 @@ static int killall(int sig, Set *pids, bool send_sighup) {
}
if (kill(pid, sig) >= 0) {
n_killed++;
if (pids) {
r = set_put(pids, PID_TO_PTR(pid));
if (r < 0)
@ -205,13 +244,20 @@ static int killall(int sig, Set *pids, bool send_sighup) {
}
}
return set_size(pids);
return n_killed;
}
void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup, usec_t timeout) {
int broadcast_signal(int sig, bool wait_for_exit, bool send_sighup, usec_t timeout) {
int n_children_left;
sigset_t mask, oldmask;
_cleanup_set_free_ Set *pids = NULL;
/* Send the specified signal to all remaining processes, if not excluded by ignore_proc().
* Return:
* - The number of processes still "alive" after the timeout (that should have been killed)
* if the function needs to wait for the end of the processes (wait_for_exit).
* - Otherwise, the number of processes to which the specified signal was sent */
if (wait_for_exit)
pids = set_new(NULL);
@ -222,13 +268,15 @@ void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup, usec_t time
if (kill(-1, SIGSTOP) < 0 && errno != ESRCH)
log_warning_errno(errno, "kill(-1, SIGSTOP) failed: %m");
killall(sig, pids, send_sighup);
n_children_left = killall(sig, pids, send_sighup);
if (kill(-1, SIGCONT) < 0 && errno != ESRCH)
log_warning_errno(errno, "kill(-1, SIGCONT) failed: %m");
if (wait_for_exit)
wait_for_children(pids, &mask, timeout);
if (wait_for_exit && n_children_left > 0)
n_children_left = wait_for_children(pids, &mask, timeout);
assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
return n_children_left;
}

View file

@ -3,4 +3,4 @@
#include "time-util.h"
void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup, usec_t timeout);
int broadcast_signal(int sig, bool wait_for_exit, bool send_sighup, usec_t timeout);

View file

@ -32,6 +32,7 @@
#include "signal-util.h"
#include "string-util.h"
#include "switch-root.h"
#include "sysctl-util.h"
#include "terminal-util.h"
#include "umount.h"
#include "util.h"
@ -255,6 +256,42 @@ static void sync_with_progress(void) {
(void) kill(pid, SIGKILL);
}
static int read_current_sysctl_printk_log_level(void) {
_cleanup_free_ char *sysctl_printk_vals = NULL, *sysctl_printk_curr = NULL;
unsigned current_lvl = 0;
const char *p;
int r;
r = sysctl_read("kernel/printk", &sysctl_printk_vals);
if (r < 0)
return log_debug_errno(r, "Cannot read sysctl kernel.printk: %m");
p = sysctl_printk_vals;
r = extract_first_word(&p, &sysctl_printk_curr, NULL, 0);
if (r > 0)
r = safe_atou(sysctl_printk_curr, &current_lvl);
else if (r == 0)
r = -EINVAL;
if (r < 0)
return log_debug_errno(r, "Unexpected sysctl kernel.printk content: %s", sysctl_printk_vals);
return current_lvl;
}
static void bump_sysctl_printk_log_level(int min_level) {
/* Set the logging level to be able to see messages with log level smaller or equal to min_level */
int current_lvl = read_current_sysctl_printk_log_level();
if (current_lvl >= 0 && current_lvl <= min_level) {
char buf[DECIMAL_STR_MAX(int)];
xsprintf(buf, "%d", min_level + 1);
int r = sysctl_write("kernel/printk", buf);
if (r < 0)
log_debug_errno(r, "Failed to bump kernel.printk to %s: %m", buf);
}
}
int main(int argc, char *argv[]) {
bool need_umount, need_swapoff, need_loop_detach, need_dm_detach;
bool in_container, use_watchdog = false, can_initrd;
@ -305,6 +342,16 @@ int main(int argc, char *argv[]) {
(void) cg_get_root_path(&cgroup);
in_container = detect_container() > 0;
/* If the logging messages are going to KMSG, and if we are not running from a container,
* then try to update the sysctl kernel.printk current value in order to see "info" messages;
* This current log level is not updated if already big enough.
*/
if (!in_container && IN_SET(log_get_target(), LOG_TARGET_AUTO,
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_KMSG))
bump_sysctl_printk_log_level(LOG_INFO);
use_watchdog = getenv("WATCHDOG_USEC");
watchdog_device = getenv("WATCHDOG_DEVICE");
if (watchdog_device) {