1 /* SPDX-License-Identifier: GPL-2.0 */ 2 3 #define _GNU_SOURCE 4 5 #include <errno.h> 6 #include <fcntl.h> 7 #include <linux/limits.h> 8 #include <signal.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <sys/stat.h> 13 #include <sys/types.h> 14 #include <sys/wait.h> 15 #include <unistd.h> 16 17 #include "cgroup_util.h" 18 19 static ssize_t read_text(const char *path, char *buf, size_t max_len) 20 { 21 ssize_t len; 22 int fd; 23 24 fd = open(path, O_RDONLY); 25 if (fd < 0) 26 return fd; 27 28 len = read(fd, buf, max_len - 1); 29 if (len < 0) 30 goto out; 31 32 buf[len] = 0; 33 out: 34 close(fd); 35 return len; 36 } 37 38 static ssize_t write_text(const char *path, char *buf, size_t len) 39 { 40 int fd; 41 42 fd = open(path, O_WRONLY | O_APPEND); 43 if (fd < 0) 44 return fd; 45 46 len = write(fd, buf, len); 47 if (len < 0) { 48 close(fd); 49 return len; 50 } 51 52 close(fd); 53 54 return len; 55 } 56 57 char *cg_name(const char *root, const char *name) 58 { 59 size_t len = strlen(root) + strlen(name) + 2; 60 char *ret = malloc(len); 61 62 if (name) 63 snprintf(ret, len, "%s/%s", root, name); 64 65 return ret; 66 } 67 68 char *cg_name_indexed(const char *root, const char *name, int index) 69 { 70 size_t len = strlen(root) + strlen(name) + 10; 71 char *ret = malloc(len); 72 73 if (name) 74 snprintf(ret, len, "%s/%s_%d", root, name, index); 75 76 return ret; 77 } 78 79 int cg_read(const char *cgroup, const char *control, char *buf, size_t len) 80 { 81 char path[PATH_MAX]; 82 83 snprintf(path, sizeof(path), "%s/%s", cgroup, control); 84 85 if (read_text(path, buf, len) >= 0) 86 return 0; 87 88 return -1; 89 } 90 91 int cg_read_strcmp(const char *cgroup, const char *control, 92 const char *expected) 93 { 94 size_t size = strlen(expected) + 1; 95 char *buf; 96 97 buf = malloc(size); 98 if (!buf) 99 return -1; 100 101 if (cg_read(cgroup, control, buf, size)) 102 return -1; 103 104 return strcmp(expected, buf); 105 } 106 107 int cg_read_strstr(const char *cgroup, const char *control, const char *needle) 108 { 109 char buf[PAGE_SIZE]; 110 111 if (cg_read(cgroup, control, buf, sizeof(buf))) 112 return -1; 113 114 return strstr(buf, needle) ? 0 : -1; 115 } 116 117 long cg_read_long(const char *cgroup, const char *control) 118 { 119 char buf[128]; 120 121 if (cg_read(cgroup, control, buf, sizeof(buf))) 122 return -1; 123 124 return atol(buf); 125 } 126 127 long cg_read_key_long(const char *cgroup, const char *control, const char *key) 128 { 129 char buf[PAGE_SIZE]; 130 char *ptr; 131 132 if (cg_read(cgroup, control, buf, sizeof(buf))) 133 return -1; 134 135 ptr = strstr(buf, key); 136 if (!ptr) 137 return -1; 138 139 return atol(ptr + strlen(key)); 140 } 141 142 int cg_write(const char *cgroup, const char *control, char *buf) 143 { 144 char path[PATH_MAX]; 145 size_t len = strlen(buf); 146 147 snprintf(path, sizeof(path), "%s/%s", cgroup, control); 148 149 if (write_text(path, buf, len) == len) 150 return 0; 151 152 return -1; 153 } 154 155 int cg_find_unified_root(char *root, size_t len) 156 { 157 char buf[10 * PAGE_SIZE]; 158 char *fs, *mount, *type; 159 const char delim[] = "\n\t "; 160 161 if (read_text("/proc/self/mounts", buf, sizeof(buf)) <= 0) 162 return -1; 163 164 /* 165 * Example: 166 * cgroup /sys/fs/cgroup cgroup2 rw,seclabel,noexec,relatime 0 0 167 */ 168 for (fs = strtok(buf, delim); fs; fs = strtok(NULL, delim)) { 169 mount = strtok(NULL, delim); 170 type = strtok(NULL, delim); 171 strtok(NULL, delim); 172 strtok(NULL, delim); 173 strtok(NULL, delim); 174 175 if (strcmp(fs, "cgroup") == 0 && 176 strcmp(type, "cgroup2") == 0) { 177 strncpy(root, mount, len); 178 return 0; 179 } 180 } 181 182 return -1; 183 } 184 185 int cg_create(const char *cgroup) 186 { 187 return mkdir(cgroup, 0644); 188 } 189 190 static int cg_killall(const char *cgroup) 191 { 192 char buf[PAGE_SIZE]; 193 char *ptr = buf; 194 195 if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf))) 196 return -1; 197 198 while (ptr < buf + sizeof(buf)) { 199 int pid = strtol(ptr, &ptr, 10); 200 201 if (pid == 0) 202 break; 203 if (*ptr) 204 ptr++; 205 else 206 break; 207 if (kill(pid, SIGKILL)) 208 return -1; 209 } 210 211 return 0; 212 } 213 214 int cg_destroy(const char *cgroup) 215 { 216 int ret; 217 218 retry: 219 ret = rmdir(cgroup); 220 if (ret && errno == EBUSY) { 221 ret = cg_killall(cgroup); 222 if (ret) 223 return ret; 224 usleep(100); 225 goto retry; 226 } 227 228 if (ret && errno == ENOENT) 229 ret = 0; 230 231 return ret; 232 } 233 234 int cg_run(const char *cgroup, 235 int (*fn)(const char *cgroup, void *arg), 236 void *arg) 237 { 238 int pid, retcode; 239 240 pid = fork(); 241 if (pid < 0) { 242 return pid; 243 } else if (pid == 0) { 244 char buf[64]; 245 246 snprintf(buf, sizeof(buf), "%d", getpid()); 247 if (cg_write(cgroup, "cgroup.procs", buf)) 248 exit(EXIT_FAILURE); 249 exit(fn(cgroup, arg)); 250 } else { 251 waitpid(pid, &retcode, 0); 252 if (WIFEXITED(retcode)) 253 return WEXITSTATUS(retcode); 254 else 255 return -1; 256 } 257 } 258 259 int cg_run_nowait(const char *cgroup, 260 int (*fn)(const char *cgroup, void *arg), 261 void *arg) 262 { 263 int pid; 264 265 pid = fork(); 266 if (pid == 0) { 267 char buf[64]; 268 269 snprintf(buf, sizeof(buf), "%d", getpid()); 270 if (cg_write(cgroup, "cgroup.procs", buf)) 271 exit(EXIT_FAILURE); 272 exit(fn(cgroup, arg)); 273 } 274 275 return pid; 276 } 277 278 int get_temp_fd(void) 279 { 280 return open(".", O_TMPFILE | O_RDWR | O_EXCL); 281 } 282 283 int alloc_pagecache(int fd, size_t size) 284 { 285 char buf[PAGE_SIZE]; 286 struct stat st; 287 int i; 288 289 if (fstat(fd, &st)) 290 goto cleanup; 291 292 size += st.st_size; 293 294 if (ftruncate(fd, size)) 295 goto cleanup; 296 297 for (i = 0; i < size; i += sizeof(buf)) 298 read(fd, buf, sizeof(buf)); 299 300 return 0; 301 302 cleanup: 303 return -1; 304 } 305 306 int alloc_anon(const char *cgroup, void *arg) 307 { 308 size_t size = (unsigned long)arg; 309 char *buf, *ptr; 310 311 buf = malloc(size); 312 for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE) 313 *ptr = 0; 314 315 free(buf); 316 return 0; 317 } 318