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> 884092dbcSRoman Gushchin #include <signal.h> 984092dbcSRoman Gushchin #include <stdio.h> 1084092dbcSRoman Gushchin #include <stdlib.h> 1184092dbcSRoman Gushchin #include <string.h> 1284092dbcSRoman Gushchin #include <sys/stat.h> 1384092dbcSRoman Gushchin #include <sys/types.h> 1484092dbcSRoman Gushchin #include <sys/wait.h> 1584092dbcSRoman Gushchin #include <unistd.h> 1684092dbcSRoman Gushchin 1784092dbcSRoman Gushchin #include "cgroup_util.h" 189bd5910dSChristian Brauner #include "../clone3/clone3_selftests.h" 1984092dbcSRoman Gushchin 2084092dbcSRoman Gushchin static ssize_t read_text(const char *path, char *buf, size_t max_len) 2184092dbcSRoman Gushchin { 2284092dbcSRoman Gushchin ssize_t len; 2384092dbcSRoman Gushchin int fd; 2484092dbcSRoman Gushchin 2584092dbcSRoman Gushchin fd = open(path, O_RDONLY); 2684092dbcSRoman Gushchin if (fd < 0) 2784092dbcSRoman Gushchin return fd; 2884092dbcSRoman Gushchin 2984092dbcSRoman Gushchin len = read(fd, buf, max_len - 1); 3084092dbcSRoman Gushchin if (len < 0) 3184092dbcSRoman Gushchin goto out; 3284092dbcSRoman Gushchin 3384092dbcSRoman Gushchin buf[len] = 0; 3484092dbcSRoman Gushchin out: 3584092dbcSRoman Gushchin close(fd); 3684092dbcSRoman Gushchin return len; 3784092dbcSRoman Gushchin } 3884092dbcSRoman Gushchin 3953c3daf8SDan Carpenter static ssize_t write_text(const char *path, char *buf, ssize_t len) 4084092dbcSRoman Gushchin { 4184092dbcSRoman Gushchin int fd; 4284092dbcSRoman Gushchin 4384092dbcSRoman Gushchin fd = open(path, O_WRONLY | O_APPEND); 4484092dbcSRoman Gushchin if (fd < 0) 4584092dbcSRoman Gushchin return fd; 4684092dbcSRoman Gushchin 4784092dbcSRoman Gushchin len = write(fd, buf, len); 4884092dbcSRoman Gushchin if (len < 0) { 4984092dbcSRoman Gushchin close(fd); 5084092dbcSRoman Gushchin return len; 5184092dbcSRoman Gushchin } 5284092dbcSRoman Gushchin 5384092dbcSRoman Gushchin close(fd); 5484092dbcSRoman Gushchin 5584092dbcSRoman Gushchin return len; 5684092dbcSRoman Gushchin } 5784092dbcSRoman Gushchin 5884092dbcSRoman Gushchin char *cg_name(const char *root, const char *name) 5984092dbcSRoman Gushchin { 6084092dbcSRoman Gushchin size_t len = strlen(root) + strlen(name) + 2; 6184092dbcSRoman Gushchin char *ret = malloc(len); 6284092dbcSRoman Gushchin 6384092dbcSRoman Gushchin snprintf(ret, len, "%s/%s", root, name); 6484092dbcSRoman Gushchin 6584092dbcSRoman Gushchin return ret; 6684092dbcSRoman Gushchin } 6784092dbcSRoman Gushchin 6884092dbcSRoman Gushchin char *cg_name_indexed(const char *root, const char *name, int index) 6984092dbcSRoman Gushchin { 7084092dbcSRoman Gushchin size_t len = strlen(root) + strlen(name) + 10; 7184092dbcSRoman Gushchin char *ret = malloc(len); 7284092dbcSRoman Gushchin 7384092dbcSRoman Gushchin snprintf(ret, len, "%s/%s_%d", root, name, index); 7484092dbcSRoman Gushchin 7584092dbcSRoman Gushchin return ret; 7684092dbcSRoman Gushchin } 7784092dbcSRoman Gushchin 785313bfe4SRoman Gushchin char *cg_control(const char *cgroup, const char *control) 795313bfe4SRoman Gushchin { 805313bfe4SRoman Gushchin size_t len = strlen(cgroup) + strlen(control) + 2; 815313bfe4SRoman Gushchin char *ret = malloc(len); 825313bfe4SRoman Gushchin 835313bfe4SRoman Gushchin snprintf(ret, len, "%s/%s", cgroup, control); 845313bfe4SRoman Gushchin 855313bfe4SRoman Gushchin return ret; 865313bfe4SRoman Gushchin } 875313bfe4SRoman Gushchin 8884092dbcSRoman Gushchin int cg_read(const char *cgroup, const char *control, char *buf, size_t len) 8984092dbcSRoman Gushchin { 9084092dbcSRoman Gushchin char path[PATH_MAX]; 9184092dbcSRoman Gushchin 9284092dbcSRoman Gushchin snprintf(path, sizeof(path), "%s/%s", cgroup, control); 9384092dbcSRoman Gushchin 9484092dbcSRoman Gushchin if (read_text(path, buf, len) >= 0) 9584092dbcSRoman Gushchin return 0; 9684092dbcSRoman Gushchin 9784092dbcSRoman Gushchin return -1; 9884092dbcSRoman Gushchin } 9984092dbcSRoman Gushchin 10084092dbcSRoman Gushchin int cg_read_strcmp(const char *cgroup, const char *control, 10184092dbcSRoman Gushchin const char *expected) 10284092dbcSRoman Gushchin { 10348c2bb0bSJay Kamat size_t size; 10484092dbcSRoman Gushchin char *buf; 10548c2bb0bSJay Kamat int ret; 10648c2bb0bSJay Kamat 10748c2bb0bSJay Kamat /* Handle the case of comparing against empty string */ 10848c2bb0bSJay Kamat if (!expected) 10948c2bb0bSJay Kamat size = 32; 11048c2bb0bSJay Kamat else 11148c2bb0bSJay Kamat size = strlen(expected) + 1; 11284092dbcSRoman Gushchin 11384092dbcSRoman Gushchin buf = malloc(size); 11484092dbcSRoman Gushchin if (!buf) 11584092dbcSRoman Gushchin return -1; 11684092dbcSRoman Gushchin 11748c2bb0bSJay Kamat if (cg_read(cgroup, control, buf, size)) { 11848c2bb0bSJay Kamat free(buf); 11984092dbcSRoman Gushchin return -1; 12048c2bb0bSJay Kamat } 12184092dbcSRoman Gushchin 12248c2bb0bSJay Kamat ret = strcmp(expected, buf); 12348c2bb0bSJay Kamat free(buf); 12448c2bb0bSJay Kamat return ret; 12584092dbcSRoman Gushchin } 12684092dbcSRoman Gushchin 12784092dbcSRoman Gushchin int cg_read_strstr(const char *cgroup, const char *control, const char *needle) 12884092dbcSRoman Gushchin { 12984092dbcSRoman Gushchin char buf[PAGE_SIZE]; 13084092dbcSRoman Gushchin 13184092dbcSRoman Gushchin if (cg_read(cgroup, control, buf, sizeof(buf))) 13284092dbcSRoman Gushchin return -1; 13384092dbcSRoman Gushchin 13484092dbcSRoman Gushchin return strstr(buf, needle) ? 0 : -1; 13584092dbcSRoman Gushchin } 13684092dbcSRoman Gushchin 13784092dbcSRoman Gushchin long cg_read_long(const char *cgroup, const char *control) 13884092dbcSRoman Gushchin { 13984092dbcSRoman Gushchin char buf[128]; 14084092dbcSRoman Gushchin 14184092dbcSRoman Gushchin if (cg_read(cgroup, control, buf, sizeof(buf))) 14284092dbcSRoman Gushchin return -1; 14384092dbcSRoman Gushchin 14484092dbcSRoman Gushchin return atol(buf); 14584092dbcSRoman Gushchin } 14684092dbcSRoman Gushchin 14784092dbcSRoman Gushchin long cg_read_key_long(const char *cgroup, const char *control, const char *key) 14884092dbcSRoman Gushchin { 14984092dbcSRoman Gushchin char buf[PAGE_SIZE]; 15084092dbcSRoman Gushchin char *ptr; 15184092dbcSRoman Gushchin 15284092dbcSRoman Gushchin if (cg_read(cgroup, control, buf, sizeof(buf))) 15384092dbcSRoman Gushchin return -1; 15484092dbcSRoman Gushchin 15584092dbcSRoman Gushchin ptr = strstr(buf, key); 15684092dbcSRoman Gushchin if (!ptr) 15784092dbcSRoman Gushchin return -1; 15884092dbcSRoman Gushchin 15984092dbcSRoman Gushchin return atol(ptr + strlen(key)); 16084092dbcSRoman Gushchin } 16184092dbcSRoman Gushchin 16211318989SMichal Koutný long cg_read_lc(const char *cgroup, const char *control) 16311318989SMichal Koutný { 16411318989SMichal Koutný char buf[PAGE_SIZE]; 16511318989SMichal Koutný const char delim[] = "\n"; 16611318989SMichal Koutný char *line; 16711318989SMichal Koutný long cnt = 0; 16811318989SMichal Koutný 16911318989SMichal Koutný if (cg_read(cgroup, control, buf, sizeof(buf))) 17011318989SMichal Koutný return -1; 17111318989SMichal Koutný 17211318989SMichal Koutný for (line = strtok(buf, delim); line; line = strtok(NULL, delim)) 17311318989SMichal Koutný cnt++; 17411318989SMichal Koutný 17511318989SMichal Koutný return cnt; 17611318989SMichal Koutný } 17711318989SMichal Koutný 17884092dbcSRoman Gushchin int cg_write(const char *cgroup, const char *control, char *buf) 17984092dbcSRoman Gushchin { 18084092dbcSRoman Gushchin char path[PATH_MAX]; 18153c3daf8SDan Carpenter ssize_t len = strlen(buf); 18284092dbcSRoman Gushchin 18384092dbcSRoman Gushchin snprintf(path, sizeof(path), "%s/%s", cgroup, control); 18484092dbcSRoman Gushchin 18584092dbcSRoman Gushchin if (write_text(path, buf, len) == len) 18684092dbcSRoman Gushchin return 0; 18784092dbcSRoman Gushchin 18884092dbcSRoman Gushchin return -1; 18984092dbcSRoman Gushchin } 19084092dbcSRoman Gushchin 19184092dbcSRoman Gushchin int cg_find_unified_root(char *root, size_t len) 19284092dbcSRoman Gushchin { 19384092dbcSRoman Gushchin char buf[10 * PAGE_SIZE]; 19484092dbcSRoman Gushchin char *fs, *mount, *type; 19584092dbcSRoman Gushchin const char delim[] = "\n\t "; 19684092dbcSRoman Gushchin 19784092dbcSRoman Gushchin if (read_text("/proc/self/mounts", buf, sizeof(buf)) <= 0) 19884092dbcSRoman Gushchin return -1; 19984092dbcSRoman Gushchin 20084092dbcSRoman Gushchin /* 20184092dbcSRoman Gushchin * Example: 20284092dbcSRoman Gushchin * cgroup /sys/fs/cgroup cgroup2 rw,seclabel,noexec,relatime 0 0 20384092dbcSRoman Gushchin */ 20484092dbcSRoman Gushchin for (fs = strtok(buf, delim); fs; fs = strtok(NULL, delim)) { 20584092dbcSRoman Gushchin mount = strtok(NULL, delim); 20684092dbcSRoman Gushchin type = strtok(NULL, delim); 20784092dbcSRoman Gushchin strtok(NULL, delim); 20884092dbcSRoman Gushchin strtok(NULL, delim); 20984092dbcSRoman Gushchin strtok(NULL, delim); 21084092dbcSRoman Gushchin 211b59b1baaSChris Down if (strcmp(type, "cgroup2") == 0) { 21284092dbcSRoman Gushchin strncpy(root, mount, len); 21384092dbcSRoman Gushchin return 0; 21484092dbcSRoman Gushchin } 21584092dbcSRoman Gushchin } 21684092dbcSRoman Gushchin 21784092dbcSRoman Gushchin return -1; 21884092dbcSRoman Gushchin } 21984092dbcSRoman Gushchin 22084092dbcSRoman Gushchin int cg_create(const char *cgroup) 22184092dbcSRoman Gushchin { 22284092dbcSRoman Gushchin return mkdir(cgroup, 0644); 22384092dbcSRoman Gushchin } 22484092dbcSRoman Gushchin 2255313bfe4SRoman Gushchin int cg_wait_for_proc_count(const char *cgroup, int count) 2265313bfe4SRoman Gushchin { 2275313bfe4SRoman Gushchin char buf[10 * PAGE_SIZE] = {0}; 2285313bfe4SRoman Gushchin int attempts; 2295313bfe4SRoman Gushchin char *ptr; 2305313bfe4SRoman Gushchin 2315313bfe4SRoman Gushchin for (attempts = 10; attempts >= 0; attempts--) { 2325313bfe4SRoman Gushchin int nr = 0; 2335313bfe4SRoman Gushchin 2345313bfe4SRoman Gushchin if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf))) 2355313bfe4SRoman Gushchin break; 2365313bfe4SRoman Gushchin 2375313bfe4SRoman Gushchin for (ptr = buf; *ptr; ptr++) 2385313bfe4SRoman Gushchin if (*ptr == '\n') 2395313bfe4SRoman Gushchin nr++; 2405313bfe4SRoman Gushchin 2415313bfe4SRoman Gushchin if (nr >= count) 2425313bfe4SRoman Gushchin return 0; 2435313bfe4SRoman Gushchin 2445313bfe4SRoman Gushchin usleep(100000); 2455313bfe4SRoman Gushchin } 2465313bfe4SRoman Gushchin 2475313bfe4SRoman Gushchin return -1; 2485313bfe4SRoman Gushchin } 2495313bfe4SRoman Gushchin 2505313bfe4SRoman Gushchin int cg_killall(const char *cgroup) 25184092dbcSRoman Gushchin { 25284092dbcSRoman Gushchin char buf[PAGE_SIZE]; 25384092dbcSRoman Gushchin char *ptr = buf; 25484092dbcSRoman Gushchin 25584092dbcSRoman Gushchin if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf))) 25684092dbcSRoman Gushchin return -1; 25784092dbcSRoman Gushchin 25884092dbcSRoman Gushchin while (ptr < buf + sizeof(buf)) { 25984092dbcSRoman Gushchin int pid = strtol(ptr, &ptr, 10); 26084092dbcSRoman Gushchin 26184092dbcSRoman Gushchin if (pid == 0) 26284092dbcSRoman Gushchin break; 26384092dbcSRoman Gushchin if (*ptr) 26484092dbcSRoman Gushchin ptr++; 26584092dbcSRoman Gushchin else 26684092dbcSRoman Gushchin break; 26784092dbcSRoman Gushchin if (kill(pid, SIGKILL)) 26884092dbcSRoman Gushchin return -1; 26984092dbcSRoman Gushchin } 27084092dbcSRoman Gushchin 27184092dbcSRoman Gushchin return 0; 27284092dbcSRoman Gushchin } 27384092dbcSRoman Gushchin 27484092dbcSRoman Gushchin int cg_destroy(const char *cgroup) 27584092dbcSRoman Gushchin { 27684092dbcSRoman Gushchin int ret; 27784092dbcSRoman Gushchin 27884092dbcSRoman Gushchin retry: 27984092dbcSRoman Gushchin ret = rmdir(cgroup); 28084092dbcSRoman Gushchin if (ret && errno == EBUSY) { 281ff9fb7cbSRoman Gushchin cg_killall(cgroup); 28284092dbcSRoman Gushchin usleep(100); 28384092dbcSRoman Gushchin goto retry; 28484092dbcSRoman Gushchin } 28584092dbcSRoman Gushchin 28684092dbcSRoman Gushchin if (ret && errno == ENOENT) 28784092dbcSRoman Gushchin ret = 0; 28884092dbcSRoman Gushchin 28984092dbcSRoman Gushchin return ret; 29084092dbcSRoman Gushchin } 29184092dbcSRoman Gushchin 2925313bfe4SRoman Gushchin int cg_enter(const char *cgroup, int pid) 2935313bfe4SRoman Gushchin { 2945313bfe4SRoman Gushchin char pidbuf[64]; 2955313bfe4SRoman Gushchin 2965313bfe4SRoman Gushchin snprintf(pidbuf, sizeof(pidbuf), "%d", pid); 2975313bfe4SRoman Gushchin return cg_write(cgroup, "cgroup.procs", pidbuf); 2985313bfe4SRoman Gushchin } 2995313bfe4SRoman Gushchin 300d863cb03SClaudio int cg_enter_current(const char *cgroup) 301d863cb03SClaudio { 30258c9f75bSMichal Koutný return cg_write(cgroup, "cgroup.procs", "0"); 30358c9f75bSMichal Koutný } 304d863cb03SClaudio 30558c9f75bSMichal Koutný int cg_enter_current_thread(const char *cgroup) 30658c9f75bSMichal Koutný { 30758c9f75bSMichal Koutný return cg_write(cgroup, "cgroup.threads", "0"); 308d863cb03SClaudio } 309d863cb03SClaudio 31084092dbcSRoman Gushchin int cg_run(const char *cgroup, 31184092dbcSRoman Gushchin int (*fn)(const char *cgroup, void *arg), 31284092dbcSRoman Gushchin void *arg) 31384092dbcSRoman Gushchin { 31484092dbcSRoman Gushchin int pid, retcode; 31584092dbcSRoman Gushchin 31684092dbcSRoman Gushchin pid = fork(); 31784092dbcSRoman Gushchin if (pid < 0) { 31884092dbcSRoman Gushchin return pid; 31984092dbcSRoman Gushchin } else if (pid == 0) { 32084092dbcSRoman Gushchin char buf[64]; 32184092dbcSRoman Gushchin 32284092dbcSRoman Gushchin snprintf(buf, sizeof(buf), "%d", getpid()); 32384092dbcSRoman Gushchin if (cg_write(cgroup, "cgroup.procs", buf)) 32484092dbcSRoman Gushchin exit(EXIT_FAILURE); 32584092dbcSRoman Gushchin exit(fn(cgroup, arg)); 32684092dbcSRoman Gushchin } else { 32784092dbcSRoman Gushchin waitpid(pid, &retcode, 0); 32884092dbcSRoman Gushchin if (WIFEXITED(retcode)) 32984092dbcSRoman Gushchin return WEXITSTATUS(retcode); 33084092dbcSRoman Gushchin else 33184092dbcSRoman Gushchin return -1; 33284092dbcSRoman Gushchin } 33384092dbcSRoman Gushchin } 33484092dbcSRoman Gushchin 3359bd5910dSChristian Brauner pid_t clone_into_cgroup(int cgroup_fd) 3369bd5910dSChristian Brauner { 3379bd5910dSChristian Brauner #ifdef CLONE_ARGS_SIZE_VER2 3389bd5910dSChristian Brauner pid_t pid; 3399bd5910dSChristian Brauner 3409bd5910dSChristian Brauner struct clone_args args = { 3419bd5910dSChristian Brauner .flags = CLONE_INTO_CGROUP, 3429bd5910dSChristian Brauner .exit_signal = SIGCHLD, 3439bd5910dSChristian Brauner .cgroup = cgroup_fd, 3449bd5910dSChristian Brauner }; 3459bd5910dSChristian Brauner 3469bd5910dSChristian Brauner pid = sys_clone3(&args, sizeof(struct clone_args)); 3479bd5910dSChristian Brauner /* 3489bd5910dSChristian Brauner * Verify that this is a genuine test failure: 3499bd5910dSChristian Brauner * ENOSYS -> clone3() not available 3509bd5910dSChristian Brauner * E2BIG -> CLONE_INTO_CGROUP not available 3519bd5910dSChristian Brauner */ 3529bd5910dSChristian Brauner if (pid < 0 && (errno == ENOSYS || errno == E2BIG)) 3539bd5910dSChristian Brauner goto pretend_enosys; 3549bd5910dSChristian Brauner 3559bd5910dSChristian Brauner return pid; 3569bd5910dSChristian Brauner 3579bd5910dSChristian Brauner pretend_enosys: 3589bd5910dSChristian Brauner #endif 3599bd5910dSChristian Brauner errno = ENOSYS; 3609bd5910dSChristian Brauner return -ENOSYS; 3619bd5910dSChristian Brauner } 3629bd5910dSChristian Brauner 3639bd5910dSChristian Brauner int clone_reap(pid_t pid, int options) 3649bd5910dSChristian Brauner { 3659bd5910dSChristian Brauner int ret; 3669bd5910dSChristian Brauner siginfo_t info = { 3679bd5910dSChristian Brauner .si_signo = 0, 3689bd5910dSChristian Brauner }; 3699bd5910dSChristian Brauner 3709bd5910dSChristian Brauner again: 3719bd5910dSChristian Brauner ret = waitid(P_PID, pid, &info, options | __WALL | __WNOTHREAD); 3729bd5910dSChristian Brauner if (ret < 0) { 3739bd5910dSChristian Brauner if (errno == EINTR) 3749bd5910dSChristian Brauner goto again; 3759bd5910dSChristian Brauner return -1; 3769bd5910dSChristian Brauner } 3779bd5910dSChristian Brauner 3789bd5910dSChristian Brauner if (options & WEXITED) { 3799bd5910dSChristian Brauner if (WIFEXITED(info.si_status)) 3809bd5910dSChristian Brauner return WEXITSTATUS(info.si_status); 3819bd5910dSChristian Brauner } 3829bd5910dSChristian Brauner 3839bd5910dSChristian Brauner if (options & WSTOPPED) { 3849bd5910dSChristian Brauner if (WIFSTOPPED(info.si_status)) 3859bd5910dSChristian Brauner return WSTOPSIG(info.si_status); 3869bd5910dSChristian Brauner } 3879bd5910dSChristian Brauner 3889bd5910dSChristian Brauner if (options & WCONTINUED) { 3899bd5910dSChristian Brauner if (WIFCONTINUED(info.si_status)) 3909bd5910dSChristian Brauner return 0; 3919bd5910dSChristian Brauner } 3929bd5910dSChristian Brauner 3939bd5910dSChristian Brauner return -1; 3949bd5910dSChristian Brauner } 3959bd5910dSChristian Brauner 3969bd5910dSChristian Brauner int dirfd_open_opath(const char *dir) 3979bd5910dSChristian Brauner { 3989bd5910dSChristian Brauner return open(dir, O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW | O_PATH); 3999bd5910dSChristian Brauner } 4009bd5910dSChristian Brauner 4019bd5910dSChristian Brauner #define close_prot_errno(fd) \ 4029bd5910dSChristian Brauner if (fd >= 0) { \ 4039bd5910dSChristian Brauner int _e_ = errno; \ 4049bd5910dSChristian Brauner close(fd); \ 4059bd5910dSChristian Brauner errno = _e_; \ 4069bd5910dSChristian Brauner } 4079bd5910dSChristian Brauner 4089bd5910dSChristian Brauner static int clone_into_cgroup_run_nowait(const char *cgroup, 4099bd5910dSChristian Brauner int (*fn)(const char *cgroup, void *arg), 4109bd5910dSChristian Brauner void *arg) 4119bd5910dSChristian Brauner { 4129bd5910dSChristian Brauner int cgroup_fd; 4139bd5910dSChristian Brauner pid_t pid; 4149bd5910dSChristian Brauner 4159bd5910dSChristian Brauner cgroup_fd = dirfd_open_opath(cgroup); 4169bd5910dSChristian Brauner if (cgroup_fd < 0) 4179bd5910dSChristian Brauner return -1; 4189bd5910dSChristian Brauner 4199bd5910dSChristian Brauner pid = clone_into_cgroup(cgroup_fd); 4209bd5910dSChristian Brauner close_prot_errno(cgroup_fd); 4219bd5910dSChristian Brauner if (pid == 0) 4229bd5910dSChristian Brauner exit(fn(cgroup, arg)); 4239bd5910dSChristian Brauner 4249bd5910dSChristian Brauner return pid; 4259bd5910dSChristian Brauner } 4269bd5910dSChristian Brauner 42784092dbcSRoman Gushchin int cg_run_nowait(const char *cgroup, 42884092dbcSRoman Gushchin int (*fn)(const char *cgroup, void *arg), 42984092dbcSRoman Gushchin void *arg) 43084092dbcSRoman Gushchin { 43184092dbcSRoman Gushchin int pid; 43284092dbcSRoman Gushchin 4339bd5910dSChristian Brauner pid = clone_into_cgroup_run_nowait(cgroup, fn, arg); 4349bd5910dSChristian Brauner if (pid > 0) 4359bd5910dSChristian Brauner return pid; 4369bd5910dSChristian Brauner 4379bd5910dSChristian Brauner /* Genuine test failure. */ 4389bd5910dSChristian Brauner if (pid < 0 && errno != ENOSYS) 4399bd5910dSChristian Brauner return -1; 4409bd5910dSChristian Brauner 44184092dbcSRoman Gushchin pid = fork(); 44284092dbcSRoman Gushchin if (pid == 0) { 44384092dbcSRoman Gushchin char buf[64]; 44484092dbcSRoman Gushchin 44584092dbcSRoman Gushchin snprintf(buf, sizeof(buf), "%d", getpid()); 44684092dbcSRoman Gushchin if (cg_write(cgroup, "cgroup.procs", buf)) 44784092dbcSRoman Gushchin exit(EXIT_FAILURE); 44884092dbcSRoman Gushchin exit(fn(cgroup, arg)); 44984092dbcSRoman Gushchin } 45084092dbcSRoman Gushchin 45184092dbcSRoman Gushchin return pid; 45284092dbcSRoman Gushchin } 45384092dbcSRoman Gushchin 45484092dbcSRoman Gushchin int get_temp_fd(void) 45584092dbcSRoman Gushchin { 45684092dbcSRoman Gushchin return open(".", O_TMPFILE | O_RDWR | O_EXCL); 45784092dbcSRoman Gushchin } 45884092dbcSRoman Gushchin 45984092dbcSRoman Gushchin int alloc_pagecache(int fd, size_t size) 46084092dbcSRoman Gushchin { 46184092dbcSRoman Gushchin char buf[PAGE_SIZE]; 46284092dbcSRoman Gushchin struct stat st; 46384092dbcSRoman Gushchin int i; 46484092dbcSRoman Gushchin 46584092dbcSRoman Gushchin if (fstat(fd, &st)) 46684092dbcSRoman Gushchin goto cleanup; 46784092dbcSRoman Gushchin 46884092dbcSRoman Gushchin size += st.st_size; 46984092dbcSRoman Gushchin 47084092dbcSRoman Gushchin if (ftruncate(fd, size)) 47184092dbcSRoman Gushchin goto cleanup; 47284092dbcSRoman Gushchin 47384092dbcSRoman Gushchin for (i = 0; i < size; i += sizeof(buf)) 47484092dbcSRoman Gushchin read(fd, buf, sizeof(buf)); 47584092dbcSRoman Gushchin 47684092dbcSRoman Gushchin return 0; 47784092dbcSRoman Gushchin 47884092dbcSRoman Gushchin cleanup: 47984092dbcSRoman Gushchin return -1; 48084092dbcSRoman Gushchin } 48184092dbcSRoman Gushchin 48284092dbcSRoman Gushchin int alloc_anon(const char *cgroup, void *arg) 48384092dbcSRoman Gushchin { 48484092dbcSRoman Gushchin size_t size = (unsigned long)arg; 48584092dbcSRoman Gushchin char *buf, *ptr; 48684092dbcSRoman Gushchin 48784092dbcSRoman Gushchin buf = malloc(size); 48884092dbcSRoman Gushchin for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE) 48984092dbcSRoman Gushchin *ptr = 0; 49084092dbcSRoman Gushchin 49184092dbcSRoman Gushchin free(buf); 49284092dbcSRoman Gushchin return 0; 49384092dbcSRoman Gushchin } 494478b2784SMike Rapoport 495478b2784SMike Rapoport int is_swap_enabled(void) 496478b2784SMike Rapoport { 497478b2784SMike Rapoport char buf[PAGE_SIZE]; 498478b2784SMike Rapoport const char delim[] = "\n"; 499478b2784SMike Rapoport int cnt = 0; 500478b2784SMike Rapoport char *line; 501478b2784SMike Rapoport 502478b2784SMike Rapoport if (read_text("/proc/swaps", buf, sizeof(buf)) <= 0) 503478b2784SMike Rapoport return -1; 504478b2784SMike Rapoport 505478b2784SMike Rapoport for (line = strtok(buf, delim); line; line = strtok(NULL, delim)) 506478b2784SMike Rapoport cnt++; 507478b2784SMike Rapoport 508478b2784SMike Rapoport return cnt > 1; 509478b2784SMike Rapoport } 510a987785dSJay Kamat 511a987785dSJay Kamat int set_oom_adj_score(int pid, int score) 512a987785dSJay Kamat { 513a987785dSJay Kamat char path[PATH_MAX]; 514a987785dSJay Kamat int fd, len; 515a987785dSJay Kamat 516a987785dSJay Kamat sprintf(path, "/proc/%d/oom_score_adj", pid); 517a987785dSJay Kamat 518a987785dSJay Kamat fd = open(path, O_WRONLY | O_APPEND); 519a987785dSJay Kamat if (fd < 0) 520a987785dSJay Kamat return fd; 521a987785dSJay Kamat 522a987785dSJay Kamat len = dprintf(fd, "%d", score); 523a987785dSJay Kamat if (len < 0) { 524a987785dSJay Kamat close(fd); 525a987785dSJay Kamat return len; 526a987785dSJay Kamat } 527a987785dSJay Kamat 528a987785dSJay Kamat close(fd); 529a987785dSJay Kamat return 0; 530a987785dSJay Kamat } 5315313bfe4SRoman Gushchin 53258c9f75bSMichal Koutný ssize_t proc_read_text(int pid, bool thread, const char *item, char *buf, size_t size) 5335313bfe4SRoman Gushchin { 5345313bfe4SRoman Gushchin char path[PATH_MAX]; 5355313bfe4SRoman Gushchin 53658c9f75bSMichal Koutný if (!pid) 53758c9f75bSMichal Koutný snprintf(path, sizeof(path), "/proc/%s/%s", 53858c9f75bSMichal Koutný thread ? "thread-self" : "self", item); 53958c9f75bSMichal Koutný else 5405313bfe4SRoman Gushchin snprintf(path, sizeof(path), "/proc/%d/%s", pid, item); 5415313bfe4SRoman Gushchin 5425313bfe4SRoman Gushchin return read_text(path, buf, size); 5435313bfe4SRoman Gushchin } 54411318989SMichal Koutný 54511318989SMichal Koutný int proc_read_strstr(int pid, bool thread, const char *item, const char *needle) 54611318989SMichal Koutný { 54711318989SMichal Koutný char buf[PAGE_SIZE]; 54811318989SMichal Koutný 54911318989SMichal Koutný if (proc_read_text(pid, thread, item, buf, sizeof(buf)) < 0) 55011318989SMichal Koutný return -1; 55111318989SMichal Koutný 55211318989SMichal Koutný return strstr(buf, needle) ? 0 : -1; 55311318989SMichal Koutný } 5549bd5910dSChristian Brauner 5559bd5910dSChristian Brauner int clone_into_cgroup_run_wait(const char *cgroup) 5569bd5910dSChristian Brauner { 5579bd5910dSChristian Brauner int cgroup_fd; 5589bd5910dSChristian Brauner pid_t pid; 5599bd5910dSChristian Brauner 5609bd5910dSChristian Brauner cgroup_fd = dirfd_open_opath(cgroup); 5619bd5910dSChristian Brauner if (cgroup_fd < 0) 5629bd5910dSChristian Brauner return -1; 5639bd5910dSChristian Brauner 5649bd5910dSChristian Brauner pid = clone_into_cgroup(cgroup_fd); 5659bd5910dSChristian Brauner close_prot_errno(cgroup_fd); 5669bd5910dSChristian Brauner if (pid < 0) 5679bd5910dSChristian Brauner return -1; 5689bd5910dSChristian Brauner 5699bd5910dSChristian Brauner if (pid == 0) 5709bd5910dSChristian Brauner exit(EXIT_SUCCESS); 5719bd5910dSChristian Brauner 5729bd5910dSChristian Brauner /* 5739bd5910dSChristian Brauner * We don't care whether this fails. We only care whether the initial 5749bd5910dSChristian Brauner * clone succeeded. 5759bd5910dSChristian Brauner */ 5769bd5910dSChristian Brauner (void)clone_reap(pid, WEXITED); 5779bd5910dSChristian Brauner return 0; 5789bd5910dSChristian Brauner } 579