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" 1884092dbcSRoman Gushchin 1984092dbcSRoman Gushchin static ssize_t read_text(const char *path, char *buf, size_t max_len) 2084092dbcSRoman Gushchin { 2184092dbcSRoman Gushchin ssize_t len; 2284092dbcSRoman Gushchin int fd; 2384092dbcSRoman Gushchin 2484092dbcSRoman Gushchin fd = open(path, O_RDONLY); 2584092dbcSRoman Gushchin if (fd < 0) 2684092dbcSRoman Gushchin return fd; 2784092dbcSRoman Gushchin 2884092dbcSRoman Gushchin len = read(fd, buf, max_len - 1); 2984092dbcSRoman Gushchin if (len < 0) 3084092dbcSRoman Gushchin goto out; 3184092dbcSRoman Gushchin 3284092dbcSRoman Gushchin buf[len] = 0; 3384092dbcSRoman Gushchin out: 3484092dbcSRoman Gushchin close(fd); 3584092dbcSRoman Gushchin return len; 3684092dbcSRoman Gushchin } 3784092dbcSRoman Gushchin 3884092dbcSRoman Gushchin static ssize_t write_text(const char *path, char *buf, size_t len) 3984092dbcSRoman Gushchin { 4084092dbcSRoman Gushchin int fd; 4184092dbcSRoman Gushchin 4284092dbcSRoman Gushchin fd = open(path, O_WRONLY | O_APPEND); 4384092dbcSRoman Gushchin if (fd < 0) 4484092dbcSRoman Gushchin return fd; 4584092dbcSRoman Gushchin 4684092dbcSRoman Gushchin len = write(fd, buf, len); 4784092dbcSRoman Gushchin if (len < 0) { 4884092dbcSRoman Gushchin close(fd); 4984092dbcSRoman Gushchin return len; 5084092dbcSRoman Gushchin } 5184092dbcSRoman Gushchin 5284092dbcSRoman Gushchin close(fd); 5384092dbcSRoman Gushchin 5484092dbcSRoman Gushchin return len; 5584092dbcSRoman Gushchin } 5684092dbcSRoman Gushchin 5784092dbcSRoman Gushchin char *cg_name(const char *root, const char *name) 5884092dbcSRoman Gushchin { 5984092dbcSRoman Gushchin size_t len = strlen(root) + strlen(name) + 2; 6084092dbcSRoman Gushchin char *ret = malloc(len); 6184092dbcSRoman Gushchin 6284092dbcSRoman Gushchin if (name) 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 if (name) 7484092dbcSRoman Gushchin snprintf(ret, len, "%s/%s_%d", root, name, index); 7584092dbcSRoman Gushchin 7684092dbcSRoman Gushchin return ret; 7784092dbcSRoman Gushchin } 7884092dbcSRoman Gushchin 7984092dbcSRoman Gushchin int cg_read(const char *cgroup, const char *control, char *buf, size_t len) 8084092dbcSRoman Gushchin { 8184092dbcSRoman Gushchin char path[PATH_MAX]; 8284092dbcSRoman Gushchin 8384092dbcSRoman Gushchin snprintf(path, sizeof(path), "%s/%s", cgroup, control); 8484092dbcSRoman Gushchin 8584092dbcSRoman Gushchin if (read_text(path, buf, len) >= 0) 8684092dbcSRoman Gushchin return 0; 8784092dbcSRoman Gushchin 8884092dbcSRoman Gushchin return -1; 8984092dbcSRoman Gushchin } 9084092dbcSRoman Gushchin 9184092dbcSRoman Gushchin int cg_read_strcmp(const char *cgroup, const char *control, 9284092dbcSRoman Gushchin const char *expected) 9384092dbcSRoman Gushchin { 9484092dbcSRoman Gushchin size_t size = strlen(expected) + 1; 9584092dbcSRoman Gushchin char *buf; 9684092dbcSRoman Gushchin 9784092dbcSRoman Gushchin buf = malloc(size); 9884092dbcSRoman Gushchin if (!buf) 9984092dbcSRoman Gushchin return -1; 10084092dbcSRoman Gushchin 10184092dbcSRoman Gushchin if (cg_read(cgroup, control, buf, size)) 10284092dbcSRoman Gushchin return -1; 10384092dbcSRoman Gushchin 10484092dbcSRoman Gushchin return strcmp(expected, buf); 10584092dbcSRoman Gushchin } 10684092dbcSRoman Gushchin 10784092dbcSRoman Gushchin int cg_read_strstr(const char *cgroup, const char *control, const char *needle) 10884092dbcSRoman Gushchin { 10984092dbcSRoman Gushchin char buf[PAGE_SIZE]; 11084092dbcSRoman Gushchin 11184092dbcSRoman Gushchin if (cg_read(cgroup, control, buf, sizeof(buf))) 11284092dbcSRoman Gushchin return -1; 11384092dbcSRoman Gushchin 11484092dbcSRoman Gushchin return strstr(buf, needle) ? 0 : -1; 11584092dbcSRoman Gushchin } 11684092dbcSRoman Gushchin 11784092dbcSRoman Gushchin long cg_read_long(const char *cgroup, const char *control) 11884092dbcSRoman Gushchin { 11984092dbcSRoman Gushchin char buf[128]; 12084092dbcSRoman Gushchin 12184092dbcSRoman Gushchin if (cg_read(cgroup, control, buf, sizeof(buf))) 12284092dbcSRoman Gushchin return -1; 12384092dbcSRoman Gushchin 12484092dbcSRoman Gushchin return atol(buf); 12584092dbcSRoman Gushchin } 12684092dbcSRoman Gushchin 12784092dbcSRoman Gushchin long cg_read_key_long(const char *cgroup, const char *control, const char *key) 12884092dbcSRoman Gushchin { 12984092dbcSRoman Gushchin char buf[PAGE_SIZE]; 13084092dbcSRoman Gushchin char *ptr; 13184092dbcSRoman Gushchin 13284092dbcSRoman Gushchin if (cg_read(cgroup, control, buf, sizeof(buf))) 13384092dbcSRoman Gushchin return -1; 13484092dbcSRoman Gushchin 13584092dbcSRoman Gushchin ptr = strstr(buf, key); 13684092dbcSRoman Gushchin if (!ptr) 13784092dbcSRoman Gushchin return -1; 13884092dbcSRoman Gushchin 13984092dbcSRoman Gushchin return atol(ptr + strlen(key)); 14084092dbcSRoman Gushchin } 14184092dbcSRoman Gushchin 14284092dbcSRoman Gushchin int cg_write(const char *cgroup, const char *control, char *buf) 14384092dbcSRoman Gushchin { 14484092dbcSRoman Gushchin char path[PATH_MAX]; 14584092dbcSRoman Gushchin size_t len = strlen(buf); 14684092dbcSRoman Gushchin 14784092dbcSRoman Gushchin snprintf(path, sizeof(path), "%s/%s", cgroup, control); 14884092dbcSRoman Gushchin 14984092dbcSRoman Gushchin if (write_text(path, buf, len) == len) 15084092dbcSRoman Gushchin return 0; 15184092dbcSRoman Gushchin 15284092dbcSRoman Gushchin return -1; 15384092dbcSRoman Gushchin } 15484092dbcSRoman Gushchin 15584092dbcSRoman Gushchin int cg_find_unified_root(char *root, size_t len) 15684092dbcSRoman Gushchin { 15784092dbcSRoman Gushchin char buf[10 * PAGE_SIZE]; 15884092dbcSRoman Gushchin char *fs, *mount, *type; 15984092dbcSRoman Gushchin const char delim[] = "\n\t "; 16084092dbcSRoman Gushchin 16184092dbcSRoman Gushchin if (read_text("/proc/self/mounts", buf, sizeof(buf)) <= 0) 16284092dbcSRoman Gushchin return -1; 16384092dbcSRoman Gushchin 16484092dbcSRoman Gushchin /* 16584092dbcSRoman Gushchin * Example: 16684092dbcSRoman Gushchin * cgroup /sys/fs/cgroup cgroup2 rw,seclabel,noexec,relatime 0 0 16784092dbcSRoman Gushchin */ 16884092dbcSRoman Gushchin for (fs = strtok(buf, delim); fs; fs = strtok(NULL, delim)) { 16984092dbcSRoman Gushchin mount = strtok(NULL, delim); 17084092dbcSRoman Gushchin type = strtok(NULL, delim); 17184092dbcSRoman Gushchin strtok(NULL, delim); 17284092dbcSRoman Gushchin strtok(NULL, delim); 17384092dbcSRoman Gushchin strtok(NULL, delim); 17484092dbcSRoman Gushchin 17584092dbcSRoman Gushchin if (strcmp(fs, "cgroup") == 0 && 17684092dbcSRoman Gushchin strcmp(type, "cgroup2") == 0) { 17784092dbcSRoman Gushchin strncpy(root, mount, len); 17884092dbcSRoman Gushchin return 0; 17984092dbcSRoman Gushchin } 18084092dbcSRoman Gushchin } 18184092dbcSRoman Gushchin 18284092dbcSRoman Gushchin return -1; 18384092dbcSRoman Gushchin } 18484092dbcSRoman Gushchin 18584092dbcSRoman Gushchin int cg_create(const char *cgroup) 18684092dbcSRoman Gushchin { 18784092dbcSRoman Gushchin return mkdir(cgroup, 0644); 18884092dbcSRoman Gushchin } 18984092dbcSRoman Gushchin 19084092dbcSRoman Gushchin static int cg_killall(const char *cgroup) 19184092dbcSRoman Gushchin { 19284092dbcSRoman Gushchin char buf[PAGE_SIZE]; 19384092dbcSRoman Gushchin char *ptr = buf; 19484092dbcSRoman Gushchin 19584092dbcSRoman Gushchin if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf))) 19684092dbcSRoman Gushchin return -1; 19784092dbcSRoman Gushchin 19884092dbcSRoman Gushchin while (ptr < buf + sizeof(buf)) { 19984092dbcSRoman Gushchin int pid = strtol(ptr, &ptr, 10); 20084092dbcSRoman Gushchin 20184092dbcSRoman Gushchin if (pid == 0) 20284092dbcSRoman Gushchin break; 20384092dbcSRoman Gushchin if (*ptr) 20484092dbcSRoman Gushchin ptr++; 20584092dbcSRoman Gushchin else 20684092dbcSRoman Gushchin break; 20784092dbcSRoman Gushchin if (kill(pid, SIGKILL)) 20884092dbcSRoman Gushchin return -1; 20984092dbcSRoman Gushchin } 21084092dbcSRoman Gushchin 21184092dbcSRoman Gushchin return 0; 21284092dbcSRoman Gushchin } 21384092dbcSRoman Gushchin 21484092dbcSRoman Gushchin int cg_destroy(const char *cgroup) 21584092dbcSRoman Gushchin { 21684092dbcSRoman Gushchin int ret; 21784092dbcSRoman Gushchin 21884092dbcSRoman Gushchin retry: 21984092dbcSRoman Gushchin ret = rmdir(cgroup); 22084092dbcSRoman Gushchin if (ret && errno == EBUSY) { 22184092dbcSRoman Gushchin ret = cg_killall(cgroup); 22284092dbcSRoman Gushchin if (ret) 22384092dbcSRoman Gushchin return ret; 22484092dbcSRoman Gushchin usleep(100); 22584092dbcSRoman Gushchin goto retry; 22684092dbcSRoman Gushchin } 22784092dbcSRoman Gushchin 22884092dbcSRoman Gushchin if (ret && errno == ENOENT) 22984092dbcSRoman Gushchin ret = 0; 23084092dbcSRoman Gushchin 23184092dbcSRoman Gushchin return ret; 23284092dbcSRoman Gushchin } 23384092dbcSRoman Gushchin 23484092dbcSRoman Gushchin int cg_run(const char *cgroup, 23584092dbcSRoman Gushchin int (*fn)(const char *cgroup, void *arg), 23684092dbcSRoman Gushchin void *arg) 23784092dbcSRoman Gushchin { 23884092dbcSRoman Gushchin int pid, retcode; 23984092dbcSRoman Gushchin 24084092dbcSRoman Gushchin pid = fork(); 24184092dbcSRoman Gushchin if (pid < 0) { 24284092dbcSRoman Gushchin return pid; 24384092dbcSRoman Gushchin } else if (pid == 0) { 24484092dbcSRoman Gushchin char buf[64]; 24584092dbcSRoman Gushchin 24684092dbcSRoman Gushchin snprintf(buf, sizeof(buf), "%d", getpid()); 24784092dbcSRoman Gushchin if (cg_write(cgroup, "cgroup.procs", buf)) 24884092dbcSRoman Gushchin exit(EXIT_FAILURE); 24984092dbcSRoman Gushchin exit(fn(cgroup, arg)); 25084092dbcSRoman Gushchin } else { 25184092dbcSRoman Gushchin waitpid(pid, &retcode, 0); 25284092dbcSRoman Gushchin if (WIFEXITED(retcode)) 25384092dbcSRoman Gushchin return WEXITSTATUS(retcode); 25484092dbcSRoman Gushchin else 25584092dbcSRoman Gushchin return -1; 25684092dbcSRoman Gushchin } 25784092dbcSRoman Gushchin } 25884092dbcSRoman Gushchin 25984092dbcSRoman Gushchin int cg_run_nowait(const char *cgroup, 26084092dbcSRoman Gushchin int (*fn)(const char *cgroup, void *arg), 26184092dbcSRoman Gushchin void *arg) 26284092dbcSRoman Gushchin { 26384092dbcSRoman Gushchin int pid; 26484092dbcSRoman Gushchin 26584092dbcSRoman Gushchin pid = fork(); 26684092dbcSRoman Gushchin if (pid == 0) { 26784092dbcSRoman Gushchin char buf[64]; 26884092dbcSRoman Gushchin 26984092dbcSRoman Gushchin snprintf(buf, sizeof(buf), "%d", getpid()); 27084092dbcSRoman Gushchin if (cg_write(cgroup, "cgroup.procs", buf)) 27184092dbcSRoman Gushchin exit(EXIT_FAILURE); 27284092dbcSRoman Gushchin exit(fn(cgroup, arg)); 27384092dbcSRoman Gushchin } 27484092dbcSRoman Gushchin 27584092dbcSRoman Gushchin return pid; 27684092dbcSRoman Gushchin } 27784092dbcSRoman Gushchin 27884092dbcSRoman Gushchin int get_temp_fd(void) 27984092dbcSRoman Gushchin { 28084092dbcSRoman Gushchin return open(".", O_TMPFILE | O_RDWR | O_EXCL); 28184092dbcSRoman Gushchin } 28284092dbcSRoman Gushchin 28384092dbcSRoman Gushchin int alloc_pagecache(int fd, size_t size) 28484092dbcSRoman Gushchin { 28584092dbcSRoman Gushchin char buf[PAGE_SIZE]; 28684092dbcSRoman Gushchin struct stat st; 28784092dbcSRoman Gushchin int i; 28884092dbcSRoman Gushchin 28984092dbcSRoman Gushchin if (fstat(fd, &st)) 29084092dbcSRoman Gushchin goto cleanup; 29184092dbcSRoman Gushchin 29284092dbcSRoman Gushchin size += st.st_size; 29384092dbcSRoman Gushchin 29484092dbcSRoman Gushchin if (ftruncate(fd, size)) 29584092dbcSRoman Gushchin goto cleanup; 29684092dbcSRoman Gushchin 29784092dbcSRoman Gushchin for (i = 0; i < size; i += sizeof(buf)) 29884092dbcSRoman Gushchin read(fd, buf, sizeof(buf)); 29984092dbcSRoman Gushchin 30084092dbcSRoman Gushchin return 0; 30184092dbcSRoman Gushchin 30284092dbcSRoman Gushchin cleanup: 30384092dbcSRoman Gushchin return -1; 30484092dbcSRoman Gushchin } 30584092dbcSRoman Gushchin 30684092dbcSRoman Gushchin int alloc_anon(const char *cgroup, void *arg) 30784092dbcSRoman Gushchin { 30884092dbcSRoman Gushchin size_t size = (unsigned long)arg; 30984092dbcSRoman Gushchin char *buf, *ptr; 31084092dbcSRoman Gushchin 31184092dbcSRoman Gushchin buf = malloc(size); 31284092dbcSRoman Gushchin for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE) 31384092dbcSRoman Gushchin *ptr = 0; 31484092dbcSRoman Gushchin 31584092dbcSRoman Gushchin free(buf); 31684092dbcSRoman Gushchin return 0; 31784092dbcSRoman Gushchin } 318478b2784SMike Rapoport 319478b2784SMike Rapoport int is_swap_enabled(void) 320478b2784SMike Rapoport { 321478b2784SMike Rapoport char buf[PAGE_SIZE]; 322478b2784SMike Rapoport const char delim[] = "\n"; 323478b2784SMike Rapoport int cnt = 0; 324478b2784SMike Rapoport char *line; 325478b2784SMike Rapoport 326478b2784SMike Rapoport if (read_text("/proc/swaps", buf, sizeof(buf)) <= 0) 327478b2784SMike Rapoport return -1; 328478b2784SMike Rapoport 329478b2784SMike Rapoport for (line = strtok(buf, delim); line; line = strtok(NULL, delim)) 330478b2784SMike Rapoport cnt++; 331478b2784SMike Rapoport 332478b2784SMike Rapoport return cnt > 1; 333478b2784SMike Rapoport } 334