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 
cgroupfs_find_mountpoint(char * buf,size_t maxlen,const char * subsys)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 }
108