0001
0002 #define _GNU_SOURCE
0003 #include <sched.h>
0004 #include <sys/mount.h>
0005 #include <sys/stat.h>
0006 #include <sys/types.h>
0007 #include <linux/limits.h>
0008 #include <stdio.h>
0009 #include <stdlib.h>
0010 #include <linux/sched.h>
0011 #include <fcntl.h>
0012 #include <unistd.h>
0013 #include <ftw.h>
0014
0015 #include "cgroup_helpers.h"
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030 #define WALK_FD_LIMIT 16
0031
0032 #define CGROUP_MOUNT_PATH "/mnt"
0033 #define CGROUP_MOUNT_DFLT "/sys/fs/cgroup"
0034 #define NETCLS_MOUNT_PATH CGROUP_MOUNT_DFLT "/net_cls"
0035 #define CGROUP_WORK_DIR "/cgroup-test-work-dir"
0036 #define format_cgroup_path(buf, path) \
0037 snprintf(buf, sizeof(buf), "%s%s%d%s", CGROUP_MOUNT_PATH, \
0038 CGROUP_WORK_DIR, getpid(), path)
0039
0040 #define format_classid_path(buf) \
0041 snprintf(buf, sizeof(buf), "%s%s", NETCLS_MOUNT_PATH, \
0042 CGROUP_WORK_DIR)
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052 static int enable_all_controllers(char *cgroup_path)
0053 {
0054 char path[PATH_MAX + 1];
0055 char buf[PATH_MAX];
0056 char *c, *c2;
0057 int fd, cfd;
0058 ssize_t len;
0059
0060 snprintf(path, sizeof(path), "%s/cgroup.controllers", cgroup_path);
0061 fd = open(path, O_RDONLY);
0062 if (fd < 0) {
0063 log_err("Opening cgroup.controllers: %s", path);
0064 return 1;
0065 }
0066
0067 len = read(fd, buf, sizeof(buf) - 1);
0068 if (len < 0) {
0069 close(fd);
0070 log_err("Reading cgroup.controllers: %s", path);
0071 return 1;
0072 }
0073 buf[len] = 0;
0074 close(fd);
0075
0076
0077 if (len == 0)
0078 return 0;
0079
0080 snprintf(path, sizeof(path), "%s/cgroup.subtree_control", cgroup_path);
0081 cfd = open(path, O_RDWR);
0082 if (cfd < 0) {
0083 log_err("Opening cgroup.subtree_control: %s", path);
0084 return 1;
0085 }
0086
0087 for (c = strtok_r(buf, " ", &c2); c; c = strtok_r(NULL, " ", &c2)) {
0088 if (dprintf(cfd, "+%s\n", c) <= 0) {
0089 log_err("Enabling controller %s: %s", c, path);
0090 close(cfd);
0091 return 1;
0092 }
0093 }
0094 close(cfd);
0095 return 0;
0096 }
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107 int setup_cgroup_environment(void)
0108 {
0109 char cgroup_workdir[PATH_MAX - 24];
0110
0111 format_cgroup_path(cgroup_workdir, "");
0112
0113 if (unshare(CLONE_NEWNS)) {
0114 log_err("unshare");
0115 return 1;
0116 }
0117
0118 if (mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL)) {
0119 log_err("mount fakeroot");
0120 return 1;
0121 }
0122
0123 if (mount("none", CGROUP_MOUNT_PATH, "cgroup2", 0, NULL) && errno != EBUSY) {
0124 log_err("mount cgroup2");
0125 return 1;
0126 }
0127
0128
0129 cleanup_cgroup_environment();
0130
0131 if (mkdir(cgroup_workdir, 0777) && errno != EEXIST) {
0132 log_err("mkdir cgroup work dir");
0133 return 1;
0134 }
0135
0136 if (enable_all_controllers(cgroup_workdir))
0137 return 1;
0138
0139 return 0;
0140 }
0141
0142 static int nftwfunc(const char *filename, const struct stat *statptr,
0143 int fileflags, struct FTW *pfwt)
0144 {
0145 if ((fileflags & FTW_D) && rmdir(filename))
0146 log_err("Removing cgroup: %s", filename);
0147 return 0;
0148 }
0149
0150 static int join_cgroup_from_top(const char *cgroup_path)
0151 {
0152 char cgroup_procs_path[PATH_MAX + 1];
0153 pid_t pid = getpid();
0154 int fd, rc = 0;
0155
0156 snprintf(cgroup_procs_path, sizeof(cgroup_procs_path),
0157 "%s/cgroup.procs", cgroup_path);
0158
0159 fd = open(cgroup_procs_path, O_WRONLY);
0160 if (fd < 0) {
0161 log_err("Opening Cgroup Procs: %s", cgroup_procs_path);
0162 return 1;
0163 }
0164
0165 if (dprintf(fd, "%d\n", pid) < 0) {
0166 log_err("Joining Cgroup");
0167 rc = 1;
0168 }
0169
0170 close(fd);
0171 return rc;
0172 }
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185 int join_cgroup(const char *path)
0186 {
0187 char cgroup_path[PATH_MAX + 1];
0188
0189 format_cgroup_path(cgroup_path, path);
0190 return join_cgroup_from_top(cgroup_path);
0191 }
0192
0193
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204
0205
0206 void cleanup_cgroup_environment(void)
0207 {
0208 char cgroup_workdir[PATH_MAX + 1];
0209
0210 format_cgroup_path(cgroup_workdir, "");
0211 join_cgroup_from_top(CGROUP_MOUNT_PATH);
0212 nftw(cgroup_workdir, nftwfunc, WALK_FD_LIMIT, FTW_DEPTH | FTW_MOUNT);
0213 }
0214
0215
0216
0217
0218
0219
0220
0221
0222
0223
0224
0225 int create_and_get_cgroup(const char *path)
0226 {
0227 char cgroup_path[PATH_MAX + 1];
0228 int fd;
0229
0230 format_cgroup_path(cgroup_path, path);
0231 if (mkdir(cgroup_path, 0777) && errno != EEXIST) {
0232 log_err("mkdiring cgroup %s .. %s", path, cgroup_path);
0233 return -1;
0234 }
0235
0236 fd = open(cgroup_path, O_RDONLY);
0237 if (fd < 0) {
0238 log_err("Opening Cgroup");
0239 return -1;
0240 }
0241
0242 return fd;
0243 }
0244
0245
0246
0247
0248
0249
0250
0251
0252
0253 unsigned long long get_cgroup_id(const char *path)
0254 {
0255 int dirfd, err, flags, mount_id, fhsize;
0256 union {
0257 unsigned long long cgid;
0258 unsigned char raw_bytes[8];
0259 } id;
0260 char cgroup_workdir[PATH_MAX + 1];
0261 struct file_handle *fhp, *fhp2;
0262 unsigned long long ret = 0;
0263
0264 format_cgroup_path(cgroup_workdir, path);
0265
0266 dirfd = AT_FDCWD;
0267 flags = 0;
0268 fhsize = sizeof(*fhp);
0269 fhp = calloc(1, fhsize);
0270 if (!fhp) {
0271 log_err("calloc");
0272 return 0;
0273 }
0274 err = name_to_handle_at(dirfd, cgroup_workdir, fhp, &mount_id, flags);
0275 if (err >= 0 || fhp->handle_bytes != 8) {
0276 log_err("name_to_handle_at");
0277 goto free_mem;
0278 }
0279
0280 fhsize = sizeof(struct file_handle) + fhp->handle_bytes;
0281 fhp2 = realloc(fhp, fhsize);
0282 if (!fhp2) {
0283 log_err("realloc");
0284 goto free_mem;
0285 }
0286 err = name_to_handle_at(dirfd, cgroup_workdir, fhp2, &mount_id, flags);
0287 fhp = fhp2;
0288 if (err < 0) {
0289 log_err("name_to_handle_at");
0290 goto free_mem;
0291 }
0292
0293 memcpy(id.raw_bytes, fhp->f_handle, 8);
0294 ret = id.cgid;
0295
0296 free_mem:
0297 free(fhp);
0298 return ret;
0299 }
0300
0301 int cgroup_setup_and_join(const char *path) {
0302 int cg_fd;
0303
0304 if (setup_cgroup_environment()) {
0305 fprintf(stderr, "Failed to setup cgroup environment\n");
0306 return -EINVAL;
0307 }
0308
0309 cg_fd = create_and_get_cgroup(path);
0310 if (cg_fd < 0) {
0311 fprintf(stderr, "Failed to create test cgroup\n");
0312 cleanup_cgroup_environment();
0313 return cg_fd;
0314 }
0315
0316 if (join_cgroup(path)) {
0317 fprintf(stderr, "Failed to join cgroup\n");
0318 cleanup_cgroup_environment();
0319 return -EINVAL;
0320 }
0321 return cg_fd;
0322 }
0323
0324
0325
0326
0327
0328
0329
0330
0331
0332
0333 int setup_classid_environment(void)
0334 {
0335 char cgroup_workdir[PATH_MAX + 1];
0336
0337 format_classid_path(cgroup_workdir);
0338
0339 if (mount("tmpfs", CGROUP_MOUNT_DFLT, "tmpfs", 0, NULL) &&
0340 errno != EBUSY) {
0341 log_err("mount cgroup base");
0342 return 1;
0343 }
0344
0345 if (mkdir(NETCLS_MOUNT_PATH, 0777) && errno != EEXIST) {
0346 log_err("mkdir cgroup net_cls");
0347 return 1;
0348 }
0349
0350 if (mount("net_cls", NETCLS_MOUNT_PATH, "cgroup", 0, "net_cls") &&
0351 errno != EBUSY) {
0352 log_err("mount cgroup net_cls");
0353 return 1;
0354 }
0355
0356 cleanup_classid_environment();
0357
0358 if (mkdir(cgroup_workdir, 0777) && errno != EEXIST) {
0359 log_err("mkdir cgroup work dir");
0360 return 1;
0361 }
0362
0363 return 0;
0364 }
0365
0366
0367
0368
0369
0370
0371
0372
0373
0374
0375
0376 int set_classid(unsigned int id)
0377 {
0378 char cgroup_workdir[PATH_MAX - 42];
0379 char cgroup_classid_path[PATH_MAX + 1];
0380 int fd, rc = 0;
0381
0382 format_classid_path(cgroup_workdir);
0383 snprintf(cgroup_classid_path, sizeof(cgroup_classid_path),
0384 "%s/net_cls.classid", cgroup_workdir);
0385
0386 fd = open(cgroup_classid_path, O_WRONLY);
0387 if (fd < 0) {
0388 log_err("Opening cgroup classid: %s", cgroup_classid_path);
0389 return 1;
0390 }
0391
0392 if (dprintf(fd, "%u\n", id) < 0) {
0393 log_err("Setting cgroup classid");
0394 rc = 1;
0395 }
0396
0397 close(fd);
0398 return rc;
0399 }
0400
0401
0402
0403
0404
0405
0406
0407
0408
0409
0410 int join_classid(void)
0411 {
0412 char cgroup_workdir[PATH_MAX + 1];
0413
0414 format_classid_path(cgroup_workdir);
0415 return join_cgroup_from_top(cgroup_workdir);
0416 }
0417
0418
0419
0420
0421
0422
0423
0424
0425
0426 void cleanup_classid_environment(void)
0427 {
0428 char cgroup_workdir[PATH_MAX + 1];
0429
0430 format_classid_path(cgroup_workdir);
0431 join_cgroup_from_top(NETCLS_MOUNT_PATH);
0432 nftw(cgroup_workdir, nftwfunc, WALK_FD_LIMIT, FTW_DEPTH | FTW_MOUNT);
0433 }