Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

imp: exec: send SIGKILL to cgroup/children on SIGUSR1 #186

Merged
merged 11 commits into from
Nov 1, 2024
4 changes: 2 additions & 2 deletions doc/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
sphinx==3.4.3
sphinx<6.0.0
sphinx-rtd-theme>=0.5.2
docutils>=0.14,<0.18
Jinja2<3.1
urllib3<2
jinja2<3.10
4 changes: 4 additions & 0 deletions src/imp/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ IMP_SOURCES = \
passwd.h \
pidinfo.c \
pidinfo.h \
signals.c \
signals.h \
cgroup.c \
cgroup.h \
kill.c \
run.c \
exec/user.h \
Expand Down
245 changes: 245 additions & 0 deletions src/imp/cgroup.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
/************************************************************\
* Copyright 2024 Lawrence Livermore National Security, LLC
* (c.f. AUTHORS, NOTICE.LLNS, COPYING)
*
* This file is part of the Flux resource manager framework.
* For details, see https://github.com/flux-framework.
*
* SPDX-License-Identifier: LGPL-3.0
\************************************************************/

#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <ctype.h>
#include <stdbool.h>
#include <limits.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#ifdef HAVE_LINUX_MAGIC_H
#include <linux/magic.h>
#endif
#ifndef TMPFS_MAGIC
#define TMPFS_MAGIC 0x01021994 /* from linux/magic.h */
#endif
#ifndef CGROUP_SUPER_MAGIC
garlick marked this conversation as resolved.
Show resolved Hide resolved
#define CGROUP_SUPER_MAGIC 0x27e0eb
#endif
#ifndef CGROUP2_SUPER_MAGIC
#define CGROUP2_SUPER_MAGIC 0x63677270
#endif
#include <signal.h>

#include "src/libutil/strlcpy.h"

#include "cgroup.h"
#include "imp_log.h"

static char *remove_leading_dotdot (char *relpath)
{
while (strncmp (relpath, "/..", 3) == 0)
relpath += 3;
return relpath;
}

/*
* Look up the current cgroup relative path from /proc/self/cgroup.
*
* If cgroup->unified is true, then look for the first entry where
* 'subsys' is an empty string.
*
* Otherwise, use the `name=systemd` line.
*
* See NOTES: /proc/[pid]/cgroup in cgroups(7).
*/
static int cgroup_init_path (struct cgroup_info *cgroup)
{
int rc = -1;
int n;
FILE *fp;
size_t size = 0;
char *line = NULL;
int saved_errno;

if (!(fp = fopen ("/proc/self/cgroup", "r")))
return -1;

while ((n = getline (&line, &size, fp)) >= 0) {
char *nl;
char *relpath = NULL;
char *subsys = strchr (line, ':');
if ((nl = strchr (line, '\n')))
*nl = '\0';
if (subsys == NULL
|| *(++subsys) == '\0'
|| !(relpath = strchr (subsys, ':')))
continue;

/* Nullify subsys, relpath is already nul-terminated at newline */
*(relpath++) = '\0';

/* Remove leading /.. in relpath. This could be due to cgroup
* mounted in a container.
*/
relpath = remove_leading_dotdot (relpath);

/* If unified cgroups are being used, then stop when we find
* subsys="". Otherwise stop at subsys="name=systemd":
*/
if ((cgroup->unified && subsys[0] == '\0')
|| (!cgroup->unified && strcmp (subsys, "name=systemd") == 0)) {
int len = sizeof (cgroup->path);
if (snprintf (cgroup->path,
len,
"%s%s",
cgroup->mount_dir,
relpath) < len)
rc = 0;
break;
}
}
if (rc < 0)
errno = ENOENT;

saved_errno = errno;
free (line);
fclose (fp);
errno = saved_errno;
return rc;
}

/* Determine if this system is using the unified (v2) or legacy (v1)
* cgroups hierarchy (See https://systemd.io/CGROUP_DELEGATION/)
* and mount point for systemd managed cgroups.
*/
static int cgroup_init_mount_dir_and_type (struct cgroup_info *cg)
{
struct statfs fs;

/* Assume unified unless we discover otherwise
*/
cg->unified = true;

/* Check if either /sys/fs/cgroup or /sys/fs/cgroup/unified
* are mounted as type cgroup2. If so, use this as the mount dir
* (Note: these paths are guaranteed to fit in cg->mount_dir, so
* no need to check for truncation)
*/
(void) strlcpy (cg->mount_dir, "/sys/fs/cgroup", sizeof (cg->mount_dir));
if (statfs (cg->mount_dir, &fs) < 0)
return -1;

/* if cgroup2 fs mounted: unified hierarchy for all users of cgroupfs
*/
if (fs.f_type == CGROUP2_SUPER_MAGIC)
return 0;

/* O/w, check if cgroup2 unified hierarchy mounted at
* /sys/fs/cgroup/unified
*/
(void) strlcpy (cg->mount_dir,
"/sys/fs/cgroup/unified",
sizeof (cg->mount_dir));
if (statfs (cg->mount_dir, &fs) < 0)
return -1;

if (fs.f_type == CGROUP2_SUPER_MAGIC)
return 0;

/* O/w, if /sys/fs/cgroup is mounted as tmpfs, we need to check
* for /sys/fs/cgroup/systemd mounted as cgroupfs (legacy).
*/
if (fs.f_type == TMPFS_MAGIC) {

(void) strlcpy (cg->mount_dir,
"/sys/fs/cgroup/systemd",
sizeof (cg->mount_dir));
if (statfs (cg->mount_dir, &fs) == 0
&& fs.f_type == CGROUP_SUPER_MAGIC) {
cg->unified = false;
return 0;
}
}

/* Unable to determine cgroup mount point and/or unified vs legacy */
return -1;
}

void cgroup_info_destroy (struct cgroup_info *cg)
{
if (cg) {
int saved_errno = errno;
free (cg);
errno = saved_errno;
}
}

struct cgroup_info *cgroup_info_create (void)
{
struct cgroup_info *cgroup = calloc (1, sizeof (*cgroup));
if (!cgroup)
return NULL;

if (cgroup_init_mount_dir_and_type (cgroup) < 0
|| cgroup_init_path (cgroup) < 0) {
cgroup_info_destroy (cgroup);
return NULL;
}
/* Note: GNU basename(3) never modifies its argument. (_GNU_SOURCE
* is defined in config.h.)
*/
if (strncmp (basename (cgroup->path), "imp-shell", 9) == 0)
cgroup->use_cgroup_kill = true;

return cgroup;
}

int cgroup_kill (struct cgroup_info *cgroup, int sig)
{
int count = 0;
int rc = 0;
int saved_errno = 0;
char path [PATH_MAX+14]; /* cgroup->path[PATH_MAX] + "/cgroup.procs" */
FILE *fp;
unsigned long child;
pid_t current_pid = getpid ();

/* Note: path is guaranteed to have enough space to append "/cgroup.procs"
*/
(void) snprintf (path, sizeof (path), "%s/cgroup.procs", cgroup->path);
garlick marked this conversation as resolved.
Show resolved Hide resolved

if (!(fp = fopen (path, "r")))
return -1;
while (fscanf (fp, "%lu", &child) == 1) {
pid_t pid = child;
if (pid == current_pid)
continue;
if (kill (pid, sig) < 0) {
saved_errno = errno;
rc = -1;
imp_warn ("Failed to send signal %d to pid %lu",
sig,
child);
continue;
}
count++;
}
fclose (fp);
if (rc < 0 && count == 0) {
count = -1;
errno = saved_errno;
}
return count;
}

/* vi: ts=4 sw=4 expandtab
*/
30 changes: 30 additions & 0 deletions src/imp/cgroup.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/************************************************************\
* Copyright 2024 Lawrence Livermore National Security, LLC
* (c.f. AUTHORS, NOTICE.LLNS, COPYING)
*
* This file is part of the Flux resource manager framework.
* For details, see https://github.com/flux-framework.
*
* SPDX-License-Identifier: LGPL-3.0
\************************************************************/

#ifndef HAVE_IMP_CGROUP_H
#define HAVE_IMP_CGROUP_H 1

struct cgroup_info {
char mount_dir[PATH_MAX + 1];
char path[PATH_MAX + 1];
bool unified;
bool use_cgroup_kill;
};

struct cgroup_info *cgroup_info_create (void);

void cgroup_info_destroy (struct cgroup_info *cgroup);

/* Send signal to all pids (excluding the current pid) in the
* current cgroup.
*/
int cgroup_kill (struct cgroup_info *cgroup, int sig);

#endif /* !HAVE_IMP_CGROUP_H */
Loading
Loading