Linux 6.9-rc1
[linux-2.6-microblaze.git] / tools / lib / api / fs / cgroup.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/stringify.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include "fs.h"
10
11 struct cgroupfs_cache_entry {
12         char    subsys[32];
13         char    mountpoint[PATH_MAX];
14 };
15
16 /* just cache last used one */
17 static struct cgroupfs_cache_entry *cached;
18
19 int cgroupfs_find_mountpoint(char *buf, size_t maxlen, const char *subsys)
20 {
21         FILE *fp;
22         char *line = NULL;
23         size_t len = 0;
24         char *p, *path;
25         char mountpoint[PATH_MAX];
26
27         if (cached && !strcmp(cached->subsys, subsys)) {
28                 if (strlen(cached->mountpoint) < maxlen) {
29                         strcpy(buf, cached->mountpoint);
30                         return 0;
31                 }
32                 return -1;
33         }
34
35         fp = fopen("/proc/mounts", "r");
36         if (!fp)
37                 return -1;
38
39         /*
40          * in order to handle split hierarchy, we need to scan /proc/mounts
41          * and inspect every cgroupfs mount point to find one that has
42          * the given subsystem.  If we found v1, just use it.  If not we can
43          * use v2 path as a fallback.
44          */
45         mountpoint[0] = '\0';
46
47         /*
48          * The /proc/mounts has the follow format:
49          *
50          *   <devname> <mount point> <fs type> <options> ...
51          *
52          */
53         while (getline(&line, &len, fp) != -1) {
54                 /* skip devname */
55                 p = strchr(line, ' ');
56                 if (p == NULL)
57                         continue;
58
59                 /* save the mount point */
60                 path = ++p;
61                 p = strchr(p, ' ');
62                 if (p == NULL)
63                         continue;
64
65                 *p++ = '\0';
66
67                 /* check filesystem type */
68                 if (strncmp(p, "cgroup", 6))
69                         continue;
70
71                 if (p[6] == '2') {
72                         /* save cgroup v2 path */
73                         strcpy(mountpoint, path);
74                         continue;
75                 }
76
77                 /* now we have cgroup v1, check the options for subsystem */
78                 p += 7;
79
80                 p = strstr(p, subsys);
81                 if (p == NULL)
82                         continue;
83
84                 /* sanity check: it should be separated by a space or a comma */
85                 if (!strchr(" ,", p[-1]) || !strchr(" ,", p[strlen(subsys)]))
86                         continue;
87
88                 strcpy(mountpoint, path);
89                 break;
90         }
91         free(line);
92         fclose(fp);
93
94         if (!cached)
95                 cached = calloc(1, sizeof(*cached));
96
97         if (cached) {
98                 strncpy(cached->subsys, subsys, sizeof(cached->subsys) - 1);
99                 strcpy(cached->mountpoint, mountpoint);
100         }
101
102         if (mountpoint[0] && strlen(mountpoint) < maxlen) {
103                 strcpy(buf, mountpoint);
104                 return 0;
105         }
106         return -1;
107 }