X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fkillall.c;h=57ed41c5afe4d2eb62e3adb20562a65f9de10e24;hp=55200ffa4847e2eb8053c51201a451335c094603;hb=454f7158c6cecd18555c5e7dd556e3d544301b52;hpb=f274ece0f76b5709408821e317e87aef76123db6 diff --git a/src/core/killall.c b/src/core/killall.c index 55200ffa4..57ed41c5a 100644 --- a/src/core/killall.c +++ b/src/core/killall.c @@ -22,17 +22,19 @@ #include #include #include +#include #include "util.h" #include "def.h" #include "killall.h" +#include "set.h" -#define TIMEOUT_USEC (5 * USEC_PER_SEC) +#define TIMEOUT_USEC (10 * USEC_PER_SEC) static bool ignore_proc(pid_t pid) { - char buf[PATH_MAX]; - FILE *f; + _cleanup_fclose_ FILE *f = NULL; char c; + const char *p; size_t count; uid_t uid; int r; @@ -49,15 +51,12 @@ static bool ignore_proc(pid_t pid) { if (uid != 0) return false; - snprintf(buf, sizeof(buf), "/proc/%lu/cmdline", (unsigned long) pid); - char_array_0(buf); - - f = fopen(buf, "re"); + p = procfs_file_alloca(pid, "cmdline"); + f = fopen(p, "re"); if (!f) return true; /* not really, but has the desired effect */ count = fread(&c, 1, 1, f); - fclose(f); /* Kernel threads have an empty cmdline */ if (count <= 0) @@ -73,38 +72,68 @@ static bool ignore_proc(pid_t pid) { return false; } -static void wait_for_children(int n_processes, sigset_t *mask) { +static void wait_for_children(Set *pids, sigset_t *mask) { usec_t until; assert(mask); + if (set_isempty(pids)) + return; + until = now(CLOCK_MONOTONIC) + TIMEOUT_USEC; for (;;) { struct timespec ts; int k; usec_t n; + void *p; + Iterator i; + /* First, let the kernel inform us about killed + * children. Most processes will probably be our + * children, but some are not (might be our + * grandchildren instead...). */ for (;;) { - pid_t pid = waitpid(-1, NULL, WNOHANG); + pid_t pid; + pid = waitpid(-1, NULL, WNOHANG); if (pid == 0) break; + if (pid < 0) { + if (errno == ECHILD) + break; - if (pid < 0 && errno == ECHILD) + log_error("waitpid() failed: %m"); return; + } + + set_remove(pids, ULONG_TO_PTR(pid)); + } + + /* Now explicitly check who might be remaining, who + * might not be our child. */ + SET_FOREACH(p, pids, i) { - if (n_processes > 0) - if (--n_processes == 0) - return; + /* We misuse getpgid as a check whether a + * process still exists. */ + if (getpgid((pid_t) PTR_TO_ULONG(p)) >= 0) + continue; + + if (errno != ESRCH) + continue; + + set_remove(pids, p); } + if (set_isempty(pids)) + return; + n = now(CLOCK_MONOTONIC); if (n >= until) return; timespec_store(&ts, until - n); - - if ((k = sigtimedwait(mask, NULL, &ts)) != SIGCHLD) { + k = sigtimedwait(mask, NULL, &ts); + if (k != SIGCHLD) { if (k < 0 && errno != EAGAIN) { log_error("sigtimedwait() failed: %m"); @@ -117,10 +146,9 @@ static void wait_for_children(int n_processes, sigset_t *mask) { } } -static int killall(int sig) { - DIR *dir; +static int killall(int sig, Set *pids, bool send_sighup) { + _cleanup_closedir_ DIR *dir = NULL; struct dirent *d; - unsigned int n_processes = 0; dir = opendir("/proc"); if (!dir) @@ -139,20 +167,45 @@ static int killall(int sig) { if (ignore_proc(pid)) continue; - if (kill(pid, sig) >= 0) - n_processes++; - else if (errno != ENOENT) + if (sig == SIGKILL) { + _cleanup_free_ char *s; + + get_process_comm(pid, &s); + log_notice("Sending SIGKILL to PID "PID_FMT" (%s).", pid, strna(s)); + } + + if (kill(pid, sig) >= 0) { + if (pids) + set_put(pids, ULONG_TO_PTR(pid)); + } else if (errno != ENOENT) log_warning("Could not kill %d: %m", pid); - } - closedir(dir); + if (send_sighup) { + /* Optionally, also send a SIGHUP signal, but + only if the process has a controlling + tty. This is useful to allow handling of + shells which ignore SIGTERM but react to + SIGHUP. We do not send this to processes that + have no controlling TTY since we don't want to + trigger reloads of daemon processes. Also we + make sure to only send this after SIGTERM so + that SIGTERM is always first in the queue. */ - return n_processes; + + if (get_ctty_devnr(pid, NULL) >= 0) + kill(pid, SIGHUP); + } + } + + return set_size(pids); } -void broadcast_signal(int sig, bool wait_for_exit) { +void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup) { sigset_t mask, oldmask; - int n_processes; + Set *pids = NULL; + + if (wait_for_exit) + pids = set_new(trivial_hash_func, trivial_compare_func); assert_se(sigemptyset(&mask) == 0); assert_se(sigaddset(&mask, SIGCHLD) == 0); @@ -161,17 +214,15 @@ void broadcast_signal(int sig, bool wait_for_exit) { if (kill(-1, SIGSTOP) < 0 && errno != ESRCH) log_warning("kill(-1, SIGSTOP) failed: %m"); - n_processes = killall(sig); + killall(sig, pids, send_sighup); if (kill(-1, SIGCONT) < 0 && errno != ESRCH) log_warning("kill(-1, SIGCONT) failed: %m"); - if (n_processes <= 0) - goto finish; - if (wait_for_exit) - wait_for_children(n_processes, &mask); + wait_for_children(pids, &mask); + + assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0); -finish: - sigprocmask(SIG_SETMASK, &oldmask, NULL); + set_free(pids); }