root/tools/testing/selftests/cgroup/cgroup_util.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. read_text
  2. write_text
  3. cg_name
  4. cg_name_indexed
  5. cg_control
  6. cg_read
  7. cg_read_strcmp
  8. cg_read_strstr
  9. cg_read_long
  10. cg_read_key_long
  11. cg_write
  12. cg_find_unified_root
  13. cg_create
  14. cg_wait_for_proc_count
  15. cg_killall
  16. cg_destroy
  17. cg_enter
  18. cg_enter_current
  19. cg_run
  20. cg_run_nowait
  21. get_temp_fd
  22. alloc_pagecache
  23. alloc_anon
  24. is_swap_enabled
  25. set_oom_adj_score
  26. proc_read_text

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

/* [<][>][^][v][top][bottom][index][help] */