service: rework killing logic so that we always kill the main process, even if it left our service cgroup

Related to:

http://bugzilla.redhat.com/show_bug.cgi?id=626477
This commit is contained in:
Lennart Poettering 2010-08-31 23:24:47 +02:00
parent 22f4096ca9
commit ca949c9dcf
8 changed files with 149 additions and 72 deletions

View file

@ -166,12 +166,12 @@ int cg_rmdir(const char *controller, const char *path) {
return r < 0 ? -errno : 0; return r < 0 ? -errno : 0;
} }
int cg_kill(const char *controller, const char *path, int sig, bool ignore_self) { int cg_kill(const char *controller, const char *path, int sig, bool ignore_self, Set *s) {
bool done = false; bool done = false;
Set *s;
int r, ret = 0; int r, ret = 0;
pid_t my_pid; pid_t my_pid;
FILE *f = NULL; FILE *f = NULL;
Set *allocated_set = NULL;
assert(controller); assert(controller);
assert(path); assert(path);
@ -181,8 +181,9 @@ int cg_kill(const char *controller, const char *path, int sig, bool ignore_self)
* is repeated until no further processes are added to the * is repeated until no further processes are added to the
* tasks list, to properly handle forking processes */ * tasks list, to properly handle forking processes */
if (!(s = set_new(trivial_hash_func, trivial_compare_func))) if (!s)
return -ENOMEM; if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
return -ENOMEM;
my_pid = getpid(); my_pid = getpid();
@ -240,7 +241,8 @@ int cg_kill(const char *controller, const char *path, int sig, bool ignore_self)
} while (!done); } while (!done);
finish: finish:
set_free(s); if (allocated_set)
set_free(allocated_set);
if (f) if (f)
fclose(f); fclose(f);
@ -248,16 +250,21 @@ finish:
return ret; return ret;
} }
int cg_kill_recursive(const char *controller, const char *path, int sig, bool ignore_self, bool rem) { int cg_kill_recursive(const char *controller, const char *path, int sig, bool ignore_self, bool rem, Set *s) {
int r, ret = 0; int r, ret = 0;
DIR *d = NULL; DIR *d = NULL;
char *fn; char *fn;
Set *allocated_set = NULL;
assert(path); assert(path);
assert(controller); assert(controller);
assert(sig >= 0); assert(sig >= 0);
ret = cg_kill(controller, path, sig, ignore_self); if (!s)
if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
return -ENOMEM;
ret = cg_kill(controller, path, sig, ignore_self, s);
if ((r = cg_enumerate_subgroups(controller, path, &d)) < 0) { if ((r = cg_enumerate_subgroups(controller, path, &d)) < 0) {
if (ret >= 0 && r != -ENOENT) if (ret >= 0 && r != -ENOENT)
@ -279,7 +286,7 @@ int cg_kill_recursive(const char *controller, const char *path, int sig, bool ig
goto finish; goto finish;
} }
r = cg_kill_recursive(controller, p, sig, ignore_self, rem); r = cg_kill_recursive(controller, p, sig, ignore_self, rem, s);
free(p); free(p);
if (r != 0 && ret >= 0) if (r != 0 && ret >= 0)
@ -299,6 +306,9 @@ finish:
if (d) if (d)
closedir(d); closedir(d);
if (allocated_set)
set_free(allocated_set);
return ret; return ret;
} }
@ -323,7 +333,7 @@ int cg_kill_recursive_and_wait(const char *controller, const char *path, bool re
else else
sig = 0; sig = 0;
if ((r = cg_kill_recursive(controller, path, sig, true, rem)) <= 0) if ((r = cg_kill_recursive(controller, path, sig, true, rem, NULL)) <= 0)
return r; return r;
usleep(50 * USEC_PER_MSEC); usleep(50 * USEC_PER_MSEC);

View file

@ -37,8 +37,8 @@ int cg_read_pid(FILE *f, pid_t *_pid);
int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d); int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d);
int cg_read_subgroup(DIR *d, char **fn); int cg_read_subgroup(DIR *d, char **fn);
int cg_kill(const char *controller, const char *path, int sig, bool ignore_self); int cg_kill(const char *controller, const char *path, int sig, bool ignore_self, Set *s);
int cg_kill_recursive(const char *controller, const char *path, int sig, bool ignore_self, bool remove); int cg_kill_recursive(const char *controller, const char *path, int sig, bool ignore_self, bool remove, Set *s);
int cg_kill_recursive_and_wait(const char *controller, const char *path, bool remove); int cg_kill_recursive_and_wait(const char *controller, const char *path, bool remove);
int cg_migrate(const char *controller, const char *from, const char *to, bool ignore_self); int cg_migrate(const char *controller, const char *from, const char *to, bool ignore_self);

View file

@ -138,7 +138,7 @@ int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid) {
return 0; return 0;
} }
int cgroup_bonding_kill(CGroupBonding *b, int sig) { int cgroup_bonding_kill(CGroupBonding *b, int sig, Set *s) {
int r; int r;
assert(b); assert(b);
@ -149,25 +149,36 @@ int cgroup_bonding_kill(CGroupBonding *b, int sig) {
assert(b->realized); assert(b->realized);
return cg_kill_recursive(b->controller, b->path, sig, true, false); return cg_kill_recursive(b->controller, b->path, sig, true, false, s);
} }
int cgroup_bonding_kill_list(CGroupBonding *first, int sig) { int cgroup_bonding_kill_list(CGroupBonding *first, int sig, Set *s) {
CGroupBonding *b; CGroupBonding *b;
int r = -EAGAIN; Set *allocated_set = NULL;
int ret = -EAGAIN, r;
if (!s)
if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
return -ENOMEM;
LIST_FOREACH(by_unit, b, first) { LIST_FOREACH(by_unit, b, first) {
if ((r = cgroup_bonding_kill(b, sig)) < 0) { if ((r = cgroup_bonding_kill(b, sig, s)) < 0) {
if (r == -EAGAIN || r == -ESRCH) if (r == -EAGAIN || r == -ESRCH)
continue; continue;
return r; ret = r;
goto finish;
} }
return 0; if (ret < 0 || r > 0)
ret = r;
} }
return r; finish:
if (allocated_set)
set_free(allocated_set);
return ret;
} }
/* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we /* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we

View file

@ -58,8 +58,8 @@ void cgroup_bonding_free_list(CGroupBonding *first);
int cgroup_bonding_install(CGroupBonding *b, pid_t pid); int cgroup_bonding_install(CGroupBonding *b, pid_t pid);
int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid); int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid);
int cgroup_bonding_kill(CGroupBonding *b, int sig); int cgroup_bonding_kill(CGroupBonding *b, int sig, Set *s);
int cgroup_bonding_kill_list(CGroupBonding *first, int sig); int cgroup_bonding_kill_list(CGroupBonding *first, int sig, Set *s);
void cgroup_bonding_trim(CGroupBonding *first, bool delete_root); void cgroup_bonding_trim(CGroupBonding *first, bool delete_root);
void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root); void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root);

View file

@ -622,7 +622,8 @@ static void mount_enter_mounted(Mount *m, bool success) {
static void mount_enter_signal(Mount *m, MountState state, bool success) { static void mount_enter_signal(Mount *m, MountState state, bool success) {
int r; int r;
bool sent = false; Set *pid_set = NULL;
bool wait_for_exit = false;
assert(m); assert(m);
@ -634,26 +635,39 @@ static void mount_enter_signal(Mount *m, MountState state, bool success) {
state == MOUNT_UNMOUNTING_SIGTERM || state == MOUNT_UNMOUNTING_SIGTERM ||
state == MOUNT_REMOUNTING_SIGTERM) ? m->exec_context.kill_signal : SIGKILL; state == MOUNT_REMOUNTING_SIGTERM) ? m->exec_context.kill_signal : SIGKILL;
if (m->exec_context.kill_mode == KILL_CONTROL_GROUP) { if (m->control_pid > 0) {
if (kill(m->exec_context.kill_mode == KILL_PROCESS_GROUP ?
-m->control_pid :
m->control_pid, sig) < 0 && errno != ESRCH)
if ((r = cgroup_bonding_kill_list(m->meta.cgroup_bondings, sig)) < 0) { log_warning("Failed to kill control process %li: %m", (long) m->control_pid);
if (r != -EAGAIN && r != -ESRCH) else
goto fail; wait_for_exit = true;
} else
sent = true;
} }
if (!sent && m->control_pid > 0) if (m->exec_context.kill_mode == KILL_CONTROL_GROUP) {
if (kill(m->exec_context.kill_mode == KILL_PROCESS ?
m->control_pid :
-m->control_pid, sig) < 0 && errno != ESRCH) {
r = -errno; if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) {
r = -ENOMEM;
goto fail; goto fail;
} }
/* Exclude the control pid from being killed via the cgroup */
if (m->control_pid > 0)
if ((r = set_put(pid_set, LONG_TO_PTR(m->control_pid))) < 0)
goto fail;
if ((r = cgroup_bonding_kill_list(m->meta.cgroup_bondings, sig, pid_set)) < 0) {
if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
log_warning("Failed to kill control group: %s", strerror(-r));
} else if (r > 0)
wait_for_exit = true;
set_free(pid_set);
}
} }
if (sent) { if (wait_for_exit) {
if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0) if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0)
goto fail; goto fail;
@ -672,6 +686,9 @@ fail:
mount_enter_mounted(m, false); mount_enter_mounted(m, false);
else else
mount_enter_dead(m, false); mount_enter_dead(m, false);
if (pid_set)
set_free(pid_set);
} }
static void mount_enter_unmounting(Mount *m, bool success) { static void mount_enter_unmounting(Mount *m, bool success) {

View file

@ -1570,7 +1570,8 @@ fail:
static void service_enter_signal(Service *s, ServiceState state, bool success) { static void service_enter_signal(Service *s, ServiceState state, bool success) {
int r; int r;
bool sent = false; Set *pid_set = NULL;
bool wait_for_exit = false;
assert(s); assert(s);
@ -1580,38 +1581,53 @@ static void service_enter_signal(Service *s, ServiceState state, bool success) {
if (s->exec_context.kill_mode != KILL_NONE) { if (s->exec_context.kill_mode != KILL_NONE) {
int sig = (state == SERVICE_STOP_SIGTERM || state == SERVICE_FINAL_SIGTERM) ? s->exec_context.kill_signal : SIGKILL; int sig = (state == SERVICE_STOP_SIGTERM || state == SERVICE_FINAL_SIGTERM) ? s->exec_context.kill_signal : SIGKILL;
if (s->exec_context.kill_mode == KILL_CONTROL_GROUP) { if (s->main_pid > 0) {
if (kill(s->exec_context.kill_mode == KILL_PROCESS_GROUP ?
-s->main_pid :
s->main_pid, sig) < 0 && errno != ESRCH)
if ((r = cgroup_bonding_kill_list(s->meta.cgroup_bondings, sig)) < 0) { log_warning("Failed to kill main process %li: %m", (long) s->main_pid);
if (r != -EAGAIN && r != -ESRCH) else
goto fail; wait_for_exit = true;
} else
sent = true;
} }
if (!sent) { if (s->control_pid > 0) {
r = 0; if (kill(s->exec_context.kill_mode == KILL_PROCESS_GROUP ?
-s->control_pid :
s->control_pid, sig) < 0 && errno != ESRCH)
if (s->main_pid > 0) { log_warning("Failed to kill control process %li: %m", (long) s->control_pid);
if (kill(s->exec_context.kill_mode == KILL_PROCESS ? s->main_pid : -s->main_pid, sig) < 0 && errno != ESRCH) else
r = -errno; wait_for_exit = true;
else }
sent = true;
}
if (s->control_pid > 0) { if (s->exec_context.kill_mode == KILL_CONTROL_GROUP) {
if (kill(s->exec_context.kill_mode == KILL_PROCESS ? s->control_pid : -s->control_pid, sig) < 0 && errno != ESRCH)
r = -errno;
else
sent = true;
}
if (r < 0) if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) {
r = -ENOMEM;
goto fail; goto fail;
}
/* Exclude the main/control pids from being killed via the cgroup */
if (s->main_pid > 0)
if ((r = set_put(pid_set, LONG_TO_PTR(s->main_pid))) < 0)
goto fail;
if (s->control_pid > 0)
if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0)
goto fail;
if ((r = cgroup_bonding_kill_list(s->meta.cgroup_bondings, sig, pid_set)) < 0) {
if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
log_warning("Failed to kill control group: %s", strerror(-r));
} else
wait_for_exit = true;
set_free(pid_set);
} }
} }
if (sent && (s->main_pid > 0 || s->control_pid > 0)) { if (wait_for_exit) {
if (s->timeout_usec > 0) if (s->timeout_usec > 0)
if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
goto fail; goto fail;
@ -1631,6 +1647,9 @@ fail:
service_enter_stop_post(s, false); service_enter_stop_post(s, false);
else else
service_enter_dead(s, false, true); service_enter_dead(s, false, true);
if (pid_set)
set_free(pid_set);
} }
static void service_enter_stop(Service *s, bool success) { static void service_enter_stop(Service *s, bool success) {

View file

@ -997,7 +997,8 @@ fail:
static void socket_enter_signal(Socket *s, SocketState state, bool success) { static void socket_enter_signal(Socket *s, SocketState state, bool success) {
int r; int r;
bool sent = false; Set *pid_set = NULL;
bool wait_for_exit = false;
assert(s); assert(s);
@ -1007,23 +1008,39 @@ static void socket_enter_signal(Socket *s, SocketState state, bool success) {
if (s->exec_context.kill_mode != KILL_NONE) { if (s->exec_context.kill_mode != KILL_NONE) {
int sig = (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_FINAL_SIGTERM) ? s->exec_context.kill_signal : SIGKILL; int sig = (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_FINAL_SIGTERM) ? s->exec_context.kill_signal : SIGKILL;
if (s->exec_context.kill_mode == KILL_CONTROL_GROUP) { if (s->control_pid > 0) {
if (kill(s->exec_context.kill_mode == KILL_PROCESS_GROUP ?
-s->control_pid :
s->control_pid, sig) < 0 && errno != ESRCH)
if ((r = cgroup_bonding_kill_list(s->meta.cgroup_bondings, sig)) < 0) { log_warning("Failed to kill control process %li: %m", (long) s->control_pid);
if (r != -EAGAIN && r != -ESRCH) else
goto fail; wait_for_exit = true;
} else
sent = true;
} }
if (!sent && s->control_pid > 0) if (s->exec_context.kill_mode == KILL_CONTROL_GROUP) {
if (kill(s->exec_context.kill_mode == KILL_PROCESS ? s->control_pid : -s->control_pid, sig) < 0 && errno != ESRCH) {
r = -errno; if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) {
r = -ENOMEM;
goto fail; goto fail;
} }
/* Exclude the control pid from being killed via the cgroup */
if (s->control_pid > 0)
if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0)
goto fail;
if ((r = cgroup_bonding_kill_list(s->meta.cgroup_bondings, sig, pid_set)) < 0) {
if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
log_warning("Failed to kill control group: %s", strerror(-r));
} else if (r > 0)
wait_for_exit = true;
set_free(pid_set);
}
} }
if (sent && s->control_pid > 0) { if (wait_for_exit) {
if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0) if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
goto fail; goto fail;
@ -1042,6 +1059,9 @@ fail:
socket_enter_stop_post(s, false); socket_enter_stop_post(s, false);
else else
socket_enter_dead(s, false); socket_enter_dead(s, false);
if (pid_set)
set_free(pid_set);
} }
static void socket_enter_stop_pre(Socket *s, bool success) { static void socket_enter_stop_pre(Socket *s, bool success) {

View file

@ -61,16 +61,16 @@ int main(int argc, char*argv[]) {
assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", false) > 0); assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", false) > 0);
assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", false) == 0); assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", false) == 0);
assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false, false) == 0); assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false, false, NULL) == 0);
assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false, false) > 0); assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false, false, NULL) > 0);
assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", "/test-a", false, false) > 0); assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", "/test-a", false, false) > 0);
assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", false) == 0); assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", false) == 0);
assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", false) > 0); assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", false) > 0);
assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false, false) > 0); assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false, false, NULL) > 0);
assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false, false) == 0); assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false, false, NULL) == 0);
cg_trim(SYSTEMD_CGROUP_CONTROLLER, "/", false); cg_trim(SYSTEMD_CGROUP_CONTROLLER, "/", false);