aboutsummaryrefslogtreecommitdiffstats
path: root/sandbox/cgroup.c
blob: 885824fdb12c65ca4a9432846320fdc25d57e37f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include "sandbox.h"
#include <mntent.h>

static void cgroup_config(const char *template, const char *name, const char *str)
{
	char filename[strlen(template) + strlen(name) + 2];
	snprintf(filename, sizeof(filename), "%s/%s", template, name);
	int fd = open(filename, O_TRUNC | O_WRONLY);
	if (fd == -1)
		bug("open cgroup property: %s", filename);
	if (write(fd, str, strlen(str)) == -1)
		bug("write cgroup error (val)");
	close(fd);
}

static void cgroup_config_int(const char *template, const char *name, uint64_t val)
{
	char buf[21]; // 0xffff_ffff_ffff_ffff.to_s.size #=> 20
	snprintf(buf, sizeof(buf), "%"PRIu64, val);
	cgroup_config(template, name, buf);
}

/// on_exit でよばれる
static void cgroup_destroy(unused int status, void *cgroup_)
{
	char *cgroup = (char *)cgroup_;
	struct stat sb;
	if (stat(cgroup, &sb))
		bug("cgroup does not exist?");

	if (rmdir(cgroup)) {
		// まだプロセスが生き残ってる?
		// pids.max を 0 にして fork を禁止してから SIGKILL るよ
		cgroup_config_int(cgroup, "pids.max", 0);
		char *procs_file = xformat("%s/cgroup.procs", cgroup);
		FILE *f = fopen(procs_file, "r");
		if (!f)
			bug("cgroup.procs open error");
		int spid;
		while (fscanf(f, "%d", &spid) != EOF)
			kill(spid, SIGKILL);
		while (waitpid(-1, NULL, __WALL) != -1);
		if (errno != ECHILD)
			bug("waitpid failed");
		fclose(f);
		if (rmdir(cgroup))
			bug("cgroup rmdir error");
	}
	free(cgroup);
}

static int cgroup_find(bool *is_mounted)
{
	*is_mounted = false;

	FILE* mtab = setmntent("/etc/mtab", "r");
	if (!mtab)
		return error("failed to open /etc/mtab");

	struct mntent *e;
	while ((e = getmntent(mtab))) {
		if (strcmp(e->mnt_dir, POE_CGROUP_ROOT))
			continue;
		/* something is mounted */
		if (strcmp(e->mnt_type, "cgroup2")) /* is not cgroup2 */
			return error("other filesystem is mounted");
		*is_mounted = true;
		break;
	}

	endmntent(mtab);
	return 0;
}

struct cgroups *poe_cgroups_init(const char *tmpdir)
{
	struct cgroups *cg = xmalloc(sizeof(*cg));

	bool is_mounted;
	if (cgroup_find(&is_mounted))
		return NULL;
	if (!is_mounted) {
		struct stat sb;
		if (stat(POE_CGROUP_ROOT, &sb)) {
			if (errno != ENOENT) {
				error("cgroup root dir not accessible");
				return NULL;
			}
			/* if not exist */
			if (mkdir(POE_CGROUP_ROOT, 0755)) {
				error("can't create cgroup root dir");
				return NULL;
			}
		}
		if (mount("poe-cgroup2", POE_CGROUP_ROOT, "cgroup2", 0, NULL)) {
			error("can't mount cgroup root dir");
			return NULL;
		}
	}

	/* create sub group */
	struct stat sb;
	if (stat(POE_CGROUP_ROOT"/poe", &sb)) {
		if (errno != ENOENT) {
			error("can't access sub cgroup");
			return NULL;
		}

		if (mkdir(POE_CGROUP_ROOT"/poe", 0755)) {
			error("can't create sub cgroup");
			return NULL;
		}
	}

	/* then, configure it */
	cgroup_config(POE_CGROUP_ROOT"/poe", "cgroup.subtree_control", "+memory +pids");

	// 他の poe-sandbox が使用するかもしれないので、マウントした cgroup は削除しない
	// どうしようこれ

	return cg;
}

void poe_cgroups_destroy(struct cgroups *cg)
{
	free(cg);
}

int poe_cgroups_add(struct cgroups *cg, pid_t pid)
{
	char *template = xstrdup(POE_CGROUP_ROOT"/poe/poe-sandbox-XXXXXX");
	if (!mkdtemp(template))
		return error("child cgroup create failed");
	cgroup_config_int(template, "cgroup.procs", pid);
	// enable this when Linux 4.6 released, which will include cpu controller
	// cgroup_config_int(template, "cpu.shares", 512);
	cgroup_config_int(template, "memory.max", POE_MEMORY_LIMIT);
	cgroup_config_int(template, "memory.swap.max", POE_MEMORY_LIMIT);
	cgroup_config_int(template, "pids.max", POE_TASKS_LIMIT);

	if (on_exit(cgroup_destroy, template))
		bug("on_exit for cgroup failed");

	return 0;
}