summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSven Eden <yamakuzure@gmx.net>2017-01-09 05:53:00 +0100
committerSven Eden <yamakuzure@gmx.net>2017-03-14 10:22:32 +0100
commit4ebadef69ff96e92122a133cfecadf60816c4fa3 (patch)
treed7d65cccf110e8b12a4353c0d91db2b0b83f08dc
parentbd1ce0bdbfc97a958a6c56fe062bed892f08444a (diff)
Prep v226: Apply missing fixes and changes to src/basic
-rw-r--r--Makefile-man.am9
-rw-r--r--src/basic/cgroup-util.c750
-rw-r--r--src/basic/cgroup-util.h58
-rw-r--r--src/basic/conf-files.h7
-rw-r--r--src/basic/macro.h3
-rw-r--r--src/basic/missing.h8
-rw-r--r--src/basic/ring.h3
-rw-r--r--src/basic/selinux-util.c10
-rw-r--r--src/basic/special.h3
-rw-r--r--src/basic/terminal-util.c19
-rw-r--r--src/basic/terminal-util.h2
-rw-r--r--src/basic/time-util.c53
-rw-r--r--src/basic/time-util.h4
-rw-r--r--src/basic/unit-name.c38
-rw-r--r--src/basic/unit-name.h3
-rw-r--r--src/basic/util.c8
-rw-r--r--src/basic/util.h6
-rw-r--r--src/basic/virt.c6
18 files changed, 716 insertions, 274 deletions
diff --git a/Makefile-man.am b/Makefile-man.am
index 35eec7798..845f96a41 100644
--- a/Makefile-man.am
+++ b/Makefile-man.am
@@ -363,15 +363,6 @@ man/sd_session_is_remote.html: man/sd_session_is_active.html
endif
-if HAVE_PYTHON
-MANPAGES += \
- man/systemd.index.7
-MANPAGES_ALIAS += \
- #
-
-
-endif
-
# Really, do not edit this file.
EXTRA_DIST += \
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index b9fd9a6d4..ec42e258a 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -29,7 +29,6 @@
#include <sys/types.h>
#include <ftw.h>
-#include "cgroup-util.h"
#include "set.h"
#include "macro.h"
#include "util.h"
@@ -41,6 +40,7 @@
#include "special.h"
#include "mkdir.h"
#include "login-util.h"
+#include "cgroup-util.h"
int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
_cleanup_free_ char *fs = NULL;
@@ -187,7 +187,7 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo
if (ignore_self && pid == my_pid)
continue;
- if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
+ if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
continue;
/* If we haven't killed this process yet, kill
@@ -197,7 +197,7 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo
ret = -errno;
} else {
if (sigcont && sig != SIGKILL)
- kill(pid, SIGCONT);
+ (void) kill(pid, SIGCONT);
if (ret == 0)
ret = 1;
@@ -205,7 +205,7 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo
done = false;
- r = set_put(s, LONG_TO_PTR(pid));
+ r = set_put(s, PID_TO_PTR(pid));
if (r < 0) {
if (ret >= 0)
return r;
@@ -233,7 +233,7 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo
int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool rem, Set *s) {
_cleanup_set_free_ Set *allocated_set = NULL;
_cleanup_closedir_ DIR *d = NULL;
- int r, ret = 0;
+ int r, ret;
char *fn;
assert(path);
@@ -264,7 +264,7 @@ int cg_kill_recursive(const char *controller, const char *path, int sig, bool si
return -ENOMEM;
r = cg_kill_recursive(controller, p, sig, sigcont, ignore_self, rem, s);
- if (ret >= 0 && r != 0)
+ if (r != 0 && ret >= 0)
ret = r;
}
@@ -318,7 +318,7 @@ int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char
if (ignore_self && pid == my_pid)
continue;
- if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
+ if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
continue;
/* Ignore kernel threads. Since they can only
@@ -338,7 +338,7 @@ int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char
done = false;
- r = set_put(s, LONG_TO_PTR(pid));
+ r = set_put(s, PID_TO_PTR(pid));
if (r < 0) {
if (ret >= 0)
return r;
@@ -390,13 +390,9 @@ int cg_migrate_recursive(
p = strjoin(pfrom, "/", fn, NULL);
free(fn);
- if (!p) {
- if (ret >= 0)
+ if (!p)
return -ENOMEM;
- return ret;
- }
-
r = cg_migrate_recursive(cfrom, p, cto, pto, ignore_self, rem);
if (r != 0 && ret >= 0)
ret = r;
@@ -436,114 +432,174 @@ int cg_migrate_recursive_fallback(
/* This didn't work? Then let's try all prefixes of the destination */
PATH_FOREACH_PREFIX(prefix, pto) {
- r = cg_migrate_recursive(cfrom, pfrom, cto, prefix, ignore_self, rem);
- if (r >= 0)
- break;
+ int q;
+
+ q = cg_migrate_recursive(cfrom, pfrom, cto, prefix, ignore_self, rem);
+ if (q >= 0)
+ return q;
}
}
- return 0;
+ return r;
}
-static const char *normalize_controller(const char *controller) {
+static const char *controller_to_dirname(const char *controller) {
+ const char *e;
assert(controller);
- if (startswith(controller, "name="))
- return controller + 5;
- else
+ /* Converts a controller name to the directory name below
+ * /sys/fs/cgroup/ we want to mount it to. Effectively, this
+ * just cuts off the name= prefixed used for named
+ * hierarchies, if it is specified. */
+
+ e = startswith(controller, "name=");
+ if (e)
+ return e;
+
return controller;
}
-static int join_path(const char *controller, const char *path, const char *suffix, char **fs) {
+static int join_path_legacy(const char *controller, const char *path, const char *suffix, char **fs) {
+ const char *dn;
char *t = NULL;
- if (!isempty(controller)) {
- if (!isempty(path) && !isempty(suffix))
- t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL);
- else if (!isempty(path))
- t = strjoin("/sys/fs/cgroup/", controller, "/", path, NULL);
- else if (!isempty(suffix))
- t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL);
- else
- t = strappend("/sys/fs/cgroup/", controller);
- } else {
- if (!isempty(path) && !isempty(suffix))
- t = strjoin(path, "/", suffix, NULL);
- else if (!isempty(path))
- t = strdup(path);
+ assert(fs);
+ assert(controller);
+
+ dn = controller_to_dirname(controller);
+
+ if (isempty(path) && isempty(suffix))
+ t = strappend("/sys/fs/cgroup/", dn);
+ else if (isempty(path))
+ t = strjoin("/sys/fs/cgroup/", dn, "/", suffix, NULL);
+ else if (isempty(suffix))
+ t = strjoin("/sys/fs/cgroup/", dn, "/", path, NULL);
else
- return -EINVAL;
+ t = strjoin("/sys/fs/cgroup/", dn, "/", path, "/", suffix, NULL);
+ if (!t)
+ return -ENOMEM;
+
+ *fs = t;
+ return 0;
}
+static int join_path_unified(const char *path, const char *suffix, char **fs) {
+ char *t;
+
+ assert(fs);
+
+ if (isempty(path) && isempty(suffix))
+ t = strdup("/sys/fs/cgroup");
+ else if (isempty(path))
+ t = strappend("/sys/fs/cgroup/", suffix);
+ else if (isempty(suffix))
+ t = strappend("/sys/fs/cgroup/", path);
+ else
+ t = strjoin("/sys/fs/cgroup/", path, "/", suffix, NULL);
if (!t)
return -ENOMEM;
- *fs = path_kill_slashes(t);
+ *fs = t;
return 0;
}
int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
- const char *p;
- static thread_local bool good = false;
+ int unified, r;
assert(fs);
- if (controller && !cg_controller_is_valid(controller))
+ if (!controller) {
+ char *t;
+
+ /* If no controller is specified, we return the path
+ * *below* the controllers, without any prefix. */
+
+ if (!path && !suffix)
return -EINVAL;
- if (_unlikely_(!good)) {
- int r;
+ if (!suffix)
+ t = strdup(path);
+ else if (!path)
+ t = strdup(suffix);
+ else
+ t = strjoin(path, "/", suffix, NULL);
+ if (!t)
+ return -ENOMEM;
- r = path_is_mount_point("/sys/fs/cgroup", 0);
+ *fs = path_kill_slashes(t);
+ return 0;
+ }
+
+ if (!cg_controller_is_valid(controller))
+ return -EINVAL;
+
+ unified = cg_unified();
+ if (unified < 0)
+ return unified;
+
+ if (unified > 0)
+ r = join_path_unified(path, suffix, fs);
+ else
+ r = join_path_legacy(controller, path, suffix, fs);
if (r < 0)
return r;
- if (r == 0)
- return -ENOENT;
- /* Cache this to save a few stat()s */
- good = true;
+ path_kill_slashes(*fs);
+ return 0;
}
- p = controller ? normalize_controller(controller) : NULL;
+static int controller_is_accessible(const char *controller) {
+ int unified;
- return join_path(p, path, suffix, fs);
-}
+ assert(controller);
-static int check_hierarchy(const char *p) {
- const char *cc;
+ /* Checks whether a specific controller is accessible,
+ * i.e. its hierarchy mounted. In the unified hierarchy all
+ * controllers are considered accessible, except for the named
+ * hierarchies */
- assert(p);
+ if (!cg_controller_is_valid(controller))
+ return -EINVAL;
+
+ unified = cg_unified();
+ if (unified < 0)
+ return unified;
+ if (unified > 0) {
+ /* We don't support named hierarchies if we are using
+ * the unified hierarchy. */
- if (!filename_is_valid(p))
+ if (streq(controller, ELOGIND_CGROUP_CONTROLLER))
return 0;
- /* Check if this controller actually really exists */
- cc = strjoina("/sys/fs/cgroup/", p);
+ if (startswith(controller, "name="))
+ return -EOPNOTSUPP;
+
+ } else {
+ const char *cc, *dn;
+
+ dn = controller_to_dirname(controller);
+ cc = strjoina("/sys/fs/cgroup/", dn);
+
if (laccess(cc, F_OK) < 0)
return -errno;
+ }
return 0;
}
int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
- const char *p;
int r;
+ assert(controller);
assert(fs);
- if (!cg_controller_is_valid(controller))
- return -EINVAL;
-
- /* Normalize the controller syntax */
- p = normalize_controller(controller);
-
- /* Check if this controller actually really exists */
- r = check_hierarchy(p);
+ /* Check if the specified controller is actually accessible */
+ r = controller_is_accessible(controller);
if (r < 0)
return r;
- return join_path(p, path, suffix, fs);
+ return cg_get_path(controller, path, suffix, fs);
}
static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
@@ -557,7 +613,7 @@ static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct
if (ftwbuf->level < 1)
return 0;
- rmdir(path);
+ (void) rmdir(path);
return 0;
}
@@ -572,8 +628,14 @@ int cg_trim(const char *controller, const char *path, bool delete_root) {
return r;
errno = 0;
- if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0)
- r = errno ? -errno : -EIO;
+ if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) {
+ if (errno == ENOENT)
+ r = 0;
+ else if (errno != 0)
+ r = -errno;
+ else
+ r = -EIO;
+ }
if (delete_root) {
if (rmdir(fs) < 0 && errno != ENOENT)
@@ -583,23 +645,6 @@ int cg_trim(const char *controller, const char *path, bool delete_root) {
return r;
}
-/// UNNEDED by elogind
-#if 0
-int cg_delete(const char *controller, const char *path) {
- _cleanup_free_ char *parent = NULL;
- int r;
-
- assert(path);
-
- r = path_get_parent(path, &parent);
- if (r < 0)
- return r;
-
- r = cg_migrate_recursive(controller, path, controller, parent, false, true);
- return r == -ENOENT ? 0 : r;
-}
-#endif // 0
-
int cg_create(const char *controller, const char *path) {
_cleanup_free_ char *fs = NULL;
int r;
@@ -678,13 +723,15 @@ int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
* the destination */
PATH_FOREACH_PREFIX(prefix, path) {
- r = cg_attach(controller, prefix, pid);
- if (r >= 0)
- break;
+ int q;
+
+ q = cg_attach(controller, prefix, pid);
+ if (q >= 0)
+ return q;
}
}
- return 0;
+ return r;
}
/// UNNEEDED by elogind
@@ -699,7 +746,8 @@ int cg_set_group_access(
_cleanup_free_ char *fs = NULL;
int r;
- assert(path);
+ if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID)
+ return 0;
if (mode != MODE_INVALID)
mode &= 0777;
@@ -719,7 +767,7 @@ int cg_set_task_access(
gid_t gid) {
_cleanup_free_ char *fs = NULL, *procs = NULL;
- int r;
+ int r, unified;
assert(path);
@@ -737,13 +785,18 @@ int cg_set_task_access(
if (r < 0)
return r;
+ unified = cg_unified();
+ if (unified < 0)
+ return unified;
+ if (unified)
+ return 0;
+
/* Compatibility, Always keep values for "tasks" in sync with
* "cgroup.procs" */
- r = cg_get_path(controller, path, "tasks", &procs);
- if (r < 0)
- return r;
+ if (cg_get_path(controller, path, "tasks", &procs) >= 0)
+ (void) chmod_and_chown(procs, mode, uid, gid);
- return chmod_and_chown(procs, mode, uid, gid);
+ return 0;
}
#endif // 0
@@ -751,35 +804,49 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
const char *fs;
- size_t cs;
+ size_t cs = 0;
+ int unified;
assert(path);
assert(pid >= 0);
+ unified = cg_unified();
+ if (unified < 0)
+ return unified;
+ if (unified == 0) {
if (controller) {
if (!cg_controller_is_valid(controller))
return -EINVAL;
-
- controller = normalize_controller(controller);
} else
controller = ELOGIND_CGROUP_CONTROLLER;
- fs = procfs_file_alloca(pid, "cgroup");
+ cs = strlen(controller);
+ }
+ fs = procfs_file_alloca(pid, "cgroup");
f = fopen(fs, "re");
if (!f)
return errno == ENOENT ? -ESRCH : -errno;
- cs = strlen(controller);
-
FOREACH_LINE(line, f, return -errno) {
- char *l, *p, *e;
+ char *e, *p;
+
+ truncate_nl(line);
+
+ if (unified) {
+ e = startswith(line, "0:");
+ if (!e)
+ continue;
+
+ e = strchr(e, ':');
+ if (!e)
+ continue;
+ } else {
+ char *l;
size_t k;
const char *word, *state;
bool found = false;
- truncate_nl(line);
-
l = strchr(line, ':');
if (!l)
continue;
@@ -790,24 +857,16 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
continue;
*e = 0;
-
FOREACH_WORD_SEPARATOR(word, k, l, ",", state) {
-
if (k == cs && memcmp(word, controller, cs) == 0) {
found = true;
break;
}
-
- if (k == 5 + cs &&
- memcmp(word, "name=", 5) == 0 &&
- memcmp(word+5, controller, cs) == 0) {
- found = true;
- break;
- }
}
if (!found)
continue;
+ }
p = strdup(e + 1);
if (!p)
@@ -817,18 +876,24 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
return 0;
}
- return -ENOENT;
+ return -ENODATA;
}
/// UNNEEDED by elogind
#if 0
int cg_install_release_agent(const char *controller, const char *agent) {
_cleanup_free_ char *fs = NULL, *contents = NULL;
- char *sc;
- int r;
+ const char *sc;
+ int r, unified;
assert(agent);
+ unified = cg_unified();
+ if (unified < 0)
+ return unified;
+ if (unified) /* doesn't apply to unified hierarchy */
+ return -EOPNOTSUPP;
+
r = cg_get_path(controller, NULL, "release_agent", &fs);
if (r < 0)
return r;
@@ -838,7 +903,7 @@ int cg_install_release_agent(const char *controller, const char *agent) {
return r;
sc = strstrip(contents);
- if (sc[0] == 0) {
+ if (isempty(sc)) {
r = write_string_file_no_create(fs, agent);
if (r < 0)
return r;
@@ -872,7 +937,13 @@ int cg_install_release_agent(const char *controller, const char *agent) {
int cg_uninstall_release_agent(const char *controller) {
_cleanup_free_ char *fs = NULL;
- int r;
+ int r, unified;
+
+ unified = cg_unified();
+ if (unified < 0)
+ return unified;
+ if (unified) /* Doesn't apply to unified hierarchy */
+ return -EOPNOTSUPP;
r = cg_get_path(controller, NULL, "notify_on_release", &fs);
if (r < 0)
@@ -896,49 +967,69 @@ int cg_uninstall_release_agent(const char *controller) {
}
#endif // 0
-int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
+int cg_is_empty(const char *controller, const char *path) {
_cleanup_fclose_ FILE *f = NULL;
- pid_t pid = 0, self_pid;
- bool found = false;
+ pid_t pid;
int r;
assert(path);
r = cg_enumerate_processes(controller, path, &f);
+ if (r == -ENOENT)
+ return 1;
if (r < 0)
- return r == -ENOENT ? 1 : r;
+ return r;
+
+ r = cg_read_pid(f, &pid);
+ if (r < 0)
+ return r;
- self_pid = getpid();
+ return r == 0;
+}
- while ((r = cg_read_pid(f, &pid)) > 0) {
+int cg_is_empty_recursive(const char *controller, const char *path) {
+ int unified, r;
- if (ignore_self && pid == self_pid)
- continue;
+ assert(path);
- found = true;
- break;
- }
+ /* The root cgroup is always populated */
+ if (controller && (isempty(path) || path_equal(path, "/")))
+ return false;
+
+ unified = cg_unified();
+ if (unified < 0)
+ return unified;
+
+ if (unified > 0) {
+ _cleanup_free_ char *populated = NULL, *t = NULL;
+
+ /* On the unified hierarchy we can check empty state
+ * via the "cgroup.populated" attribute. */
+ r = cg_get_path(controller, path, "cgroup.populated", &populated);
if (r < 0)
return r;
- return !found;
-}
+ r = read_one_line_file(populated, &t);
+ if (r == -ENOENT)
+ return 1;
+ if (r < 0)
+ return r;
-int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) {
+ return streq(t, "0");
+ } else {
_cleanup_closedir_ DIR *d = NULL;
char *fn;
- int r;
- assert(path);
-
- r = cg_is_empty(controller, path, ignore_self);
+ r = cg_is_empty(controller, path);
if (r <= 0)
return r;
r = cg_enumerate_subgroups(controller, path, &d);
+ if (r == -ENOENT)
+ return 1;
if (r < 0)
- return r == -ENOENT ? 1 : r;
+ return r;
while ((r = cg_read_subgroup(d, &fn)) > 0) {
_cleanup_free_ char *p = NULL;
@@ -948,21 +1039,20 @@ int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_
if (!p)
return -ENOMEM;
- r = cg_is_empty_recursive(controller, p, ignore_self);
+ r = cg_is_empty_recursive(controller, p);
if (r <= 0)
return r;
}
-
if (r < 0)
return r;
- return 1;
+ return true;
+ }
}
int cg_split_spec(const char *spec, char **controller, char **path) {
- const char *e;
char *t = NULL, *u = NULL;
- _cleanup_free_ char *v = NULL;
+ const char *e;
assert(spec);
@@ -990,7 +1080,7 @@ int cg_split_spec(const char *spec, char **controller, char **path) {
return -EINVAL;
if (controller) {
- t = strdup(normalize_controller(spec));
+ t = strdup(spec);
if (!t)
return -ENOMEM;
@@ -1003,10 +1093,7 @@ int cg_split_spec(const char *spec, char **controller, char **path) {
return 0;
}
- v = strndup(spec, e-spec);
- if (!v)
- return -ENOMEM;
- t = strdup(normalize_controller(v));
+ t = strndup(spec, e-spec);
if (!t)
return -ENOMEM;
if (!cg_controller_is_valid(t)) {
@@ -1014,13 +1101,9 @@ int cg_split_spec(const char *spec, char **controller, char **path) {
return -EINVAL;
}
- if (streq(e+1, "")) {
- u = strdup("/");
- if (!u) {
- free(t);
- return -ENOMEM;
- }
- } else {
+ if (isempty(e+1))
+ u = NULL;
+ else {
u = strdup(e+1);
if (!u) {
free(t);
@@ -1087,7 +1170,11 @@ int cg_get_root_path(char **path) {
if (r < 0)
return r;
- e = endswith(p, "/" SPECIAL_SYSTEM_SLICE);
+ e = endswith(p, "/" SPECIAL_INIT_SCOPE);
+ if (!e)
+ e = endswith(p, "/" SPECIAL_SYSTEM_SLICE); /* legacy */
+ if (!e)
+ e = endswith(p, "/system"); /* even more legacy */
if (e)
*e = 0;
@@ -1115,7 +1202,7 @@ int cg_shift_path(const char *cgroup, const char *root, const char **shifted) {
}
p = path_startswith(cgroup, root);
- if (p)
+ if (p && p > cgroup)
*shifted = p - 1;
else
*shifted = cgroup;
@@ -1379,17 +1466,15 @@ int cg_pid_get_user_unit(pid_t pid, char **unit) {
}
int cg_path_get_machine_name(const char *path, char **machine) {
- _cleanup_free_ char *u = NULL, *sl = NULL;
+ _cleanup_free_ char *u = NULL;
+ const char *sl;
int r;
r = cg_path_get_unit(path, &u);
if (r < 0)
return r;
- sl = strjoin("/run/systemd/machines/unit:", u, NULL);
- if (!sl)
- return -ENOMEM;
-
+ sl = strjoina("/run/systemd/machines/unit:", u);
return readlink_malloc(sl, machine);
}
@@ -1582,30 +1667,37 @@ char *cg_escape(const char *p) {
p[0] == '.' ||
streq(p, "notify_on_release") ||
streq(p, "release_agent") ||
- streq(p, "tasks"))
+ streq(p, "tasks") ||
+ startswith(p, "cgroup."))
need_prefix = true;
else {
const char *dot;
dot = strrchr(p, '.');
if (dot) {
+ CGroupController c;
+ size_t l = dot - p;
- if (dot - p == 6 && memcmp(p, "cgroup", 6) == 0)
- need_prefix = true;
- else {
- char *n;
+ for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
+ const char *n;
- n = strndupa(p, dot - p);
+ n = cgroup_controller_to_string(c);
+
+ if (l != strlen(n))
+ continue;
+
+ if (memcmp(p, n, l) != 0)
+ continue;
- if (check_hierarchy(n) >= 0)
need_prefix = true;
+ break;
}
}
}
if (need_prefix)
return strappend("_", p);
- else
+
return strdup(p);
}
@@ -1742,19 +1834,11 @@ int cg_get_attribute(const char *controller, const char *path, const char *attri
}
#endif // 0
-static const char mask_names[] =
- "cpu\0"
- "cpuacct\0"
- "blkio\0"
- "memory\0"
- "devices\0";
-
/// UNNEEDED by elogind
#if 0
-int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path) {
- CGroupControllerMask bit = 1;
- const char *n;
- int r;
+int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
+ CGroupController c;
+ int r, unified;
/* This one will create a cgroup in our private tree, but also
* duplicate it in the trees specified in mask, and remove it
@@ -1765,44 +1849,58 @@ int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask ma
if (r < 0)
return r;
- /* Then, do the same in the other hierarchies */
- NULSTR_FOREACH(n, mask_names) {
+ /* If we are in the unified hierarchy, we are done now */
+ unified = cg_unified();
+ if (unified < 0)
+ return unified;
+ if (unified > 0)
+ return 0;
+
+ /* Otherwise, do the same in the other hierarchies */
+ for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
+ CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
+ const char *n;
+
+ n = cgroup_controller_to_string(c);
+
if (mask & bit)
- cg_create(n, path);
+ (void) cg_create(n, path);
else if (supported & bit)
- cg_trim(n, path, true);
-
- bit <<= 1;
+ (void) cg_trim(n, path, true);
}
return 0;
}
#endif // 0
-int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
- CGroupControllerMask bit = 1;
- const char *n;
- int r;
+int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
+ CGroupController c;
+ int r, unified;
r = cg_attach(ELOGIND_CGROUP_CONTROLLER, path, pid);
if (r < 0)
return r;
- NULSTR_FOREACH(n, mask_names) {
+ unified = cg_unified();
+ if (unified < 0)
+ return unified;
+ if (unified > 0)
+ return 0;
- if (supported & bit) {
+ for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
+ CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
const char *p = NULL;
+ if (!(supported & bit))
+ continue;
+
if (path_callback)
p = path_callback(bit, userdata);
if (!p)
p = path;
- cg_attach_fallback(n, p, pid);
- }
-
- bit <<= 1;
+ (void) cg_attach_fallback(cgroup_controller_to_string(c), p, pid);
}
return 0;
@@ -1810,27 +1908,26 @@ int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t
/// UNNEEDED by elogind
#if 0
-int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
+int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
Iterator i;
void *pidp;
int r = 0;
SET_FOREACH(pidp, pids, i) {
- pid_t pid = PTR_TO_LONG(pidp);
+ pid_t pid = PTR_TO_PID(pidp);
int q;
q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
- if (q < 0)
+ if (q < 0 && r >= 0)
r = q;
}
return r;
}
-int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
- CGroupControllerMask bit = 1;
- const char *n;
- int r;
+int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
+ CGroupController c;
+ int r = 0, unified;
if (!path_equal(from, to)) {
r = cg_migrate_recursive(ELOGIND_CGROUP_CONTROLLER, from, ELOGIND_CGROUP_CONTROLLER, to, false, true);
@@ -1838,56 +1935,127 @@ int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, cons
return r;
}
- NULSTR_FOREACH(n, mask_names) {
- if (supported & bit) {
+ unified = cg_unified();
+ if (unified < 0)
+ return unified;
+ if (unified > 0)
+ return r;
+
+ for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
+ CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
const char *p = NULL;
+ if (!(supported & bit))
+ continue;
+
if (to_callback)
p = to_callback(bit, userdata);
if (!p)
p = to;
- cg_migrate_recursive_fallback(ELOGIND_CGROUP_CONTROLLER, to, n, p, false, false);
- }
-
- bit <<= 1;
+ (void) cg_migrate_recursive_fallback(ELOGIND_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, false, false);
}
return 0;
}
-int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root) {
- CGroupControllerMask bit = 1;
- const char *n;
- int r;
+int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) {
+ CGroupController c;
+ int r, unified;
r = cg_trim(ELOGIND_CGROUP_CONTROLLER, path, delete_root);
if (r < 0)
return r;
- NULSTR_FOREACH(n, mask_names) {
- if (supported & bit)
- cg_trim(n, path, delete_root);
+ unified = cg_unified();
+ if (unified < 0)
+ return unified;
+ if (unified > 0)
+ return r;
+
+ for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
+ CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
+
+ if (!(supported & bit))
+ continue;
- bit <<= 1;
+ (void) cg_trim(cgroup_controller_to_string(c), path, delete_root);
}
return 0;
}
-CGroupControllerMask cg_mask_supported(void) {
- CGroupControllerMask bit = 1, mask = 0;
- const char *n;
+int cg_mask_supported(CGroupMask *ret) {
+ CGroupMask mask = 0;
+ int r, unified;
+
+ /* Determines the mask of supported cgroup controllers. Only
+ * includes controllers we can make sense of and that are
+ * actually accessible. */
+
+ unified = cg_unified();
+ if (unified < 0)
+ return unified;
+ if (unified > 0) {
+ _cleanup_free_ char *root = NULL, *controllers = NULL, *path = NULL;
+ const char *c;
+
+ /* In the unified hierarchy we can read the supported
+ * and accessible controllers from a the top-level
+ * cgroup attribute */
+
+ r = cg_get_root_path(&root);
+ if (r < 0)
+ return r;
+
+ r = cg_get_path(ELOGIND_CGROUP_CONTROLLER, root, "cgroup.controllers", &path);
+ if (r < 0)
+ return r;
+
+ r = read_one_line_file(path, &controllers);
+ if (r < 0)
+ return r;
+
+ c = controllers;
+ for (;;) {
+ _cleanup_free_ char *n = NULL;
+ CGroupController v;
- NULSTR_FOREACH(n, mask_names) {
- if (check_hierarchy(n) >= 0)
- mask |= bit;
+ r = extract_first_word(&c, &n, NULL, 0);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
- bit <<= 1;
+ v = cgroup_controller_from_string(n);
+ if (v < 0)
+ continue;
+
+ mask |= CGROUP_CONTROLLER_TO_MASK(v);
}
- return mask;
+ /* Currently, we only support the memory controller in
+ * the unified hierarchy, mask everything else off. */
+ mask &= CGROUP_MASK_MEMORY;
+
+ } else {
+ CGroupController c;
+
+ /* In the legacy hierarchy, we check whether which
+ * hierarchies are mounted. */
+
+ for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
+ const char *n;
+
+ n = cgroup_controller_to_string(c);
+ if (controller_is_accessible(n) >= 0)
+ mask |= CGROUP_CONTROLLER_TO_MASK(c);
+ }
+ }
+
+ *ret = mask;
+ return 0;
}
int cg_kernel_controllers(Set *controllers) {
@@ -1897,6 +2065,11 @@ int cg_kernel_controllers(Set *controllers) {
assert(controllers);
+ /* Determines the full list of kernel-known controllers. Might
+ * include controllers we don't actually support, arbitrary
+ * named hierarchies and controllers that aren't currently
+ * accessible (because not mounted). */
+
f = fopen("/proc/cgroups", "re");
if (!f) {
if (errno == ENOENT)
@@ -1917,7 +2090,7 @@ int cg_kernel_controllers(Set *controllers) {
if (feof(f))
break;
- if (ferror(f) && errno)
+ if (ferror(f) && errno != 0)
return -errno;
return -EBADMSG;
@@ -1928,7 +2101,7 @@ int cg_kernel_controllers(Set *controllers) {
continue;
}
- if (!filename_is_valid(controller)) {
+ if (!cg_controller_is_valid(controller)) {
free(controller);
return -EBADMSG;
}
@@ -1941,3 +2114,122 @@ int cg_kernel_controllers(Set *controllers) {
return 0;
}
#endif // 0
+
+static thread_local int unified_cache = -1;
+
+int cg_unified(void) {
+ struct statfs fs;
+
+ /* Checks if we support the unified hierarchy. Returns an
+ * error when the cgroup hierarchies aren't mounted yet or we
+ * have any other trouble determining if the unified hierarchy
+ * is supported. */
+
+ if (unified_cache >= 0)
+ return unified_cache;
+
+ if (statfs("/sys/fs/cgroup/", &fs) < 0)
+ return -errno;
+
+ if (F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC))
+ unified_cache = true;
+ else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC))
+ unified_cache = false;
+ else
+ return -ENOEXEC;
+
+ return unified_cache;
+}
+
+void cg_unified_flush(void) {
+ unified_cache = -1;
+}
+
+int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
+ _cleanup_free_ char *fs = NULL;
+ CGroupController c;
+ int r, unified;
+
+ assert(p);
+
+ if (supported == 0)
+ return 0;
+
+ unified = cg_unified();
+ if (unified < 0)
+ return unified;
+ if (!unified) /* on the legacy hiearchy there's no joining of controllers defined */
+ return 0;
+
+ r = cg_get_path(ELOGIND_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs);
+ if (r < 0)
+ return r;
+
+ for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
+ CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
+ const char *n;
+
+ if (!(supported & bit))
+ continue;
+
+ n = cgroup_controller_to_string(c);
+ {
+ char s[1 + strlen(n) + 1];
+
+ s[0] = mask & bit ? '+' : '-';
+ strcpy(s + 1, n);
+
+ r = write_string_file(fs, s, 0);
+ if (r < 0)
+ log_debug_errno(r, "Failed to enable controller %s for %s (%s): %m", n, p, fs);
+ }
+ }
+
+ return 0;
+}
+
+bool cg_is_unified_wanted(void) {
+ static thread_local int wanted = -1;
+ int r, unified;
+
+ /* If the hierarchy is already mounted, then follow whatever
+ * was chosen for it. */
+ unified = cg_unified();
+ if (unified >= 0)
+ return unified;
+
+ /* Otherwise, let's see what the kernel command line has to
+ * say. Since checking that is expensive, let's cache the
+ * result. */
+ if (wanted >= 0)
+ return wanted;
+
+ r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy", NULL);
+ if (r > 0)
+ return (wanted = true);
+ else {
+ _cleanup_free_ char *value = NULL;
+
+ r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy=", &value);
+ if (r < 0)
+ return false;
+ if (r == 0)
+ return (wanted = false);
+
+ return (wanted = parse_boolean(value) > 0);
+ }
+}
+
+bool cg_is_legacy_wanted(void) {
+ return !cg_is_unified_wanted();
+}
+
+static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
+ [CGROUP_CONTROLLER_CPU] = "cpu",
+ [CGROUP_CONTROLLER_CPUACCT] = "cpuacct",
+ [CGROUP_CONTROLLER_BLKIO] = "blkio",
+ [CGROUP_CONTROLLER_MEMORY] = "memory",
+ [CGROUP_CONTROLLER_DEVICE] = "devices",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);
diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h
index 9d0cc8515..7b0ec648a 100644
--- a/src/basic/cgroup-util.h
+++ b/src/basic/cgroup-util.h
@@ -28,15 +28,28 @@
#include "set.h"
#include "def.h"
+/* An enum of well known cgroup controllers */
+typedef enum CGroupController {
+ CGROUP_CONTROLLER_CPU,
+ CGROUP_CONTROLLER_CPUACCT,
+ CGROUP_CONTROLLER_BLKIO,
+ CGROUP_CONTROLLER_MEMORY,
+ CGROUP_CONTROLLER_DEVICE,
+ _CGROUP_CONTROLLER_MAX,
+ _CGROUP_CONTROLLER_INVALID = -1,
+} CGroupController;
+
+#define CGROUP_CONTROLLER_TO_MASK(c) (1 << (c))
+
/* A bit mask of well known cgroup controllers */
-typedef enum CGroupControllerMask {
- CGROUP_CPU = 1,
- CGROUP_CPUACCT = 2,
- CGROUP_BLKIO = 4,
- CGROUP_MEMORY = 8,
- CGROUP_DEVICE = 16,
- _CGROUP_CONTROLLER_MASK_ALL = 31
-} CGroupControllerMask;
+typedef enum CGroupMask {
+ CGROUP_MASK_CPU = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPU),
+ CGROUP_MASK_CPUACCT = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPUACCT),
+ CGROUP_MASK_BLKIO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BLKIO),
+ CGROUP_MASK_MEMORY = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_MEMORY),
+ CGROUP_MASK_DEVICE = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_DEVICE),
+ _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
+} CGroupMask;
/*
* General rules:
@@ -77,7 +90,6 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path);
int cg_trim(const char *controller, const char *path, bool delete_root);
int cg_rmdir(const char *controller, const char *path);
-// UNNEEDED int cg_delete(const char *controller, const char *path);
int cg_create(const char *controller, const char *path);
int cg_attach(const char *controller, const char *path, pid_t pid);
@@ -93,8 +105,8 @@ int cg_attach_fallback(const char *controller, const char *path, pid_t pid);
// UNNEEDED int cg_install_release_agent(const char *controller, const char *agent);
// UNNEEDED int cg_uninstall_release_agent(const char *controller);
-int cg_is_empty(const char *controller, const char *path, bool ignore_self);
-int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self);
+int cg_is_empty(const char *controller, const char *path);
+int cg_is_empty_recursive(const char *controller, const char *path);
int cg_get_root_path(char **path);
@@ -126,14 +138,24 @@ bool cg_controller_is_valid(const char *p);
// UNNEEDED int cg_slice_to_path(const char *unit, char **ret);
-typedef const char* (*cg_migrate_callback_t)(CGroupControllerMask mask, void *userdata);
+typedef const char* (*cg_migrate_callback_t)(CGroupMask mask, void *userdata);
-// UNNEEDED int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path);
-int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t callback, void *userdata);
-// UNNEEDED int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata);
-// UNNEEDED int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata);
-// UNNEEDED int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root);
+// UNNEEDED int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path);
+int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t callback, void *userdata);
+// UNNEEDED int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata);
+// UNNEEDED int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata);
+// UNNEEDED int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root);
+int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p);
-// UNNEEDED CGroupControllerMask cg_mask_supported(void);
+// UNNEEDED int cg_mask_supported(CGroupMask *ret);
// UNNEEDED int cg_kernel_controllers(Set *controllers);
+
+int cg_unified(void);
+void cg_unified_flush(void);
+
+bool cg_is_unified_wanted(void);
+bool cg_is_legacy_wanted(void);
+
+const char* cgroup_controller_to_string(CGroupController c) _const_;
+CGroupController cgroup_controller_from_string(const char *s) _pure_;
diff --git a/src/basic/conf-files.h b/src/basic/conf-files.h
index 3169a907f..d8aebc5e5 100644
--- a/src/basic/conf-files.h
+++ b/src/basic/conf-files.h
@@ -22,7 +22,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-
-int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...);
-int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs);
-int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *dirs);
+int conf_files_list(char ***ret, const char *suffix, const char *root, const char *dir, ...);
+int conf_files_list_strv(char ***ret, const char *suffix, const char *root, const char* const* dirs);
+int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, const char *dirs);
diff --git a/src/basic/macro.h b/src/basic/macro.h
index 627d768b7..cbc3ca97b 100644
--- a/src/basic/macro.h
+++ b/src/basic/macro.h
@@ -298,6 +298,9 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
#define PTR_TO_GID(p) ((gid_t) (((uintptr_t) (p))-1))
#define GID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
+#define PTR_TO_PID(p) ((pid_t) ((uintptr_t) p))
+#define PID_TO_PTR(p) ((void*) ((uintptr_t) p))
+
#define memzero(x,l) (memset((x), 0, (l)))
#define zero(x) (memzero(&(x), sizeof(x)))
diff --git a/src/basic/missing.h b/src/basic/missing.h
index 37e1133d3..37c3f6774 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -480,6 +480,14 @@ struct btrfs_ioctl_quota_ctl_args {
#define BTRFS_SUPER_MAGIC 0x9123683E
#endif
+#ifndef CGROUP_SUPER_MAGIC
+#define CGROUP_SUPER_MAGIC 0x27e0eb
+#endif
+
+#ifndef TMPFS_MAGIC
+#define TMPFS_MAGIC 0x01021994
+#endif
+
#ifndef MS_MOVE
#define MS_MOVE 8192
#endif
diff --git a/src/basic/ring.h b/src/basic/ring.h
index fc857e7a9..dcd0a3e8b 100644
--- a/src/basic/ring.h
+++ b/src/basic/ring.h
@@ -50,7 +50,6 @@ int ring_push(Ring *r, const void *u8, size_t size);
void ring_pull(Ring *r, size_t size);
/* return size of occupied buffer in bytes */
-static inline size_t ring_get_size(Ring *r)
-{
+static inline size_t ring_get_size(Ring *r) {
return r->used;
}
diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c
index 03ebab32c..cea80b678 100644
--- a/src/basic/selinux-util.c
+++ b/src/basic/selinux-util.c
@@ -207,11 +207,11 @@ int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
if (!mac_selinux_use())
return -EOPNOTSUPP;
- r = getcon(&mycon);
+ r = getcon_raw(&mycon);
if (r < 0)
return -errno;
- r = getfilecon(exe, &fcon);
+ r = getfilecon_raw(exe, &fcon);
if (r < 0)
return -errno;
@@ -233,7 +233,7 @@ int mac_selinux_get_our_label(char **label) {
if (!mac_selinux_use())
return -EOPNOTSUPP;
- r = getcon(label);
+ r = getcon_raw(label);
if (r < 0)
return -errno;
#endif
@@ -257,7 +257,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *
if (!mac_selinux_use())
return -EOPNOTSUPP;
- r = getcon(&mycon);
+ r = getcon_raw(&mycon);
if (r < 0)
return -errno;
@@ -268,7 +268,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *
if (!exec_label) {
/* If there is no context set for next exec let's use context
of target executable */
- r = getfilecon(exe, &fcon);
+ r = getfilecon_raw(exe, &fcon);
if (r < 0)
return -errno;
}
diff --git a/src/basic/special.h b/src/basic/special.h
index e51310eb6..f30458f25 100644
--- a/src/basic/special.h
+++ b/src/basic/special.h
@@ -115,3 +115,6 @@
#define SPECIAL_USER_SLICE "user.slice"
#define SPECIAL_MACHINE_SLICE "machine.slice"
#define SPECIAL_ROOT_SLICE "-.slice"
+
+/* The scope unit systemd itself lives in. */
+#define SPECIAL_INIT_SCOPE "init.scope"
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index 73e5c2e57..51abc78ce 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -1092,3 +1092,22 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
return 0;
}
+
+int ptsname_namespace(int pty, char **ret) {
+ int no = -1, r;
+
+ /* Like ptsname(), but doesn't assume that the path is
+ * accessible in the local namespace. */
+
+ r = ioctl(pty, TIOCGPTN, &no);
+ if (r < 0)
+ return -errno;
+
+ if (no < 0)
+ return -EIO;
+
+ if (asprintf(ret, "/dev/pts/%i", no) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h
index 5964ae5e6..fc61e3abd 100644
--- a/src/basic/terminal-util.h
+++ b/src/basic/terminal-util.h
@@ -107,3 +107,5 @@ int get_ctty(pid_t, dev_t *_devnr, char **r);
int getttyname_malloc(int fd, char **r);
// UNNEEDED int getttyname_harder(int fd, char **r);
+
+int ptsname_namespace(int pty, char **ret);
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index 6c8973ce3..15185a2bb 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -26,6 +26,7 @@
#include "util.h"
#include "time-util.h"
+#include "path-util.h"
#include "strv.h"
usec_t now(clockid_t clock_id) {
@@ -36,6 +37,14 @@ usec_t now(clockid_t clock_id) {
return timespec_load(&ts);
}
+nsec_t now_nsec(clockid_t clock_id) {
+ struct timespec ts;
+
+ assert_se(clock_gettime(clock_id, &ts) == 0);
+
+ return timespec_load_nsec(&ts);
+}
+
dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
assert(ts);
@@ -132,6 +141,18 @@ usec_t timespec_load(const struct timespec *ts) {
(usec_t) ts->tv_nsec / NSEC_PER_USEC;
}
+nsec_t timespec_load_nsec(const struct timespec *ts) {
+ assert(ts);
+
+ if (ts->tv_sec == (time_t) -1 &&
+ ts->tv_nsec == (long) -1)
+ return NSEC_INFINITY;
+
+ return
+ (nsec_t) ts->tv_sec * NSEC_PER_SEC +
+ (nsec_t) ts->tv_nsec;
+}
+
struct timespec *timespec_store(struct timespec *ts, usec_t u) {
assert(ts);
@@ -985,7 +1006,10 @@ bool timezone_is_valid(const char *name) {
const char *p, *t;
struct stat st;
- if (!name || *name == 0 || *name == '/')
+ if (isempty(name))
+ return false;
+
+ if (name[0] == '/')
return false;
for (p = name; *p; p++) {
@@ -1036,3 +1060,30 @@ clockid_t clock_boottime_or_monotonic(void) {
return clock;
}
+
+int get_timezone(char **tz) {
+ _cleanup_free_ char *t = NULL;
+ const char *e;
+ char *z;
+ int r;
+
+ r = readlink_malloc("/etc/localtime", &t);
+ if (r < 0)
+ return r; /* returns EINVAL if not a symlink */
+
+ e = path_startswith(t, "/usr/share/zoneinfo/");
+ if (!e)
+ e = path_startswith(t, "../usr/share/zoneinfo/");
+ if (!e)
+ return -EINVAL;
+
+ if (!timezone_is_valid(e))
+ return -EINVAL;
+
+ z = strdup(e);
+ if (!z)
+ return -ENOMEM;
+
+ *tz = z;
+ return 0;
+}
diff --git a/src/basic/time-util.h b/src/basic/time-util.h
index 84ba371b9..9cd6949b2 100644
--- a/src/basic/time-util.h
+++ b/src/basic/time-util.h
@@ -87,6 +87,8 @@ struct timespec *timespec_store(struct timespec *ts, usec_t u);
usec_t timeval_load(const struct timeval *tv) _pure_;
struct timeval *timeval_store(struct timeval *tv, usec_t u);
+nsec_t timespec_load_nsec(const struct timespec *ts) _pure_;
+
char *format_timestamp(char *buf, size_t l, usec_t t);
// UNNEEDED char *format_timestamp_utc(char *buf, size_t l, usec_t t);
char *format_timestamp_us(char *buf, size_t l, usec_t t);
@@ -110,3 +112,5 @@ int parse_nsec(const char *t, nsec_t *nsec);
clockid_t clock_boottime_or_monotonic(void);
#define xstrftime(buf, fmt, tm) assert_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0)
+
+int get_timezone(char **timezone);
diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c
index f8965262c..0f5b7163e 100644
--- a/src/basic/unit-name.c
+++ b/src/basic/unit-name.c
@@ -598,6 +598,42 @@ int unit_name_from_dbus_path(const char *path, char **name) {
return 0;
}
+const char* unit_dbus_interface_from_type(UnitType t) {
+
+ static const char *const table[_UNIT_TYPE_MAX] = {
+ [UNIT_SERVICE] = "org.freedesktop.systemd1.Service",
+ [UNIT_SOCKET] = "org.freedesktop.systemd1.Socket",
+ [UNIT_BUSNAME] = "org.freedesktop.systemd1.BusName",
+ [UNIT_TARGET] = "org.freedesktop.systemd1.Target",
+ [UNIT_SNAPSHOT] = "org.freedesktop.systemd1.Snapshot",
+ [UNIT_DEVICE] = "org.freedesktop.systemd1.Device",
+ [UNIT_MOUNT] = "org.freedesktop.systemd1.Mount",
+ [UNIT_AUTOMOUNT] = "org.freedesktop.systemd1.Automount",
+ [UNIT_SWAP] = "org.freedesktop.systemd1.Swap",
+ [UNIT_TIMER] = "org.freedesktop.systemd1.Timer",
+ [UNIT_PATH] = "org.freedesktop.systemd1.Path",
+ [UNIT_SLICE] = "org.freedesktop.systemd1.Slice",
+ [UNIT_SCOPE] = "org.freedesktop.systemd1.Scope",
+ };
+
+ if (t < 0)
+ return NULL;
+ if (t >= _UNIT_TYPE_MAX)
+ return NULL;
+
+ return table[t];
+}
+
+const char *unit_dbus_interface_from_name(const char *name) {
+ UnitType t;
+
+ t = unit_name_to_type(name);
+ if (t < 0)
+ return NULL;
+
+ return unit_dbus_interface_from_type(t);
+}
+
static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) {
const char *valid_chars;
@@ -802,7 +838,7 @@ static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
[UNIT_TIMER] = "timer",
[UNIT_PATH] = "path",
[UNIT_SLICE] = "slice",
- [UNIT_SCOPE] = "scope"
+ [UNIT_SCOPE] = "scope",
};
DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
diff --git a/src/basic/unit-name.h b/src/basic/unit-name.h
index 2a954e539..2e04c7d20 100644
--- a/src/basic/unit-name.h
+++ b/src/basic/unit-name.h
@@ -152,6 +152,9 @@ int unit_name_from_path(const char *path, const char *suffix, char **ret);
char *unit_dbus_path_from_name(const char *name);
int unit_name_from_dbus_path(const char *path, char **name);
+const char* unit_dbus_interface_from_type(UnitType t);
+const char *unit_dbus_interface_from_name(const char *name);
+
typedef enum UnitNameMangle {
UNIT_NAME_NOGLOB,
UNIT_NAME_GLOB,
diff --git a/src/basic/util.c b/src/basic/util.c
index 473165f24..3df91488c 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -403,7 +403,10 @@ int parse_uid(const char *s, uid_t* ret_uid) {
return -ERANGE;
if (!uid_is_valid(uid))
- return -ENXIO;
+ return -ENXIO; /* we return ENXIO instead of EINVAL
+ * here, to make it easy to distuingish
+ * invalid numeric uids invalid
+ * strings. */
if (ret_uid)
*ret_uid = uid;
@@ -6198,6 +6201,9 @@ int openpt_in_namespace(pid_t pid, int flags) {
if (master < 0)
_exit(EXIT_FAILURE);
+ if (unlockpt(master) < 0)
+ _exit(EXIT_FAILURE);
+
cmsg = CMSG_FIRSTHDR(&mh);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
diff --git a/src/basic/util.h b/src/basic/util.h
index 4e5a57324..afe5ec6a5 100644
--- a/src/basic/util.h
+++ b/src/basic/util.h
@@ -366,6 +366,9 @@ int fd_is_temporary_fs(int fd);
int pipe_eof(int fd);
+DEFINE_TRIVIAL_CLEANUP_FUNC(cpu_set_t*, CPU_FREE);
+#define _cleanup_cpu_free_ _cleanup_(CPU_FREEp)
+
// UNNEEDED cpu_set_t* cpu_set_malloc(unsigned *ncpus);
#define xsprintf(buf, fmt, ...) assert_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf))
@@ -397,8 +400,6 @@ bool nulstr_contains(const char*nulstr, const char *needle);
// UNNEEDED bool plymouth_running(void);
-bool machine_name_is_valid(const char *s) _pure_;
-
char* strshorten(char *s, size_t l);
// UNNEEDED int symlink_idempotent(const char *from, const char *to);
@@ -566,6 +567,7 @@ _pure_ static inline bool string_is_glob(const char *p) {
// UNNEEDED void *arg);
#define _(String) gettext (String)
+#define N_(String) String
// UNNEEDED void init_gettext(void);
bool is_locale_utf8(void);
diff --git a/src/basic/virt.c b/src/basic/virt.c
index 764de955a..1f9843bab 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -156,7 +156,8 @@ static int detect_vm_dmi(const char **_id) {
"VMW\0" "vmware\0"
"innotek GmbH\0" "oracle\0"
"Xen\0" "xen\0"
- "Bochs\0" "bochs\0";
+ "Bochs\0" "bochs\0"
+ "Parallels\0" "parallels\0";
unsigned i;
for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
@@ -244,8 +245,9 @@ int detect_vm(const char **id) {
r = detect_vm_dmi(&_id);
/* kvm with and without Virtualbox */
+ /* Parallels exports KVMKVMKVM leaf */
if (streq_ptr(_id_cpuid, "kvm")) {
- if (r > 0 && streq(_id, "oracle"))
+ if (r > 0 && (streq(_id, "oracle") || streq(_id, "parallels")))
goto finish;
_id = _id_cpuid;