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