blob: 77f030e38ba3eca468bc47809f22e7fdbec59a8d [file] [log] [blame]
/*
* cg_map.c cgroup v2 cache
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Authors: Dmitry Yakunin <zeil@yandex-team.ru>
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <linux/types.h>
#include <linux/limits.h>
#include <ftw.h>
#include "cg_map.h"
#include "list.h"
#include "utils.h"
struct cg_cache {
struct hlist_node id_hash;
__u64 id;
char path[];
};
#define IDMAP_SIZE 1024
static struct hlist_head id_head[IDMAP_SIZE];
static struct cg_cache *cg_get_by_id(__u64 id)
{
unsigned int h = id & (IDMAP_SIZE - 1);
struct hlist_node *n;
hlist_for_each(n, &id_head[h]) {
struct cg_cache *cg;
cg = container_of(n, struct cg_cache, id_hash);
if (cg->id == id)
return cg;
}
return NULL;
}
static struct cg_cache *cg_entry_create(__u64 id, const char *path)
{
unsigned int h = id & (IDMAP_SIZE - 1);
struct cg_cache *cg;
cg = malloc(sizeof(*cg) + strlen(path) + 1);
if (!cg) {
fprintf(stderr,
"Failed to allocate memory for cgroup2 cache entry");
return NULL;
}
cg->id = id;
strcpy(cg->path, path);
hlist_add_head(&cg->id_hash, &id_head[h]);
return cg;
}
static int mntlen;
static int nftw_fn(const char *fpath, const struct stat *sb,
int typeflag, struct FTW *ftw)
{
const char *path;
__u64 id;
if (typeflag != FTW_D)
return 0;
id = get_cgroup2_id(fpath);
if (!id)
return -1;
path = fpath + mntlen;
if (*path == '\0')
/* root cgroup */
path = "/";
if (!cg_entry_create(id, path))
return -1;
return 0;
}
static void cg_init_map(void)
{
char *mnt;
mnt = find_cgroup2_mount(false);
if (!mnt)
exit(1);
mntlen = strlen(mnt);
if (nftw(mnt, nftw_fn, 1024, FTW_MOUNT) < 0)
exit(1);
free(mnt);
}
const char *cg_id_to_path(__u64 id)
{
static int initialized;
static char buf[64];
const struct cg_cache *cg;
char *path;
if (!initialized) {
cg_init_map();
initialized = 1;
}
cg = cg_get_by_id(id);
if (cg)
return cg->path;
path = get_cgroup2_path(id, false);
if (path) {
cg = cg_entry_create(id, path);
free(path);
if (cg)
return cg->path;
}
snprintf(buf, sizeof(buf), "unreachable:%llx", id);
return buf;
}