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