Home | History | Annotate | Download | only in cgroup
      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