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,bool * nsdelegate)198*c81b6d64STianchen Ding int cg_find_unified_root(char *root, size_t len, bool *nsdelegate)
19984092dbcSRoman Gushchin {
20084092dbcSRoman Gushchin char buf[10 * PAGE_SIZE];
201*c81b6d64STianchen Ding char *fs, *mount, *type, *options;
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);
214*c81b6d64STianchen Ding options = 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);
220*c81b6d64STianchen Ding if (nsdelegate)
221*c81b6d64STianchen Ding *nsdelegate = !!strstr(options, "nsdelegate");
22284092dbcSRoman Gushchin return 0;
22384092dbcSRoman Gushchin }
22484092dbcSRoman Gushchin }
22584092dbcSRoman Gushchin
22684092dbcSRoman Gushchin return -1;
22784092dbcSRoman Gushchin }
22884092dbcSRoman Gushchin
cg_create(const char * cgroup)22984092dbcSRoman Gushchin int cg_create(const char *cgroup)
23084092dbcSRoman Gushchin {
231b09c2baaSTejun Heo return mkdir(cgroup, 0755);
23284092dbcSRoman Gushchin }
23384092dbcSRoman Gushchin
cg_wait_for_proc_count(const char * cgroup,int count)2345313bfe4SRoman Gushchin int cg_wait_for_proc_count(const char *cgroup, int count)
2355313bfe4SRoman Gushchin {
2365313bfe4SRoman Gushchin char buf[10 * PAGE_SIZE] = {0};
2375313bfe4SRoman Gushchin int attempts;
2385313bfe4SRoman Gushchin char *ptr;
2395313bfe4SRoman Gushchin
2405313bfe4SRoman Gushchin for (attempts = 10; attempts >= 0; attempts--) {
2415313bfe4SRoman Gushchin int nr = 0;
2425313bfe4SRoman Gushchin
2435313bfe4SRoman Gushchin if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf)))
2445313bfe4SRoman Gushchin break;
2455313bfe4SRoman Gushchin
2465313bfe4SRoman Gushchin for (ptr = buf; *ptr; ptr++)
2475313bfe4SRoman Gushchin if (*ptr == '\n')
2485313bfe4SRoman Gushchin nr++;
2495313bfe4SRoman Gushchin
2505313bfe4SRoman Gushchin if (nr >= count)
2515313bfe4SRoman Gushchin return 0;
2525313bfe4SRoman Gushchin
2535313bfe4SRoman Gushchin usleep(100000);
2545313bfe4SRoman Gushchin }
2555313bfe4SRoman Gushchin
2565313bfe4SRoman Gushchin return -1;
2575313bfe4SRoman Gushchin }
2585313bfe4SRoman Gushchin
cg_killall(const char * cgroup)2595313bfe4SRoman Gushchin int cg_killall(const char *cgroup)
26084092dbcSRoman Gushchin {
26184092dbcSRoman Gushchin char buf[PAGE_SIZE];
26284092dbcSRoman Gushchin char *ptr = buf;
26384092dbcSRoman Gushchin
2640de3103fSChristian Brauner /* If cgroup.kill exists use it. */
2650de3103fSChristian Brauner if (!cg_write(cgroup, "cgroup.kill", "1"))
2660de3103fSChristian Brauner return 0;
2670de3103fSChristian Brauner
26884092dbcSRoman Gushchin if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf)))
26984092dbcSRoman Gushchin return -1;
27084092dbcSRoman Gushchin
27184092dbcSRoman Gushchin while (ptr < buf + sizeof(buf)) {
27284092dbcSRoman Gushchin int pid = strtol(ptr, &ptr, 10);
27384092dbcSRoman Gushchin
27484092dbcSRoman Gushchin if (pid == 0)
27584092dbcSRoman Gushchin break;
27684092dbcSRoman Gushchin if (*ptr)
27784092dbcSRoman Gushchin ptr++;
27884092dbcSRoman Gushchin else
27984092dbcSRoman Gushchin break;
28084092dbcSRoman Gushchin if (kill(pid, SIGKILL))
28184092dbcSRoman Gushchin return -1;
28284092dbcSRoman Gushchin }
28384092dbcSRoman Gushchin
28484092dbcSRoman Gushchin return 0;
28584092dbcSRoman Gushchin }
28684092dbcSRoman Gushchin
cg_destroy(const char * cgroup)28784092dbcSRoman Gushchin int cg_destroy(const char *cgroup)
28884092dbcSRoman Gushchin {
28984092dbcSRoman Gushchin int ret;
29084092dbcSRoman Gushchin
29112101424SMichal Koutný if (!cgroup)
29212101424SMichal Koutný return 0;
29384092dbcSRoman Gushchin retry:
29484092dbcSRoman Gushchin ret = rmdir(cgroup);
29584092dbcSRoman Gushchin if (ret && errno == EBUSY) {
296ff9fb7cbSRoman Gushchin cg_killall(cgroup);
29784092dbcSRoman Gushchin usleep(100);
29884092dbcSRoman Gushchin goto retry;
29984092dbcSRoman Gushchin }
30084092dbcSRoman Gushchin
30184092dbcSRoman Gushchin if (ret && errno == ENOENT)
30284092dbcSRoman Gushchin ret = 0;
30384092dbcSRoman Gushchin
30484092dbcSRoman Gushchin return ret;
30584092dbcSRoman Gushchin }
30684092dbcSRoman Gushchin
cg_enter(const char * cgroup,int pid)3075313bfe4SRoman Gushchin int cg_enter(const char *cgroup, int pid)
3085313bfe4SRoman Gushchin {
3095313bfe4SRoman Gushchin char pidbuf[64];
3105313bfe4SRoman Gushchin
3115313bfe4SRoman Gushchin snprintf(pidbuf, sizeof(pidbuf), "%d", pid);
3125313bfe4SRoman Gushchin return cg_write(cgroup, "cgroup.procs", pidbuf);
3135313bfe4SRoman Gushchin }
3145313bfe4SRoman Gushchin
cg_enter_current(const char * cgroup)315d863cb03SClaudio int cg_enter_current(const char *cgroup)
316d863cb03SClaudio {
31758c9f75bSMichal Koutný return cg_write(cgroup, "cgroup.procs", "0");
31858c9f75bSMichal Koutný }
319d863cb03SClaudio
cg_enter_current_thread(const char * cgroup)32058c9f75bSMichal Koutný int cg_enter_current_thread(const char *cgroup)
32158c9f75bSMichal Koutný {
32258c9f75bSMichal Koutný return cg_write(cgroup, "cgroup.threads", "0");
323d863cb03SClaudio }
324d863cb03SClaudio
cg_run(const char * cgroup,int (* fn)(const char * cgroup,void * arg),void * arg)32584092dbcSRoman Gushchin int cg_run(const char *cgroup,
32684092dbcSRoman Gushchin int (*fn)(const char *cgroup, void *arg),
32784092dbcSRoman Gushchin void *arg)
32884092dbcSRoman Gushchin {
32984092dbcSRoman Gushchin int pid, retcode;
33084092dbcSRoman Gushchin
33184092dbcSRoman Gushchin pid = fork();
33284092dbcSRoman Gushchin if (pid < 0) {
33384092dbcSRoman Gushchin return pid;
33484092dbcSRoman Gushchin } else if (pid == 0) {
33584092dbcSRoman Gushchin char buf[64];
33684092dbcSRoman Gushchin
33784092dbcSRoman Gushchin snprintf(buf, sizeof(buf), "%d", getpid());
33884092dbcSRoman Gushchin if (cg_write(cgroup, "cgroup.procs", buf))
33984092dbcSRoman Gushchin exit(EXIT_FAILURE);
34084092dbcSRoman Gushchin exit(fn(cgroup, arg));
34184092dbcSRoman Gushchin } else {
34284092dbcSRoman Gushchin waitpid(pid, &retcode, 0);
34384092dbcSRoman Gushchin if (WIFEXITED(retcode))
34484092dbcSRoman Gushchin return WEXITSTATUS(retcode);
34584092dbcSRoman Gushchin else
34684092dbcSRoman Gushchin return -1;
34784092dbcSRoman Gushchin }
34884092dbcSRoman Gushchin }
34984092dbcSRoman Gushchin
clone_into_cgroup(int cgroup_fd)3509bd5910dSChristian Brauner pid_t clone_into_cgroup(int cgroup_fd)
3519bd5910dSChristian Brauner {
3529bd5910dSChristian Brauner #ifdef CLONE_ARGS_SIZE_VER2
3539bd5910dSChristian Brauner pid_t pid;
3549bd5910dSChristian Brauner
355c2e46f6bSSachin Sant struct __clone_args args = {
3569bd5910dSChristian Brauner .flags = CLONE_INTO_CGROUP,
3579bd5910dSChristian Brauner .exit_signal = SIGCHLD,
3589bd5910dSChristian Brauner .cgroup = cgroup_fd,
3599bd5910dSChristian Brauner };
3609bd5910dSChristian Brauner
361c2e46f6bSSachin Sant pid = sys_clone3(&args, sizeof(struct __clone_args));
3629bd5910dSChristian Brauner /*
3639bd5910dSChristian Brauner * Verify that this is a genuine test failure:
3649bd5910dSChristian Brauner * ENOSYS -> clone3() not available
3659bd5910dSChristian Brauner * E2BIG -> CLONE_INTO_CGROUP not available
3669bd5910dSChristian Brauner */
3679bd5910dSChristian Brauner if (pid < 0 && (errno == ENOSYS || errno == E2BIG))
3689bd5910dSChristian Brauner goto pretend_enosys;
3699bd5910dSChristian Brauner
3709bd5910dSChristian Brauner return pid;
3719bd5910dSChristian Brauner
3729bd5910dSChristian Brauner pretend_enosys:
3739bd5910dSChristian Brauner #endif
3749bd5910dSChristian Brauner errno = ENOSYS;
3759bd5910dSChristian Brauner return -ENOSYS;
3769bd5910dSChristian Brauner }
3779bd5910dSChristian Brauner
clone_reap(pid_t pid,int options)3789bd5910dSChristian Brauner int clone_reap(pid_t pid, int options)
3799bd5910dSChristian Brauner {
3809bd5910dSChristian Brauner int ret;
3819bd5910dSChristian Brauner siginfo_t info = {
3829bd5910dSChristian Brauner .si_signo = 0,
3839bd5910dSChristian Brauner };
3849bd5910dSChristian Brauner
3859bd5910dSChristian Brauner again:
3869bd5910dSChristian Brauner ret = waitid(P_PID, pid, &info, options | __WALL | __WNOTHREAD);
3879bd5910dSChristian Brauner if (ret < 0) {
3889bd5910dSChristian Brauner if (errno == EINTR)
3899bd5910dSChristian Brauner goto again;
3909bd5910dSChristian Brauner return -1;
3919bd5910dSChristian Brauner }
3929bd5910dSChristian Brauner
3939bd5910dSChristian Brauner if (options & WEXITED) {
3949bd5910dSChristian Brauner if (WIFEXITED(info.si_status))
3959bd5910dSChristian Brauner return WEXITSTATUS(info.si_status);
3969bd5910dSChristian Brauner }
3979bd5910dSChristian Brauner
3989bd5910dSChristian Brauner if (options & WSTOPPED) {
3999bd5910dSChristian Brauner if (WIFSTOPPED(info.si_status))
4009bd5910dSChristian Brauner return WSTOPSIG(info.si_status);
4019bd5910dSChristian Brauner }
4029bd5910dSChristian Brauner
4039bd5910dSChristian Brauner if (options & WCONTINUED) {
4049bd5910dSChristian Brauner if (WIFCONTINUED(info.si_status))
4059bd5910dSChristian Brauner return 0;
4069bd5910dSChristian Brauner }
4079bd5910dSChristian Brauner
4089bd5910dSChristian Brauner return -1;
4099bd5910dSChristian Brauner }
4109bd5910dSChristian Brauner
dirfd_open_opath(const char * dir)4119bd5910dSChristian Brauner int dirfd_open_opath(const char *dir)
4129bd5910dSChristian Brauner {
4139bd5910dSChristian Brauner return open(dir, O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW | O_PATH);
4149bd5910dSChristian Brauner }
4159bd5910dSChristian Brauner
4169bd5910dSChristian Brauner #define close_prot_errno(fd) \
4179bd5910dSChristian Brauner if (fd >= 0) { \
4189bd5910dSChristian Brauner int _e_ = errno; \
4199bd5910dSChristian Brauner close(fd); \
4209bd5910dSChristian Brauner errno = _e_; \
4219bd5910dSChristian Brauner }
4229bd5910dSChristian Brauner
clone_into_cgroup_run_nowait(const char * cgroup,int (* fn)(const char * cgroup,void * arg),void * arg)4239bd5910dSChristian Brauner static int clone_into_cgroup_run_nowait(const char *cgroup,
4249bd5910dSChristian Brauner int (*fn)(const char *cgroup, void *arg),
4259bd5910dSChristian Brauner void *arg)
4269bd5910dSChristian Brauner {
4279bd5910dSChristian Brauner int cgroup_fd;
4289bd5910dSChristian Brauner pid_t pid;
4299bd5910dSChristian Brauner
4309bd5910dSChristian Brauner cgroup_fd = dirfd_open_opath(cgroup);
4319bd5910dSChristian Brauner if (cgroup_fd < 0)
4329bd5910dSChristian Brauner return -1;
4339bd5910dSChristian Brauner
4349bd5910dSChristian Brauner pid = clone_into_cgroup(cgroup_fd);
4359bd5910dSChristian Brauner close_prot_errno(cgroup_fd);
4369bd5910dSChristian Brauner if (pid == 0)
4379bd5910dSChristian Brauner exit(fn(cgroup, arg));
4389bd5910dSChristian Brauner
4399bd5910dSChristian Brauner return pid;
4409bd5910dSChristian Brauner }
4419bd5910dSChristian Brauner
cg_run_nowait(const char * cgroup,int (* fn)(const char * cgroup,void * arg),void * arg)44284092dbcSRoman Gushchin int cg_run_nowait(const char *cgroup,
44384092dbcSRoman Gushchin int (*fn)(const char *cgroup, void *arg),
44484092dbcSRoman Gushchin void *arg)
44584092dbcSRoman Gushchin {
44684092dbcSRoman Gushchin int pid;
44784092dbcSRoman Gushchin
4489bd5910dSChristian Brauner pid = clone_into_cgroup_run_nowait(cgroup, fn, arg);
4499bd5910dSChristian Brauner if (pid > 0)
4509bd5910dSChristian Brauner return pid;
4519bd5910dSChristian Brauner
4529bd5910dSChristian Brauner /* Genuine test failure. */
4539bd5910dSChristian Brauner if (pid < 0 && errno != ENOSYS)
4549bd5910dSChristian Brauner return -1;
4559bd5910dSChristian Brauner
45684092dbcSRoman Gushchin pid = fork();
45784092dbcSRoman Gushchin if (pid == 0) {
45884092dbcSRoman Gushchin char buf[64];
45984092dbcSRoman Gushchin
46084092dbcSRoman Gushchin snprintf(buf, sizeof(buf), "%d", getpid());
46184092dbcSRoman Gushchin if (cg_write(cgroup, "cgroup.procs", buf))
46284092dbcSRoman Gushchin exit(EXIT_FAILURE);
46384092dbcSRoman Gushchin exit(fn(cgroup, arg));
46484092dbcSRoman Gushchin }
46584092dbcSRoman Gushchin
46684092dbcSRoman Gushchin return pid;
46784092dbcSRoman Gushchin }
46884092dbcSRoman Gushchin
get_temp_fd(void)46984092dbcSRoman Gushchin int get_temp_fd(void)
47084092dbcSRoman Gushchin {
47184092dbcSRoman Gushchin return open(".", O_TMPFILE | O_RDWR | O_EXCL);
47284092dbcSRoman Gushchin }
47384092dbcSRoman Gushchin
alloc_pagecache(int fd,size_t size)47484092dbcSRoman Gushchin int alloc_pagecache(int fd, size_t size)
47584092dbcSRoman Gushchin {
47684092dbcSRoman Gushchin char buf[PAGE_SIZE];
47784092dbcSRoman Gushchin struct stat st;
47884092dbcSRoman Gushchin int i;
47984092dbcSRoman Gushchin
48084092dbcSRoman Gushchin if (fstat(fd, &st))
48184092dbcSRoman Gushchin goto cleanup;
48284092dbcSRoman Gushchin
48384092dbcSRoman Gushchin size += st.st_size;
48484092dbcSRoman Gushchin
48584092dbcSRoman Gushchin if (ftruncate(fd, size))
48684092dbcSRoman Gushchin goto cleanup;
48784092dbcSRoman Gushchin
48884092dbcSRoman Gushchin for (i = 0; i < size; i += sizeof(buf))
48984092dbcSRoman Gushchin read(fd, buf, sizeof(buf));
49084092dbcSRoman Gushchin
49184092dbcSRoman Gushchin return 0;
49284092dbcSRoman Gushchin
49384092dbcSRoman Gushchin cleanup:
49484092dbcSRoman Gushchin return -1;
49584092dbcSRoman Gushchin }
49684092dbcSRoman Gushchin
alloc_anon(const char * cgroup,void * arg)49784092dbcSRoman Gushchin int alloc_anon(const char *cgroup, void *arg)
49884092dbcSRoman Gushchin {
49984092dbcSRoman Gushchin size_t size = (unsigned long)arg;
50084092dbcSRoman Gushchin char *buf, *ptr;
50184092dbcSRoman Gushchin
50284092dbcSRoman Gushchin buf = malloc(size);
50384092dbcSRoman Gushchin for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
50484092dbcSRoman Gushchin *ptr = 0;
50584092dbcSRoman Gushchin
50684092dbcSRoman Gushchin free(buf);
50784092dbcSRoman Gushchin return 0;
50884092dbcSRoman Gushchin }
509478b2784SMike Rapoport
is_swap_enabled(void)510478b2784SMike Rapoport int is_swap_enabled(void)
511478b2784SMike Rapoport {
512478b2784SMike Rapoport char buf[PAGE_SIZE];
513478b2784SMike Rapoport const char delim[] = "\n";
514478b2784SMike Rapoport int cnt = 0;
515478b2784SMike Rapoport char *line;
516478b2784SMike Rapoport
517478b2784SMike Rapoport if (read_text("/proc/swaps", buf, sizeof(buf)) <= 0)
518478b2784SMike Rapoport return -1;
519478b2784SMike Rapoport
520478b2784SMike Rapoport for (line = strtok(buf, delim); line; line = strtok(NULL, delim))
521478b2784SMike Rapoport cnt++;
522478b2784SMike Rapoport
523478b2784SMike Rapoport return cnt > 1;
524478b2784SMike Rapoport }
525a987785dSJay Kamat
set_oom_adj_score(int pid,int score)526a987785dSJay Kamat int set_oom_adj_score(int pid, int score)
527a987785dSJay Kamat {
528a987785dSJay Kamat char path[PATH_MAX];
529a987785dSJay Kamat int fd, len;
530a987785dSJay Kamat
531a987785dSJay Kamat sprintf(path, "/proc/%d/oom_score_adj", pid);
532a987785dSJay Kamat
533a987785dSJay Kamat fd = open(path, O_WRONLY | O_APPEND);
534a987785dSJay Kamat if (fd < 0)
535a987785dSJay Kamat return fd;
536a987785dSJay Kamat
537a987785dSJay Kamat len = dprintf(fd, "%d", score);
538a987785dSJay Kamat if (len < 0) {
539a987785dSJay Kamat close(fd);
540a987785dSJay Kamat return len;
541a987785dSJay Kamat }
542a987785dSJay Kamat
543a987785dSJay Kamat close(fd);
544a987785dSJay Kamat return 0;
545a987785dSJay Kamat }
5465313bfe4SRoman Gushchin
proc_mount_contains(const char * option)547cdc69458SDavid Vernet int proc_mount_contains(const char *option)
548cdc69458SDavid Vernet {
549cdc69458SDavid Vernet char buf[4 * PAGE_SIZE];
550cdc69458SDavid Vernet ssize_t read;
551cdc69458SDavid Vernet
552cdc69458SDavid Vernet read = read_text("/proc/mounts", buf, sizeof(buf));
553cdc69458SDavid Vernet if (read < 0)
554cdc69458SDavid Vernet return read;
555cdc69458SDavid Vernet
556cdc69458SDavid Vernet return strstr(buf, option) != NULL;
557cdc69458SDavid Vernet }
558cdc69458SDavid Vernet
proc_read_text(int pid,bool thread,const char * item,char * buf,size_t size)55958c9f75bSMichal Koutný ssize_t proc_read_text(int pid, bool thread, const char *item, char *buf, size_t size)
5605313bfe4SRoman Gushchin {
5615313bfe4SRoman Gushchin char path[PATH_MAX];
562333d073dSYueHaibing ssize_t ret;
5635313bfe4SRoman Gushchin
56458c9f75bSMichal Koutný if (!pid)
56558c9f75bSMichal Koutný snprintf(path, sizeof(path), "/proc/%s/%s",
56658c9f75bSMichal Koutný thread ? "thread-self" : "self", item);
56758c9f75bSMichal Koutný else
5685313bfe4SRoman Gushchin snprintf(path, sizeof(path), "/proc/%d/%s", pid, item);
5695313bfe4SRoman Gushchin
570333d073dSYueHaibing ret = read_text(path, buf, size);
571333d073dSYueHaibing return ret < 0 ? -1 : ret;
5725313bfe4SRoman Gushchin }
57311318989SMichal Koutný
proc_read_strstr(int pid,bool thread,const char * item,const char * needle)57411318989SMichal Koutný int proc_read_strstr(int pid, bool thread, const char *item, const char *needle)
57511318989SMichal Koutný {
57611318989SMichal Koutný char buf[PAGE_SIZE];
57711318989SMichal Koutný
57811318989SMichal Koutný if (proc_read_text(pid, thread, item, buf, sizeof(buf)) < 0)
57911318989SMichal Koutný return -1;
58011318989SMichal Koutný
58111318989SMichal Koutný return strstr(buf, needle) ? 0 : -1;
58211318989SMichal Koutný }
5839bd5910dSChristian Brauner
clone_into_cgroup_run_wait(const char * cgroup)5849bd5910dSChristian Brauner int clone_into_cgroup_run_wait(const char *cgroup)
5859bd5910dSChristian Brauner {
5869bd5910dSChristian Brauner int cgroup_fd;
5879bd5910dSChristian Brauner pid_t pid;
5889bd5910dSChristian Brauner
5899bd5910dSChristian Brauner cgroup_fd = dirfd_open_opath(cgroup);
5909bd5910dSChristian Brauner if (cgroup_fd < 0)
5919bd5910dSChristian Brauner return -1;
5929bd5910dSChristian Brauner
5939bd5910dSChristian Brauner pid = clone_into_cgroup(cgroup_fd);
5949bd5910dSChristian Brauner close_prot_errno(cgroup_fd);
5959bd5910dSChristian Brauner if (pid < 0)
5969bd5910dSChristian Brauner return -1;
5979bd5910dSChristian Brauner
5989bd5910dSChristian Brauner if (pid == 0)
5999bd5910dSChristian Brauner exit(EXIT_SUCCESS);
6009bd5910dSChristian Brauner
6019bd5910dSChristian Brauner /*
6029bd5910dSChristian Brauner * We don't care whether this fails. We only care whether the initial
6039bd5910dSChristian Brauner * clone succeeded.
6049bd5910dSChristian Brauner */
6059bd5910dSChristian Brauner (void)clone_reap(pid, WEXITED);
6069bd5910dSChristian Brauner return 0;
6079bd5910dSChristian Brauner }
6088075e4f6SChristian Brauner
__prepare_for_wait(const char * cgroup,const char * filename)6096323ec54SShakeel Butt static int __prepare_for_wait(const char *cgroup, const char *filename)
6108075e4f6SChristian Brauner {
6118075e4f6SChristian Brauner int fd, ret = -1;
6128075e4f6SChristian Brauner
6138075e4f6SChristian Brauner fd = inotify_init1(0);
6148075e4f6SChristian Brauner if (fd == -1)
6158075e4f6SChristian Brauner return fd;
6168075e4f6SChristian Brauner
6176323ec54SShakeel Butt ret = inotify_add_watch(fd, cg_control(cgroup, filename), IN_MODIFY);
6188075e4f6SChristian Brauner if (ret == -1) {
6198075e4f6SChristian Brauner close(fd);
6208075e4f6SChristian Brauner fd = -1;
6218075e4f6SChristian Brauner }
6228075e4f6SChristian Brauner
6238075e4f6SChristian Brauner return fd;
6248075e4f6SChristian Brauner }
6258075e4f6SChristian Brauner
cg_prepare_for_wait(const char * cgroup)6266323ec54SShakeel Butt int cg_prepare_for_wait(const char *cgroup)
6276323ec54SShakeel Butt {
6286323ec54SShakeel Butt return __prepare_for_wait(cgroup, "cgroup.events");
6296323ec54SShakeel Butt }
6306323ec54SShakeel Butt
memcg_prepare_for_wait(const char * cgroup)6316323ec54SShakeel Butt int memcg_prepare_for_wait(const char *cgroup)
6326323ec54SShakeel Butt {
6336323ec54SShakeel Butt return __prepare_for_wait(cgroup, "memory.events");
6346323ec54SShakeel Butt }
6356323ec54SShakeel Butt
cg_wait_for(int fd)6368075e4f6SChristian Brauner int cg_wait_for(int fd)
6378075e4f6SChristian Brauner {
6388075e4f6SChristian Brauner int ret = -1;
6398075e4f6SChristian Brauner struct pollfd fds = {
6408075e4f6SChristian Brauner .fd = fd,
6418075e4f6SChristian Brauner .events = POLLIN,
6428075e4f6SChristian Brauner };
6438075e4f6SChristian Brauner
6448075e4f6SChristian Brauner while (true) {
6458075e4f6SChristian Brauner ret = poll(&fds, 1, 10000);
6468075e4f6SChristian Brauner
6478075e4f6SChristian Brauner if (ret == -1) {
6488075e4f6SChristian Brauner if (errno == EINTR)
6498075e4f6SChristian Brauner continue;
6508075e4f6SChristian Brauner
6518075e4f6SChristian Brauner break;
6528075e4f6SChristian Brauner }
6538075e4f6SChristian Brauner
6548075e4f6SChristian Brauner if (ret > 0 && fds.revents & POLLIN) {
6558075e4f6SChristian Brauner ret = 0;
6568075e4f6SChristian Brauner break;
6578075e4f6SChristian Brauner }
6588075e4f6SChristian Brauner }
6598075e4f6SChristian Brauner
6608075e4f6SChristian Brauner return ret;
6618075e4f6SChristian Brauner }
662