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, ssize_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 snprintf(ret, len, "%s/%s", root, name); 63 64 return ret; 65 } 66 67 char *cg_name_indexed(const char *root, const char *name, int index) 68 { 69 size_t len = strlen(root) + strlen(name) + 10; 70 char *ret = malloc(len); 71 72 snprintf(ret, len, "%s/%s_%d", root, name, index); 73 74 return ret; 75 } 76 77 int cg_read(const char *cgroup, const char *control, char *buf, size_t len) 78 { 79 char path[PATH_MAX]; 80 81 snprintf(path, sizeof(path), "%s/%s", cgroup, control); 82 83 if (read_text(path, buf, len) >= 0) 84 return 0; 85 86 return -1; 87 } 88 89 int cg_read_strcmp(const char *cgroup, const char *control, 90 const char *expected) 91 { 92 size_t size; 93 char *buf; 94 int ret; 95 96 /* Handle the case of comparing against empty string */ 97 if (!expected) 98 size = 32; 99 else 100 size = strlen(expected) + 1; 101 102 buf = malloc(size); 103 if (!buf) 104 return -1; 105 106 if (cg_read(cgroup, control, buf, size)) { 107 free(buf); 108 return -1; 109 } 110 111 ret = strcmp(expected, buf); 112 free(buf); 113 return ret; 114 } 115 116 int cg_read_strstr(const char *cgroup, const char *control, const char *needle) 117 { 118 char buf[PAGE_SIZE]; 119 120 if (cg_read(cgroup, control, buf, sizeof(buf))) 121 return -1; 122 123 return strstr(buf, needle) ? 0 : -1; 124 } 125 126 long cg_read_long(const char *cgroup, const char *control) 127 { 128 char buf[128]; 129 130 if (cg_read(cgroup, control, buf, sizeof(buf))) 131 return -1; 132 133 return atol(buf); 134 } 135 136 long cg_read_key_long(const char *cgroup, const char *control, const char *key) 137 { 138 char buf[PAGE_SIZE]; 139 char *ptr; 140 141 if (cg_read(cgroup, control, buf, sizeof(buf))) 142 return -1; 143 144 ptr = strstr(buf, key); 145 if (!ptr) 146 return -1; 147 148 return atol(ptr + strlen(key)); 149 } 150 151 int cg_write(const char *cgroup, const char *control, char *buf) 152 { 153 char path[PATH_MAX]; 154 ssize_t len = strlen(buf); 155 156 snprintf(path, sizeof(path), "%s/%s", cgroup, control); 157 158 if (write_text(path, buf, len) == len) 159 return 0; 160 161 return -1; 162 } 163 164 int cg_find_unified_root(char *root, size_t len) 165 { 166 char buf[10 * PAGE_SIZE]; 167 char *fs, *mount, *type; 168 const char delim[] = "\n\t "; 169 170 if (read_text("/proc/self/mounts", buf, sizeof(buf)) <= 0) 171 return -1; 172 173 /* 174 * Example: 175 * cgroup /sys/fs/cgroup cgroup2 rw,seclabel,noexec,relatime 0 0 176 */ 177 for (fs = strtok(buf, delim); fs; fs = strtok(NULL, delim)) { 178 mount = strtok(NULL, delim); 179 type = strtok(NULL, delim); 180 strtok(NULL, delim); 181 strtok(NULL, delim); 182 strtok(NULL, delim); 183 184 if (strcmp(fs, "cgroup") == 0 && 185 strcmp(type, "cgroup2") == 0) { 186 strncpy(root, mount, len); 187 return 0; 188 } 189 } 190 191 return -1; 192 } 193 194 int cg_create(const char *cgroup) 195 { 196 return mkdir(cgroup, 0644); 197 } 198 199 static int cg_killall(const char *cgroup) 200 { 201 char buf[PAGE_SIZE]; 202 char *ptr = buf; 203 204 if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf))) 205 return -1; 206 207 while (ptr < buf + sizeof(buf)) { 208 int pid = strtol(ptr, &ptr, 10); 209 210 if (pid == 0) 211 break; 212 if (*ptr) 213 ptr++; 214 else 215 break; 216 if (kill(pid, SIGKILL)) 217 return -1; 218 } 219 220 return 0; 221 } 222 223 int cg_destroy(const char *cgroup) 224 { 225 int ret; 226 227 retry: 228 ret = rmdir(cgroup); 229 if (ret && errno == EBUSY) { 230 ret = cg_killall(cgroup); 231 if (ret) 232 return ret; 233 usleep(100); 234 goto retry; 235 } 236 237 if (ret && errno == ENOENT) 238 ret = 0; 239 240 return ret; 241 } 242 243 int cg_enter_current(const char *cgroup) 244 { 245 char pidbuf[64]; 246 247 snprintf(pidbuf, sizeof(pidbuf), "%d", getpid()); 248 return cg_write(cgroup, "cgroup.procs", pidbuf); 249 } 250 251 int cg_run(const char *cgroup, 252 int (*fn)(const char *cgroup, void *arg), 253 void *arg) 254 { 255 int pid, retcode; 256 257 pid = fork(); 258 if (pid < 0) { 259 return pid; 260 } else if (pid == 0) { 261 char buf[64]; 262 263 snprintf(buf, sizeof(buf), "%d", getpid()); 264 if (cg_write(cgroup, "cgroup.procs", buf)) 265 exit(EXIT_FAILURE); 266 exit(fn(cgroup, arg)); 267 } else { 268 waitpid(pid, &retcode, 0); 269 if (WIFEXITED(retcode)) 270 return WEXITSTATUS(retcode); 271 else 272 return -1; 273 } 274 } 275 276 int cg_run_nowait(const char *cgroup, 277 int (*fn)(const char *cgroup, void *arg), 278 void *arg) 279 { 280 int pid; 281 282 pid = fork(); 283 if (pid == 0) { 284 char buf[64]; 285 286 snprintf(buf, sizeof(buf), "%d", getpid()); 287 if (cg_write(cgroup, "cgroup.procs", buf)) 288 exit(EXIT_FAILURE); 289 exit(fn(cgroup, arg)); 290 } 291 292 return pid; 293 } 294 295 int get_temp_fd(void) 296 { 297 return open(".", O_TMPFILE | O_RDWR | O_EXCL); 298 } 299 300 int alloc_pagecache(int fd, size_t size) 301 { 302 char buf[PAGE_SIZE]; 303 struct stat st; 304 int i; 305 306 if (fstat(fd, &st)) 307 goto cleanup; 308 309 size += st.st_size; 310 311 if (ftruncate(fd, size)) 312 goto cleanup; 313 314 for (i = 0; i < size; i += sizeof(buf)) 315 read(fd, buf, sizeof(buf)); 316 317 return 0; 318 319 cleanup: 320 return -1; 321 } 322 323 int alloc_anon(const char *cgroup, void *arg) 324 { 325 size_t size = (unsigned long)arg; 326 char *buf, *ptr; 327 328 buf = malloc(size); 329 for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE) 330 *ptr = 0; 331 332 free(buf); 333 return 0; 334 } 335 336 int is_swap_enabled(void) 337 { 338 char buf[PAGE_SIZE]; 339 const char delim[] = "\n"; 340 int cnt = 0; 341 char *line; 342 343 if (read_text("/proc/swaps", buf, sizeof(buf)) <= 0) 344 return -1; 345 346 for (line = strtok(buf, delim); line; line = strtok(NULL, delim)) 347 cnt++; 348 349 return cnt > 1; 350 } 351 352 int set_oom_adj_score(int pid, int score) 353 { 354 char path[PATH_MAX]; 355 int fd, len; 356 357 sprintf(path, "/proc/%d/oom_score_adj", pid); 358 359 fd = open(path, O_WRONLY | O_APPEND); 360 if (fd < 0) 361 return fd; 362 363 len = dprintf(fd, "%d", score); 364 if (len < 0) { 365 close(fd); 366 return len; 367 } 368 369 close(fd); 370 return 0; 371 } 372