cgroup_helpers.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. // SPDX-License-Identifier: GPL-2.0
  2. #define _GNU_SOURCE
  3. #include <sched.h>
  4. #include <sys/mount.h>
  5. #include <sys/stat.h>
  6. #include <sys/types.h>
  7. #include <linux/limits.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <linux/sched.h>
  11. #include <fcntl.h>
  12. #include <unistd.h>
  13. #include <ftw.h>
  14. #include "cgroup_helpers.h"
  15. #include "bpf_util.h"
  16. /*
  17. * To avoid relying on the system setup, when setup_cgroup_env is called
  18. * we create a new mount namespace, and cgroup namespace. The cgroupv2
  19. * root is mounted at CGROUP_MOUNT_PATH. Unfortunately, most people don't
  20. * have cgroupv2 enabled at this point in time. It's easier to create our
  21. * own mount namespace and manage it ourselves. We assume /mnt exists.
  22. *
  23. * Related cgroupv1 helpers are named *classid*(), since we only use the
  24. * net_cls controller for tagging net_cls.classid. We assume the default
  25. * mount under /sys/fs/cgroup/net_cls, which should be the case for the
  26. * vast majority of users.
  27. */
  28. #define WALK_FD_LIMIT 16
  29. #define CGROUP_MOUNT_PATH "/mnt"
  30. #define CGROUP_MOUNT_DFLT "/sys/fs/cgroup"
  31. #define NETCLS_MOUNT_PATH CGROUP_MOUNT_DFLT "/net_cls"
  32. #define CGROUP_WORK_DIR "/cgroup-test-work-dir"
  33. #define format_cgroup_path_pid(buf, path, pid) \
  34. snprintf(buf, sizeof(buf), "%s%s%d%s", CGROUP_MOUNT_PATH, \
  35. CGROUP_WORK_DIR, pid, path)
  36. #define format_cgroup_path(buf, path) \
  37. format_cgroup_path_pid(buf, path, getpid())
  38. #define format_parent_cgroup_path(buf, path) \
  39. format_cgroup_path_pid(buf, path, getppid())
  40. #define format_classid_path_pid(buf, pid) \
  41. snprintf(buf, sizeof(buf), "%s%s%d", NETCLS_MOUNT_PATH, \
  42. CGROUP_WORK_DIR, pid)
  43. #define format_classid_path(buf) \
  44. format_classid_path_pid(buf, getpid())
  45. static __thread bool cgroup_workdir_mounted;
  46. static void __cleanup_cgroup_environment(void);
  47. static int __enable_controllers(const char *cgroup_path, const char *controllers)
  48. {
  49. char path[PATH_MAX + 1];
  50. char enable[PATH_MAX + 1];
  51. char *c, *c2;
  52. int fd, cfd;
  53. ssize_t len;
  54. /* If not controllers are passed, enable all available controllers */
  55. if (!controllers) {
  56. snprintf(path, sizeof(path), "%s/cgroup.controllers",
  57. cgroup_path);
  58. fd = open(path, O_RDONLY);
  59. if (fd < 0) {
  60. log_err("Opening cgroup.controllers: %s", path);
  61. return 1;
  62. }
  63. len = read(fd, enable, sizeof(enable) - 1);
  64. if (len < 0) {
  65. close(fd);
  66. log_err("Reading cgroup.controllers: %s", path);
  67. return 1;
  68. } else if (len == 0) { /* No controllers to enable */
  69. close(fd);
  70. return 0;
  71. }
  72. enable[len] = 0;
  73. close(fd);
  74. } else {
  75. bpf_strlcpy(enable, controllers, sizeof(enable));
  76. }
  77. snprintf(path, sizeof(path), "%s/cgroup.subtree_control", cgroup_path);
  78. cfd = open(path, O_RDWR);
  79. if (cfd < 0) {
  80. log_err("Opening cgroup.subtree_control: %s", path);
  81. return 1;
  82. }
  83. for (c = strtok_r(enable, " ", &c2); c; c = strtok_r(NULL, " ", &c2)) {
  84. if (dprintf(cfd, "+%s\n", c) <= 0) {
  85. log_err("Enabling controller %s: %s", c, path);
  86. close(cfd);
  87. return 1;
  88. }
  89. }
  90. close(cfd);
  91. return 0;
  92. }
  93. /**
  94. * enable_controllers() - Enable cgroup v2 controllers
  95. * @relative_path: The cgroup path, relative to the workdir
  96. * @controllers: List of controllers to enable in cgroup.controllers format
  97. *
  98. *
  99. * Enable given cgroup v2 controllers, if @controllers is NULL, enable all
  100. * available controllers.
  101. *
  102. * If successful, 0 is returned.
  103. */
  104. int enable_controllers(const char *relative_path, const char *controllers)
  105. {
  106. char cgroup_path[PATH_MAX + 1];
  107. format_cgroup_path(cgroup_path, relative_path);
  108. return __enable_controllers(cgroup_path, controllers);
  109. }
  110. static int __write_cgroup_file(const char *cgroup_path, const char *file,
  111. const char *buf)
  112. {
  113. char file_path[PATH_MAX + 1];
  114. int fd;
  115. snprintf(file_path, sizeof(file_path), "%s/%s", cgroup_path, file);
  116. fd = open(file_path, O_RDWR);
  117. if (fd < 0) {
  118. log_err("Opening %s", file_path);
  119. return 1;
  120. }
  121. if (dprintf(fd, "%s", buf) <= 0) {
  122. log_err("Writing to %s", file_path);
  123. close(fd);
  124. return 1;
  125. }
  126. close(fd);
  127. return 0;
  128. }
  129. /**
  130. * write_cgroup_file() - Write to a cgroup file
  131. * @relative_path: The cgroup path, relative to the workdir
  132. * @file: The name of the file in cgroupfs to write to
  133. * @buf: Buffer to write to the file
  134. *
  135. * Write to a file in the given cgroup's directory.
  136. *
  137. * If successful, 0 is returned.
  138. */
  139. int write_cgroup_file(const char *relative_path, const char *file,
  140. const char *buf)
  141. {
  142. char cgroup_path[PATH_MAX - 24];
  143. format_cgroup_path(cgroup_path, relative_path);
  144. return __write_cgroup_file(cgroup_path, file, buf);
  145. }
  146. /**
  147. * write_cgroup_file_parent() - Write to a cgroup file in the parent process
  148. * workdir
  149. * @relative_path: The cgroup path, relative to the parent process workdir
  150. * @file: The name of the file in cgroupfs to write to
  151. * @buf: Buffer to write to the file
  152. *
  153. * Write to a file in the given cgroup's directory under the parent process
  154. * workdir.
  155. *
  156. * If successful, 0 is returned.
  157. */
  158. int write_cgroup_file_parent(const char *relative_path, const char *file,
  159. const char *buf)
  160. {
  161. char cgroup_path[PATH_MAX - 24];
  162. format_parent_cgroup_path(cgroup_path, relative_path);
  163. return __write_cgroup_file(cgroup_path, file, buf);
  164. }
  165. /**
  166. * setup_cgroup_environment() - Setup the cgroup environment
  167. *
  168. * After calling this function, cleanup_cgroup_environment should be called
  169. * once testing is complete.
  170. *
  171. * This function will print an error to stderr and return 1 if it is unable
  172. * to setup the cgroup environment. If setup is successful, 0 is returned.
  173. */
  174. int setup_cgroup_environment(void)
  175. {
  176. char cgroup_workdir[PATH_MAX - 24];
  177. format_cgroup_path(cgroup_workdir, "");
  178. if (mkdir(CGROUP_MOUNT_PATH, 0777) && errno != EEXIST) {
  179. log_err("mkdir mount");
  180. return 1;
  181. }
  182. if (unshare(CLONE_NEWNS)) {
  183. log_err("unshare");
  184. return 1;
  185. }
  186. if (mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL)) {
  187. log_err("mount fakeroot");
  188. return 1;
  189. }
  190. if (mount("none", CGROUP_MOUNT_PATH, "cgroup2", 0, NULL) && errno != EBUSY) {
  191. log_err("mount cgroup2");
  192. return 1;
  193. }
  194. cgroup_workdir_mounted = true;
  195. /* Cleanup existing failed runs, now that the environment is setup */
  196. __cleanup_cgroup_environment();
  197. if (mkdir(cgroup_workdir, 0777) && errno != EEXIST) {
  198. log_err("mkdir cgroup work dir");
  199. return 1;
  200. }
  201. /* Enable all available controllers to increase test coverage */
  202. if (__enable_controllers(CGROUP_MOUNT_PATH, NULL) ||
  203. __enable_controllers(cgroup_workdir, NULL))
  204. return 1;
  205. return 0;
  206. }
  207. static int nftwfunc(const char *filename, const struct stat *statptr,
  208. int fileflags, struct FTW *pfwt)
  209. {
  210. if ((fileflags & FTW_D) && rmdir(filename))
  211. log_err("Removing cgroup: %s", filename);
  212. return 0;
  213. }
  214. static int join_cgroup_from_top(const char *cgroup_path)
  215. {
  216. char cgroup_procs_path[PATH_MAX + 1];
  217. pid_t pid = getpid();
  218. int fd, rc = 0;
  219. snprintf(cgroup_procs_path, sizeof(cgroup_procs_path),
  220. "%s/cgroup.procs", cgroup_path);
  221. fd = open(cgroup_procs_path, O_WRONLY);
  222. if (fd < 0) {
  223. log_err("Opening Cgroup Procs: %s", cgroup_procs_path);
  224. return 1;
  225. }
  226. if (dprintf(fd, "%d\n", pid) < 0) {
  227. log_err("Joining Cgroup");
  228. rc = 1;
  229. }
  230. close(fd);
  231. return rc;
  232. }
  233. /**
  234. * join_cgroup() - Join a cgroup
  235. * @relative_path: The cgroup path, relative to the workdir, to join
  236. *
  237. * This function expects a cgroup to already be created, relative to the cgroup
  238. * work dir, and it joins it. For example, passing "/my-cgroup" as the path
  239. * would actually put the calling process into the cgroup
  240. * "/cgroup-test-work-dir/my-cgroup"
  241. *
  242. * On success, it returns 0, otherwise on failure it returns 1.
  243. */
  244. int join_cgroup(const char *relative_path)
  245. {
  246. char cgroup_path[PATH_MAX + 1];
  247. format_cgroup_path(cgroup_path, relative_path);
  248. return join_cgroup_from_top(cgroup_path);
  249. }
  250. /**
  251. * join_root_cgroup() - Join the root cgroup
  252. *
  253. * This function joins the root cgroup.
  254. *
  255. * On success, it returns 0, otherwise on failure it returns 1.
  256. */
  257. int join_root_cgroup(void)
  258. {
  259. return join_cgroup_from_top(CGROUP_MOUNT_PATH);
  260. }
  261. /**
  262. * join_parent_cgroup() - Join a cgroup in the parent process workdir
  263. * @relative_path: The cgroup path, relative to parent process workdir, to join
  264. *
  265. * See join_cgroup().
  266. *
  267. * On success, it returns 0, otherwise on failure it returns 1.
  268. */
  269. int join_parent_cgroup(const char *relative_path)
  270. {
  271. char cgroup_path[PATH_MAX + 1];
  272. format_parent_cgroup_path(cgroup_path, relative_path);
  273. return join_cgroup_from_top(cgroup_path);
  274. }
  275. /**
  276. * __cleanup_cgroup_environment() - Delete temporary cgroups
  277. *
  278. * This is a helper for cleanup_cgroup_environment() that is responsible for
  279. * deletion of all temporary cgroups that have been created during the test.
  280. */
  281. static void __cleanup_cgroup_environment(void)
  282. {
  283. char cgroup_workdir[PATH_MAX + 1];
  284. format_cgroup_path(cgroup_workdir, "");
  285. join_cgroup_from_top(CGROUP_MOUNT_PATH);
  286. nftw(cgroup_workdir, nftwfunc, WALK_FD_LIMIT, FTW_DEPTH | FTW_MOUNT);
  287. }
  288. /**
  289. * cleanup_cgroup_environment() - Cleanup Cgroup Testing Environment
  290. *
  291. * This is an idempotent function to delete all temporary cgroups that
  292. * have been created during the test and unmount the cgroup testing work
  293. * directory.
  294. *
  295. * At call time, it moves the calling process to the root cgroup, and then
  296. * runs the deletion process. It is idempotent, and should not fail, unless
  297. * a process is lingering.
  298. *
  299. * On failure, it will print an error to stderr, and try to continue.
  300. */
  301. void cleanup_cgroup_environment(void)
  302. {
  303. __cleanup_cgroup_environment();
  304. if (cgroup_workdir_mounted && umount(CGROUP_MOUNT_PATH))
  305. log_err("umount cgroup2");
  306. cgroup_workdir_mounted = false;
  307. }
  308. /**
  309. * get_root_cgroup() - Get the FD of the root cgroup
  310. *
  311. * On success, it returns the file descriptor. On failure, it returns -1.
  312. * If there is a failure, it prints the error to stderr.
  313. */
  314. int get_root_cgroup(void)
  315. {
  316. int fd;
  317. fd = open(CGROUP_MOUNT_PATH, O_RDONLY);
  318. if (fd < 0) {
  319. log_err("Opening root cgroup");
  320. return -1;
  321. }
  322. return fd;
  323. }
  324. /*
  325. * remove_cgroup() - Remove a cgroup
  326. * @relative_path: The cgroup path, relative to the workdir, to remove
  327. *
  328. * This function expects a cgroup to already be created, relative to the cgroup
  329. * work dir. It also expects the cgroup doesn't have any children or live
  330. * processes and it removes the cgroup.
  331. *
  332. * On failure, it will print an error to stderr.
  333. */
  334. void remove_cgroup(const char *relative_path)
  335. {
  336. char cgroup_path[PATH_MAX + 1];
  337. format_cgroup_path(cgroup_path, relative_path);
  338. if (rmdir(cgroup_path))
  339. log_err("rmdiring cgroup %s .. %s", relative_path, cgroup_path);
  340. }
  341. /**
  342. * create_and_get_cgroup() - Create a cgroup, relative to workdir, and get the FD
  343. * @relative_path: The cgroup path, relative to the workdir, to join
  344. *
  345. * This function creates a cgroup under the top level workdir and returns the
  346. * file descriptor. It is idempotent.
  347. *
  348. * On success, it returns the file descriptor. On failure it returns -1.
  349. * If there is a failure, it prints the error to stderr.
  350. */
  351. int create_and_get_cgroup(const char *relative_path)
  352. {
  353. char cgroup_path[PATH_MAX + 1];
  354. int fd;
  355. format_cgroup_path(cgroup_path, relative_path);
  356. if (mkdir(cgroup_path, 0777) && errno != EEXIST) {
  357. log_err("mkdiring cgroup %s .. %s", relative_path, cgroup_path);
  358. return -1;
  359. }
  360. fd = open(cgroup_path, O_RDONLY);
  361. if (fd < 0) {
  362. log_err("Opening Cgroup");
  363. return -1;
  364. }
  365. return fd;
  366. }
  367. /**
  368. * get_cgroup_id_from_path - Get cgroup id for a particular cgroup path
  369. * @cgroup_workdir: The absolute cgroup path
  370. *
  371. * On success, it returns the cgroup id. On failure it returns 0,
  372. * which is an invalid cgroup id.
  373. * If there is a failure, it prints the error to stderr.
  374. */
  375. static unsigned long long get_cgroup_id_from_path(const char *cgroup_workdir)
  376. {
  377. int dirfd, err, flags, mount_id, fhsize;
  378. union {
  379. unsigned long long cgid;
  380. unsigned char raw_bytes[8];
  381. } id;
  382. struct file_handle *fhp, *fhp2;
  383. unsigned long long ret = 0;
  384. dirfd = AT_FDCWD;
  385. flags = 0;
  386. fhsize = sizeof(*fhp);
  387. fhp = calloc(1, fhsize);
  388. if (!fhp) {
  389. log_err("calloc");
  390. return 0;
  391. }
  392. err = name_to_handle_at(dirfd, cgroup_workdir, fhp, &mount_id, flags);
  393. if (err >= 0 || fhp->handle_bytes != 8) {
  394. log_err("name_to_handle_at");
  395. goto free_mem;
  396. }
  397. fhsize = sizeof(struct file_handle) + fhp->handle_bytes;
  398. fhp2 = realloc(fhp, fhsize);
  399. if (!fhp2) {
  400. log_err("realloc");
  401. goto free_mem;
  402. }
  403. err = name_to_handle_at(dirfd, cgroup_workdir, fhp2, &mount_id, flags);
  404. fhp = fhp2;
  405. if (err < 0) {
  406. log_err("name_to_handle_at");
  407. goto free_mem;
  408. }
  409. memcpy(id.raw_bytes, fhp->f_handle, 8);
  410. ret = id.cgid;
  411. free_mem:
  412. free(fhp);
  413. return ret;
  414. }
  415. unsigned long long get_cgroup_id(const char *relative_path)
  416. {
  417. char cgroup_workdir[PATH_MAX + 1];
  418. format_cgroup_path(cgroup_workdir, relative_path);
  419. return get_cgroup_id_from_path(cgroup_workdir);
  420. }
  421. int cgroup_setup_and_join(const char *path) {
  422. int cg_fd;
  423. if (setup_cgroup_environment()) {
  424. fprintf(stderr, "Failed to setup cgroup environment\n");
  425. return -EINVAL;
  426. }
  427. cg_fd = create_and_get_cgroup(path);
  428. if (cg_fd < 0) {
  429. fprintf(stderr, "Failed to create test cgroup\n");
  430. cleanup_cgroup_environment();
  431. return cg_fd;
  432. }
  433. if (join_cgroup(path)) {
  434. fprintf(stderr, "Failed to join cgroup\n");
  435. cleanup_cgroup_environment();
  436. return -EINVAL;
  437. }
  438. return cg_fd;
  439. }
  440. /**
  441. * setup_classid_environment() - Setup the cgroupv1 net_cls environment
  442. *
  443. * This function should only be called in a custom mount namespace, e.g.
  444. * created by running setup_cgroup_environment.
  445. *
  446. * After calling this function, cleanup_classid_environment should be called
  447. * once testing is complete.
  448. *
  449. * This function will print an error to stderr and return 1 if it is unable
  450. * to setup the cgroup environment. If setup is successful, 0 is returned.
  451. */
  452. int setup_classid_environment(void)
  453. {
  454. char cgroup_workdir[PATH_MAX + 1];
  455. format_classid_path(cgroup_workdir);
  456. if (mount("tmpfs", CGROUP_MOUNT_DFLT, "tmpfs", 0, NULL) &&
  457. errno != EBUSY) {
  458. log_err("mount cgroup base");
  459. return 1;
  460. }
  461. if (mkdir(NETCLS_MOUNT_PATH, 0777) && errno != EEXIST) {
  462. log_err("mkdir cgroup net_cls");
  463. return 1;
  464. }
  465. if (mount("net_cls", NETCLS_MOUNT_PATH, "cgroup", 0, "net_cls")) {
  466. if (errno != EBUSY) {
  467. log_err("mount cgroup net_cls");
  468. return 1;
  469. }
  470. if (rmdir(NETCLS_MOUNT_PATH)) {
  471. log_err("rmdir cgroup net_cls");
  472. return 1;
  473. }
  474. if (umount(CGROUP_MOUNT_DFLT)) {
  475. log_err("umount cgroup base");
  476. return 1;
  477. }
  478. }
  479. cleanup_classid_environment();
  480. if (mkdir(cgroup_workdir, 0777) && errno != EEXIST) {
  481. log_err("mkdir cgroup work dir");
  482. return 1;
  483. }
  484. return 0;
  485. }
  486. /**
  487. * set_classid() - Set a cgroupv1 net_cls classid
  488. *
  489. * Writes the classid into the cgroup work dir's net_cls.classid
  490. * file in order to later on trigger socket tagging.
  491. *
  492. * We leverage the current pid as the classid, ensuring unique identification.
  493. *
  494. * On success, it returns 0, otherwise on failure it returns 1. If there
  495. * is a failure, it prints the error to stderr.
  496. */
  497. int set_classid(void)
  498. {
  499. char cgroup_workdir[PATH_MAX - 42];
  500. char cgroup_classid_path[PATH_MAX + 1];
  501. int fd, rc = 0;
  502. format_classid_path(cgroup_workdir);
  503. snprintf(cgroup_classid_path, sizeof(cgroup_classid_path),
  504. "%s/net_cls.classid", cgroup_workdir);
  505. fd = open(cgroup_classid_path, O_WRONLY);
  506. if (fd < 0) {
  507. log_err("Opening cgroup classid: %s", cgroup_classid_path);
  508. return 1;
  509. }
  510. if (dprintf(fd, "%u\n", getpid()) < 0) {
  511. log_err("Setting cgroup classid");
  512. rc = 1;
  513. }
  514. close(fd);
  515. return rc;
  516. }
  517. /**
  518. * join_classid() - Join a cgroupv1 net_cls classid
  519. *
  520. * This function expects the cgroup work dir to be already created, as we
  521. * join it here. This causes the process sockets to be tagged with the given
  522. * net_cls classid.
  523. *
  524. * On success, it returns 0, otherwise on failure it returns 1.
  525. */
  526. int join_classid(void)
  527. {
  528. char cgroup_workdir[PATH_MAX + 1];
  529. format_classid_path(cgroup_workdir);
  530. return join_cgroup_from_top(cgroup_workdir);
  531. }
  532. /**
  533. * cleanup_classid_environment() - Cleanup the cgroupv1 net_cls environment
  534. *
  535. * At call time, it moves the calling process to the root cgroup, and then
  536. * runs the deletion process.
  537. *
  538. * On failure, it will print an error to stderr, and try to continue.
  539. */
  540. void cleanup_classid_environment(void)
  541. {
  542. char cgroup_workdir[PATH_MAX + 1];
  543. format_classid_path(cgroup_workdir);
  544. join_cgroup_from_top(NETCLS_MOUNT_PATH);
  545. nftw(cgroup_workdir, nftwfunc, WALK_FD_LIMIT, FTW_DEPTH | FTW_MOUNT);
  546. }
  547. /**
  548. * get_classid_cgroup_id - Get the cgroup id of a net_cls cgroup
  549. */
  550. unsigned long long get_classid_cgroup_id(void)
  551. {
  552. char cgroup_workdir[PATH_MAX + 1];
  553. format_classid_path(cgroup_workdir);
  554. return get_cgroup_id_from_path(cgroup_workdir);
  555. }
  556. /**
  557. * get_cgroup1_hierarchy_id - Retrieves the ID of a cgroup1 hierarchy from the cgroup1 subsys name.
  558. * @subsys_name: The cgroup1 subsys name, which can be retrieved from /proc/self/cgroup. It can be
  559. * a named cgroup like "name=systemd", a controller name like "net_cls", or multi-controllers like
  560. * "net_cls,net_prio".
  561. */
  562. int get_cgroup1_hierarchy_id(const char *subsys_name)
  563. {
  564. char *c, *c2, *c3, *c4;
  565. bool found = false;
  566. char line[1024];
  567. FILE *file;
  568. int i, id;
  569. if (!subsys_name)
  570. return -1;
  571. file = fopen("/proc/self/cgroup", "r");
  572. if (!file) {
  573. log_err("fopen /proc/self/cgroup");
  574. return -1;
  575. }
  576. while (fgets(line, 1024, file)) {
  577. i = 0;
  578. for (c = strtok_r(line, ":", &c2); c && i < 2; c = strtok_r(NULL, ":", &c2)) {
  579. if (i == 0) {
  580. id = strtol(c, NULL, 10);
  581. } else if (i == 1) {
  582. if (!strcmp(c, subsys_name)) {
  583. found = true;
  584. break;
  585. }
  586. /* Multiple subsystems may share one single mount point */
  587. for (c3 = strtok_r(c, ",", &c4); c3;
  588. c3 = strtok_r(NULL, ",", &c4)) {
  589. if (!strcmp(c, subsys_name)) {
  590. found = true;
  591. break;
  592. }
  593. }
  594. }
  595. i++;
  596. }
  597. if (found)
  598. break;
  599. }
  600. fclose(file);
  601. return found ? id : -1;
  602. }
  603. /**
  604. * open_classid() - Open a cgroupv1 net_cls classid
  605. *
  606. * This function expects the cgroup work dir to be already created, as we
  607. * open it here.
  608. *
  609. * On success, it returns the file descriptor. On failure it returns -1.
  610. */
  611. int open_classid(void)
  612. {
  613. char cgroup_workdir[PATH_MAX + 1];
  614. format_classid_path(cgroup_workdir);
  615. return open(cgroup_workdir, O_RDONLY);
  616. }