Merge pull request #3762 from poettering/sigkill-log

log about all processes we forcibly kill
This commit is contained in:
Martin Pitt
2016-07-22 09:18:30 +02:00
committed by GitHub
13 changed files with 178 additions and 75 deletions
+48 -21
View File
@@ -197,7 +197,15 @@ int cg_rmdir(const char *controller, const char *path) {
return 0;
}
int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s) {
int cg_kill(
const char *controller,
const char *path,
int sig,
CGroupFlags flags,
Set *s,
cg_kill_log_func_t log_kill,
void *userdata) {
_cleanup_set_free_ Set *allocated_set = NULL;
bool done = false;
int r, ret = 0;
@@ -205,6 +213,11 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo
assert(sig >= 0);
/* Don't send SIGCONT twice. Also, SIGKILL always works even when process is suspended, hence don't send
* SIGCONT on SIGKILL. */
if (IN_SET(sig, SIGCONT, SIGKILL))
flags &= ~CGROUP_SIGCONT;
/* This goes through the tasks list and kills them all. This
* is repeated until no further processes are added to the
* tasks list, to properly handle forking processes */
@@ -232,19 +245,22 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo
while ((r = cg_read_pid(f, &pid)) > 0) {
if (ignore_self && pid == my_pid)
if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid)
continue;
if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
continue;
if (log_kill)
log_kill(pid, sig, userdata);
/* If we haven't killed this process yet, kill
* it */
if (kill(pid, sig) < 0) {
if (ret >= 0 && errno != ESRCH)
ret = -errno;
} else {
if (sigcont && sig != SIGKILL)
if (flags & CGROUP_SIGCONT)
(void) kill(pid, SIGCONT);
if (ret == 0)
@@ -278,7 +294,15 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo
return ret;
}
int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool rem, Set *s) {
int cg_kill_recursive(
const char *controller,
const char *path,
int sig,
CGroupFlags flags,
Set *s,
cg_kill_log_func_t log_kill,
void *userdata) {
_cleanup_set_free_ Set *allocated_set = NULL;
_cleanup_closedir_ DIR *d = NULL;
int r, ret;
@@ -293,7 +317,7 @@ int cg_kill_recursive(const char *controller, const char *path, int sig, bool si
return -ENOMEM;
}
ret = cg_kill(controller, path, sig, sigcont, ignore_self, s);
ret = cg_kill(controller, path, sig, flags, s, log_kill, userdata);
r = cg_enumerate_subgroups(controller, path, &d);
if (r < 0) {
@@ -311,15 +335,14 @@ int cg_kill_recursive(const char *controller, const char *path, int sig, bool si
if (!p)
return -ENOMEM;
r = cg_kill_recursive(controller, p, sig, sigcont, ignore_self, rem, s);
r = cg_kill_recursive(controller, p, sig, flags, s, log_kill, userdata);
if (r != 0 && ret >= 0)
ret = r;
}
if (ret >= 0 && r < 0)
ret = r;
if (rem) {
if (flags & CGROUP_REMOVE) {
r = cg_rmdir(controller, path);
if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
return r;
@@ -328,7 +351,13 @@ int cg_kill_recursive(const char *controller, const char *path, int sig, bool si
return ret;
}
int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self) {
int cg_migrate(
const char *cfrom,
const char *pfrom,
const char *cto,
const char *pto,
CGroupFlags flags) {
bool done = false;
_cleanup_set_free_ Set *s = NULL;
int r, ret = 0;
@@ -363,7 +392,7 @@ int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char
/* This might do weird stuff if we aren't a
* single-threaded program. However, we
* luckily know we are not */
if (ignore_self && pid == my_pid)
if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid)
continue;
if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
@@ -411,8 +440,7 @@ int cg_migrate_recursive(
const char *pfrom,
const char *cto,
const char *pto,
bool ignore_self,
bool rem) {
CGroupFlags flags) {
_cleanup_closedir_ DIR *d = NULL;
int r, ret = 0;
@@ -423,7 +451,7 @@ int cg_migrate_recursive(
assert(cto);
assert(pto);
ret = cg_migrate(cfrom, pfrom, cto, pto, ignore_self);
ret = cg_migrate(cfrom, pfrom, cto, pto, flags);
r = cg_enumerate_subgroups(cfrom, pfrom, &d);
if (r < 0) {
@@ -441,7 +469,7 @@ int cg_migrate_recursive(
if (!p)
return -ENOMEM;
r = cg_migrate_recursive(cfrom, p, cto, pto, ignore_self, rem);
r = cg_migrate_recursive(cfrom, p, cto, pto, flags);
if (r != 0 && ret >= 0)
ret = r;
}
@@ -449,7 +477,7 @@ int cg_migrate_recursive(
if (r < 0 && ret >= 0)
ret = r;
if (rem) {
if (flags & CGROUP_REMOVE) {
r = cg_rmdir(cfrom, pfrom);
if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
return r;
@@ -463,8 +491,7 @@ int cg_migrate_recursive_fallback(
const char *pfrom,
const char *cto,
const char *pto,
bool ignore_self,
bool rem) {
CGroupFlags flags) {
int r;
@@ -473,7 +500,7 @@ int cg_migrate_recursive_fallback(
assert(cto);
assert(pto);
r = cg_migrate_recursive(cfrom, pfrom, cto, pto, ignore_self, rem);
r = cg_migrate_recursive(cfrom, pfrom, cto, pto, flags);
if (r < 0) {
char prefix[strlen(pto) + 1];
@@ -482,7 +509,7 @@ int cg_migrate_recursive_fallback(
PATH_FOREACH_PREFIX(prefix, pto) {
int q;
q = cg_migrate_recursive(cfrom, pfrom, cto, prefix, ignore_self, rem);
q = cg_migrate_recursive(cfrom, pfrom, cto, prefix, flags);
if (q >= 0)
return q;
}
@@ -1955,7 +1982,7 @@ int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to
int r = 0, unified;
if (!path_equal(from, to)) {
r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, CGROUP_REMOVE);
if (r < 0)
return r;
}
@@ -1979,7 +2006,7 @@ int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to
if (!p)
p = to;
(void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, false, false);
(void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, 0);
}
return 0;
+13 -5
View File
@@ -135,12 +135,20 @@ int cg_read_event(const char *controller, const char *path, const char *event,
int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d);
int cg_read_subgroup(DIR *d, char **fn);
int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s);
int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool remove, Set *s);
typedef enum CGroupFlags {
CGROUP_SIGCONT = 1,
CGROUP_IGNORE_SELF = 2,
CGROUP_REMOVE = 4,
} CGroupFlags;
int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self);
int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self, bool remove);
int cg_migrate_recursive_fallback(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self, bool rem);
typedef void (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata);
int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags);
int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags);
int cg_migrate_recursive_fallback(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags);
int cg_split_spec(const char *spec, char **controller, char **path);
int cg_mangle_path(const char *path, char **result);
+4 -2
View File
@@ -625,8 +625,10 @@ int kill_and_sigcont(pid_t pid, int sig) {
r = kill(pid, sig) < 0 ? -errno : 0;
if (r >= 0)
kill(pid, SIGCONT);
/* If this worked, also send SIGCONT, unless we already just sent a SIGCONT, or SIGKILL was sent which isn't
* affected by a process being suspended anyway. */
if (r >= 0 && !IN_SET(SIGCONT, SIGKILL))
(void) kill(pid, SIGCONT);
return r;
}
+1 -1
View File
@@ -1705,7 +1705,7 @@ int manager_setup_cgroup(Manager *m) {
/* also, move all other userspace processes remaining
* in the root cgroup into that scope. */
r = cg_migrate(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, SYSTEMD_CGROUP_CONTROLLER, scope_path, false);
r = cg_migrate(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, SYSTEMD_CGROUP_CONTROLLER, scope_path, 0);
if (r < 0)
log_warning_errno(r, "Couldn't move remaining userspace processes, ignoring: %m");
+1 -1
View File
@@ -225,5 +225,5 @@ int bus_scope_send_request_stop(Scope *s) {
if (r < 0)
return r;
return sd_bus_send_to(UNIT(s)->manager->api_bus, m, /* s->controller */ NULL, NULL);
return sd_bus_send_to(UNIT(s)->manager->api_bus, m, s->controller, NULL);
}
+14 -2
View File
@@ -240,7 +240,7 @@ static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) {
/* If we have a controller set let's ask the controller nicely
* to terminate the scope, instead of us going directly into
* SIGTERM beserk mode */
* SIGTERM berserk mode */
if (state == SCOPE_STOP_SIGTERM)
skip_signal = bus_scope_send_request_stop(s) > 0;
@@ -248,7 +248,9 @@ static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) {
r = unit_kill_context(
UNIT(s),
&s->kill_context,
state != SCOPE_STOP_SIGTERM ? KILL_KILL : KILL_TERMINATE,
state != SCOPE_STOP_SIGTERM ? KILL_KILL :
s->was_abandoned ? KILL_TERMINATE_AND_LOG :
KILL_TERMINATE,
-1, -1, false);
if (r < 0)
goto fail;
@@ -369,6 +371,7 @@ static int scope_serialize(Unit *u, FILE *f, FDSet *fds) {
assert(fds);
unit_serialize_item(u, f, "state", scope_state_to_string(s->state));
unit_serialize_item(u, f, "was-abandoned", yes_no(s->was_abandoned));
return 0;
}
@@ -389,6 +392,14 @@ static int scope_deserialize_item(Unit *u, const char *key, const char *value, F
else
s->deserialized_state = state;
} else if (streq(key, "was-abandoned")) {
int k;
k = parse_boolean(value);
if (k < 0)
log_unit_debug(u, "Failed to parse boolean value: %s", value);
else
s->was_abandoned = k;
} else
log_unit_debug(u, "Unknown serialization key: %s", key);
@@ -474,6 +485,7 @@ int scope_abandon(Scope *s) {
if (!IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED))
return -ESTALE;
s->was_abandoned = true;
s->controller = mfree(s->controller);
/* The client is no longer watching the remaining processes,
+1
View File
@@ -45,6 +45,7 @@ struct Scope {
usec_t timeout_stop_usec;
char *controller;
bool was_abandoned;
sd_event_source *timer_event_source;
};
+1 -1
View File
@@ -1674,7 +1674,7 @@ static void service_kill_control_processes(Service *s) {
return;
p = strjoina(UNIT(s)->cgroup_path, "/control");
cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, p, SIGKILL, true, true, true, NULL);
cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, p, SIGKILL, CGROUP_SIGCONT|CGROUP_IGNORE_SELF|CGROUP_REMOVE, NULL, NULL, NULL);
}
static void service_enter_start(Service *s) {
+74 -24
View File
@@ -3144,7 +3144,7 @@ int unit_kill_common(
if (!pid_set)
return -ENOMEM;
q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, false, false, pid_set);
q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, 0, pid_set, NULL, NULL);
if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT)
r = q;
else
@@ -3512,6 +3512,43 @@ int unit_make_transient(Unit *u) {
return 0;
}
static void log_kill(pid_t pid, int sig, void *userdata) {
_cleanup_free_ char *comm = NULL;
(void) get_process_comm(pid, &comm);
/* Don't log about processes marked with brackets, under the assumption that these are temporary processes
only, like for example systemd's own PAM stub process. */
if (comm && comm[0] == '(')
return;
log_unit_notice(userdata,
"Killing process " PID_FMT " (%s) with signal SIG%s.",
pid,
strna(comm),
signal_to_string(sig));
}
static int operation_to_signal(KillContext *c, KillOperation k) {
assert(c);
switch (k) {
case KILL_TERMINATE:
case KILL_TERMINATE_AND_LOG:
return c->kill_signal;
case KILL_KILL:
return SIGKILL;
case KILL_ABORT:
return SIGABRT;
default:
assert_not_reached("KillOperation unknown");
}
}
int unit_kill_context(
Unit *u,
KillContext *c,
@@ -3520,58 +3557,63 @@ int unit_kill_context(
pid_t control_pid,
bool main_pid_alien) {
bool wait_for_exit = false;
bool wait_for_exit = false, send_sighup;
cg_kill_log_func_t log_func;
int sig, r;
assert(u);
assert(c);
/* Kill the processes belonging to this unit, in preparation for shutting the unit down. Returns > 0 if we
* killed something worth waiting for, 0 otherwise. */
if (c->kill_mode == KILL_NONE)
return 0;
switch (k) {
case KILL_KILL:
sig = SIGKILL;
break;
case KILL_ABORT:
sig = SIGABRT;
break;
case KILL_TERMINATE:
sig = c->kill_signal;
break;
default:
assert_not_reached("KillOperation unknown");
}
sig = operation_to_signal(c, k);
send_sighup =
c->send_sighup &&
IN_SET(k, KILL_TERMINATE, KILL_TERMINATE_AND_LOG) &&
sig != SIGHUP;
log_func =
k != KILL_TERMINATE ||
IN_SET(sig, SIGKILL, SIGABRT) ? log_kill : NULL;
if (main_pid > 0) {
r = kill_and_sigcont(main_pid, sig);
if (log_func)
log_func(main_pid, sig, u);
r = kill_and_sigcont(main_pid, sig);
if (r < 0 && r != -ESRCH) {
_cleanup_free_ char *comm = NULL;
get_process_comm(main_pid, &comm);
(void) get_process_comm(main_pid, &comm);
log_unit_warning_errno(u, r, "Failed to kill main process " PID_FMT " (%s), ignoring: %m", main_pid, strna(comm));
} else {
if (!main_pid_alien)
wait_for_exit = true;
if (c->send_sighup && k == KILL_TERMINATE)
if (r != -ESRCH && send_sighup)
(void) kill(main_pid, SIGHUP);
}
}
if (control_pid > 0) {
r = kill_and_sigcont(control_pid, sig);
if (log_func)
log_func(control_pid, sig, u);
r = kill_and_sigcont(control_pid, sig);
if (r < 0 && r != -ESRCH) {
_cleanup_free_ char *comm = NULL;
get_process_comm(control_pid, &comm);
(void) get_process_comm(control_pid, &comm);
log_unit_warning_errno(u, r, "Failed to kill control process " PID_FMT " (%s), ignoring: %m", control_pid, strna(comm));
} else {
wait_for_exit = true;
if (c->send_sighup && k == KILL_TERMINATE)
if (r != -ESRCH && send_sighup)
(void) kill(control_pid, SIGHUP);
}
}
@@ -3585,7 +3627,11 @@ int unit_kill_context(
if (!pid_set)
return -ENOMEM;
r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, sig, true, k != KILL_TERMINATE, false, pid_set);
r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path,
sig,
CGROUP_SIGCONT|CGROUP_IGNORE_SELF,
pid_set,
log_func, u);
if (r < 0) {
if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
log_unit_warning_errno(u, r, "Failed to kill control group %s, ignoring: %m", u->cgroup_path);
@@ -3610,14 +3656,18 @@ int unit_kill_context(
(detect_container() == 0 && !unit_cgroup_delegate(u)))
wait_for_exit = true;
if (c->send_sighup && k != KILL_KILL) {
if (send_sighup) {
set_free(pid_set);
pid_set = unit_pid_set(main_pid, control_pid);
if (!pid_set)
return -ENOMEM;
cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, SIGHUP, false, true, false, pid_set);
cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path,
SIGHUP,
CGROUP_IGNORE_SELF,
pid_set,
NULL, NULL);
}
}
}
+1
View File
@@ -36,6 +36,7 @@ typedef struct UnitStatusMessageFormats UnitStatusMessageFormats;
typedef enum KillOperation {
KILL_TERMINATE,
KILL_TERMINATE_AND_LOG,
KILL_KILL,
KILL_ABORT,
_KILL_OPERATION_MAX,
+14 -12
View File
@@ -603,7 +603,6 @@ int session_start(Session *s) {
static int session_stop_scope(Session *s, bool force) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char *job = NULL;
int r;
assert(s);
@@ -611,22 +610,25 @@ static int session_stop_scope(Session *s, bool force) {
if (!s->scope)
return 0;
/* Let's always abandon the scope first. This tells systemd that we are not interested anymore, and everything
* that is left in in the scope is "left-over". Informing systemd about this has the benefit that it will log
* when killing any processes left after this point. */
r = manager_abandon_scope(s->manager, s->scope, &error);
if (r < 0)
log_warning_errno(r, "Failed to abandon session scope, ignoring: %s", bus_error_message(&error, r));
/* Optionally, let's kill everything that's left now. */
if (force || manager_shall_kill(s->manager, s->user->name)) {
char *job = NULL;
r = manager_stop_unit(s->manager, s->scope, &error, &job);
if (r < 0) {
log_error("Failed to stop session scope: %s", bus_error_message(&error, r));
return r;
}
if (r < 0)
return log_error_errno(r, "Failed to stop session scope: %s", bus_error_message(&error, r));
free(s->scope_job);
s->scope_job = job;
} else {
r = manager_abandon_scope(s->manager, s->scope, &error);
if (r < 0) {
log_error("Failed to abandon session scope: %s", bus_error_message(&error, r));
return r;
}
}
} else
s->scope_job = mfree(s->scope_job);
return 0;
}
+5 -5
View File
@@ -60,16 +60,16 @@ int main(int argc, char*argv[]) {
assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a") > 0);
assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b") == 0);
assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false, false, false, NULL) == 0);
assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false, false, false, NULL) > 0);
assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, 0, NULL, NULL, NULL) == 0);
assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, 0, NULL, NULL, NULL) > 0);
assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", SYSTEMD_CGROUP_CONTROLLER, "/test-a", false, false) > 0);
assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0) > 0);
assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0);
assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b") > 0);
assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false, false, false, NULL) > 0);
assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false, false, false, NULL) == 0);
assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, 0, NULL, NULL, NULL) > 0);
assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, 0, NULL, NULL, NULL) == 0);
cg_trim(SYSTEMD_CGROUP_CONTROLLER, "/", false);
+1 -1
View File
@@ -1256,7 +1256,7 @@ static int on_post(sd_event_source *s, void *userdata) {
return r;
} else if (manager->cgroup)
/* cleanup possible left-over processes in our cgroup */
cg_kill(SYSTEMD_CGROUP_CONTROLLER, manager->cgroup, SIGKILL, false, true, NULL);
cg_kill(SYSTEMD_CGROUP_CONTROLLER, manager->cgroup, SIGKILL, CGROUP_IGNORE_SELF, NULL, NULL, NULL);
}
}