assert(fs);
+ if (controller && !cg_controller_is_valid(controller, true))
+ return -EINVAL;
+
if (_unlikely_(!good)) {
int r;
assert(fs);
- if (isempty(controller))
+ if (!cg_controller_is_valid(controller, true))
return -EINVAL;
/* Normalize the controller syntax */
}
int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
- char fs[sizeof("/proc/") - 1 + DECIMAL_STR_MAX(pid_t) + sizeof("/cgroup")];
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
+ const char *fs;
size_t cs;
assert(path);
assert(pid >= 0);
- if (!controller)
+ if (controller) {
+ if (!cg_controller_is_valid(controller, true))
+ return -EINVAL;
+
+ controller = normalize_controller(controller);
+ } else
controller = SYSTEMD_CGROUP_CONTROLLER;
if (pid == 0)
- pid = getpid();
+ fs = "/proc/self/cgroup";
+ else
+ fs = procfs_file_alloca(pid, "cgroup");
- sprintf(fs, "/proc/%lu/cgroup", (unsigned long) pid);
f = fopen(fs, "re");
if (!f)
return errno == ENOENT ? -ESRCH : -errno;
cs = strlen(controller);
FOREACH_LINE(line, f, return -errno) {
- char *l, *p;
+ char *l, *p, *w, *e;
+ size_t k;
+ char *state;
+ bool found = false;
truncate_nl(line);
continue;
l++;
- if (!strneq(l, controller, cs))
+ e = strchr(l, ':');
+ if (!e)
continue;
- if (l[cs] != ':')
+ *e = 0;
+
+ FOREACH_WORD_SEPARATOR(w, k, l, ",", state) {
+
+ if (k == cs && memcmp(w, controller, cs) == 0) {
+ found = true;
+ break;
+ }
+
+ if (k == 5 + cs &&
+ memcmp(w, "name=", 5) == 0 &&
+ memcmp(w+5, controller, cs) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
continue;
- p = strdup(l + cs + 1);
+ p = strdup(e + 1);
if (!p)
return -ENOMEM;
int cg_split_spec(const char *spec, char **controller, char **path) {
const char *e;
char *t = NULL, *u = NULL;
+ _cleanup_free_ char *v = NULL;
assert(spec);
if (!t)
return -ENOMEM;
+ path_kill_slashes(t);
*path = t;
}
e = strchr(spec, ':');
if (!e) {
- if (!filename_is_safe(spec))
+ if (!cg_controller_is_valid(spec, true))
return -EINVAL;
if (controller) {
- t = strdup(spec);
+ t = strdup(normalize_controller(spec));
if (!t)
return -ENOMEM;
return 0;
}
- t = strndup(spec, e-spec);
+ v = strndup(spec, e-spec);
+ if (!v)
+ return -ENOMEM;
+ t = strdup(normalize_controller(v));
if (!t)
return -ENOMEM;
- if (!filename_is_safe(t)) {
+ if (!cg_controller_is_valid(t, true)) {
free(t);
return -EINVAL;
}
free(t);
return -ENOMEM;
}
- if (!path_is_safe(u)) {
+ if (!path_is_safe(u) ||
+ !path_is_absolute(u)) {
free(t);
free(u);
return -EINVAL;
}
+ path_kill_slashes(u);
+
if (controller)
*controller = t;
else
if (!controller)
controller = "systemd";
- else if (controller[0] == 0 ||
- strchr(controller, ':') ||
- strchr(controller, '/'))
- return -EINVAL;
+ else {
+ if (!cg_controller_is_valid(controller, true))
+ return -EINVAL;
+
+ controller = normalize_controller(controller);
+ }
if (!path_is_absolute(path))
return -EINVAL;
- controller = normalize_controller(controller);
-
s = strjoin(controller, ":", path, NULL);
if (!s)
return -ENOMEM;
+ path_kill_slashes(s + strlen(controller) + 1);
+
*spec = s;
return 0;
}
int cg_mangle_path(const char *path, char **result) {
- char *t, *c, *p;
+ _cleanup_free_ char *c = NULL, *p = NULL;
+ char *t;
int r;
assert(path);
if (!t)
return -ENOMEM;
+ path_kill_slashes(t);
*result = t;
return 0;
}
if (r < 0)
return r;
- r = cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
- free(c);
- free(p);
-
- return r;
+ return cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
}
int cg_get_system_path(char **path) {
return 0;
}
-int cg_get_machine_path(char **path) {
- _cleanup_free_ char *root = NULL;
+int cg_get_machine_path(const char *machine, char **path) {
+ _cleanup_free_ char *root = NULL, *escaped = NULL;
char *p;
assert(path);
- if (cg_get_root_path(&root) < 0 || streq(root, "/"))
- p = strdup("/machine");
- else
- p = strappend(root, "/machine");
+ if (machine) {
+ const char *name = strappenda(machine, ".nspawn");
+ escaped = cg_escape(name);
+ if (!escaped)
+ return -ENOMEM;
+ }
+
+ p = strjoin(cg_get_root_path(&root) >= 0 && !streq(root, "/") ? root : "",
+ "/machine", machine ? "/" : "", machine ? escaped : "", NULL);
if (!p)
return -ENOMEM;
p = normalize_controller(*f);
- if (streq(*f, "systemd")) {
+ if (streq(p, "systemd")) {
+ free(*f);
+ continue;
+ }
+
+ if (!cg_controller_is_valid(p, true)) {
+ log_warning("Controller %s is not valid, removing from controllers list.", p);
free(*f);
continue;
}
r = check_hierarchy(p);
if (r < 0) {
- log_debug("Controller %s is not available, removing from controllers list.", *f);
+ log_debug("Controller %s is not available, removing from controllers list.", p);
free(*f);
continue;
}
}
int cg_path_decode_unit(const char *cgroup, char **unit){
- _cleanup_free_ char *unescaped = NULL;
char *p, *e, *c, *s, *k;
assert(cgroup);
return cg_path_get_unit(cgroup, unit);
}
-static const char *skip_label(const char *e) {
+_pure_ static const char *skip_label(const char *e) {
assert(e);
e = strchr(e, '/');
if (!c)
return -ENOMEM;
- if (!filename_is_safe(c)) {
+ if (!cg_controller_is_valid(c, false)) {
free(c);
return -EINVAL;
}
/* The return value of this function (unlike cg_unescape())
* needs free()! */
- if (p[0] == '_' || streq(p, "notify_on_release") || streq(p, "release_agent") || streq(p, "tasks"))
+ if (p[0] == 0 ||
+ p[0] == '_' ||
+ p[0] == '.' ||
+ streq(p, "notify_on_release") ||
+ streq(p, "release_agent") ||
+ streq(p, "tasks"))
need_prefix = true;
else {
const char *dot;
return (char*) p;
}
+
+#define CONTROLLER_VALID \
+ "0123456789" \
+ "abcdefghijklmnopqrstuvwxyz" \
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+ "_"
+
+bool cg_controller_is_valid(const char *p, bool allow_named) {
+ const char *t, *s;
+
+ if (!p)
+ return false;
+
+ if (allow_named) {
+ s = startswith(p, "name=");
+ if (s)
+ p = s;
+ }
+
+ if (*p == 0 || *p == '_')
+ return false;
+
+ for (t = p; *t; t++)
+ if (!strchr(CONTROLLER_VALID, *t))
+ return false;
+
+ if (t - p > FILENAME_MAX)
+ return false;
+
+ return true;
+}