1
2#define _GNU_SOURCE
3#include <sched.h>
4#include <sys/mount.h>
5#include <sys/stat.h>
6#include <sys/types.h>
7#include <linux/limits.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <linux/sched.h>
11#include <fcntl.h>
12#include <unistd.h>
13#include <ftw.h>
14
15#include "cgroup_helpers.h"
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30#define WALK_FD_LIMIT 16
31
32#define CGROUP_MOUNT_PATH "/mnt"
33#define CGROUP_MOUNT_DFLT "/sys/fs/cgroup"
34#define NETCLS_MOUNT_PATH CGROUP_MOUNT_DFLT "/net_cls"
35#define CGROUP_WORK_DIR "/cgroup-test-work-dir"
36
37#define format_cgroup_path(buf, path) \
38 snprintf(buf, sizeof(buf), "%s%s%s", CGROUP_MOUNT_PATH, \
39 CGROUP_WORK_DIR, path)
40
41#define format_classid_path(buf) \
42 snprintf(buf, sizeof(buf), "%s%s", NETCLS_MOUNT_PATH, \
43 CGROUP_WORK_DIR)
44
45
46
47
48
49
50
51
52
53static int enable_all_controllers(char *cgroup_path)
54{
55 char path[PATH_MAX + 1];
56 char buf[PATH_MAX];
57 char *c, *c2;
58 int fd, cfd;
59 ssize_t len;
60
61 snprintf(path, sizeof(path), "%s/cgroup.controllers", cgroup_path);
62 fd = open(path, O_RDONLY);
63 if (fd < 0) {
64 log_err("Opening cgroup.controllers: %s", path);
65 return 1;
66 }
67
68 len = read(fd, buf, sizeof(buf) - 1);
69 if (len < 0) {
70 close(fd);
71 log_err("Reading cgroup.controllers: %s", path);
72 return 1;
73 }
74 buf[len] = 0;
75 close(fd);
76
77
78 if (len == 0)
79 return 0;
80
81 snprintf(path, sizeof(path), "%s/cgroup.subtree_control", cgroup_path);
82 cfd = open(path, O_RDWR);
83 if (cfd < 0) {
84 log_err("Opening cgroup.subtree_control: %s", path);
85 return 1;
86 }
87
88 for (c = strtok_r(buf, " ", &c2); c; c = strtok_r(NULL, " ", &c2)) {
89 if (dprintf(cfd, "+%s\n", c) <= 0) {
90 log_err("Enabling controller %s: %s", c, path);
91 close(cfd);
92 return 1;
93 }
94 }
95 close(cfd);
96 return 0;
97}
98
99
100
101
102
103
104
105
106
107
108int setup_cgroup_environment(void)
109{
110 char cgroup_workdir[PATH_MAX - 24];
111
112 format_cgroup_path(cgroup_workdir, "");
113
114 if (unshare(CLONE_NEWNS)) {
115 log_err("unshare");
116 return 1;
117 }
118
119 if (mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL)) {
120 log_err("mount fakeroot");
121 return 1;
122 }
123
124 if (mount("none", CGROUP_MOUNT_PATH, "cgroup2", 0, NULL) && errno != EBUSY) {
125 log_err("mount cgroup2");
126 return 1;
127 }
128
129
130 cleanup_cgroup_environment();
131
132 if (mkdir(cgroup_workdir, 0777) && errno != EEXIST) {
133 log_err("mkdir cgroup work dir");
134 return 1;
135 }
136
137 if (enable_all_controllers(cgroup_workdir))
138 return 1;
139
140 return 0;
141}
142
143static int nftwfunc(const char *filename, const struct stat *statptr,
144 int fileflags, struct FTW *pfwt)
145{
146 if ((fileflags & FTW_D) && rmdir(filename))
147 log_err("Removing cgroup: %s", filename);
148 return 0;
149}
150
151static int join_cgroup_from_top(const char *cgroup_path)
152{
153 char cgroup_procs_path[PATH_MAX + 1];
154 pid_t pid = getpid();
155 int fd, rc = 0;
156
157 snprintf(cgroup_procs_path, sizeof(cgroup_procs_path),
158 "%s/cgroup.procs", cgroup_path);
159
160 fd = open(cgroup_procs_path, O_WRONLY);
161 if (fd < 0) {
162 log_err("Opening Cgroup Procs: %s", cgroup_procs_path);
163 return 1;
164 }
165
166 if (dprintf(fd, "%d\n", pid) < 0) {
167 log_err("Joining Cgroup");
168 rc = 1;
169 }
170
171 close(fd);
172 return rc;
173}
174
175
176
177
178
179
180
181
182
183
184
185
186int join_cgroup(const char *path)
187{
188 char cgroup_path[PATH_MAX + 1];
189
190 format_cgroup_path(cgroup_path, path);
191 return join_cgroup_from_top(cgroup_path);
192}
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207void cleanup_cgroup_environment(void)
208{
209 char cgroup_workdir[PATH_MAX + 1];
210
211 format_cgroup_path(cgroup_workdir, "");
212 join_cgroup_from_top(CGROUP_MOUNT_PATH);
213 nftw(cgroup_workdir, nftwfunc, WALK_FD_LIMIT, FTW_DEPTH | FTW_MOUNT);
214}
215
216
217
218
219
220
221
222
223
224
225
226int create_and_get_cgroup(const char *path)
227{
228 char cgroup_path[PATH_MAX + 1];
229 int fd;
230
231 format_cgroup_path(cgroup_path, path);
232 if (mkdir(cgroup_path, 0777) && errno != EEXIST) {
233 log_err("mkdiring cgroup %s .. %s", path, cgroup_path);
234 return -1;
235 }
236
237 fd = open(cgroup_path, O_RDONLY);
238 if (fd < 0) {
239 log_err("Opening Cgroup");
240 return -1;
241 }
242
243 return fd;
244}
245
246
247
248
249
250
251
252
253
254unsigned long long get_cgroup_id(const char *path)
255{
256 int dirfd, err, flags, mount_id, fhsize;
257 union {
258 unsigned long long cgid;
259 unsigned char raw_bytes[8];
260 } id;
261 char cgroup_workdir[PATH_MAX + 1];
262 struct file_handle *fhp, *fhp2;
263 unsigned long long ret = 0;
264
265 format_cgroup_path(cgroup_workdir, path);
266
267 dirfd = AT_FDCWD;
268 flags = 0;
269 fhsize = sizeof(*fhp);
270 fhp = calloc(1, fhsize);
271 if (!fhp) {
272 log_err("calloc");
273 return 0;
274 }
275 err = name_to_handle_at(dirfd, cgroup_workdir, fhp, &mount_id, flags);
276 if (err >= 0 || fhp->handle_bytes != 8) {
277 log_err("name_to_handle_at");
278 goto free_mem;
279 }
280
281 fhsize = sizeof(struct file_handle) + fhp->handle_bytes;
282 fhp2 = realloc(fhp, fhsize);
283 if (!fhp2) {
284 log_err("realloc");
285 goto free_mem;
286 }
287 err = name_to_handle_at(dirfd, cgroup_workdir, fhp2, &mount_id, flags);
288 fhp = fhp2;
289 if (err < 0) {
290 log_err("name_to_handle_at");
291 goto free_mem;
292 }
293
294 memcpy(id.raw_bytes, fhp->f_handle, 8);
295 ret = id.cgid;
296
297free_mem:
298 free(fhp);
299 return ret;
300}
301
302int cgroup_setup_and_join(const char *path) {
303 int cg_fd;
304
305 if (setup_cgroup_environment()) {
306 fprintf(stderr, "Failed to setup cgroup environment\n");
307 return -EINVAL;
308 }
309
310 cg_fd = create_and_get_cgroup(path);
311 if (cg_fd < 0) {
312 fprintf(stderr, "Failed to create test cgroup\n");
313 cleanup_cgroup_environment();
314 return cg_fd;
315 }
316
317 if (join_cgroup(path)) {
318 fprintf(stderr, "Failed to join cgroup\n");
319 cleanup_cgroup_environment();
320 return -EINVAL;
321 }
322 return cg_fd;
323}
324
325
326
327
328
329
330
331
332
333
334int setup_classid_environment(void)
335{
336 char cgroup_workdir[PATH_MAX + 1];
337
338 format_classid_path(cgroup_workdir);
339
340 if (mount("tmpfs", CGROUP_MOUNT_DFLT, "tmpfs", 0, NULL) &&
341 errno != EBUSY) {
342 log_err("mount cgroup base");
343 return 1;
344 }
345
346 if (mkdir(NETCLS_MOUNT_PATH, 0777) && errno != EEXIST) {
347 log_err("mkdir cgroup net_cls");
348 return 1;
349 }
350
351 if (mount("net_cls", NETCLS_MOUNT_PATH, "cgroup", 0, "net_cls") &&
352 errno != EBUSY) {
353 log_err("mount cgroup net_cls");
354 return 1;
355 }
356
357 cleanup_classid_environment();
358
359 if (mkdir(cgroup_workdir, 0777) && errno != EEXIST) {
360 log_err("mkdir cgroup work dir");
361 return 1;
362 }
363
364 return 0;
365}
366
367
368
369
370
371
372
373
374
375
376
377int set_classid(unsigned int id)
378{
379 char cgroup_workdir[PATH_MAX - 42];
380 char cgroup_classid_path[PATH_MAX + 1];
381 int fd, rc = 0;
382
383 format_classid_path(cgroup_workdir);
384 snprintf(cgroup_classid_path, sizeof(cgroup_classid_path),
385 "%s/net_cls.classid", cgroup_workdir);
386
387 fd = open(cgroup_classid_path, O_WRONLY);
388 if (fd < 0) {
389 log_err("Opening cgroup classid: %s", cgroup_classid_path);
390 return 1;
391 }
392
393 if (dprintf(fd, "%u\n", id) < 0) {
394 log_err("Setting cgroup classid");
395 rc = 1;
396 }
397
398 close(fd);
399 return rc;
400}
401
402
403
404
405
406
407
408
409
410
411int join_classid(void)
412{
413 char cgroup_workdir[PATH_MAX + 1];
414
415 format_classid_path(cgroup_workdir);
416 return join_cgroup_from_top(cgroup_workdir);
417}
418
419
420
421
422
423
424
425
426
427void cleanup_classid_environment(void)
428{
429 char cgroup_workdir[PATH_MAX + 1];
430
431 format_classid_path(cgroup_workdir);
432 join_cgroup_from_top(NETCLS_MOUNT_PATH);
433 nftw(cgroup_workdir, nftwfunc, WALK_FD_LIMIT, FTW_DEPTH | FTW_MOUNT);
434}
435