cgroup_helpers.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. #define _GNU_SOURCE
  2. #include <sched.h>
  3. #include <sys/mount.h>
  4. #include <sys/stat.h>
  5. #include <sys/types.h>
  6. #include <linux/limits.h>
  7. #include <stdio.h>
  8. #include <linux/sched.h>
  9. #include <fcntl.h>
  10. #include <unistd.h>
  11. #include <ftw.h>
  12. #include "cgroup_helpers.h"
  13. /*
  14. * To avoid relying on the system setup, when setup_cgroup_env is called
  15. * we create a new mount namespace, and cgroup namespace. The cgroup2
  16. * root is mounted at CGROUP_MOUNT_PATH
  17. *
  18. * Unfortunately, most people don't have cgroupv2 enabled at this point in time.
  19. * It's easier to create our own mount namespace and manage it ourselves.
  20. *
  21. * We assume /mnt exists.
  22. */
  23. #define WALK_FD_LIMIT 16
  24. #define CGROUP_MOUNT_PATH "/mnt"
  25. #define CGROUP_WORK_DIR "/cgroup-test-work-dir"
  26. #define format_cgroup_path(buf, path) \
  27. snprintf(buf, sizeof(buf), "%s%s%s", CGROUP_MOUNT_PATH, \
  28. CGROUP_WORK_DIR, path)
  29. /**
  30. * setup_cgroup_environment() - Setup the cgroup environment
  31. *
  32. * After calling this function, cleanup_cgroup_environment should be called
  33. * once testing is complete.
  34. *
  35. * This function will print an error to stderr and return 1 if it is unable
  36. * to setup the cgroup environment. If setup is successful, 0 is returned.
  37. */
  38. int setup_cgroup_environment(void)
  39. {
  40. char cgroup_workdir[PATH_MAX + 1];
  41. format_cgroup_path(cgroup_workdir, "");
  42. if (unshare(CLONE_NEWNS)) {
  43. log_err("unshare");
  44. return 1;
  45. }
  46. if (mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL)) {
  47. log_err("mount fakeroot");
  48. return 1;
  49. }
  50. if (mount("none", CGROUP_MOUNT_PATH, "cgroup2", 0, NULL)) {
  51. log_err("mount cgroup2");
  52. return 1;
  53. }
  54. /* Cleanup existing failed runs, now that the environment is setup */
  55. cleanup_cgroup_environment();
  56. if (mkdir(cgroup_workdir, 0777) && errno != EEXIST) {
  57. log_err("mkdir cgroup work dir");
  58. return 1;
  59. }
  60. return 0;
  61. }
  62. static int nftwfunc(const char *filename, const struct stat *statptr,
  63. int fileflags, struct FTW *pfwt)
  64. {
  65. if ((fileflags & FTW_D) && rmdir(filename))
  66. log_err("Removing cgroup: %s", filename);
  67. return 0;
  68. }
  69. static int join_cgroup_from_top(char *cgroup_path)
  70. {
  71. char cgroup_procs_path[PATH_MAX + 1];
  72. pid_t pid = getpid();
  73. int fd, rc = 0;
  74. snprintf(cgroup_procs_path, sizeof(cgroup_procs_path),
  75. "%s/cgroup.procs", cgroup_path);
  76. fd = open(cgroup_procs_path, O_WRONLY);
  77. if (fd < 0) {
  78. log_err("Opening Cgroup Procs: %s", cgroup_procs_path);
  79. return 1;
  80. }
  81. if (dprintf(fd, "%d\n", pid) < 0) {
  82. log_err("Joining Cgroup");
  83. rc = 1;
  84. }
  85. close(fd);
  86. return rc;
  87. }
  88. /**
  89. * join_cgroup() - Join a cgroup
  90. * @path: The cgroup path, relative to the workdir, to join
  91. *
  92. * This function expects a cgroup to already be created, relative to the cgroup
  93. * work dir, and it joins it. For example, passing "/my-cgroup" as the path
  94. * would actually put the calling process into the cgroup
  95. * "/cgroup-test-work-dir/my-cgroup"
  96. *
  97. * On success, it returns 0, otherwise on failure it returns 1.
  98. */
  99. int join_cgroup(char *path)
  100. {
  101. char cgroup_path[PATH_MAX + 1];
  102. format_cgroup_path(cgroup_path, path);
  103. return join_cgroup_from_top(cgroup_path);
  104. }
  105. /**
  106. * cleanup_cgroup_environment() - Cleanup Cgroup Testing Environment
  107. *
  108. * This is an idempotent function to delete all temporary cgroups that
  109. * have been created during the test, including the cgroup testing work
  110. * directory.
  111. *
  112. * At call time, it moves the calling process to the root cgroup, and then
  113. * runs the deletion process. It is idempotent, and should not fail, unless
  114. * a process is lingering.
  115. *
  116. * On failure, it will print an error to stderr, and try to continue.
  117. */
  118. void cleanup_cgroup_environment(void)
  119. {
  120. char cgroup_workdir[PATH_MAX + 1];
  121. format_cgroup_path(cgroup_workdir, "");
  122. join_cgroup_from_top(CGROUP_MOUNT_PATH);
  123. nftw(cgroup_workdir, nftwfunc, WALK_FD_LIMIT, FTW_DEPTH | FTW_MOUNT);
  124. }
  125. /**
  126. * create_and_get_cgroup() - Create a cgroup, relative to workdir, and get the FD
  127. * @path: The cgroup path, relative to the workdir, to join
  128. *
  129. * This function creates a cgroup under the top level workdir and returns the
  130. * file descriptor. It is idempotent.
  131. *
  132. * On success, it returns the file descriptor. On failure it returns 0.
  133. * If there is a failure, it prints the error to stderr.
  134. */
  135. int create_and_get_cgroup(char *path)
  136. {
  137. char cgroup_path[PATH_MAX + 1];
  138. int fd;
  139. format_cgroup_path(cgroup_path, path);
  140. if (mkdir(cgroup_path, 0777) && errno != EEXIST) {
  141. log_err("mkdiring cgroup");
  142. return 0;
  143. }
  144. fd = open(cgroup_path, O_RDONLY);
  145. if (fd < 0) {
  146. log_err("Opening Cgroup");
  147. return 0;
  148. }
  149. return fd;
  150. }