162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/stringify.h>
362306a36Sopenharmony_ci#include <sys/types.h>
462306a36Sopenharmony_ci#include <sys/stat.h>
562306a36Sopenharmony_ci#include <fcntl.h>
662306a36Sopenharmony_ci#include <stdio.h>
762306a36Sopenharmony_ci#include <stdlib.h>
862306a36Sopenharmony_ci#include <string.h>
962306a36Sopenharmony_ci#include "fs.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistruct cgroupfs_cache_entry {
1262306a36Sopenharmony_ci	char	subsys[32];
1362306a36Sopenharmony_ci	char	mountpoint[PATH_MAX];
1462306a36Sopenharmony_ci};
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/* just cache last used one */
1762306a36Sopenharmony_cistatic struct cgroupfs_cache_entry *cached;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ciint cgroupfs_find_mountpoint(char *buf, size_t maxlen, const char *subsys)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	FILE *fp;
2262306a36Sopenharmony_ci	char *line = NULL;
2362306a36Sopenharmony_ci	size_t len = 0;
2462306a36Sopenharmony_ci	char *p, *path;
2562306a36Sopenharmony_ci	char mountpoint[PATH_MAX];
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	if (cached && !strcmp(cached->subsys, subsys)) {
2862306a36Sopenharmony_ci		if (strlen(cached->mountpoint) < maxlen) {
2962306a36Sopenharmony_ci			strcpy(buf, cached->mountpoint);
3062306a36Sopenharmony_ci			return 0;
3162306a36Sopenharmony_ci		}
3262306a36Sopenharmony_ci		return -1;
3362306a36Sopenharmony_ci	}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	fp = fopen("/proc/mounts", "r");
3662306a36Sopenharmony_ci	if (!fp)
3762306a36Sopenharmony_ci		return -1;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	/*
4062306a36Sopenharmony_ci	 * in order to handle split hierarchy, we need to scan /proc/mounts
4162306a36Sopenharmony_ci	 * and inspect every cgroupfs mount point to find one that has
4262306a36Sopenharmony_ci	 * the given subsystem.  If we found v1, just use it.  If not we can
4362306a36Sopenharmony_ci	 * use v2 path as a fallback.
4462306a36Sopenharmony_ci	 */
4562306a36Sopenharmony_ci	mountpoint[0] = '\0';
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	/*
4862306a36Sopenharmony_ci	 * The /proc/mounts has the follow format:
4962306a36Sopenharmony_ci	 *
5062306a36Sopenharmony_ci	 *   <devname> <mount point> <fs type> <options> ...
5162306a36Sopenharmony_ci	 *
5262306a36Sopenharmony_ci	 */
5362306a36Sopenharmony_ci	while (getline(&line, &len, fp) != -1) {
5462306a36Sopenharmony_ci		/* skip devname */
5562306a36Sopenharmony_ci		p = strchr(line, ' ');
5662306a36Sopenharmony_ci		if (p == NULL)
5762306a36Sopenharmony_ci			continue;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci		/* save the mount point */
6062306a36Sopenharmony_ci		path = ++p;
6162306a36Sopenharmony_ci		p = strchr(p, ' ');
6262306a36Sopenharmony_ci		if (p == NULL)
6362306a36Sopenharmony_ci			continue;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci		*p++ = '\0';
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci		/* check filesystem type */
6862306a36Sopenharmony_ci		if (strncmp(p, "cgroup", 6))
6962306a36Sopenharmony_ci			continue;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		if (p[6] == '2') {
7262306a36Sopenharmony_ci			/* save cgroup v2 path */
7362306a36Sopenharmony_ci			strcpy(mountpoint, path);
7462306a36Sopenharmony_ci			continue;
7562306a36Sopenharmony_ci		}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci		/* now we have cgroup v1, check the options for subsystem */
7862306a36Sopenharmony_ci		p += 7;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		p = strstr(p, subsys);
8162306a36Sopenharmony_ci		if (p == NULL)
8262306a36Sopenharmony_ci			continue;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci		/* sanity check: it should be separated by a space or a comma */
8562306a36Sopenharmony_ci		if (!strchr(" ,", p[-1]) || !strchr(" ,", p[strlen(subsys)]))
8662306a36Sopenharmony_ci			continue;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci		strcpy(mountpoint, path);
8962306a36Sopenharmony_ci		break;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci	free(line);
9262306a36Sopenharmony_ci	fclose(fp);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (!cached)
9562306a36Sopenharmony_ci		cached = calloc(1, sizeof(*cached));
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (cached) {
9862306a36Sopenharmony_ci		strncpy(cached->subsys, subsys, sizeof(cached->subsys) - 1);
9962306a36Sopenharmony_ci		strcpy(cached->mountpoint, mountpoint);
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (mountpoint[0] && strlen(mountpoint) < maxlen) {
10362306a36Sopenharmony_ci		strcpy(buf, mountpoint);
10462306a36Sopenharmony_ci		return 0;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci	return -1;
10762306a36Sopenharmony_ci}
108