/* * To avoid relying on the system setup, when setup_cgroup_env is called * we create a new mount namespace, and cgroup namespace. The cgroupv2 * root is mounted at CGROUP_MOUNT_PATH. Unfortunately, most people don't * have cgroupv2 enabled at this point in time. It's easier to create our * own mount namespace and manage it ourselves. We assume /mnt exists. * * Related cgroupv1 helpers are named *classid*(), since we only use the * net_cls controller for tagging net_cls.classid. We assume the default * mount under /sys/fs/cgroup/net_cls, which should be the case for the * vast majority of users.
*/
for (c = strtok_r(enable, " ", &c2); c; c = strtok_r(NULL, " ", &c2)) { if (dprintf(cfd, "+%s\n", c) <= 0) {
log_err("Enabling controller %s: %s", c, path);
close(cfd); return 1;
}
}
close(cfd); return 0;
}
/** * enable_controllers() - Enable cgroup v2 controllers * @relative_path: The cgroup path, relative to the workdir * @controllers: List of controllers to enable in cgroup.controllers format * * * Enable given cgroup v2 controllers, if @controllers is NULL, enable all * available controllers. * * If successful, 0 is returned.
*/ int enable_controllers(constchar *relative_path, constchar *controllers)
{ char cgroup_path[PATH_MAX + 1];
if (dprintf(fd, "%s", buf) <= 0) {
log_err("Writing to %s", file_path);
close(fd); return 1;
}
close(fd); return 0;
}
/** * write_cgroup_file() - Write to a cgroup file * @relative_path: The cgroup path, relative to the workdir * @file: The name of the file in cgroupfs to write to * @buf: Buffer to write to the file * * Write to a file in the given cgroup's directory. * * If successful, 0 is returned.
*/ int write_cgroup_file(constchar *relative_path, constchar *file, constchar *buf)
{ char cgroup_path[PATH_MAX - 24];
/** * write_cgroup_file_parent() - Write to a cgroup file in the parent process * workdir * @relative_path: The cgroup path, relative to the parent process workdir * @file: The name of the file in cgroupfs to write to * @buf: Buffer to write to the file * * Write to a file in the given cgroup's directory under the parent process * workdir. * * If successful, 0 is returned.
*/ int write_cgroup_file_parent(constchar *relative_path, constchar *file, constchar *buf)
{ char cgroup_path[PATH_MAX - 24];
/** * setup_cgroup_environment() - Setup the cgroup environment * * After calling this function, cleanup_cgroup_environment should be called * once testing is complete. * * This function will print an error to stderr and return 1 if it is unable * to setup the cgroup environment. If setup is successful, 0 is returned.
*/ int setup_cgroup_environment(void)
{ char cgroup_workdir[PATH_MAX - 24];
/* Cleanup existing failed runs, now that the environment is setup */
__cleanup_cgroup_environment();
if (mkdir(cgroup_workdir, 0777) && errno != EEXIST) {
log_err("mkdir cgroup work dir"); return 1;
}
/* Enable all available controllers to increase test coverage */ if (__enable_controllers(CGROUP_MOUNT_PATH, NULL) ||
__enable_controllers(cgroup_workdir, NULL)) return 1;
return 0;
}
staticint nftwfunc(constchar *filename, conststruct stat *statptr, int fileflags, struct FTW *pfwt)
{ if ((fileflags & FTW_D) && rmdir(filename))
log_err("Removing cgroup: %s", filename); return 0;
}
/** * join_cgroup() - Join a cgroup * @relative_path: The cgroup path, relative to the workdir, to join * * This function expects a cgroup to already be created, relative to the cgroup * work dir, and it joins it. For example, passing "/my-cgroup" as the path * would actually put the calling process into the cgroup * "/cgroup-test-work-dir/my-cgroup" * * On success, it returns 0, otherwise on failure it returns 1.
*/ int join_cgroup(constchar *relative_path)
{ char cgroup_path[PATH_MAX + 1];
/** * join_root_cgroup() - Join the root cgroup * * This function joins the root cgroup. * * On success, it returns 0, otherwise on failure it returns 1.
*/ int join_root_cgroup(void)
{ return join_cgroup_from_top(CGROUP_MOUNT_PATH);
}
/** * join_parent_cgroup() - Join a cgroup in the parent process workdir * @relative_path: The cgroup path, relative to parent process workdir, to join * * See join_cgroup(). * * On success, it returns 0, otherwise on failure it returns 1.
*/ int join_parent_cgroup(constchar *relative_path)
{ char cgroup_path[PATH_MAX + 1];
/** * set_cgroup_xattr() - Set xattr on a cgroup dir * @relative_path: The cgroup path, relative to the workdir, to set xattr * @name: xattr name * @value: xattr value * * This function set xattr on cgroup dir. * * On success, it returns 0, otherwise on failure it returns -1.
*/ int set_cgroup_xattr(constchar *relative_path, constchar *name, constchar *value)
{ char cgroup_path[PATH_MAX + 1];
/** * __cleanup_cgroup_environment() - Delete temporary cgroups * * This is a helper for cleanup_cgroup_environment() that is responsible for * deletion of all temporary cgroups that have been created during the test.
*/ staticvoid __cleanup_cgroup_environment(void)
{ char cgroup_workdir[PATH_MAX + 1];
/** * cleanup_cgroup_environment() - Cleanup Cgroup Testing Environment * * This is an idempotent function to delete all temporary cgroups that * have been created during the test and unmount the cgroup testing work * directory. * * At call time, it moves the calling process to the root cgroup, and then * runs the deletion process. It is idempotent, and should not fail, unless * a process is lingering. * * On failure, it will print an error to stderr, and try to continue.
*/ void cleanup_cgroup_environment(void)
{
__cleanup_cgroup_environment(); if (cgroup_workdir_mounted && umount(CGROUP_MOUNT_PATH))
log_err("umount cgroup2");
cgroup_workdir_mounted = false;
}
/** * get_root_cgroup() - Get the FD of the root cgroup * * On success, it returns the file descriptor. On failure, it returns -1. * If there is a failure, it prints the error to stderr.
*/ int get_root_cgroup(void)
{ int fd;
/* * remove_cgroup() - Remove a cgroup * @relative_path: The cgroup path, relative to the workdir, to remove * * This function expects a cgroup to already be created, relative to the cgroup * work dir. It also expects the cgroup doesn't have any children or live * processes and it removes the cgroup. * * On failure, it will print an error to stderr.
*/ void remove_cgroup(constchar *relative_path)
{ char cgroup_path[PATH_MAX + 1];
/** * create_and_get_cgroup() - Create a cgroup, relative to workdir, and get the FD * @relative_path: The cgroup path, relative to the workdir, to join * * This function creates a cgroup under the top level workdir and returns the * file descriptor. It is idempotent. * * On success, it returns the file descriptor. On failure it returns -1. * If there is a failure, it prints the error to stderr.
*/ int create_and_get_cgroup(constchar *relative_path)
{ char cgroup_path[PATH_MAX + 1]; int fd;
/** * get_cgroup_id_from_path - Get cgroup id for a particular cgroup path * @cgroup_workdir: The absolute cgroup path * * On success, it returns the cgroup id. On failure it returns 0, * which is an invalid cgroup id. * If there is a failure, it prints the error to stderr.
*/ staticunsignedlonglong get_cgroup_id_from_path(constchar *cgroup_workdir)
{ int dirfd, err, flags, mount_id, fhsize; union { unsignedlonglong cgid; unsignedchar raw_bytes[8];
} id; struct file_handle *fhp, *fhp2; unsignedlonglong ret = 0;
int cgroup_setup_and_join(constchar *path) { int cg_fd;
if (setup_cgroup_environment()) {
fprintf(stderr, "Failed to setup cgroup environment\n"); return -EINVAL;
}
cg_fd = create_and_get_cgroup(path); if (cg_fd < 0) {
fprintf(stderr, "Failed to create test cgroup\n");
cleanup_cgroup_environment(); return cg_fd;
}
if (join_cgroup(path)) {
fprintf(stderr, "Failed to join cgroup\n");
cleanup_cgroup_environment(); return -EINVAL;
} return cg_fd;
}
/** * setup_classid_environment() - Setup the cgroupv1 net_cls environment * * This function should only be called in a custom mount namespace, e.g. * created by running setup_cgroup_environment. * * After calling this function, cleanup_classid_environment should be called * once testing is complete. * * This function will print an error to stderr and return 1 if it is unable * to setup the cgroup environment. If setup is successful, 0 is returned.
*/ int setup_classid_environment(void)
{ char cgroup_workdir[PATH_MAX + 1];
if (mount("net_cls", NETCLS_MOUNT_PATH, "cgroup", 0, "net_cls")) { if (errno != EBUSY) {
log_err("mount cgroup net_cls"); return 1;
}
if (rmdir(NETCLS_MOUNT_PATH)) {
log_err("rmdir cgroup net_cls"); return 1;
} if (umount(CGROUP_MOUNT_DFLT)) {
log_err("umount cgroup base"); return 1;
}
}
cleanup_classid_environment();
if (mkdir(cgroup_workdir, 0777) && errno != EEXIST) {
log_err("mkdir cgroup work dir"); return 1;
}
return 0;
}
/** * set_classid() - Set a cgroupv1 net_cls classid * * Writes the classid into the cgroup work dir's net_cls.classid * file in order to later on trigger socket tagging. * * We leverage the current pid as the classid, ensuring unique identification. * * On success, it returns 0, otherwise on failure it returns 1. If there * is a failure, it prints the error to stderr.
*/ int set_classid(void)
{ char cgroup_workdir[PATH_MAX - 42]; char cgroup_classid_path[PATH_MAX + 1]; int fd, rc = 0;
/** * join_classid() - Join a cgroupv1 net_cls classid * * This function expects the cgroup work dir to be already created, as we * join it here. This causes the process sockets to be tagged with the given * net_cls classid. * * On success, it returns 0, otherwise on failure it returns 1.
*/ int join_classid(void)
{ char cgroup_workdir[PATH_MAX + 1];
/** * cleanup_classid_environment() - Cleanup the cgroupv1 net_cls environment * * At call time, it moves the calling process to the root cgroup, and then * runs the deletion process. * * On failure, it will print an error to stderr, and try to continue.
*/ void cleanup_classid_environment(void)
{ char cgroup_workdir[PATH_MAX + 1];
/** * get_classid_cgroup_id - Get the cgroup id of a net_cls cgroup
*/ unsignedlonglong get_classid_cgroup_id(void)
{ char cgroup_workdir[PATH_MAX + 1];
/** * get_cgroup1_hierarchy_id - Retrieves the ID of a cgroup1 hierarchy from the cgroup1 subsys name. * @subsys_name: The cgroup1 subsys name, which can be retrieved from /proc/self/cgroup. It can be * a named cgroup like "name=systemd", a controller name like "net_cls", or multi-controllers like * "net_cls,net_prio".
*/ int get_cgroup1_hierarchy_id(constchar *subsys_name)
{ char *c, *c2, *c3, *c4; bool found = false; char line[1024];
FILE *file; int i, id;
while (fgets(line, 1024, file)) {
i = 0; for (c = strtok_r(line, ":", &c2); c && i < 2; c = strtok_r(NULL, ":", &c2)) { if (i == 0) {
id = strtol(c, NULL, 10);
} elseif (i == 1) { if (!strcmp(c, subsys_name)) {
found = true; break;
}
/* Multiple subsystems may share one single mount point */ for (c3 = strtok_r(c, ",", &c4); c3;
c3 = strtok_r(NULL, ",", &c4)) { if (!strcmp(c, subsys_name)) {
found = true; break;
}
}
}
i++;
} if (found) break;
}
fclose(file); return found ? id : -1;
}
/** * open_classid() - Open a cgroupv1 net_cls classid * * This function expects the cgroup work dir to be already created, as we * open it here. * * On success, it returns the file descriptor. On failure it returns -1.
*/ int open_classid(void)
{ char cgroup_workdir[PATH_MAX + 1];
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.