184092dbcSRoman Gushchin /* SPDX-License-Identifier: GPL-2.0 */
284092dbcSRoman Gushchin 
384092dbcSRoman Gushchin #define _GNU_SOURCE
484092dbcSRoman Gushchin 
584092dbcSRoman Gushchin #include <errno.h>
684092dbcSRoman Gushchin #include <fcntl.h>
784092dbcSRoman Gushchin #include <linux/limits.h>
88075e4f6SChristian Brauner #include <poll.h>
984092dbcSRoman Gushchin #include <signal.h>
1084092dbcSRoman Gushchin #include <stdio.h>
1184092dbcSRoman Gushchin #include <stdlib.h>
1284092dbcSRoman Gushchin #include <string.h>
138075e4f6SChristian Brauner #include <sys/inotify.h>
1484092dbcSRoman Gushchin #include <sys/stat.h>
1584092dbcSRoman Gushchin #include <sys/types.h>
1684092dbcSRoman Gushchin #include <sys/wait.h>
1784092dbcSRoman Gushchin #include <unistd.h>
1884092dbcSRoman Gushchin 
1984092dbcSRoman Gushchin #include "cgroup_util.h"
209bd5910dSChristian Brauner #include "../clone3/clone3_selftests.h"
2184092dbcSRoman Gushchin 
226c26df84SYosry Ahmed /* Returns read len on success, or -errno on failure. */
read_text(const char * path,char * buf,size_t max_len)2384092dbcSRoman Gushchin static ssize_t read_text(const char *path, char *buf, size_t max_len)
2484092dbcSRoman Gushchin {
2584092dbcSRoman Gushchin 	ssize_t len;
2684092dbcSRoman Gushchin 	int fd;
2784092dbcSRoman Gushchin 
2884092dbcSRoman Gushchin 	fd = open(path, O_RDONLY);
2984092dbcSRoman Gushchin 	if (fd < 0)
306c26df84SYosry Ahmed 		return -errno;
3184092dbcSRoman Gushchin 
3284092dbcSRoman Gushchin 	len = read(fd, buf, max_len - 1);
3384092dbcSRoman Gushchin 
346c26df84SYosry Ahmed 	if (len >= 0)
3584092dbcSRoman Gushchin 		buf[len] = 0;
366c26df84SYosry Ahmed 
3784092dbcSRoman Gushchin 	close(fd);
386c26df84SYosry Ahmed 	return len < 0 ? -errno : len;
3984092dbcSRoman Gushchin }
4084092dbcSRoman Gushchin 
416c26df84SYosry Ahmed /* Returns written len on success, or -errno on failure. */
write_text(const char * path,char * buf,ssize_t len)4253c3daf8SDan Carpenter static ssize_t write_text(const char *path, char *buf, ssize_t len)
4384092dbcSRoman Gushchin {
4484092dbcSRoman Gushchin 	int fd;
4584092dbcSRoman Gushchin 
4684092dbcSRoman Gushchin 	fd = open(path, O_WRONLY | O_APPEND);
4784092dbcSRoman Gushchin 	if (fd < 0)
486c26df84SYosry Ahmed 		return -errno;
4984092dbcSRoman Gushchin 
5084092dbcSRoman Gushchin 	len = write(fd, buf, len);
5184092dbcSRoman Gushchin 	close(fd);
526c26df84SYosry Ahmed 	return len < 0 ? -errno : len;
5384092dbcSRoman Gushchin }
5484092dbcSRoman Gushchin 
cg_name(const char * root,const char * name)5584092dbcSRoman Gushchin char *cg_name(const char *root, const char *name)
5684092dbcSRoman Gushchin {
5784092dbcSRoman Gushchin 	size_t len = strlen(root) + strlen(name) + 2;
5884092dbcSRoman Gushchin 	char *ret = malloc(len);
5984092dbcSRoman Gushchin 
6084092dbcSRoman Gushchin 	snprintf(ret, len, "%s/%s", root, name);
6184092dbcSRoman Gushchin 
6284092dbcSRoman Gushchin 	return ret;
6384092dbcSRoman Gushchin }
6484092dbcSRoman Gushchin 
cg_name_indexed(const char * root,const char * name,int index)6584092dbcSRoman Gushchin char *cg_name_indexed(const char *root, const char *name, int index)
6684092dbcSRoman Gushchin {
6784092dbcSRoman Gushchin 	size_t len = strlen(root) + strlen(name) + 10;
6884092dbcSRoman Gushchin 	char *ret = malloc(len);
6984092dbcSRoman Gushchin 
7084092dbcSRoman Gushchin 	snprintf(ret, len, "%s/%s_%d", root, name, index);
7184092dbcSRoman Gushchin 
7284092dbcSRoman Gushchin 	return ret;
7384092dbcSRoman Gushchin }
7484092dbcSRoman Gushchin 
cg_control(const char * cgroup,const char * control)755313bfe4SRoman Gushchin char *cg_control(const char *cgroup, const char *control)
765313bfe4SRoman Gushchin {
775313bfe4SRoman Gushchin 	size_t len = strlen(cgroup) + strlen(control) + 2;
785313bfe4SRoman Gushchin 	char *ret = malloc(len);
795313bfe4SRoman Gushchin 
805313bfe4SRoman Gushchin 	snprintf(ret, len, "%s/%s", cgroup, control);
815313bfe4SRoman Gushchin 
825313bfe4SRoman Gushchin 	return ret;
835313bfe4SRoman Gushchin }
845313bfe4SRoman Gushchin 
856c26df84SYosry Ahmed /* Returns 0 on success, or -errno on failure. */
cg_read(const char * cgroup,const char * control,char * buf,size_t len)8684092dbcSRoman Gushchin int cg_read(const char *cgroup, const char *control, char *buf, size_t len)
8784092dbcSRoman Gushchin {
8884092dbcSRoman Gushchin 	char path[PATH_MAX];
896c26df84SYosry Ahmed 	ssize_t ret;
9084092dbcSRoman Gushchin 
9184092dbcSRoman Gushchin 	snprintf(path, sizeof(path), "%s/%s", cgroup, control);
9284092dbcSRoman Gushchin 
936c26df84SYosry Ahmed 	ret = read_text(path, buf, len);
946c26df84SYosry Ahmed 	return ret >= 0 ? 0 : ret;
9584092dbcSRoman Gushchin }
9684092dbcSRoman Gushchin 
cg_read_strcmp(const char * cgroup,const char * control,const char * expected)9784092dbcSRoman Gushchin int cg_read_strcmp(const char *cgroup, const char *control,
9884092dbcSRoman Gushchin 		   const char *expected)
9984092dbcSRoman Gushchin {
10048c2bb0bSJay Kamat 	size_t size;
10184092dbcSRoman Gushchin 	char *buf;
10248c2bb0bSJay Kamat 	int ret;
10348c2bb0bSJay Kamat 
10448c2bb0bSJay Kamat 	/* Handle the case of comparing against empty string */
10548c2bb0bSJay Kamat 	if (!expected)
106d8300206SGaurav Singh 		return -1;
10748c2bb0bSJay Kamat 	else
10848c2bb0bSJay Kamat 		size = strlen(expected) + 1;
10984092dbcSRoman Gushchin 
11084092dbcSRoman Gushchin 	buf = malloc(size);
11184092dbcSRoman Gushchin 	if (!buf)
11284092dbcSRoman Gushchin 		return -1;
11384092dbcSRoman Gushchin 
11448c2bb0bSJay Kamat 	if (cg_read(cgroup, control, buf, size)) {
11548c2bb0bSJay Kamat 		free(buf);
11684092dbcSRoman Gushchin 		return -1;
11748c2bb0bSJay Kamat 	}
11884092dbcSRoman Gushchin 
11948c2bb0bSJay Kamat 	ret = strcmp(expected, buf);
12048c2bb0bSJay Kamat 	free(buf);
12148c2bb0bSJay Kamat 	return ret;
12284092dbcSRoman Gushchin }
12384092dbcSRoman Gushchin 
cg_read_strstr(const char * cgroup,const char * control,const char * needle)12484092dbcSRoman Gushchin int cg_read_strstr(const char *cgroup, const char *control, const char *needle)
12584092dbcSRoman Gushchin {
12684092dbcSRoman Gushchin 	char buf[PAGE_SIZE];
12784092dbcSRoman Gushchin 
12884092dbcSRoman Gushchin 	if (cg_read(cgroup, control, buf, sizeof(buf)))
12984092dbcSRoman Gushchin 		return -1;
13084092dbcSRoman Gushchin 
13184092dbcSRoman Gushchin 	return strstr(buf, needle) ? 0 : -1;
13284092dbcSRoman Gushchin }
13384092dbcSRoman Gushchin 
cg_read_long(const char * cgroup,const char * control)13484092dbcSRoman Gushchin long cg_read_long(const char *cgroup, const char *control)
13584092dbcSRoman Gushchin {
13684092dbcSRoman Gushchin 	char buf[128];
13784092dbcSRoman Gushchin 
13884092dbcSRoman Gushchin 	if (cg_read(cgroup, control, buf, sizeof(buf)))
13984092dbcSRoman Gushchin 		return -1;
14084092dbcSRoman Gushchin 
14184092dbcSRoman Gushchin 	return atol(buf);
14284092dbcSRoman Gushchin }
14384092dbcSRoman Gushchin 
cg_read_key_long(const char * cgroup,const char * control,const char * key)14484092dbcSRoman Gushchin long cg_read_key_long(const char *cgroup, const char *control, const char *key)
14584092dbcSRoman Gushchin {
14684092dbcSRoman Gushchin 	char buf[PAGE_SIZE];
14784092dbcSRoman Gushchin 	char *ptr;
14884092dbcSRoman Gushchin 
14984092dbcSRoman Gushchin 	if (cg_read(cgroup, control, buf, sizeof(buf)))
15084092dbcSRoman Gushchin 		return -1;
15184092dbcSRoman Gushchin 
15284092dbcSRoman Gushchin 	ptr = strstr(buf, key);
15384092dbcSRoman Gushchin 	if (!ptr)
15484092dbcSRoman Gushchin 		return -1;
15584092dbcSRoman Gushchin 
15684092dbcSRoman Gushchin 	return atol(ptr + strlen(key));
15784092dbcSRoman Gushchin }
15884092dbcSRoman Gushchin 
cg_read_lc(const char * cgroup,const char * control)15911318989SMichal Koutný long cg_read_lc(const char *cgroup, const char *control)
16011318989SMichal Koutný {
16111318989SMichal Koutný 	char buf[PAGE_SIZE];
16211318989SMichal Koutný 	const char delim[] = "\n";
16311318989SMichal Koutný 	char *line;
16411318989SMichal Koutný 	long cnt = 0;
16511318989SMichal Koutný 
16611318989SMichal Koutný 	if (cg_read(cgroup, control, buf, sizeof(buf)))
16711318989SMichal Koutný 		return -1;
16811318989SMichal Koutný 
16911318989SMichal Koutný 	for (line = strtok(buf, delim); line; line = strtok(NULL, delim))
17011318989SMichal Koutný 		cnt++;
17111318989SMichal Koutný 
17211318989SMichal Koutný 	return cnt;
17311318989SMichal Koutný }
17411318989SMichal Koutný 
1756c26df84SYosry Ahmed /* Returns 0 on success, or -errno on failure. */
cg_write(const char * cgroup,const char * control,char * buf)17684092dbcSRoman Gushchin int cg_write(const char *cgroup, const char *control, char *buf)
17784092dbcSRoman Gushchin {
17884092dbcSRoman Gushchin 	char path[PATH_MAX];
1796c26df84SYosry Ahmed 	ssize_t len = strlen(buf), ret;
18084092dbcSRoman Gushchin 
18184092dbcSRoman Gushchin 	snprintf(path, sizeof(path), "%s/%s", cgroup, control);
1826c26df84SYosry Ahmed 	ret = write_text(path, buf, len);
1836c26df84SYosry Ahmed 	return ret == len ? 0 : ret;
18484092dbcSRoman Gushchin }
18584092dbcSRoman Gushchin 
cg_write_numeric(const char * cgroup,const char * control,long value)1866376b22cSDavid Vernet int cg_write_numeric(const char *cgroup, const char *control, long value)
1876376b22cSDavid Vernet {
1886376b22cSDavid Vernet 	char buf[64];
1896376b22cSDavid Vernet 	int ret;
1906376b22cSDavid Vernet 
1916376b22cSDavid Vernet 	ret = sprintf(buf, "%lu", value);
1926376b22cSDavid Vernet 	if (ret < 0)
1936376b22cSDavid Vernet 		return ret;
1946376b22cSDavid Vernet 
1956376b22cSDavid Vernet 	return cg_write(cgroup, control, buf);
1966376b22cSDavid Vernet }
1976376b22cSDavid Vernet 
cg_find_unified_root(char * root,size_t len)19884092dbcSRoman Gushchin int cg_find_unified_root(char *root, size_t len)
19984092dbcSRoman Gushchin {
20084092dbcSRoman Gushchin 	char buf[10 * PAGE_SIZE];
20184092dbcSRoman Gushchin 	char *fs, *mount, *type;
20284092dbcSRoman Gushchin 	const char delim[] = "\n\t ";
20384092dbcSRoman Gushchin 
20484092dbcSRoman Gushchin 	if (read_text("/proc/self/mounts", buf, sizeof(buf)) <= 0)
20584092dbcSRoman Gushchin 		return -1;
20684092dbcSRoman Gushchin 
20784092dbcSRoman Gushchin 	/*
20884092dbcSRoman Gushchin 	 * Example:
20984092dbcSRoman Gushchin 	 * cgroup /sys/fs/cgroup cgroup2 rw,seclabel,noexec,relatime 0 0
21084092dbcSRoman Gushchin 	 */
21184092dbcSRoman Gushchin 	for (fs = strtok(buf, delim); fs; fs = strtok(NULL, delim)) {
21284092dbcSRoman Gushchin 		mount = strtok(NULL, delim);
21384092dbcSRoman Gushchin 		type = strtok(NULL, delim);
21484092dbcSRoman Gushchin 		strtok(NULL, delim);
21584092dbcSRoman Gushchin 		strtok(NULL, delim);
21684092dbcSRoman Gushchin 		strtok(NULL, delim);
21784092dbcSRoman Gushchin 
218b59b1baaSChris Down 		if (strcmp(type, "cgroup2") == 0) {
21984092dbcSRoman Gushchin 			strncpy(root, mount, len);
22084092dbcSRoman Gushchin 			return 0;
22184092dbcSRoman Gushchin 		}
22284092dbcSRoman Gushchin 	}
22384092dbcSRoman Gushchin 
22484092dbcSRoman Gushchin 	return -1;
22584092dbcSRoman Gushchin }
22684092dbcSRoman Gushchin 
cg_create(const char * cgroup)22784092dbcSRoman Gushchin int cg_create(const char *cgroup)
22884092dbcSRoman Gushchin {
229b09c2baaSTejun Heo 	return mkdir(cgroup, 0755);
23084092dbcSRoman Gushchin }
23184092dbcSRoman Gushchin 
cg_wait_for_proc_count(const char * cgroup,int count)2325313bfe4SRoman Gushchin int cg_wait_for_proc_count(const char *cgroup, int count)
2335313bfe4SRoman Gushchin {
2345313bfe4SRoman Gushchin 	char buf[10 * PAGE_SIZE] = {0};
2355313bfe4SRoman Gushchin 	int attempts;
2365313bfe4SRoman Gushchin 	char *ptr;
2375313bfe4SRoman Gushchin 
2385313bfe4SRoman Gushchin 	for (attempts = 10; attempts >= 0; attempts--) {
2395313bfe4SRoman Gushchin 		int nr = 0;
2405313bfe4SRoman Gushchin 
2415313bfe4SRoman Gushchin 		if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf)))
2425313bfe4SRoman Gushchin 			break;
2435313bfe4SRoman Gushchin 
2445313bfe4SRoman Gushchin 		for (ptr = buf; *ptr; ptr++)
2455313bfe4SRoman Gushchin 			if (*ptr == '\n')
2465313bfe4SRoman Gushchin 				nr++;
2475313bfe4SRoman Gushchin 
2485313bfe4SRoman Gushchin 		if (nr >= count)
2495313bfe4SRoman Gushchin 			return 0;
2505313bfe4SRoman Gushchin 
2515313bfe4SRoman Gushchin 		usleep(100000);
2525313bfe4SRoman Gushchin 	}
2535313bfe4SRoman Gushchin 
2545313bfe4SRoman Gushchin 	return -1;
2555313bfe4SRoman Gushchin }
2565313bfe4SRoman Gushchin 
cg_killall(const char * cgroup)2575313bfe4SRoman Gushchin int cg_killall(const char *cgroup)
25884092dbcSRoman Gushchin {
25984092dbcSRoman Gushchin 	char buf[PAGE_SIZE];
26084092dbcSRoman Gushchin 	char *ptr = buf;
26184092dbcSRoman Gushchin 
2620de3103fSChristian Brauner 	/* If cgroup.kill exists use it. */
2630de3103fSChristian Brauner 	if (!cg_write(cgroup, "cgroup.kill", "1"))
2640de3103fSChristian Brauner 		return 0;
2650de3103fSChristian Brauner 
26684092dbcSRoman Gushchin 	if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf)))
26784092dbcSRoman Gushchin 		return -1;
26884092dbcSRoman Gushchin 
26984092dbcSRoman Gushchin 	while (ptr < buf + sizeof(buf)) {
27084092dbcSRoman Gushchin 		int pid = strtol(ptr, &ptr, 10);
27184092dbcSRoman Gushchin 
27284092dbcSRoman Gushchin 		if (pid == 0)
27384092dbcSRoman Gushchin 			break;
27484092dbcSRoman Gushchin 		if (*ptr)
27584092dbcSRoman Gushchin 			ptr++;
27684092dbcSRoman Gushchin 		else
27784092dbcSRoman Gushchin 			break;
27884092dbcSRoman Gushchin 		if (kill(pid, SIGKILL))
27984092dbcSRoman Gushchin 			return -1;
28084092dbcSRoman Gushchin 	}
28184092dbcSRoman Gushchin 
28284092dbcSRoman Gushchin 	return 0;
28384092dbcSRoman Gushchin }
28484092dbcSRoman Gushchin 
cg_destroy(const char * cgroup)28584092dbcSRoman Gushchin int cg_destroy(const char *cgroup)
28684092dbcSRoman Gushchin {
28784092dbcSRoman Gushchin 	int ret;
28884092dbcSRoman Gushchin 
289*12101424SMichal Koutný 	if (!cgroup)
290*12101424SMichal Koutný 		return 0;
29184092dbcSRoman Gushchin retry:
29284092dbcSRoman Gushchin 	ret = rmdir(cgroup);
29384092dbcSRoman Gushchin 	if (ret && errno == EBUSY) {
294ff9fb7cbSRoman Gushchin 		cg_killall(cgroup);
29584092dbcSRoman Gushchin 		usleep(100);
29684092dbcSRoman Gushchin 		goto retry;
29784092dbcSRoman Gushchin 	}
29884092dbcSRoman Gushchin 
29984092dbcSRoman Gushchin 	if (ret && errno == ENOENT)
30084092dbcSRoman Gushchin 		ret = 0;
30184092dbcSRoman Gushchin 
30284092dbcSRoman Gushchin 	return ret;
30384092dbcSRoman Gushchin }
30484092dbcSRoman Gushchin 
cg_enter(const char * cgroup,int pid)3055313bfe4SRoman Gushchin int cg_enter(const char *cgroup, int pid)
3065313bfe4SRoman Gushchin {
3075313bfe4SRoman Gushchin 	char pidbuf[64];
3085313bfe4SRoman Gushchin 
3095313bfe4SRoman Gushchin 	snprintf(pidbuf, sizeof(pidbuf), "%d", pid);
3105313bfe4SRoman Gushchin 	return cg_write(cgroup, "cgroup.procs", pidbuf);
3115313bfe4SRoman Gushchin }
3125313bfe4SRoman Gushchin 
cg_enter_current(const char * cgroup)313d863cb03SClaudio int cg_enter_current(const char *cgroup)
314d863cb03SClaudio {
31558c9f75bSMichal Koutný 	return cg_write(cgroup, "cgroup.procs", "0");
31658c9f75bSMichal Koutný }
317d863cb03SClaudio 
cg_enter_current_thread(const char * cgroup)31858c9f75bSMichal Koutný int cg_enter_current_thread(const char *cgroup)
31958c9f75bSMichal Koutný {
32058c9f75bSMichal Koutný 	return cg_write(cgroup, "cgroup.threads", "0");
321d863cb03SClaudio }
322d863cb03SClaudio 
cg_run(const char * cgroup,int (* fn)(const char * cgroup,void * arg),void * arg)32384092dbcSRoman Gushchin int cg_run(const char *cgroup,
32484092dbcSRoman Gushchin 	   int (*fn)(const char *cgroup, void *arg),
32584092dbcSRoman Gushchin 	   void *arg)
32684092dbcSRoman Gushchin {
32784092dbcSRoman Gushchin 	int pid, retcode;
32884092dbcSRoman Gushchin 
32984092dbcSRoman Gushchin 	pid = fork();
33084092dbcSRoman Gushchin 	if (pid < 0) {
33184092dbcSRoman Gushchin 		return pid;
33284092dbcSRoman Gushchin 	} else if (pid == 0) {
33384092dbcSRoman Gushchin 		char buf[64];
33484092dbcSRoman Gushchin 
33584092dbcSRoman Gushchin 		snprintf(buf, sizeof(buf), "%d", getpid());
33684092dbcSRoman Gushchin 		if (cg_write(cgroup, "cgroup.procs", buf))
33784092dbcSRoman Gushchin 			exit(EXIT_FAILURE);
33884092dbcSRoman Gushchin 		exit(fn(cgroup, arg));
33984092dbcSRoman Gushchin 	} else {
34084092dbcSRoman Gushchin 		waitpid(pid, &retcode, 0);
34184092dbcSRoman Gushchin 		if (WIFEXITED(retcode))
34284092dbcSRoman Gushchin 			return WEXITSTATUS(retcode);
34384092dbcSRoman Gushchin 		else
34484092dbcSRoman Gushchin 			return -1;
34584092dbcSRoman Gushchin 	}
34684092dbcSRoman Gushchin }
34784092dbcSRoman Gushchin 
clone_into_cgroup(int cgroup_fd)3489bd5910dSChristian Brauner pid_t clone_into_cgroup(int cgroup_fd)
3499bd5910dSChristian Brauner {
3509bd5910dSChristian Brauner #ifdef CLONE_ARGS_SIZE_VER2
3519bd5910dSChristian Brauner 	pid_t pid;
3529bd5910dSChristian Brauner 
353c2e46f6bSSachin Sant 	struct __clone_args args = {
3549bd5910dSChristian Brauner 		.flags = CLONE_INTO_CGROUP,
3559bd5910dSChristian Brauner 		.exit_signal = SIGCHLD,
3569bd5910dSChristian Brauner 		.cgroup = cgroup_fd,
3579bd5910dSChristian Brauner 	};
3589bd5910dSChristian Brauner 
359c2e46f6bSSachin Sant 	pid = sys_clone3(&args, sizeof(struct __clone_args));
3609bd5910dSChristian Brauner 	/*
3619bd5910dSChristian Brauner 	 * Verify that this is a genuine test failure:
3629bd5910dSChristian Brauner 	 * ENOSYS -> clone3() not available
3639bd5910dSChristian Brauner 	 * E2BIG  -> CLONE_INTO_CGROUP not available
3649bd5910dSChristian Brauner 	 */
3659bd5910dSChristian Brauner 	if (pid < 0 && (errno == ENOSYS || errno == E2BIG))
3669bd5910dSChristian Brauner 		goto pretend_enosys;
3679bd5910dSChristian Brauner 
3689bd5910dSChristian Brauner 	return pid;
3699bd5910dSChristian Brauner 
3709bd5910dSChristian Brauner pretend_enosys:
3719bd5910dSChristian Brauner #endif
3729bd5910dSChristian Brauner 	errno = ENOSYS;
3739bd5910dSChristian Brauner 	return -ENOSYS;
3749bd5910dSChristian Brauner }
3759bd5910dSChristian Brauner 
clone_reap(pid_t pid,int options)3769bd5910dSChristian Brauner int clone_reap(pid_t pid, int options)
3779bd5910dSChristian Brauner {
3789bd5910dSChristian Brauner 	int ret;
3799bd5910dSChristian Brauner 	siginfo_t info = {
3809bd5910dSChristian Brauner 		.si_signo = 0,
3819bd5910dSChristian Brauner 	};
3829bd5910dSChristian Brauner 
3839bd5910dSChristian Brauner again:
3849bd5910dSChristian Brauner 	ret = waitid(P_PID, pid, &info, options | __WALL | __WNOTHREAD);
3859bd5910dSChristian Brauner 	if (ret < 0) {
3869bd5910dSChristian Brauner 		if (errno == EINTR)
3879bd5910dSChristian Brauner 			goto again;
3889bd5910dSChristian Brauner 		return -1;
3899bd5910dSChristian Brauner 	}
3909bd5910dSChristian Brauner 
3919bd5910dSChristian Brauner 	if (options & WEXITED) {
3929bd5910dSChristian Brauner 		if (WIFEXITED(info.si_status))
3939bd5910dSChristian Brauner 			return WEXITSTATUS(info.si_status);
3949bd5910dSChristian Brauner 	}
3959bd5910dSChristian Brauner 
3969bd5910dSChristian Brauner 	if (options & WSTOPPED) {
3979bd5910dSChristian Brauner 		if (WIFSTOPPED(info.si_status))
3989bd5910dSChristian Brauner 			return WSTOPSIG(info.si_status);
3999bd5910dSChristian Brauner 	}
4009bd5910dSChristian Brauner 
4019bd5910dSChristian Brauner 	if (options & WCONTINUED) {
4029bd5910dSChristian Brauner 		if (WIFCONTINUED(info.si_status))
4039bd5910dSChristian Brauner 			return 0;
4049bd5910dSChristian Brauner 	}
4059bd5910dSChristian Brauner 
4069bd5910dSChristian Brauner 	return -1;
4079bd5910dSChristian Brauner }
4089bd5910dSChristian Brauner 
dirfd_open_opath(const char * dir)4099bd5910dSChristian Brauner int dirfd_open_opath(const char *dir)
4109bd5910dSChristian Brauner {
4119bd5910dSChristian Brauner 	return open(dir, O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW | O_PATH);
4129bd5910dSChristian Brauner }
4139bd5910dSChristian Brauner 
4149bd5910dSChristian Brauner #define close_prot_errno(fd)                                                   \
4159bd5910dSChristian Brauner 	if (fd >= 0) {                                                         \
4169bd5910dSChristian Brauner 		int _e_ = errno;                                               \
4179bd5910dSChristian Brauner 		close(fd);                                                     \
4189bd5910dSChristian Brauner 		errno = _e_;                                                   \
4199bd5910dSChristian Brauner 	}
4209bd5910dSChristian Brauner 
clone_into_cgroup_run_nowait(const char * cgroup,int (* fn)(const char * cgroup,void * arg),void * arg)4219bd5910dSChristian Brauner static int clone_into_cgroup_run_nowait(const char *cgroup,
4229bd5910dSChristian Brauner 					int (*fn)(const char *cgroup, void *arg),
4239bd5910dSChristian Brauner 					void *arg)
4249bd5910dSChristian Brauner {
4259bd5910dSChristian Brauner 	int cgroup_fd;
4269bd5910dSChristian Brauner 	pid_t pid;
4279bd5910dSChristian Brauner 
4289bd5910dSChristian Brauner 	cgroup_fd =  dirfd_open_opath(cgroup);
4299bd5910dSChristian Brauner 	if (cgroup_fd < 0)
4309bd5910dSChristian Brauner 		return -1;
4319bd5910dSChristian Brauner 
4329bd5910dSChristian Brauner 	pid = clone_into_cgroup(cgroup_fd);
4339bd5910dSChristian Brauner 	close_prot_errno(cgroup_fd);
4349bd5910dSChristian Brauner 	if (pid == 0)
4359bd5910dSChristian Brauner 		exit(fn(cgroup, arg));
4369bd5910dSChristian Brauner 
4379bd5910dSChristian Brauner 	return pid;
4389bd5910dSChristian Brauner }
4399bd5910dSChristian Brauner 
cg_run_nowait(const char * cgroup,int (* fn)(const char * cgroup,void * arg),void * arg)44084092dbcSRoman Gushchin int cg_run_nowait(const char *cgroup,
44184092dbcSRoman Gushchin 		  int (*fn)(const char *cgroup, void *arg),
44284092dbcSRoman Gushchin 		  void *arg)
44384092dbcSRoman Gushchin {
44484092dbcSRoman Gushchin 	int pid;
44584092dbcSRoman Gushchin 
4469bd5910dSChristian Brauner 	pid = clone_into_cgroup_run_nowait(cgroup, fn, arg);
4479bd5910dSChristian Brauner 	if (pid > 0)
4489bd5910dSChristian Brauner 		return pid;
4499bd5910dSChristian Brauner 
4509bd5910dSChristian Brauner 	/* Genuine test failure. */
4519bd5910dSChristian Brauner 	if (pid < 0 && errno != ENOSYS)
4529bd5910dSChristian Brauner 		return -1;
4539bd5910dSChristian Brauner 
45484092dbcSRoman Gushchin 	pid = fork();
45584092dbcSRoman Gushchin 	if (pid == 0) {
45684092dbcSRoman Gushchin 		char buf[64];
45784092dbcSRoman Gushchin 
45884092dbcSRoman Gushchin 		snprintf(buf, sizeof(buf), "%d", getpid());
45984092dbcSRoman Gushchin 		if (cg_write(cgroup, "cgroup.procs", buf))
46084092dbcSRoman Gushchin 			exit(EXIT_FAILURE);
46184092dbcSRoman Gushchin 		exit(fn(cgroup, arg));
46284092dbcSRoman Gushchin 	}
46384092dbcSRoman Gushchin 
46484092dbcSRoman Gushchin 	return pid;
46584092dbcSRoman Gushchin }
46684092dbcSRoman Gushchin 
get_temp_fd(void)46784092dbcSRoman Gushchin int get_temp_fd(void)
46884092dbcSRoman Gushchin {
46984092dbcSRoman Gushchin 	return open(".", O_TMPFILE | O_RDWR | O_EXCL);
47084092dbcSRoman Gushchin }
47184092dbcSRoman Gushchin 
alloc_pagecache(int fd,size_t size)47284092dbcSRoman Gushchin int alloc_pagecache(int fd, size_t size)
47384092dbcSRoman Gushchin {
47484092dbcSRoman Gushchin 	char buf[PAGE_SIZE];
47584092dbcSRoman Gushchin 	struct stat st;
47684092dbcSRoman Gushchin 	int i;
47784092dbcSRoman Gushchin 
47884092dbcSRoman Gushchin 	if (fstat(fd, &st))
47984092dbcSRoman Gushchin 		goto cleanup;
48084092dbcSRoman Gushchin 
48184092dbcSRoman Gushchin 	size += st.st_size;
48284092dbcSRoman Gushchin 
48384092dbcSRoman Gushchin 	if (ftruncate(fd, size))
48484092dbcSRoman Gushchin 		goto cleanup;
48584092dbcSRoman Gushchin 
48684092dbcSRoman Gushchin 	for (i = 0; i < size; i += sizeof(buf))
48784092dbcSRoman Gushchin 		read(fd, buf, sizeof(buf));
48884092dbcSRoman Gushchin 
48984092dbcSRoman Gushchin 	return 0;
49084092dbcSRoman Gushchin 
49184092dbcSRoman Gushchin cleanup:
49284092dbcSRoman Gushchin 	return -1;
49384092dbcSRoman Gushchin }
49484092dbcSRoman Gushchin 
alloc_anon(const char * cgroup,void * arg)49584092dbcSRoman Gushchin int alloc_anon(const char *cgroup, void *arg)
49684092dbcSRoman Gushchin {
49784092dbcSRoman Gushchin 	size_t size = (unsigned long)arg;
49884092dbcSRoman Gushchin 	char *buf, *ptr;
49984092dbcSRoman Gushchin 
50084092dbcSRoman Gushchin 	buf = malloc(size);
50184092dbcSRoman Gushchin 	for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
50284092dbcSRoman Gushchin 		*ptr = 0;
50384092dbcSRoman Gushchin 
50484092dbcSRoman Gushchin 	free(buf);
50584092dbcSRoman Gushchin 	return 0;
50684092dbcSRoman Gushchin }
507478b2784SMike Rapoport 
is_swap_enabled(void)508478b2784SMike Rapoport int is_swap_enabled(void)
509478b2784SMike Rapoport {
510478b2784SMike Rapoport 	char buf[PAGE_SIZE];
511478b2784SMike Rapoport 	const char delim[] = "\n";
512478b2784SMike Rapoport 	int cnt = 0;
513478b2784SMike Rapoport 	char *line;
514478b2784SMike Rapoport 
515478b2784SMike Rapoport 	if (read_text("/proc/swaps", buf, sizeof(buf)) <= 0)
516478b2784SMike Rapoport 		return -1;
517478b2784SMike Rapoport 
518478b2784SMike Rapoport 	for (line = strtok(buf, delim); line; line = strtok(NULL, delim))
519478b2784SMike Rapoport 		cnt++;
520478b2784SMike Rapoport 
521478b2784SMike Rapoport 	return cnt > 1;
522478b2784SMike Rapoport }
523a987785dSJay Kamat 
set_oom_adj_score(int pid,int score)524a987785dSJay Kamat int set_oom_adj_score(int pid, int score)
525a987785dSJay Kamat {
526a987785dSJay Kamat 	char path[PATH_MAX];
527a987785dSJay Kamat 	int fd, len;
528a987785dSJay Kamat 
529a987785dSJay Kamat 	sprintf(path, "/proc/%d/oom_score_adj", pid);
530a987785dSJay Kamat 
531a987785dSJay Kamat 	fd = open(path, O_WRONLY | O_APPEND);
532a987785dSJay Kamat 	if (fd < 0)
533a987785dSJay Kamat 		return fd;
534a987785dSJay Kamat 
535a987785dSJay Kamat 	len = dprintf(fd, "%d", score);
536a987785dSJay Kamat 	if (len < 0) {
537a987785dSJay Kamat 		close(fd);
538a987785dSJay Kamat 		return len;
539a987785dSJay Kamat 	}
540a987785dSJay Kamat 
541a987785dSJay Kamat 	close(fd);
542a987785dSJay Kamat 	return 0;
543a987785dSJay Kamat }
5445313bfe4SRoman Gushchin 
proc_mount_contains(const char * option)545cdc69458SDavid Vernet int proc_mount_contains(const char *option)
546cdc69458SDavid Vernet {
547cdc69458SDavid Vernet 	char buf[4 * PAGE_SIZE];
548cdc69458SDavid Vernet 	ssize_t read;
549cdc69458SDavid Vernet 
550cdc69458SDavid Vernet 	read = read_text("/proc/mounts", buf, sizeof(buf));
551cdc69458SDavid Vernet 	if (read < 0)
552cdc69458SDavid Vernet 		return read;
553cdc69458SDavid Vernet 
554cdc69458SDavid Vernet 	return strstr(buf, option) != NULL;
555cdc69458SDavid Vernet }
556cdc69458SDavid Vernet 
proc_read_text(int pid,bool thread,const char * item,char * buf,size_t size)55758c9f75bSMichal Koutný ssize_t proc_read_text(int pid, bool thread, const char *item, char *buf, size_t size)
5585313bfe4SRoman Gushchin {
5595313bfe4SRoman Gushchin 	char path[PATH_MAX];
560333d073dSYueHaibing 	ssize_t ret;
5615313bfe4SRoman Gushchin 
56258c9f75bSMichal Koutný 	if (!pid)
56358c9f75bSMichal Koutný 		snprintf(path, sizeof(path), "/proc/%s/%s",
56458c9f75bSMichal Koutný 			 thread ? "thread-self" : "self", item);
56558c9f75bSMichal Koutný 	else
5665313bfe4SRoman Gushchin 		snprintf(path, sizeof(path), "/proc/%d/%s", pid, item);
5675313bfe4SRoman Gushchin 
568333d073dSYueHaibing 	ret = read_text(path, buf, size);
569333d073dSYueHaibing 	return ret < 0 ? -1 : ret;
5705313bfe4SRoman Gushchin }
57111318989SMichal Koutný 
proc_read_strstr(int pid,bool thread,const char * item,const char * needle)57211318989SMichal Koutný int proc_read_strstr(int pid, bool thread, const char *item, const char *needle)
57311318989SMichal Koutný {
57411318989SMichal Koutný 	char buf[PAGE_SIZE];
57511318989SMichal Koutný 
57611318989SMichal Koutný 	if (proc_read_text(pid, thread, item, buf, sizeof(buf)) < 0)
57711318989SMichal Koutný 		return -1;
57811318989SMichal Koutný 
57911318989SMichal Koutný 	return strstr(buf, needle) ? 0 : -1;
58011318989SMichal Koutný }
5819bd5910dSChristian Brauner 
clone_into_cgroup_run_wait(const char * cgroup)5829bd5910dSChristian Brauner int clone_into_cgroup_run_wait(const char *cgroup)
5839bd5910dSChristian Brauner {
5849bd5910dSChristian Brauner 	int cgroup_fd;
5859bd5910dSChristian Brauner 	pid_t pid;
5869bd5910dSChristian Brauner 
5879bd5910dSChristian Brauner 	cgroup_fd =  dirfd_open_opath(cgroup);
5889bd5910dSChristian Brauner 	if (cgroup_fd < 0)
5899bd5910dSChristian Brauner 		return -1;
5909bd5910dSChristian Brauner 
5919bd5910dSChristian Brauner 	pid = clone_into_cgroup(cgroup_fd);
5929bd5910dSChristian Brauner 	close_prot_errno(cgroup_fd);
5939bd5910dSChristian Brauner 	if (pid < 0)
5949bd5910dSChristian Brauner 		return -1;
5959bd5910dSChristian Brauner 
5969bd5910dSChristian Brauner 	if (pid == 0)
5979bd5910dSChristian Brauner 		exit(EXIT_SUCCESS);
5989bd5910dSChristian Brauner 
5999bd5910dSChristian Brauner 	/*
6009bd5910dSChristian Brauner 	 * We don't care whether this fails. We only care whether the initial
6019bd5910dSChristian Brauner 	 * clone succeeded.
6029bd5910dSChristian Brauner 	 */
6039bd5910dSChristian Brauner 	(void)clone_reap(pid, WEXITED);
6049bd5910dSChristian Brauner 	return 0;
6059bd5910dSChristian Brauner }
6068075e4f6SChristian Brauner 
__prepare_for_wait(const char * cgroup,const char * filename)6076323ec54SShakeel Butt static int __prepare_for_wait(const char *cgroup, const char *filename)
6088075e4f6SChristian Brauner {
6098075e4f6SChristian Brauner 	int fd, ret = -1;
6108075e4f6SChristian Brauner 
6118075e4f6SChristian Brauner 	fd = inotify_init1(0);
6128075e4f6SChristian Brauner 	if (fd == -1)
6138075e4f6SChristian Brauner 		return fd;
6148075e4f6SChristian Brauner 
6156323ec54SShakeel Butt 	ret = inotify_add_watch(fd, cg_control(cgroup, filename), IN_MODIFY);
6168075e4f6SChristian Brauner 	if (ret == -1) {
6178075e4f6SChristian Brauner 		close(fd);
6188075e4f6SChristian Brauner 		fd = -1;
6198075e4f6SChristian Brauner 	}
6208075e4f6SChristian Brauner 
6218075e4f6SChristian Brauner 	return fd;
6228075e4f6SChristian Brauner }
6238075e4f6SChristian Brauner 
cg_prepare_for_wait(const char * cgroup)6246323ec54SShakeel Butt int cg_prepare_for_wait(const char *cgroup)
6256323ec54SShakeel Butt {
6266323ec54SShakeel Butt 	return __prepare_for_wait(cgroup, "cgroup.events");
6276323ec54SShakeel Butt }
6286323ec54SShakeel Butt 
memcg_prepare_for_wait(const char * cgroup)6296323ec54SShakeel Butt int memcg_prepare_for_wait(const char *cgroup)
6306323ec54SShakeel Butt {
6316323ec54SShakeel Butt 	return __prepare_for_wait(cgroup, "memory.events");
6326323ec54SShakeel Butt }
6336323ec54SShakeel Butt 
cg_wait_for(int fd)6348075e4f6SChristian Brauner int cg_wait_for(int fd)
6358075e4f6SChristian Brauner {
6368075e4f6SChristian Brauner 	int ret = -1;
6378075e4f6SChristian Brauner 	struct pollfd fds = {
6388075e4f6SChristian Brauner 		.fd = fd,
6398075e4f6SChristian Brauner 		.events = POLLIN,
6408075e4f6SChristian Brauner 	};
6418075e4f6SChristian Brauner 
6428075e4f6SChristian Brauner 	while (true) {
6438075e4f6SChristian Brauner 		ret = poll(&fds, 1, 10000);
6448075e4f6SChristian Brauner 
6458075e4f6SChristian Brauner 		if (ret == -1) {
6468075e4f6SChristian Brauner 			if (errno == EINTR)
6478075e4f6SChristian Brauner 				continue;
6488075e4f6SChristian Brauner 
6498075e4f6SChristian Brauner 			break;
6508075e4f6SChristian Brauner 		}
6518075e4f6SChristian Brauner 
6528075e4f6SChristian Brauner 		if (ret > 0 && fds.revents & POLLIN) {
6538075e4f6SChristian Brauner 			ret = 0;
6548075e4f6SChristian Brauner 			break;
6558075e4f6SChristian Brauner 		}
6568075e4f6SChristian Brauner 	}
6578075e4f6SChristian Brauner 
6588075e4f6SChristian Brauner 	return ret;
6598075e4f6SChristian Brauner }
660