The virCgroupKill method kills all PIDs found in a cgroup
The virCgroupKillRecursively method does this recursively
for child cgroups.
The virCgroupKillPainfully method does a recursive kill
several times in a row until everything has really died
---
src/libvirt_private.syms | 3 +
src/util/cgroup.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++
src/util/cgroup.h | 4 +
3 files changed, 236 insertions(+), 0 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 797a670..989b3eb 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -74,6 +74,9 @@ virCgroupGetMemoryHardLimit;
virCgroupGetMemorySoftLimit;
virCgroupGetMemoryUsage;
virCgroupGetSwapHardLimit;
+virCgroupKill;
+virCgroupKillRecursive;
+virCgroupKillPainfully;
virCgroupMounted;
virCgroupRemove;
virCgroupSetBlkioWeight;
diff --git a/src/util/cgroup.c b/src/util/cgroup.c
index b71eef9..2c7efb4 100644
--- a/src/util/cgroup.c
+++ b/src/util/cgroup.c
@@ -23,6 +23,7 @@
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <signal.h>
#include <libgen.h>
#include <dirent.h>
@@ -32,6 +33,7 @@
#include "cgroup.h"
#include "logging.h"
#include "files.h"
+#include "hash.h"
#define CGROUP_MAX_VAL 512
@@ -258,6 +260,19 @@ static int virCgroupPathOfController(virCgroupPtr group,
const char *key,
char **path)
{
+ if (controller == -1) {
+ int i;
+ for (i = 0 ; i < VIR_CGROUP_CONTROLLER_LAST ; i++) {
+ if (group->controllers[i].mountPoint &&
+ group->controllers[i].placement) {
+ controller = i;
+ break;
+ }
+ }
+ }
+ if (controller == -1)
+ return -ENOSYS;
+
if (group->controllers[controller].mountPoint == NULL)
return -ENOENT;
@@ -1291,3 +1306,217 @@ int virCgroupGetFreezerState(virCgroupPtr group, char **state)
VIR_CGROUP_CONTROLLER_CPU,
"freezer.state", state);
}
+
+static int virCgroupKillInternal(virCgroupPtr group, int signum, virHashTablePtr pids)
+{
+ int rc;
+ int killedAny = 0;
+ char *keypath = NULL;
+ bool done = false;
+ VIR_DEBUG("group=%p path=%s signum=%d pids=%p", group, group->path,
signum, pids);
+
+ rc = virCgroupPathOfController(group, -1, "tasks", &keypath);
+ if (rc != 0) {
+ VIR_DEBUG("No path of %s, tasks", group->path);
+ return rc;
+ }
+
+ /* PIDs may be forking as we kill them, so loop
+ * until there are no new PIDs found
+ */
+ while (!done) {
+ done = true;
+ FILE *fp;
+ if (!(fp = fopen(keypath, "r"))) {
+ rc = -errno;
+ VIR_DEBUG("Failed to read %s: %m\n", keypath);
+ goto cleanup;
+ } else {
+ while (!feof(fp)) {
+ unsigned long pid;
+ if (fscanf(fp, "%lu", &pid) != 1) {
+ if (feof(fp))
+ break;
+ rc = -errno;
+ break;
+ }
+ if (virHashLookup(pids, (void*)pid))
+ continue;
+
+ VIR_DEBUG("pid=%lu", pid);
+ if (kill((pid_t)pid, signum) < 0) {
+ if (errno != ESRCH) {
+ rc = -errno;
+ goto cleanup;
+ }
+ /* Leave RC == 0 since we didn't kill one */
+ } else {
+ killedAny = 1;
+ done = false;
+ }
+
+ virHashAddEntry(pids, (void*)pid, (void*)1);
+ }
+ fclose(fp);
+ }
+ }
+
+ rc = killedAny ? 1 : 0;
+
+cleanup:
+ VIR_FREE(keypath);
+
+ return rc;
+}
+
+
+static unsigned long virCgroupPidCode(const void *name)
+{
+ return (unsigned long)name;
+}
+static bool virCgroupPidEqual(const void *namea, const void *nameb)
+{
+ return namea == nameb;
+}
+static void *virCgroupPidCopy(const void *name)
+{
+ return (void*)name;
+}
+
+/*
+ * Returns
+ * < 0 : errno that occurred
+ * 0 : no PIDs killed
+ * 1 : at least one PID killed
+ */
+int virCgroupKill(virCgroupPtr group, int signum)
+{
+ VIR_DEBUG("group=%p path=%s signum=%d", group, group->path, signum);
+ int rc;
+ /* The 'tasks' file in cgroups can contain duplicated
+ * pids, so we use a hash to track which we've already
+ * killed.
+ */
+ virHashTablePtr pids = virHashCreateFull(100,
+ NULL,
+ virCgroupPidCode,
+ virCgroupPidEqual,
+ virCgroupPidCopy,
+ NULL);
+
+ rc = virCgroupKillInternal(group, signum, pids);
+
+ virHashFree(pids);
+
+ return rc;
+}
+
+
+static int virCgroupKillRecursiveInternal(virCgroupPtr group, int signum, virHashTablePtr
pids, bool dormdir)
+{
+ int rc;
+ int killedAny = 0;
+ char *keypath = NULL;
+ DIR *dp;
+ virCgroupPtr subgroup = NULL;
+ struct dirent *ent;
+ VIR_DEBUG("group=%p path=%s signum=%d pids=%p", group, group->path,
signum, pids);
+
+ rc = virCgroupPathOfController(group, -1, "", &keypath);
+ if (rc != 0) {
+ VIR_DEBUG("No path of %s, tasks", group->path);
+ return rc;
+ }
+
+ if ((rc = virCgroupKillInternal(group, signum, pids)) != 0)
+ return rc;
+
+ VIR_DEBUG("Iterate over children of %s", keypath);
+ if (!(dp = opendir(keypath))) {
+ rc = -errno;
+ return rc;
+ }
+
+ while ((ent = readdir(dp))) {
+ char *subpath;
+
+ if (STREQ(ent->d_name, "."))
+ continue;
+ if (STREQ(ent->d_name, ".."))
+ continue;
+ if (ent->d_type != DT_DIR)
+ continue;
+
+ VIR_DEBUG("Process subdir %s", ent->d_name);
+ if (virAsprintf(&subpath, "%s/%s", group->path, ent->d_name)
< 0) {
+ rc = -ENOMEM;
+ goto cleanup;
+ }
+
+ if ((rc = virCgroupNew(subpath, &subgroup)) != 0)
+ goto cleanup;
+
+ if ((rc = virCgroupKillRecursiveInternal(subgroup, signum, pids, true)) < 0)
+ goto cleanup;
+ if (rc == 1)
+ killedAny = 1;
+
+ if (dormdir)
+ virCgroupRemove(subgroup);
+
+ virCgroupFree(&subgroup);
+ }
+
+ rc = killedAny;
+
+cleanup:
+ virCgroupFree(&subgroup);
+ closedir(dp);
+
+ return rc;
+}
+
+int virCgroupKillRecursive(virCgroupPtr group, int signum)
+{
+ int rc;
+ VIR_DEBUG("group=%p path=%s signum=%d", group, group->path, signum);
+ virHashTablePtr pids = virHashCreateFull(100,
+ NULL,
+ virCgroupPidCode,
+ virCgroupPidEqual,
+ virCgroupPidCopy,
+ NULL);
+
+ rc = virCgroupKillRecursiveInternal(group, signum, pids, false);
+
+ virHashFree(pids);
+
+ return rc;
+}
+
+
+int virCgroupKillPainfully(virCgroupPtr group)
+{
+ int i;
+ int rc;
+ VIR_DEBUG("cgroup=%p path=%s", group, group->path);
+ for (i = 0 ; i < 15 ; i++) {
+ int signum;
+ if (i == 0)
+ signum = SIGTERM;
+ else if (i == 8)
+ signum = SIGKILL;
+ else
+ signum = 0; /* Just check for existance */
+
+ rc = virCgroupKillRecursive(group, signum);
+ VIR_DEBUG("Iteration %d rc=%d", i, rc);
+ /* If rc == -1 we hit error, if 0 we ran out of PIDs */
+ if (rc <= 0)
+ break;
+
+ usleep(200 * 1000);
+ }
+ VIR_DEBUG("Complete %d", rc);
+ return rc;
+}
diff --git a/src/util/cgroup.h b/src/util/cgroup.h
index f1bdd0f..d468cb3 100644
--- a/src/util/cgroup.h
+++ b/src/util/cgroup.h
@@ -90,4 +90,8 @@ int virCgroupRemove(virCgroupPtr group);
void virCgroupFree(virCgroupPtr *group);
bool virCgroupMounted(virCgroupPtr cgroup, int controller);
+int virCgroupKill(virCgroupPtr group, int signum);
+int virCgroupKillRecursive(virCgroupPtr group, int signum);
+int virCgroupKillPainfully(virCgroupPtr group);
+
#endif /* CGROUP_H */
--
1.7.4