mirror of
https://github.com/troglobit/finit.git
synced 2026-10-09 16:50:59 +07:00
Allow forking sysv/services like podman to move between cgroups
The container monitor that podman forks off when starting a container instance creates subgroups in the cgroup v2 hieararchy that we want to reuse. This patch adds cgroup_move_svc() which we call from the pidfile plugins to relocate the conmon process. Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
@@ -143,6 +143,7 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
|
||||
|
||||
/* Complement log in service.c for non-forking services */
|
||||
logit(LOG_CONSOLE | LOG_NOTICE, "Started %s[%d]", svc_ident(svc, NULL, 0), pid);
|
||||
cgroup_move_svc(svc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+127
@@ -88,6 +88,133 @@ int cgroup_watch(const char *group, const char *name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Derive cgroup name from service (mirrors group_name() in service.c)
|
||||
* For use by plugins that need to determine the cgroup path.
|
||||
*
|
||||
* MUST return the same value that group_name() in service.c returns!
|
||||
*/
|
||||
char *cgroup_svc_name(svc_t *svc, char *buf, size_t len)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
/* Use explicit cgroup leaf name if specified */
|
||||
if (svc->cgroup.leafname[0]) {
|
||||
strlcpy(buf, svc->cgroup.leafname, len);
|
||||
return buf;
|
||||
}
|
||||
|
||||
if (!svc->file[0])
|
||||
return svc_ident(svc, buf, len);
|
||||
|
||||
ptr = strrchr(svc->file, '/');
|
||||
if (ptr)
|
||||
ptr++;
|
||||
else
|
||||
ptr = svc->file;
|
||||
|
||||
strlcpy(buf, ptr, len);
|
||||
|
||||
/* Strip .conf extension - this gives us the cgroup name */
|
||||
ptr = strstr(buf, ".conf");
|
||||
if (ptr)
|
||||
*ptr = 0;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to move a PID to a service's cgroup.
|
||||
* @group: Top-level cgroup (e.g., "system", "user", "maint")
|
||||
* @name: Service-specific cgroup name (e.g., "dockerd", "nginx")
|
||||
* @pid: Process ID to move
|
||||
* @delegate: If 1, handle potential EBUSY by using supervisor/ subdirectory
|
||||
*
|
||||
* Useful for forking services where child PIDs need to be moved after they appear.
|
||||
*
|
||||
* For delegated cgroups, handles the cgroups v2 "no internal processes" rule:
|
||||
* if the parent has child cgroups, tries to reuse process-named subdirectory or
|
||||
* falls back to supervisor/ subdirectory.
|
||||
*
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int cgroup_move_pid(const char *group, const char *name, int pid, int delegate)
|
||||
{
|
||||
int rc = 0, reuse = 0;
|
||||
char path[512];
|
||||
|
||||
if (!avail)
|
||||
return 0;
|
||||
|
||||
/* Sanity checks */
|
||||
if (!group || !name || pid <= 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Disallow path traversal */
|
||||
if (strstr(group, "..") || strstr(name, "..") ||
|
||||
strchr(group, '/') || strchr(name, '/')) {
|
||||
errno = EINVAL;
|
||||
warn("Invalid cgroup path components: group=%s name=%s", group, name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), FINIT_CGPATH "/%s/%s", group, name);
|
||||
if (!fisdir(path)) {
|
||||
warn("Cgroup %s does not exist", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((rc = fnwrite(str("%d", pid), "%s/cgroup.procs", path))) {
|
||||
/*
|
||||
* EBUSY means the cgroup has children (cgroups v2 "no internal processes" rule).
|
||||
* For delegated cgroups, fall back to subdirectory.
|
||||
*/
|
||||
if (errno == EBUSY && delegate) {
|
||||
char comm[64];
|
||||
|
||||
/* Check if subdirectory matching process name, e.g., conmon, exists */
|
||||
readsnf(comm, sizeof(comm), "/proc/%d/comm", pid);
|
||||
strlcat(path, "/", sizeof(path));
|
||||
strlcat(path, comm, sizeof(path));
|
||||
if (fisdir(path))
|
||||
reuse = 1;
|
||||
else
|
||||
snprintf(path, sizeof(path), FINIT_CGPATH "/%s/%s/supervisor", group, name);
|
||||
|
||||
rc = fnwrite(str("%d", pid), "%s/cgroup.procs", path);
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
warn("Failed moving pid %d to cgroup %s", pid, path);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (reuse) {
|
||||
snprintf(path, sizeof(path), FINIT_CGPATH "/%s/%s/supervisor", group, name);
|
||||
rmdir(path);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cgroup_move_svc(svc_t *svc)
|
||||
{
|
||||
const char *group = "system";
|
||||
char name[MAX_ARG_LEN];
|
||||
|
||||
if (!avail || !svc)
|
||||
return 0;
|
||||
|
||||
if (svc->cgroup.name[0])
|
||||
group = svc->cgroup.name;
|
||||
|
||||
return cgroup_move_pid(group, cgroup_svc_name(svc, name, sizeof(name)),
|
||||
svc->pid, svc->cgroup.delegate);
|
||||
}
|
||||
|
||||
static void cgset(const char *path, char *ctrl, char *prop)
|
||||
{
|
||||
char *val;
|
||||
|
||||
@@ -53,4 +53,7 @@ char *cgroup_svc_name(svc_t *svc, char *buf, size_t len);
|
||||
int cgroup_prepare (svc_t *svc, const char *name);
|
||||
int cgroup_watch (const char *group, const char *name);
|
||||
|
||||
int cgroup_move_pid(const char *group, const char *name, int pid, int delegate);
|
||||
int cgroup_move_svc(svc_t *svc);
|
||||
|
||||
#endif /* FINIT_CGROUP_H_ */
|
||||
|
||||
Reference in New Issue
Block a user