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 2284092dbcSRoman Gushchin static ssize_t read_text(const char *path, char *buf, size_t max_len) 2384092dbcSRoman Gushchin { 2484092dbcSRoman Gushchin ssize_t len; 2584092dbcSRoman Gushchin int fd; 2684092dbcSRoman Gushchin 2784092dbcSRoman Gushchin fd = open(path, O_RDONLY); 2884092dbcSRoman Gushchin if (fd < 0) 2984092dbcSRoman Gushchin return fd; 3084092dbcSRoman Gushchin 3184092dbcSRoman Gushchin len = read(fd, buf, max_len - 1); 3284092dbcSRoman Gushchin if (len < 0) 3384092dbcSRoman Gushchin goto out; 3484092dbcSRoman Gushchin 3584092dbcSRoman Gushchin buf[len] = 0; 3684092dbcSRoman Gushchin out: 3784092dbcSRoman Gushchin close(fd); 3884092dbcSRoman Gushchin return len; 3984092dbcSRoman Gushchin } 4084092dbcSRoman Gushchin 4153c3daf8SDan Carpenter static ssize_t write_text(const char *path, char *buf, ssize_t len) 4284092dbcSRoman Gushchin { 4384092dbcSRoman Gushchin int fd; 4484092dbcSRoman Gushchin 4584092dbcSRoman Gushchin fd = open(path, O_WRONLY | O_APPEND); 4684092dbcSRoman Gushchin if (fd < 0) 4784092dbcSRoman Gushchin return fd; 4884092dbcSRoman Gushchin 4984092dbcSRoman Gushchin len = write(fd, buf, len); 5084092dbcSRoman Gushchin if (len < 0) { 5184092dbcSRoman Gushchin close(fd); 5284092dbcSRoman Gushchin return len; 5384092dbcSRoman Gushchin } 5484092dbcSRoman Gushchin 5584092dbcSRoman Gushchin close(fd); 5684092dbcSRoman Gushchin 5784092dbcSRoman Gushchin return len; 5884092dbcSRoman Gushchin } 5984092dbcSRoman Gushchin 6084092dbcSRoman Gushchin char *cg_name(const char *root, const char *name) 6184092dbcSRoman Gushchin { 6284092dbcSRoman Gushchin size_t len = strlen(root) + strlen(name) + 2; 6384092dbcSRoman Gushchin char *ret = malloc(len); 6484092dbcSRoman Gushchin 6584092dbcSRoman Gushchin snprintf(ret, len, "%s/%s", root, name); 6684092dbcSRoman Gushchin 6784092dbcSRoman Gushchin return ret; 6884092dbcSRoman Gushchin } 6984092dbcSRoman Gushchin 7084092dbcSRoman Gushchin char *cg_name_indexed(const char *root, const char *name, int index) 7184092dbcSRoman Gushchin { 7284092dbcSRoman Gushchin size_t len = strlen(root) + strlen(name) + 10; 7384092dbcSRoman Gushchin char *ret = malloc(len); 7484092dbcSRoman Gushchin 7584092dbcSRoman Gushchin snprintf(ret, len, "%s/%s_%d", root, name, index); 7684092dbcSRoman Gushchin 7784092dbcSRoman Gushchin return ret; 7884092dbcSRoman Gushchin } 7984092dbcSRoman Gushchin 805313bfe4SRoman Gushchin char *cg_control(const char *cgroup, const char *control) 815313bfe4SRoman Gushchin { 825313bfe4SRoman Gushchin size_t len = strlen(cgroup) + strlen(control) + 2; 835313bfe4SRoman Gushchin char *ret = malloc(len); 845313bfe4SRoman Gushchin 855313bfe4SRoman Gushchin snprintf(ret, len, "%s/%s", cgroup, control); 865313bfe4SRoman Gushchin 875313bfe4SRoman Gushchin return ret; 885313bfe4SRoman Gushchin } 895313bfe4SRoman Gushchin 9084092dbcSRoman Gushchin int cg_read(const char *cgroup, const char *control, char *buf, size_t len) 9184092dbcSRoman Gushchin { 9284092dbcSRoman Gushchin char path[PATH_MAX]; 9384092dbcSRoman Gushchin 9484092dbcSRoman Gushchin snprintf(path, sizeof(path), "%s/%s", cgroup, control); 9584092dbcSRoman Gushchin 9684092dbcSRoman Gushchin if (read_text(path, buf, len) >= 0) 9784092dbcSRoman Gushchin return 0; 9884092dbcSRoman Gushchin 9984092dbcSRoman Gushchin return -1; 10084092dbcSRoman Gushchin } 10184092dbcSRoman Gushchin 10284092dbcSRoman Gushchin int cg_read_strcmp(const char *cgroup, const char *control, 10384092dbcSRoman Gushchin const char *expected) 10484092dbcSRoman Gushchin { 10548c2bb0bSJay Kamat size_t size; 10684092dbcSRoman Gushchin char *buf; 10748c2bb0bSJay Kamat int ret; 10848c2bb0bSJay Kamat 10948c2bb0bSJay Kamat /* Handle the case of comparing against empty string */ 11048c2bb0bSJay Kamat if (!expected) 111d8300206SGaurav Singh return -1; 11248c2bb0bSJay Kamat else 11348c2bb0bSJay Kamat size = strlen(expected) + 1; 11484092dbcSRoman Gushchin 11584092dbcSRoman Gushchin buf = malloc(size); 11684092dbcSRoman Gushchin if (!buf) 11784092dbcSRoman Gushchin return -1; 11884092dbcSRoman Gushchin 11948c2bb0bSJay Kamat if (cg_read(cgroup, control, buf, size)) { 12048c2bb0bSJay Kamat free(buf); 12184092dbcSRoman Gushchin return -1; 12248c2bb0bSJay Kamat } 12384092dbcSRoman Gushchin 12448c2bb0bSJay Kamat ret = strcmp(expected, buf); 12548c2bb0bSJay Kamat free(buf); 12648c2bb0bSJay Kamat return ret; 12784092dbcSRoman Gushchin } 12884092dbcSRoman Gushchin 12984092dbcSRoman Gushchin int cg_read_strstr(const char *cgroup, const char *control, const char *needle) 13084092dbcSRoman Gushchin { 13184092dbcSRoman Gushchin char buf[PAGE_SIZE]; 13284092dbcSRoman Gushchin 13384092dbcSRoman Gushchin if (cg_read(cgroup, control, buf, sizeof(buf))) 13484092dbcSRoman Gushchin return -1; 13584092dbcSRoman Gushchin 13684092dbcSRoman Gushchin return strstr(buf, needle) ? 0 : -1; 13784092dbcSRoman Gushchin } 13884092dbcSRoman Gushchin 13984092dbcSRoman Gushchin long cg_read_long(const char *cgroup, const char *control) 14084092dbcSRoman Gushchin { 14184092dbcSRoman Gushchin char buf[128]; 14284092dbcSRoman Gushchin 14384092dbcSRoman Gushchin if (cg_read(cgroup, control, buf, sizeof(buf))) 14484092dbcSRoman Gushchin return -1; 14584092dbcSRoman Gushchin 14684092dbcSRoman Gushchin return atol(buf); 14784092dbcSRoman Gushchin } 14884092dbcSRoman Gushchin 14984092dbcSRoman Gushchin long cg_read_key_long(const char *cgroup, const char *control, const char *key) 15084092dbcSRoman Gushchin { 15184092dbcSRoman Gushchin char buf[PAGE_SIZE]; 15284092dbcSRoman Gushchin char *ptr; 15384092dbcSRoman Gushchin 15484092dbcSRoman Gushchin if (cg_read(cgroup, control, buf, sizeof(buf))) 15584092dbcSRoman Gushchin return -1; 15684092dbcSRoman Gushchin 15784092dbcSRoman Gushchin ptr = strstr(buf, key); 15884092dbcSRoman Gushchin if (!ptr) 15984092dbcSRoman Gushchin return -1; 16084092dbcSRoman Gushchin 16184092dbcSRoman Gushchin return atol(ptr + strlen(key)); 16284092dbcSRoman Gushchin } 16384092dbcSRoman Gushchin 16411318989SMichal Koutný long cg_read_lc(const char *cgroup, const char *control) 16511318989SMichal Koutný { 16611318989SMichal Koutný char buf[PAGE_SIZE]; 16711318989SMichal Koutný const char delim[] = "\n"; 16811318989SMichal Koutný char *line; 16911318989SMichal Koutný long cnt = 0; 17011318989SMichal Koutný 17111318989SMichal Koutný if (cg_read(cgroup, control, buf, sizeof(buf))) 17211318989SMichal Koutný return -1; 17311318989SMichal Koutný 17411318989SMichal Koutný for (line = strtok(buf, delim); line; line = strtok(NULL, delim)) 17511318989SMichal Koutný cnt++; 17611318989SMichal Koutný 17711318989SMichal Koutný return cnt; 17811318989SMichal Koutný } 17911318989SMichal Koutný 18084092dbcSRoman Gushchin int cg_write(const char *cgroup, const char *control, char *buf) 18184092dbcSRoman Gushchin { 18284092dbcSRoman Gushchin char path[PATH_MAX]; 18353c3daf8SDan Carpenter ssize_t len = strlen(buf); 18484092dbcSRoman Gushchin 18584092dbcSRoman Gushchin snprintf(path, sizeof(path), "%s/%s", cgroup, control); 18684092dbcSRoman Gushchin 18784092dbcSRoman Gushchin if (write_text(path, buf, len) == len) 18884092dbcSRoman Gushchin return 0; 18984092dbcSRoman Gushchin 19084092dbcSRoman Gushchin return -1; 19184092dbcSRoman Gushchin } 19284092dbcSRoman Gushchin 19384092dbcSRoman Gushchin int cg_find_unified_root(char *root, size_t len) 19484092dbcSRoman Gushchin { 19584092dbcSRoman Gushchin char buf[10 * PAGE_SIZE]; 19684092dbcSRoman Gushchin char *fs, *mount, *type; 19784092dbcSRoman Gushchin const char delim[] = "\n\t "; 19884092dbcSRoman Gushchin 19984092dbcSRoman Gushchin if (read_text("/proc/self/mounts", buf, sizeof(buf)) <= 0) 20084092dbcSRoman Gushchin return -1; 20184092dbcSRoman Gushchin 20284092dbcSRoman Gushchin /* 20384092dbcSRoman Gushchin * Example: 20484092dbcSRoman Gushchin * cgroup /sys/fs/cgroup cgroup2 rw,seclabel,noexec,relatime 0 0 20584092dbcSRoman Gushchin */ 20684092dbcSRoman Gushchin for (fs = strtok(buf, delim); fs; fs = strtok(NULL, delim)) { 20784092dbcSRoman Gushchin mount = strtok(NULL, delim); 20884092dbcSRoman Gushchin type = strtok(NULL, delim); 20984092dbcSRoman Gushchin strtok(NULL, delim); 21084092dbcSRoman Gushchin strtok(NULL, delim); 21184092dbcSRoman Gushchin strtok(NULL, delim); 21284092dbcSRoman Gushchin 213b59b1baaSChris Down if (strcmp(type, "cgroup2") == 0) { 21484092dbcSRoman Gushchin strncpy(root, mount, len); 21584092dbcSRoman Gushchin return 0; 21684092dbcSRoman Gushchin } 21784092dbcSRoman Gushchin } 21884092dbcSRoman Gushchin 21984092dbcSRoman Gushchin return -1; 22084092dbcSRoman Gushchin } 22184092dbcSRoman Gushchin 22284092dbcSRoman Gushchin int cg_create(const char *cgroup) 22384092dbcSRoman Gushchin { 224b09c2baaSTejun Heo return mkdir(cgroup, 0755); 22584092dbcSRoman Gushchin } 22684092dbcSRoman Gushchin 2275313bfe4SRoman Gushchin int cg_wait_for_proc_count(const char *cgroup, int count) 2285313bfe4SRoman Gushchin { 2295313bfe4SRoman Gushchin char buf[10 * PAGE_SIZE] = {0}; 2305313bfe4SRoman Gushchin int attempts; 2315313bfe4SRoman Gushchin char *ptr; 2325313bfe4SRoman Gushchin 2335313bfe4SRoman Gushchin for (attempts = 10; attempts >= 0; attempts--) { 2345313bfe4SRoman Gushchin int nr = 0; 2355313bfe4SRoman Gushchin 2365313bfe4SRoman Gushchin if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf))) 2375313bfe4SRoman Gushchin break; 2385313bfe4SRoman Gushchin 2395313bfe4SRoman Gushchin for (ptr = buf; *ptr; ptr++) 2405313bfe4SRoman Gushchin if (*ptr == '\n') 2415313bfe4SRoman Gushchin nr++; 2425313bfe4SRoman Gushchin 2435313bfe4SRoman Gushchin if (nr >= count) 2445313bfe4SRoman Gushchin return 0; 2455313bfe4SRoman Gushchin 2465313bfe4SRoman Gushchin usleep(100000); 2475313bfe4SRoman Gushchin } 2485313bfe4SRoman Gushchin 2495313bfe4SRoman Gushchin return -1; 2505313bfe4SRoman Gushchin } 2515313bfe4SRoman Gushchin 2525313bfe4SRoman Gushchin int cg_killall(const char *cgroup) 25384092dbcSRoman Gushchin { 25484092dbcSRoman Gushchin char buf[PAGE_SIZE]; 25584092dbcSRoman Gushchin char *ptr = buf; 25684092dbcSRoman Gushchin 2570de3103fSChristian Brauner /* If cgroup.kill exists use it. */ 2580de3103fSChristian Brauner if (!cg_write(cgroup, "cgroup.kill", "1")) 2590de3103fSChristian Brauner return 0; 2600de3103fSChristian Brauner 26184092dbcSRoman Gushchin if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf))) 26284092dbcSRoman Gushchin return -1; 26384092dbcSRoman Gushchin 26484092dbcSRoman Gushchin while (ptr < buf + sizeof(buf)) { 26584092dbcSRoman Gushchin int pid = strtol(ptr, &ptr, 10); 26684092dbcSRoman Gushchin 26784092dbcSRoman Gushchin if (pid == 0) 26884092dbcSRoman Gushchin break; 26984092dbcSRoman Gushchin if (*ptr) 27084092dbcSRoman Gushchin ptr++; 27184092dbcSRoman Gushchin else 27284092dbcSRoman Gushchin break; 27384092dbcSRoman Gushchin if (kill(pid, SIGKILL)) 27484092dbcSRoman Gushchin return -1; 27584092dbcSRoman Gushchin } 27684092dbcSRoman Gushchin 27784092dbcSRoman Gushchin return 0; 27884092dbcSRoman Gushchin } 27984092dbcSRoman Gushchin 28084092dbcSRoman Gushchin int cg_destroy(const char *cgroup) 28184092dbcSRoman Gushchin { 28284092dbcSRoman Gushchin int ret; 28384092dbcSRoman Gushchin 28484092dbcSRoman Gushchin retry: 28584092dbcSRoman Gushchin ret = rmdir(cgroup); 28684092dbcSRoman Gushchin if (ret && errno == EBUSY) { 287ff9fb7cbSRoman Gushchin cg_killall(cgroup); 28884092dbcSRoman Gushchin usleep(100); 28984092dbcSRoman Gushchin goto retry; 29084092dbcSRoman Gushchin } 29184092dbcSRoman Gushchin 29284092dbcSRoman Gushchin if (ret && errno == ENOENT) 29384092dbcSRoman Gushchin ret = 0; 29484092dbcSRoman Gushchin 29584092dbcSRoman Gushchin return ret; 29684092dbcSRoman Gushchin } 29784092dbcSRoman Gushchin 2985313bfe4SRoman Gushchin int cg_enter(const char *cgroup, int pid) 2995313bfe4SRoman Gushchin { 3005313bfe4SRoman Gushchin char pidbuf[64]; 3015313bfe4SRoman Gushchin 3025313bfe4SRoman Gushchin snprintf(pidbuf, sizeof(pidbuf), "%d", pid); 3035313bfe4SRoman Gushchin return cg_write(cgroup, "cgroup.procs", pidbuf); 3045313bfe4SRoman Gushchin } 3055313bfe4SRoman Gushchin 306d863cb03SClaudio int cg_enter_current(const char *cgroup) 307d863cb03SClaudio { 30858c9f75bSMichal Koutný return cg_write(cgroup, "cgroup.procs", "0"); 30958c9f75bSMichal Koutný } 310d863cb03SClaudio 31158c9f75bSMichal Koutný int cg_enter_current_thread(const char *cgroup) 31258c9f75bSMichal Koutný { 31358c9f75bSMichal Koutný return cg_write(cgroup, "cgroup.threads", "0"); 314d863cb03SClaudio } 315d863cb03SClaudio 31684092dbcSRoman Gushchin int cg_run(const char *cgroup, 31784092dbcSRoman Gushchin int (*fn)(const char *cgroup, void *arg), 31884092dbcSRoman Gushchin void *arg) 31984092dbcSRoman Gushchin { 32084092dbcSRoman Gushchin int pid, retcode; 32184092dbcSRoman Gushchin 32284092dbcSRoman Gushchin pid = fork(); 32384092dbcSRoman Gushchin if (pid < 0) { 32484092dbcSRoman Gushchin return pid; 32584092dbcSRoman Gushchin } else if (pid == 0) { 32684092dbcSRoman Gushchin char buf[64]; 32784092dbcSRoman Gushchin 32884092dbcSRoman Gushchin snprintf(buf, sizeof(buf), "%d", getpid()); 32984092dbcSRoman Gushchin if (cg_write(cgroup, "cgroup.procs", buf)) 33084092dbcSRoman Gushchin exit(EXIT_FAILURE); 33184092dbcSRoman Gushchin exit(fn(cgroup, arg)); 33284092dbcSRoman Gushchin } else { 33384092dbcSRoman Gushchin waitpid(pid, &retcode, 0); 33484092dbcSRoman Gushchin if (WIFEXITED(retcode)) 33584092dbcSRoman Gushchin return WEXITSTATUS(retcode); 33684092dbcSRoman Gushchin else 33784092dbcSRoman Gushchin return -1; 33884092dbcSRoman Gushchin } 33984092dbcSRoman Gushchin } 34084092dbcSRoman Gushchin 3419bd5910dSChristian Brauner pid_t clone_into_cgroup(int cgroup_fd) 3429bd5910dSChristian Brauner { 3439bd5910dSChristian Brauner #ifdef CLONE_ARGS_SIZE_VER2 3449bd5910dSChristian Brauner pid_t pid; 3459bd5910dSChristian Brauner 346c2e46f6bSSachin Sant struct __clone_args args = { 3479bd5910dSChristian Brauner .flags = CLONE_INTO_CGROUP, 3489bd5910dSChristian Brauner .exit_signal = SIGCHLD, 3499bd5910dSChristian Brauner .cgroup = cgroup_fd, 3509bd5910dSChristian Brauner }; 3519bd5910dSChristian Brauner 352c2e46f6bSSachin Sant pid = sys_clone3(&args, sizeof(struct __clone_args)); 3539bd5910dSChristian Brauner /* 3549bd5910dSChristian Brauner * Verify that this is a genuine test failure: 3559bd5910dSChristian Brauner * ENOSYS -> clone3() not available 3569bd5910dSChristian Brauner * E2BIG -> CLONE_INTO_CGROUP not available 3579bd5910dSChristian Brauner */ 3589bd5910dSChristian Brauner if (pid < 0 && (errno == ENOSYS || errno == E2BIG)) 3599bd5910dSChristian Brauner goto pretend_enosys; 3609bd5910dSChristian Brauner 3619bd5910dSChristian Brauner return pid; 3629bd5910dSChristian Brauner 3639bd5910dSChristian Brauner pretend_enosys: 3649bd5910dSChristian Brauner #endif 3659bd5910dSChristian Brauner errno = ENOSYS; 3669bd5910dSChristian Brauner return -ENOSYS; 3679bd5910dSChristian Brauner } 3689bd5910dSChristian Brauner 3699bd5910dSChristian Brauner int clone_reap(pid_t pid, int options) 3709bd5910dSChristian Brauner { 3719bd5910dSChristian Brauner int ret; 3729bd5910dSChristian Brauner siginfo_t info = { 3739bd5910dSChristian Brauner .si_signo = 0, 3749bd5910dSChristian Brauner }; 3759bd5910dSChristian Brauner 3769bd5910dSChristian Brauner again: 3779bd5910dSChristian Brauner ret = waitid(P_PID, pid, &info, options | __WALL | __WNOTHREAD); 3789bd5910dSChristian Brauner if (ret < 0) { 3799bd5910dSChristian Brauner if (errno == EINTR) 3809bd5910dSChristian Brauner goto again; 3819bd5910dSChristian Brauner return -1; 3829bd5910dSChristian Brauner } 3839bd5910dSChristian Brauner 3849bd5910dSChristian Brauner if (options & WEXITED) { 3859bd5910dSChristian Brauner if (WIFEXITED(info.si_status)) 3869bd5910dSChristian Brauner return WEXITSTATUS(info.si_status); 3879bd5910dSChristian Brauner } 3889bd5910dSChristian Brauner 3899bd5910dSChristian Brauner if (options & WSTOPPED) { 3909bd5910dSChristian Brauner if (WIFSTOPPED(info.si_status)) 3919bd5910dSChristian Brauner return WSTOPSIG(info.si_status); 3929bd5910dSChristian Brauner } 3939bd5910dSChristian Brauner 3949bd5910dSChristian Brauner if (options & WCONTINUED) { 3959bd5910dSChristian Brauner if (WIFCONTINUED(info.si_status)) 3969bd5910dSChristian Brauner return 0; 3979bd5910dSChristian Brauner } 3989bd5910dSChristian Brauner 3999bd5910dSChristian Brauner return -1; 4009bd5910dSChristian Brauner } 4019bd5910dSChristian Brauner 4029bd5910dSChristian Brauner int dirfd_open_opath(const char *dir) 4039bd5910dSChristian Brauner { 4049bd5910dSChristian Brauner return open(dir, O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW | O_PATH); 4059bd5910dSChristian Brauner } 4069bd5910dSChristian Brauner 4079bd5910dSChristian Brauner #define close_prot_errno(fd) \ 4089bd5910dSChristian Brauner if (fd >= 0) { \ 4099bd5910dSChristian Brauner int _e_ = errno; \ 4109bd5910dSChristian Brauner close(fd); \ 4119bd5910dSChristian Brauner errno = _e_; \ 4129bd5910dSChristian Brauner } 4139bd5910dSChristian Brauner 4149bd5910dSChristian Brauner static int clone_into_cgroup_run_nowait(const char *cgroup, 4159bd5910dSChristian Brauner int (*fn)(const char *cgroup, void *arg), 4169bd5910dSChristian Brauner void *arg) 4179bd5910dSChristian Brauner { 4189bd5910dSChristian Brauner int cgroup_fd; 4199bd5910dSChristian Brauner pid_t pid; 4209bd5910dSChristian Brauner 4219bd5910dSChristian Brauner cgroup_fd = dirfd_open_opath(cgroup); 4229bd5910dSChristian Brauner if (cgroup_fd < 0) 4239bd5910dSChristian Brauner return -1; 4249bd5910dSChristian Brauner 4259bd5910dSChristian Brauner pid = clone_into_cgroup(cgroup_fd); 4269bd5910dSChristian Brauner close_prot_errno(cgroup_fd); 4279bd5910dSChristian Brauner if (pid == 0) 4289bd5910dSChristian Brauner exit(fn(cgroup, arg)); 4299bd5910dSChristian Brauner 4309bd5910dSChristian Brauner return pid; 4319bd5910dSChristian Brauner } 4329bd5910dSChristian Brauner 43384092dbcSRoman Gushchin int cg_run_nowait(const char *cgroup, 43484092dbcSRoman Gushchin int (*fn)(const char *cgroup, void *arg), 43584092dbcSRoman Gushchin void *arg) 43684092dbcSRoman Gushchin { 43784092dbcSRoman Gushchin int pid; 43884092dbcSRoman Gushchin 4399bd5910dSChristian Brauner pid = clone_into_cgroup_run_nowait(cgroup, fn, arg); 4409bd5910dSChristian Brauner if (pid > 0) 4419bd5910dSChristian Brauner return pid; 4429bd5910dSChristian Brauner 4439bd5910dSChristian Brauner /* Genuine test failure. */ 4449bd5910dSChristian Brauner if (pid < 0 && errno != ENOSYS) 4459bd5910dSChristian Brauner return -1; 4469bd5910dSChristian Brauner 44784092dbcSRoman Gushchin pid = fork(); 44884092dbcSRoman Gushchin if (pid == 0) { 44984092dbcSRoman Gushchin char buf[64]; 45084092dbcSRoman Gushchin 45184092dbcSRoman Gushchin snprintf(buf, sizeof(buf), "%d", getpid()); 45284092dbcSRoman Gushchin if (cg_write(cgroup, "cgroup.procs", buf)) 45384092dbcSRoman Gushchin exit(EXIT_FAILURE); 45484092dbcSRoman Gushchin exit(fn(cgroup, arg)); 45584092dbcSRoman Gushchin } 45684092dbcSRoman Gushchin 45784092dbcSRoman Gushchin return pid; 45884092dbcSRoman Gushchin } 45984092dbcSRoman Gushchin 46084092dbcSRoman Gushchin int get_temp_fd(void) 46184092dbcSRoman Gushchin { 46284092dbcSRoman Gushchin return open(".", O_TMPFILE | O_RDWR | O_EXCL); 46384092dbcSRoman Gushchin } 46484092dbcSRoman Gushchin 46584092dbcSRoman Gushchin int alloc_pagecache(int fd, size_t size) 46684092dbcSRoman Gushchin { 46784092dbcSRoman Gushchin char buf[PAGE_SIZE]; 46884092dbcSRoman Gushchin struct stat st; 46984092dbcSRoman Gushchin int i; 47084092dbcSRoman Gushchin 47184092dbcSRoman Gushchin if (fstat(fd, &st)) 47284092dbcSRoman Gushchin goto cleanup; 47384092dbcSRoman Gushchin 47484092dbcSRoman Gushchin size += st.st_size; 47584092dbcSRoman Gushchin 47684092dbcSRoman Gushchin if (ftruncate(fd, size)) 47784092dbcSRoman Gushchin goto cleanup; 47884092dbcSRoman Gushchin 47984092dbcSRoman Gushchin for (i = 0; i < size; i += sizeof(buf)) 48084092dbcSRoman Gushchin read(fd, buf, sizeof(buf)); 48184092dbcSRoman Gushchin 48284092dbcSRoman Gushchin return 0; 48384092dbcSRoman Gushchin 48484092dbcSRoman Gushchin cleanup: 48584092dbcSRoman Gushchin return -1; 48684092dbcSRoman Gushchin } 48784092dbcSRoman Gushchin 48884092dbcSRoman Gushchin int alloc_anon(const char *cgroup, void *arg) 48984092dbcSRoman Gushchin { 49084092dbcSRoman Gushchin size_t size = (unsigned long)arg; 49184092dbcSRoman Gushchin char *buf, *ptr; 49284092dbcSRoman Gushchin 49384092dbcSRoman Gushchin buf = malloc(size); 49484092dbcSRoman Gushchin for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE) 49584092dbcSRoman Gushchin *ptr = 0; 49684092dbcSRoman Gushchin 49784092dbcSRoman Gushchin free(buf); 49884092dbcSRoman Gushchin return 0; 49984092dbcSRoman Gushchin } 500478b2784SMike Rapoport 501478b2784SMike Rapoport int is_swap_enabled(void) 502478b2784SMike Rapoport { 503478b2784SMike Rapoport char buf[PAGE_SIZE]; 504478b2784SMike Rapoport const char delim[] = "\n"; 505478b2784SMike Rapoport int cnt = 0; 506478b2784SMike Rapoport char *line; 507478b2784SMike Rapoport 508478b2784SMike Rapoport if (read_text("/proc/swaps", buf, sizeof(buf)) <= 0) 509478b2784SMike Rapoport return -1; 510478b2784SMike Rapoport 511478b2784SMike Rapoport for (line = strtok(buf, delim); line; line = strtok(NULL, delim)) 512478b2784SMike Rapoport cnt++; 513478b2784SMike Rapoport 514478b2784SMike Rapoport return cnt > 1; 515478b2784SMike Rapoport } 516a987785dSJay Kamat 517a987785dSJay Kamat int set_oom_adj_score(int pid, int score) 518a987785dSJay Kamat { 519a987785dSJay Kamat char path[PATH_MAX]; 520a987785dSJay Kamat int fd, len; 521a987785dSJay Kamat 522a987785dSJay Kamat sprintf(path, "/proc/%d/oom_score_adj", pid); 523a987785dSJay Kamat 524a987785dSJay Kamat fd = open(path, O_WRONLY | O_APPEND); 525a987785dSJay Kamat if (fd < 0) 526a987785dSJay Kamat return fd; 527a987785dSJay Kamat 528a987785dSJay Kamat len = dprintf(fd, "%d", score); 529a987785dSJay Kamat if (len < 0) { 530a987785dSJay Kamat close(fd); 531a987785dSJay Kamat return len; 532a987785dSJay Kamat } 533a987785dSJay Kamat 534a987785dSJay Kamat close(fd); 535a987785dSJay Kamat return 0; 536a987785dSJay Kamat } 5375313bfe4SRoman Gushchin 53858c9f75bSMichal Koutný ssize_t proc_read_text(int pid, bool thread, const char *item, char *buf, size_t size) 5395313bfe4SRoman Gushchin { 5405313bfe4SRoman Gushchin char path[PATH_MAX]; 5415313bfe4SRoman Gushchin 54258c9f75bSMichal Koutný if (!pid) 54358c9f75bSMichal Koutný snprintf(path, sizeof(path), "/proc/%s/%s", 54458c9f75bSMichal Koutný thread ? "thread-self" : "self", item); 54558c9f75bSMichal Koutný else 5465313bfe4SRoman Gushchin snprintf(path, sizeof(path), "/proc/%d/%s", pid, item); 5475313bfe4SRoman Gushchin 5485313bfe4SRoman Gushchin return read_text(path, buf, size); 5495313bfe4SRoman Gushchin } 55011318989SMichal Koutný 55111318989SMichal Koutný int proc_read_strstr(int pid, bool thread, const char *item, const char *needle) 55211318989SMichal Koutný { 55311318989SMichal Koutný char buf[PAGE_SIZE]; 55411318989SMichal Koutný 55511318989SMichal Koutný if (proc_read_text(pid, thread, item, buf, sizeof(buf)) < 0) 55611318989SMichal Koutný return -1; 55711318989SMichal Koutný 55811318989SMichal Koutný return strstr(buf, needle) ? 0 : -1; 55911318989SMichal Koutný } 5609bd5910dSChristian Brauner 5619bd5910dSChristian Brauner int clone_into_cgroup_run_wait(const char *cgroup) 5629bd5910dSChristian Brauner { 5639bd5910dSChristian Brauner int cgroup_fd; 5649bd5910dSChristian Brauner pid_t pid; 5659bd5910dSChristian Brauner 5669bd5910dSChristian Brauner cgroup_fd = dirfd_open_opath(cgroup); 5679bd5910dSChristian Brauner if (cgroup_fd < 0) 5689bd5910dSChristian Brauner return -1; 5699bd5910dSChristian Brauner 5709bd5910dSChristian Brauner pid = clone_into_cgroup(cgroup_fd); 5719bd5910dSChristian Brauner close_prot_errno(cgroup_fd); 5729bd5910dSChristian Brauner if (pid < 0) 5739bd5910dSChristian Brauner return -1; 5749bd5910dSChristian Brauner 5759bd5910dSChristian Brauner if (pid == 0) 5769bd5910dSChristian Brauner exit(EXIT_SUCCESS); 5779bd5910dSChristian Brauner 5789bd5910dSChristian Brauner /* 5799bd5910dSChristian Brauner * We don't care whether this fails. We only care whether the initial 5809bd5910dSChristian Brauner * clone succeeded. 5819bd5910dSChristian Brauner */ 5829bd5910dSChristian Brauner (void)clone_reap(pid, WEXITED); 5839bd5910dSChristian Brauner return 0; 5849bd5910dSChristian Brauner } 5858075e4f6SChristian Brauner 586*6323ec54SShakeel Butt static int __prepare_for_wait(const char *cgroup, const char *filename) 5878075e4f6SChristian Brauner { 5888075e4f6SChristian Brauner int fd, ret = -1; 5898075e4f6SChristian Brauner 5908075e4f6SChristian Brauner fd = inotify_init1(0); 5918075e4f6SChristian Brauner if (fd == -1) 5928075e4f6SChristian Brauner return fd; 5938075e4f6SChristian Brauner 594*6323ec54SShakeel Butt ret = inotify_add_watch(fd, cg_control(cgroup, filename), IN_MODIFY); 5958075e4f6SChristian Brauner if (ret == -1) { 5968075e4f6SChristian Brauner close(fd); 5978075e4f6SChristian Brauner fd = -1; 5988075e4f6SChristian Brauner } 5998075e4f6SChristian Brauner 6008075e4f6SChristian Brauner return fd; 6018075e4f6SChristian Brauner } 6028075e4f6SChristian Brauner 603*6323ec54SShakeel Butt int cg_prepare_for_wait(const char *cgroup) 604*6323ec54SShakeel Butt { 605*6323ec54SShakeel Butt return __prepare_for_wait(cgroup, "cgroup.events"); 606*6323ec54SShakeel Butt } 607*6323ec54SShakeel Butt 608*6323ec54SShakeel Butt int memcg_prepare_for_wait(const char *cgroup) 609*6323ec54SShakeel Butt { 610*6323ec54SShakeel Butt return __prepare_for_wait(cgroup, "memory.events"); 611*6323ec54SShakeel Butt } 612*6323ec54SShakeel Butt 6138075e4f6SChristian Brauner int cg_wait_for(int fd) 6148075e4f6SChristian Brauner { 6158075e4f6SChristian Brauner int ret = -1; 6168075e4f6SChristian Brauner struct pollfd fds = { 6178075e4f6SChristian Brauner .fd = fd, 6188075e4f6SChristian Brauner .events = POLLIN, 6198075e4f6SChristian Brauner }; 6208075e4f6SChristian Brauner 6218075e4f6SChristian Brauner while (true) { 6228075e4f6SChristian Brauner ret = poll(&fds, 1, 10000); 6238075e4f6SChristian Brauner 6248075e4f6SChristian Brauner if (ret == -1) { 6258075e4f6SChristian Brauner if (errno == EINTR) 6268075e4f6SChristian Brauner continue; 6278075e4f6SChristian Brauner 6288075e4f6SChristian Brauner break; 6298075e4f6SChristian Brauner } 6308075e4f6SChristian Brauner 6318075e4f6SChristian Brauner if (ret > 0 && fds.revents & POLLIN) { 6328075e4f6SChristian Brauner ret = 0; 6338075e4f6SChristian Brauner break; 6348075e4f6SChristian Brauner } 6358075e4f6SChristian Brauner } 6368075e4f6SChristian Brauner 6378075e4f6SChristian Brauner return ret; 6388075e4f6SChristian Brauner } 639