diff --git a/src/core/killall.c b/src/core/killall.c index f0ce996556..0710919640 100644 --- a/src/core/killall.c +++ b/src/core/killall.c @@ -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); + /* 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; + 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 (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; + 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; } diff --git a/src/core/killall.h b/src/core/killall.h index cbd202651c..08ae28d073 100644 --- a/src/core/killall.h +++ b/src/core/killall.h @@ -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); diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c index 842ba57f13..25a538df50 100644 --- a/src/shutdown/shutdown.c +++ b/src/shutdown/shutdown.c @@ -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, ¤t_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) {