17982a898SNamhyung Kim // SPDX-License-Identifier: GPL-2.0
27982a898SNamhyung Kim #include <linux/stringify.h>
37982a898SNamhyung Kim #include <sys/types.h>
47982a898SNamhyung Kim #include <sys/stat.h>
57982a898SNamhyung Kim #include <fcntl.h>
67982a898SNamhyung Kim #include <stdio.h>
77982a898SNamhyung Kim #include <stdlib.h>
87982a898SNamhyung Kim #include <string.h>
97982a898SNamhyung Kim #include "fs.h"
107982a898SNamhyung Kim
1148859e52SNamhyung Kim struct cgroupfs_cache_entry {
1248859e52SNamhyung Kim char subsys[32];
1348859e52SNamhyung Kim char mountpoint[PATH_MAX];
1448859e52SNamhyung Kim };
1548859e52SNamhyung Kim
1648859e52SNamhyung Kim /* just cache last used one */
17*7a3fb8b5SIan Rogers static struct cgroupfs_cache_entry *cached;
1848859e52SNamhyung Kim
cgroupfs_find_mountpoint(char * buf,size_t maxlen,const char * subsys)197982a898SNamhyung Kim int cgroupfs_find_mountpoint(char *buf, size_t maxlen, const char *subsys)
207982a898SNamhyung Kim {
217982a898SNamhyung Kim FILE *fp;
226fd99b7fSNamhyung Kim char *line = NULL;
236fd99b7fSNamhyung Kim size_t len = 0;
246fd99b7fSNamhyung Kim char *p, *path;
256fd99b7fSNamhyung Kim char mountpoint[PATH_MAX];
267982a898SNamhyung Kim
27*7a3fb8b5SIan Rogers if (cached && !strcmp(cached->subsys, subsys)) {
28*7a3fb8b5SIan Rogers if (strlen(cached->mountpoint) < maxlen) {
29*7a3fb8b5SIan Rogers strcpy(buf, cached->mountpoint);
3048859e52SNamhyung Kim return 0;
3148859e52SNamhyung Kim }
3248859e52SNamhyung Kim return -1;
3348859e52SNamhyung Kim }
3448859e52SNamhyung Kim
357982a898SNamhyung Kim fp = fopen("/proc/mounts", "r");
367982a898SNamhyung Kim if (!fp)
377982a898SNamhyung Kim return -1;
387982a898SNamhyung Kim
397982a898SNamhyung Kim /*
407982a898SNamhyung Kim * in order to handle split hierarchy, we need to scan /proc/mounts
417982a898SNamhyung Kim * and inspect every cgroupfs mount point to find one that has
4227ab1c1cSNamhyung Kim * the given subsystem. If we found v1, just use it. If not we can
4327ab1c1cSNamhyung Kim * use v2 path as a fallback.
447982a898SNamhyung Kim */
456fd99b7fSNamhyung Kim mountpoint[0] = '\0';
467982a898SNamhyung Kim
476fd99b7fSNamhyung Kim /*
486fd99b7fSNamhyung Kim * The /proc/mounts has the follow format:
496fd99b7fSNamhyung Kim *
506fd99b7fSNamhyung Kim * <devname> <mount point> <fs type> <options> ...
516fd99b7fSNamhyung Kim *
526fd99b7fSNamhyung Kim */
536fd99b7fSNamhyung Kim while (getline(&line, &len, fp) != -1) {
546fd99b7fSNamhyung Kim /* skip devname */
556fd99b7fSNamhyung Kim p = strchr(line, ' ');
566fd99b7fSNamhyung Kim if (p == NULL)
576fd99b7fSNamhyung Kim continue;
587982a898SNamhyung Kim
596fd99b7fSNamhyung Kim /* save the mount point */
606fd99b7fSNamhyung Kim path = ++p;
616fd99b7fSNamhyung Kim p = strchr(p, ' ');
626fd99b7fSNamhyung Kim if (p == NULL)
636fd99b7fSNamhyung Kim continue;
647982a898SNamhyung Kim
656fd99b7fSNamhyung Kim *p++ = '\0';
667982a898SNamhyung Kim
676fd99b7fSNamhyung Kim /* check filesystem type */
686fd99b7fSNamhyung Kim if (strncmp(p, "cgroup", 6))
696fd99b7fSNamhyung Kim continue;
706fd99b7fSNamhyung Kim
716fd99b7fSNamhyung Kim if (p[6] == '2') {
726fd99b7fSNamhyung Kim /* save cgroup v2 path */
736fd99b7fSNamhyung Kim strcpy(mountpoint, path);
746fd99b7fSNamhyung Kim continue;
756fd99b7fSNamhyung Kim }
766fd99b7fSNamhyung Kim
776fd99b7fSNamhyung Kim /* now we have cgroup v1, check the options for subsystem */
786fd99b7fSNamhyung Kim p += 7;
796fd99b7fSNamhyung Kim
806fd99b7fSNamhyung Kim p = strstr(p, subsys);
816fd99b7fSNamhyung Kim if (p == NULL)
826fd99b7fSNamhyung Kim continue;
836fd99b7fSNamhyung Kim
846fd99b7fSNamhyung Kim /* sanity check: it should be separated by a space or a comma */
856fd99b7fSNamhyung Kim if (!strchr(" ,", p[-1]) || !strchr(" ,", p[strlen(subsys)]))
866fd99b7fSNamhyung Kim continue;
876fd99b7fSNamhyung Kim
886fd99b7fSNamhyung Kim strcpy(mountpoint, path);
896fd99b7fSNamhyung Kim break;
906fd99b7fSNamhyung Kim }
916fd99b7fSNamhyung Kim free(line);
9227ab1c1cSNamhyung Kim fclose(fp);
9327ab1c1cSNamhyung Kim
94*7a3fb8b5SIan Rogers if (!cached)
95*7a3fb8b5SIan Rogers cached = calloc(1, sizeof(*cached));
96*7a3fb8b5SIan Rogers
97*7a3fb8b5SIan Rogers if (cached) {
98*7a3fb8b5SIan Rogers strncpy(cached->subsys, subsys, sizeof(cached->subsys) - 1);
99*7a3fb8b5SIan Rogers strcpy(cached->mountpoint, mountpoint);
100*7a3fb8b5SIan Rogers }
10148859e52SNamhyung Kim
1026fd99b7fSNamhyung Kim if (mountpoint[0] && strlen(mountpoint) < maxlen) {
10327ab1c1cSNamhyung Kim strcpy(buf, mountpoint);
10427ab1c1cSNamhyung Kim return 0;
10527ab1c1cSNamhyung Kim }
10627ab1c1cSNamhyung Kim return -1;
1077982a898SNamhyung Kim }
108