summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Pitt <martin.pitt@ubuntu.com>2015-09-09 07:26:01 +0200
committerMartin Pitt <martin.pitt@ubuntu.com>2015-09-09 07:26:01 +0200
commitd9dfd2336cec994f89bcce804d0d001a2bddefc4 (patch)
tree7ffa23033f081abcec0f9d9d2774cb7f885e9de4 /src
parent13d276d0d7fb7a1150663395ef30a885a7389db4 (diff)
downloadsystemd-d9dfd2336cec994f89bcce804d0d001a2bddefc4.tar.gz
Imported Upstream version 226
Diffstat (limited to 'src')
-rw-r--r--src/analyze/analyze.c49
-rw-r--r--src/basic/audit.c9
-rw-r--r--src/basic/cgroup-util.c865
-rw-r--r--src/basic/cgroup-util.h58
-rw-r--r--src/basic/conf-files.h7
-rw-r--r--src/basic/copy.c20
-rw-r--r--src/basic/def.h2
-rw-r--r--src/basic/macro.h3
-rw-r--r--src/basic/missing.h8
-rw-r--r--src/basic/process-util.c4
-rw-r--r--src/basic/refcnt.h4
-rw-r--r--src/basic/ring.h3
-rw-r--r--src/basic/selinux-util.c10
-rw-r--r--src/basic/set.h6
-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.h5
-rw-r--r--src/basic/unit-name.c38
-rw-r--r--src/basic/unit-name.h3
-rw-r--r--src/basic/util.c65
-rw-r--r--src/basic/util.h11
-rw-r--r--src/basic/virt.c6
-rw-r--r--src/boot/efi/boot.c15
-rw-r--r--src/cgls/cgls.c213
-rw-r--r--src/cgtop/cgtop.c432
-rw-r--r--src/core/automount.c1
-rw-r--r--src/core/busname.c1
-rw-r--r--src/core/cgroup.c637
-rw-r--r--src/core/cgroup.h35
-rw-r--r--src/core/dbus-cgroup.c26
-rw-r--r--src/core/dbus-unit.c112
-rw-r--r--src/core/dbus.c49
-rw-r--r--src/core/dbus.h1
-rw-r--r--src/core/device.c1
-rw-r--r--src/core/execute.h2
-rw-r--r--src/core/killall.c6
-rw-r--r--src/core/load-fragment-gperf.gperf.m42
-rw-r--r--src/core/load-fragment.c92
-rw-r--r--src/core/load-fragment.h1
-rw-r--r--src/core/main.c63
-rw-r--r--src/core/manager.c33
-rw-r--r--src/core/manager.h15
-rw-r--r--src/core/mount-setup.c11
-rw-r--r--src/core/mount.c3
-rw-r--r--src/core/path.c1
-rw-r--r--src/core/scope.c72
-rw-r--r--src/core/selinux-access.c4
-rw-r--r--src/core/selinux-setup.c1
-rw-r--r--src/core/service.c48
-rw-r--r--src/core/slice.c44
-rw-r--r--src/core/snapshot.c1
-rw-r--r--src/core/socket.c5
-rw-r--r--src/core/swap.c3
-rw-r--r--src/core/target.c1
-rw-r--r--src/core/timer.c1
-rw-r--r--src/core/unit.c331
-rw-r--r--src/core/unit.h20
-rw-r--r--src/gpt-auto-generator/gpt-auto-generator.c92
-rw-r--r--src/hostname/hostnamed.c3
-rw-r--r--src/import/importd.c6
-rw-r--r--src/journal/journald-kmsg.c2
-rw-r--r--src/libsystemd-network/dhcp-internal.h4
-rw-r--r--src/libsystemd-network/dhcp-lease-internal.h53
-rw-r--r--src/libsystemd-network/dhcp-option.c12
-rw-r--r--src/libsystemd-network/dhcp-protocol.h2
-rw-r--r--src/libsystemd-network/dhcp-server-internal.h23
-rw-r--r--src/libsystemd-network/dhcp6-lease-internal.h4
-rw-r--r--src/libsystemd-network/dhcp6-network.c6
-rw-r--r--src/libsystemd-network/dhcp6-option.c4
-rw-r--r--src/libsystemd-network/lldp-internal.c14
-rw-r--r--src/libsystemd-network/lldp-internal.h3
-rw-r--r--src/libsystemd-network/network-internal.c4
-rw-r--r--src/libsystemd-network/network-internal.h4
-rw-r--r--src/libsystemd-network/sd-dhcp-client.c76
-rw-r--r--src/libsystemd-network/sd-dhcp-lease.c791
-rw-r--r--src/libsystemd-network/sd-dhcp-server.c300
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c96
-rw-r--r--src/libsystemd-network/sd-dhcp6-lease.c39
-rw-r--r--src/libsystemd-network/sd-icmp6-nd.c71
-rw-r--r--src/libsystemd-network/sd-ipv4ll.c42
-rw-r--r--src/libsystemd-network/sd-pppoe.c38
-rw-r--r--src/libsystemd-network/test-dhcp-client.c53
-rw-r--r--src/libsystemd-network/test-dhcp-option.c8
-rw-r--r--src/libsystemd-network/test-dhcp-server.c27
-rw-r--r--src/libsystemd-network/test-dhcp6-client.c3
-rw-r--r--src/libsystemd-network/test-ipv4ll.c6
-rw-r--r--src/libsystemd/libsystemd.sym6
-rw-r--r--src/libsystemd/sd-bus/bus-container.c96
-rw-r--r--src/libsystemd/sd-bus/bus-creds.c8
-rw-r--r--src/libsystemd/sd-bus/bus-match.c2
-rw-r--r--src/libsystemd/sd-bus/bus-message.c4
-rw-r--r--src/libsystemd/sd-bus/bus-objects.c22
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c33
-rw-r--r--src/libsystemd/sd-bus/test-bus-creds.c6
-rw-r--r--src/libsystemd/sd-event/sd-event.c433
-rw-r--r--src/libsystemd/sd-event/test-event.c66
-rw-r--r--src/libsystemd/sd-hwdb/sd-hwdb.c13
-rw-r--r--src/libsystemd/sd-login/sd-login.c217
-rw-r--r--src/libsystemd/sd-login/test-login.c30
-rw-r--r--src/libsystemd/sd-network/sd-network.c22
-rw-r--r--src/libsystemd/sd-resolve/sd-resolve.c178
-rw-r--r--src/libsystemd/sd-resolve/test-resolve.c63
-rw-r--r--src/locale/localed.c4
-rw-r--r--src/login/70-power-switch.rules1
-rw-r--r--src/login/loginctl.c2
-rw-r--r--src/login/logind-dbus.c61
-rw-r--r--src/login/logind-seat-dbus.c1
-rw-r--r--src/login/logind-session-dbus.c3
-rw-r--r--src/login/logind-user-dbus.c2
-rw-r--r--src/login/pam_systemd.c35
-rw-r--r--src/machine/image-dbus.c5
-rw-r--r--src/machine/machine-dbus.c28
-rw-r--r--src/machine/machine.c14
-rw-r--r--src/machine/machinectl.c5
-rw-r--r--src/machine/machined-dbus.c1
-rw-r--r--src/network/networkctl.c9
-rw-r--r--src/network/networkd-address-pool.c3
-rw-r--r--src/network/networkd-address-pool.h43
-rw-r--r--src/network/networkd-address.c4
-rw-r--r--src/network/networkd-address.h73
-rw-r--r--src/network/networkd-dhcp4.c186
-rw-r--r--src/network/networkd-fdb.c6
-rw-r--r--src/network/networkd-fdb.h47
-rw-r--r--src/network/networkd-link.c263
-rw-r--r--src/network/networkd-link.h32
-rw-r--r--src/network/networkd-manager.c93
-rw-r--r--src/network/networkd-netdev-bond.h4
-rw-r--r--src/network/networkd-netdev-gperf.gperf1
-rw-r--r--src/network/networkd-netdev-tunnel.c45
-rw-r--r--src/network/networkd-netdev-tunnel.h6
-rw-r--r--src/network/networkd-netdev.c9
-rw-r--r--src/network/networkd-netdev.h5
-rw-r--r--src/network/networkd-network-gperf.gperf11
-rw-r--r--src/network/networkd-network.c266
-rw-r--r--src/network/networkd-network.h181
-rw-r--r--src/network/networkd-route.c8
-rw-r--r--src/network/networkd-route.h60
-rw-r--r--src/network/networkd-util.c144
-rw-r--r--src/network/networkd-util.h52
-rw-r--r--src/network/networkd.h384
-rw-r--r--src/nspawn/.gitignore1
-rw-r--r--src/nspawn/nspawn-cgroup.c162
-rw-r--r--src/nspawn/nspawn-cgroup.h29
-rw-r--r--src/nspawn/nspawn-expose-ports.c277
-rw-r--r--src/nspawn/nspawn-expose-ports.h45
-rw-r--r--src/nspawn/nspawn-gperf.gperf38
-rw-r--r--src/nspawn/nspawn-mount.c869
-rw-r--r--src/nspawn/nspawn-mount.h70
-rw-r--r--src/nspawn/nspawn-network.c440
-rw-r--r--src/nspawn/nspawn-network.h36
-rw-r--r--src/nspawn/nspawn-register.c244
-rw-r--r--src/nspawn/nspawn-register.h31
-rw-r--r--src/nspawn/nspawn-settings.c262
-rw-r--r--src/nspawn/nspawn-settings.h87
-rw-r--r--src/nspawn/nspawn-setuid.c272
-rw-r--r--src/nspawn/nspawn-setuid.h24
-rw-r--r--src/nspawn/nspawn.c2451
-rw-r--r--src/resolve/resolved-dns-packet.h2
-rw-r--r--src/run/run.c45
l---------src/shared/Makefile1
-rw-r--r--src/shared/bus-util.c72
-rw-r--r--src/shared/bus-util.h4
-rw-r--r--src/shared/cgroup-show.c8
-rw-r--r--src/shared/conf-parser.c97
-rw-r--r--src/shared/conf-parser.h4
-rw-r--r--src/shared/machine-image.c2
-rw-r--r--src/shared/pager.c31
-rw-r--r--src/systemctl/systemctl.c30
-rw-r--r--src/systemd/sd-dhcp-lease.h15
-rw-r--r--src/systemd/sd-dhcp-server.h15
-rw-r--r--src/systemd/sd-login.h36
-rw-r--r--src/systemd/sd-network.h3
-rw-r--r--src/systemd/sd-resolve.h13
-rw-r--r--src/test/test-cgroup-mask.c36
-rw-r--r--src/test/test-cgroup-util.c12
-rw-r--r--src/test/test-cgroup.c16
-rw-r--r--src/test/test-engine.c2
-rw-r--r--src/test/test-path.c2
-rw-r--r--src/test/test-sched-prio.c2
-rw-r--r--src/test/test-util.c6
-rw-r--r--src/timedate/timedated.c37
-rw-r--r--src/udev/udev-builtin-net_id.c8
-rw-r--r--src/udev/udev-builtin-uaccess.c2
-rw-r--r--src/udev/udevd.c2
186 files changed, 9190 insertions, 5269 deletions
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index db1e7f3f37..ab7fb53269 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -1092,12 +1092,59 @@ static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[]) {
return 0;
}
+static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
+ _cleanup_strv_free_ char **expanded_patterns = NULL;
+ char **pattern;
+ int r;
+
+ STRV_FOREACH(pattern, patterns) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *unit = NULL, *unit_id = NULL;
+
+ if (strv_extend(&expanded_patterns, *pattern) < 0)
+ return log_oom();
+
+ if (string_is_glob(*pattern))
+ continue;
+
+ unit = unit_dbus_path_from_name(*pattern);
+ if (!unit)
+ return log_oom();
+
+ r = sd_bus_get_property_string(
+ bus,
+ "org.freedesktop.systemd1",
+ unit,
+ "org.freedesktop.systemd1.Unit",
+ "Id",
+ &error,
+ &unit_id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
+
+ if (!streq(*pattern, unit_id)) {
+ if (strv_extend(&expanded_patterns, unit_id) < 0)
+ return log_oom();
+ }
+ }
+
+ *ret = expanded_patterns;
+ expanded_patterns = NULL; /* do not free */
+
+ return 0;
+}
+
static int dot(sd_bus *bus, char* patterns[]) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_strv_free_ char **expanded_patterns = NULL;
int r;
UnitInfo u;
+ r = expand_patterns(bus, patterns, &expanded_patterns);
+ if (r < 0)
+ return r;
+
r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
@@ -1120,7 +1167,7 @@ static int dot(sd_bus *bus, char* patterns[]) {
while ((r = bus_parse_unit_info(reply, &u)) > 0) {
- r = graph_one(bus, &u, patterns);
+ r = graph_one(bus, &u, expanded_patterns);
if (r < 0)
return r;
}
diff --git a/src/basic/audit.c b/src/basic/audit.c
index 54148fcf18..1f593aa813 100644
--- a/src/basic/audit.c
+++ b/src/basic/audit.c
@@ -36,6 +36,11 @@ int audit_session_from_pid(pid_t pid, uint32_t *id) {
assert(id);
+ /* We don't convert ENOENT to ESRCH here, since we can't
+ * really distuingish between "audit is not available in the
+ * kernel" and "the process does not exist", both which will
+ * result in ENOENT. */
+
p = procfs_file_alloca(pid, "sessionid");
r = read_one_line_file(p, &s);
@@ -47,7 +52,7 @@ int audit_session_from_pid(pid_t pid, uint32_t *id) {
return r;
if (u == AUDIT_SESSION_INVALID || u <= 0)
- return -ENXIO;
+ return -ENODATA;
*id = u;
return 0;
@@ -68,6 +73,8 @@ int audit_loginuid_from_pid(pid_t pid, uid_t *uid) {
return r;
r = parse_uid(s, &u);
+ if (r == -ENXIO) /* the UID was -1 */
+ return -ENODATA;
if (r < 0)
return r;
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index 6b3162a35f..f661a54d99 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;
@@ -113,7 +113,7 @@ int cg_read_subgroup(DIR *d, char **fn) {
assert(d);
assert(fn);
- FOREACH_DIRENT(de, d, return -errno) {
+ FOREACH_DIRENT_ALL(de, d, return -errno) {
char *b;
if (de->d_type != DT_DIR)
@@ -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,15 @@ 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
+ * exist in the root cgroup, we only check for
+ * them there. */
+ if (cfrom &&
+ (isempty(pfrom) || path_equal(pfrom, "/")) &&
+ is_kernel_thread(pid) > 0)
continue;
r = cg_attach(cto, pto, pid);
@@ -330,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;
@@ -382,12 +390,8 @@ int cg_migrate_recursive(
p = strjoin(pfrom, "/", fn, NULL);
free(fn);
- if (!p) {
- if (ret >= 0)
- return -ENOMEM;
-
- return ret;
- }
+ if (!p)
+ return -ENOMEM;
r = cg_migrate_recursive(cfrom, p, cto, pto, ignore_self, rem);
if (r != 0 && ret >= 0)
@@ -428,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
- return controller;
+ /* 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);
- else
- return -EINVAL;
- }
+ 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
+ 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))
- return -EINVAL;
+ if (!controller) {
+ char *t;
- if (_unlikely_(!good)) {
- int r;
+ /* If no controller is specified, we return the path
+ * *below* the controllers, without any prefix. */
- r = path_is_mount_point("/sys/fs/cgroup", 0);
- if (r < 0)
- return r;
- if (r == 0)
- return -ENOENT;
+ if (!path && !suffix)
+ return -EINVAL;
- /* Cache this to save a few stat()s */
- good = true;
+ if (!suffix)
+ t = strdup(path);
+ else if (!path)
+ t = strdup(suffix);
+ else
+ t = strjoin(path, "/", suffix, NULL);
+ if (!t)
+ return -ENOMEM;
+
+ *fs = path_kill_slashes(t);
+ return 0;
}
- p = controller ? normalize_controller(controller) : NULL;
+ 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;
- return join_path(p, path, suffix, fs);
+ path_kill_slashes(*fs);
+ return 0;
}
-static int check_hierarchy(const char *p) {
- const char *cc;
+static int controller_is_accessible(const char *controller) {
+ int unified;
- assert(p);
+ assert(controller);
- if (!filename_is_valid(p))
- return 0;
+ /* 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 */
- /* Check if this controller actually really exists */
- cc = strjoina("/sys/fs/cgroup/", p);
- if (laccess(cc, F_OK) < 0)
- return -errno;
+ 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 (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
+ return 0;
+
+ 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) {
@@ -549,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;
}
@@ -564,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)
@@ -575,20 +645,6 @@ int cg_trim(const char *controller, const char *path, bool delete_root) {
return r;
}
-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;
-}
-
int cg_create(const char *controller, const char *path) {
_cleanup_free_ char *fs = NULL;
int r;
@@ -664,13 +720,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;
}
int cg_set_group_access(
@@ -683,7 +741,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;
@@ -703,7 +762,7 @@ int cg_set_task_access(
gid_t gid) {
_cleanup_free_ char *fs = NULL, *procs = NULL;
- int r;
+ int r, unified;
assert(path);
@@ -721,77 +780,88 @@ 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;
}
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);
- if (controller) {
- if (!cg_controller_is_valid(controller))
- return -EINVAL;
+ unified = cg_unified();
+ if (unified < 0)
+ return unified;
+ if (unified == 0) {
+ if (controller) {
+ if (!cg_controller_is_valid(controller))
+ return -EINVAL;
+ } else
+ controller = SYSTEMD_CGROUP_CONTROLLER;
- controller = normalize_controller(controller);
- } else
- controller = SYSTEMD_CGROUP_CONTROLLER;
+ 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;
- size_t k;
- const char *word, *state;
- bool found = false;
+ char *e, *p;
truncate_nl(line);
- l = strchr(line, ':');
- if (!l)
- continue;
-
- l++;
- e = strchr(l, ':');
- if (!e)
- continue;
+ if (unified) {
+ e = startswith(line, "0:");
+ if (!e)
+ continue;
- *e = 0;
+ e = strchr(e, ':');
+ if (!e)
+ continue;
+ } else {
+ char *l;
+ size_t k;
+ const char *word, *state;
+ bool found = false;
+
+ l = strchr(line, ':');
+ if (!l)
+ continue;
- FOREACH_WORD_SEPARATOR(word, k, l, ",", state) {
+ l++;
+ e = strchr(l, ':');
+ if (!e)
+ continue;
- if (k == cs && memcmp(word, controller, cs) == 0) {
- found = true;
- break;
+ *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;
}
- if (!found)
- continue;
-
p = strdup(e + 1);
if (!p)
return -ENOMEM;
@@ -800,16 +870,22 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
return 0;
}
- return -ENOENT;
+ return -ENODATA;
}
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;
@@ -819,11 +895,11 @@ 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(fs, agent, 0);
if (r < 0)
return r;
- } else if (!streq(sc, agent))
+ } else if (!path_equal(sc, agent))
return -EEXIST;
fs = mfree(fs);
@@ -853,7 +929,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)
@@ -876,73 +958,92 @@ int cg_uninstall_release_agent(const char *controller) {
return 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;
-
- self_pid = getpid();
-
- while ((r = cg_read_pid(f, &pid)) > 0) {
-
- if (ignore_self && pid == self_pid)
- continue;
-
- found = true;
- break;
- }
+ return r;
+ r = cg_read_pid(f, &pid);
if (r < 0)
return r;
- return !found;
+ return r == 0;
}
-int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) {
- _cleanup_closedir_ DIR *d = NULL;
- char *fn;
- int r;
+int cg_is_empty_recursive(const char *controller, const char *path) {
+ int unified, r;
assert(path);
- r = cg_is_empty(controller, path, ignore_self);
- if (r <= 0)
- return r;
+ /* The root cgroup is always populated */
+ if (controller && (isempty(path) || path_equal(path, "/")))
+ return false;
- r = cg_enumerate_subgroups(controller, path, &d);
- if (r < 0)
- return r == -ENOENT ? 1 : r;
+ unified = cg_unified();
+ if (unified < 0)
+ return unified;
- while ((r = cg_read_subgroup(d, &fn)) > 0) {
- _cleanup_free_ char *p = NULL;
+ if (unified > 0) {
+ _cleanup_free_ char *populated = NULL, *t = NULL;
- p = strjoin(path, "/", fn, NULL);
- free(fn);
- if (!p)
- return -ENOMEM;
+ /* 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;
- r = cg_is_empty_recursive(controller, p, ignore_self);
+ r = read_one_line_file(populated, &t);
+ if (r == -ENOENT)
+ return 1;
+ if (r < 0)
+ return r;
+
+ return streq(t, "0");
+ } else {
+ _cleanup_closedir_ DIR *d = NULL;
+ char *fn;
+
+ r = cg_is_empty(controller, path);
if (r <= 0)
return r;
- }
- if (r < 0)
- return r;
+ r = cg_enumerate_subgroups(controller, path, &d);
+ if (r == -ENOENT)
+ return 1;
+ if (r < 0)
+ return r;
- return 1;
+ while ((r = cg_read_subgroup(d, &fn)) > 0) {
+ _cleanup_free_ char *p = NULL;
+
+ p = strjoin(path, "/", fn, NULL);
+ free(fn);
+ if (!p)
+ return -ENOMEM;
+
+ r = cg_is_empty_recursive(controller, p);
+ if (r <= 0)
+ return r;
+ }
+ if (r < 0)
+ return r;
+
+ 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);
@@ -970,7 +1071,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;
@@ -983,10 +1084,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)) {
@@ -994,13 +1092,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);
@@ -1054,7 +1148,7 @@ int cg_mangle_path(const char *path, char **result) {
if (r < 0)
return r;
- return cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
+ return cg_get_path(c ?: SYSTEMD_CGROUP_CONTROLLER, p ?: "/", NULL, result);
}
int cg_get_root_path(char **path) {
@@ -1067,7 +1161,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;
@@ -1095,7 +1193,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;
@@ -1359,17 +1457,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);
}
@@ -1562,31 +1658,38 @@ 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 = cgroup_controller_to_string(c);
- n = strndupa(p, dot - p);
+ if (l != strlen(n))
+ continue;
- if (check_hierarchy(n) >= 0)
- need_prefix = true;
+ if (memcmp(p, n, l) != 0)
+ continue;
+
+ need_prefix = true;
+ break;
}
}
}
if (need_prefix)
return strappend("_", p);
- else
- return strdup(p);
+
+ return strdup(p);
}
char *cg_unescape(const char *p) {
@@ -1719,17 +1822,9 @@ int cg_get_attribute(const char *controller, const char *path, const char *attri
return read_one_line_file(p, ret);
}
-static const char mask_names[] =
- "cpu\0"
- "cpuacct\0"
- "blkio\0"
- "memory\0"
- "devices\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
@@ -1740,69 +1835,82 @@ 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;
}
-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(SYSTEMD_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) {
- const char *p = NULL;
+ for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
+ CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
+ const char *p = NULL;
- if (path_callback)
- p = path_callback(bit, userdata);
+ if (!(supported & bit))
+ continue;
- if (!p)
- p = path;
+ if (path_callback)
+ p = path_callback(bit, userdata);
- cg_attach_fallback(n, p, pid);
- }
+ if (!p)
+ p = path;
- bit <<= 1;
+ (void) cg_attach_fallback(cgroup_controller_to_string(c), p, pid);
}
return 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(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
@@ -1810,56 +1918,127 @@ int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, cons
return r;
}
- NULSTR_FOREACH(n, mask_names) {
- if (supported & bit) {
- const char *p = NULL;
+ unified = cg_unified();
+ if (unified < 0)
+ return unified;
+ if (unified > 0)
+ return r;
- if (to_callback)
- p = to_callback(bit, userdata);
+ for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
+ CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
+ const char *p = NULL;
- if (!p)
- p = to;
+ if (!(supported & bit))
+ continue;
- cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, n, p, false, false);
- }
+ if (to_callback)
+ p = to_callback(bit, userdata);
- bit <<= 1;
+ if (!p)
+ p = to;
+
+ (void) cg_migrate_recursive_fallback(SYSTEMD_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(SYSTEMD_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);
- bit <<= 1;
+ if (!(supported & bit))
+ continue;
+
+ (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;
- NULSTR_FOREACH(n, mask_names) {
- if (check_hierarchy(n) >= 0)
- mask |= bit;
+ /* In the unified hierarchy we can read the supported
+ * and accessible controllers from a the top-level
+ * cgroup attribute */
- bit <<= 1;
+ r = cg_get_root_path(&root);
+ if (r < 0)
+ return r;
+
+ r = cg_get_path(SYSTEMD_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;
+
+ r = extract_first_word(&c, &n, NULL, 0);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ v = cgroup_controller_from_string(n);
+ if (v < 0)
+ continue;
+
+ mask |= CGROUP_CONTROLLER_TO_MASK(v);
+ }
+
+ /* 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);
+ }
}
- return mask;
+ *ret = mask;
+ return 0;
}
int cg_kernel_controllers(Set *controllers) {
@@ -1869,6 +2048,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)
@@ -1889,7 +2073,7 @@ int cg_kernel_controllers(Set *controllers) {
if (feof(f))
break;
- if (ferror(f) && errno)
+ if (ferror(f) && errno != 0)
return -errno;
return -EBADMSG;
@@ -1900,7 +2084,7 @@ int cg_kernel_controllers(Set *controllers) {
continue;
}
- if (!filename_is_valid(controller)) {
+ if (!cg_controller_is_valid(controller)) {
free(controller);
return -EBADMSG;
}
@@ -1912,3 +2096,122 @@ int cg_kernel_controllers(Set *controllers) {
return 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(SYSTEMD_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 fd72e9e5c5..6fd6d80590 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);
-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_set_task_access(const char *controller, const char *path, mode_t mode, ui
int cg_install_release_agent(const char *controller, const char *agent);
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);
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);
-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);
-int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata);
-int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata);
-int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root);
+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);
+int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata);
+int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata);
+int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root);
+int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p);
-CGroupControllerMask cg_mask_supported(void);
+int cg_mask_supported(CGroupMask *ret);
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 3169a907f1..d8aebc5e5b 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/copy.c b/src/basic/copy.c
index 33427c6a73..cc5faa80a1 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -30,7 +30,7 @@
#define COPY_BUFFER_SIZE (16*1024)
int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) {
- bool try_sendfile = true;
+ bool try_sendfile = true, try_splice = true;
int r;
assert(fdf >= 0);
@@ -69,7 +69,23 @@ int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) {
} else if (n == 0) /* EOF */
break;
else if (n > 0)
- /* Succcess! */
+ /* Success! */
+ goto next;
+ }
+
+ /* The try splice, unless we already tried */
+ if (try_splice) {
+ n = splice(fdf, NULL, fdt, NULL, m, 0);
+ if (n < 0) {
+ if (errno != EINVAL && errno != ENOSYS)
+ return -errno;
+
+ try_splice = false;
+ /* use fallback below */
+ } else if (n == 0) /* EOF */
+ break;
+ else if (n > 0)
+ /* Success! */
goto next;
}
diff --git a/src/basic/def.h b/src/basic/def.h
index 5aaba1fe87..7c4161eb72 100644
--- a/src/basic/def.h
+++ b/src/basic/def.h
@@ -35,7 +35,7 @@
* the watchdog pings will keep the loop busy. */
#define DEFAULT_EXIT_USEC (30*USEC_PER_SEC)
-#define SYSTEMD_CGROUP_CONTROLLER "systemd"
+#define SYSTEMD_CGROUP_CONTROLLER "name=systemd"
#define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT
#define SIGNALS_IGNORE SIGPIPE
diff --git a/src/basic/macro.h b/src/basic/macro.h
index 627d768b76..cbc3ca97b8 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 34ab0254dd..dc1f244d4c 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -492,6 +492,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/process-util.c b/src/basic/process-util.c
index 61f188467f..cff2d2a034 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -181,10 +181,10 @@ int is_kernel_thread(pid_t pid) {
bool eof;
FILE *f;
- if (pid == 0)
+ if (pid == 0 || pid == 1) /* pid 1, and we ourselves certainly aren't a kernel thread */
return 0;
- assert(pid > 0);
+ assert(pid > 1);
p = procfs_file_alloca(pid, "cmdline");
f = fopen(p, "re");
diff --git a/src/basic/refcnt.h b/src/basic/refcnt.h
index 0502c20a2e..8a39d69fe4 100644
--- a/src/basic/refcnt.h
+++ b/src/basic/refcnt.h
@@ -21,7 +21,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-/* A type-safe atomic refcounter */
+/* A type-safe atomic refcounter.
+ *
+ * DO NOT USE THIS UNLESS YOU ACTUALLY CARE ABOUT THREAD SAFETY! */
typedef struct {
volatile unsigned _value;
diff --git a/src/basic/ring.h b/src/basic/ring.h
index a7c44d1b56..dbd6296384 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 7c58985cd2..a39a0f775a 100644
--- a/src/basic/selinux-util.c
+++ b/src/basic/selinux-util.c
@@ -199,11 +199,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;
@@ -225,7 +225,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
@@ -249,7 +249,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;
@@ -260,7 +260,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/set.h b/src/basic/set.h
index 51e40d3a6c..4554ef2d49 100644
--- a/src/basic/set.h
+++ b/src/basic/set.h
@@ -28,12 +28,14 @@ Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
-static inline void set_free(Set *s) {
+static inline Set *set_free(Set *s) {
internal_hashmap_free(HASHMAP_BASE(s));
+ return NULL;
}
-static inline void set_free_free(Set *s) {
+static inline Set *set_free_free(Set *s) {
internal_hashmap_free_free(HASHMAP_BASE(s));
+ return NULL;
}
/* no set_free_free_free */
diff --git a/src/basic/special.h b/src/basic/special.h
index e51310eb6d..f30458f25a 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 cf55263bbf..c5ef5ab0d1 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -1074,3 +1074,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 188714f228..b9a3809a6c 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);
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 e278196c90..531931f6e1 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);
@@ -129,6 +138,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);
@@ -971,7 +992,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++) {
@@ -1021,3 +1045,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 2aba042217..de881e8fe1 100644
--- a/src/basic/time-util.h
+++ b/src/basic/time-util.h
@@ -70,6 +70,7 @@ typedef struct dual_timestamp {
#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) { 0ULL, 0ULL })
usec_t now(clockid_t clock);
+nsec_t now_nsec(clockid_t clock);
dual_timestamp* dual_timestamp_get(dual_timestamp *ts);
dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
@@ -87,6 +88,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);
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 +113,5 @@ bool timezone_is_valid(const char *name);
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 fa530da456..8742ee757f 100644
--- a/src/basic/unit-name.c
+++ b/src/basic/unit-name.c
@@ -586,6 +586,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;
@@ -787,7 +823,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 b2043d0870..28b3a555f3 100644
--- a/src/basic/unit-name.h
+++ b/src/basic/unit-name.h
@@ -152,6 +152,9 @@ int unit_name_to_path(const char *name, 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 f752595ca1..86aacad307 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -373,6 +373,19 @@ int parse_pid(const char *s, pid_t* ret_pid) {
return 0;
}
+bool uid_is_valid(uid_t uid) {
+
+ /* Some libc APIs use UID_INVALID as special placeholder */
+ if (uid == (uid_t) 0xFFFFFFFF)
+ return false;
+
+ /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */
+ if (uid == (uid_t) 0xFFFF)
+ return false;
+
+ return true;
+}
+
int parse_uid(const char *s, uid_t* ret_uid) {
unsigned long ul = 0;
uid_t uid;
@@ -389,13 +402,11 @@ int parse_uid(const char *s, uid_t* ret_uid) {
if ((unsigned long) uid != ul)
return -ERANGE;
- /* Some libc APIs use UID_INVALID as special placeholder */
- if (uid == (uid_t) 0xFFFFFFFF)
- return -ENXIO;
-
- /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */
- if (uid == (uid_t) 0xFFFF)
- return -ENXIO;
+ if (!uid_is_valid(uid))
+ 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;
@@ -4273,7 +4284,7 @@ bool is_locale_utf8(void) {
/* Check result, but ignore the result if C was set
* explicitly. */
cached_answer =
- streq(set, "C") &&
+ STR_IN_SET(set, "C", "POSIX") &&
!getenv("LC_ALL") &&
!getenv("LC_CTYPE") &&
!getenv("LANG");
@@ -5754,40 +5765,39 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
switch (state) {
case START:
- if (c == 0) {
- if (flags & EXTRACT_DONT_COALESCE_SEPARATORS)
- if (!GREEDY_REALLOC(s, allocated, sz+1))
- return -ENOMEM;
+ if (flags & EXTRACT_DONT_COALESCE_SEPARATORS)
+ if (!GREEDY_REALLOC(s, allocated, sz+1))
+ return -ENOMEM;
+
+ if (c == 0)
goto finish_force_terminate;
- } else if (strchr(separators, c)) {
+ else if (strchr(separators, c)) {
if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
- if (!GREEDY_REALLOC(s, allocated, sz+1))
- return -ENOMEM;
(*p) ++;
goto finish_force_next;
}
break;
}
+ /* We found a non-blank character, so we will always
+ * want to return a string (even if it is empty),
+ * allocate it here. */
+ if (!GREEDY_REALLOC(s, allocated, sz+1))
+ return -ENOMEM;
+
state = VALUE;
/* fallthrough */
case VALUE:
if (c == 0)
goto finish_force_terminate;
- else if (c == '\'' && (flags & EXTRACT_QUOTES)) {
- if (!GREEDY_REALLOC(s, allocated, sz+1))
- return -ENOMEM;
-
+ else if (c == '\'' && (flags & EXTRACT_QUOTES))
state = SINGLE_QUOTE;
- } else if (c == '\\')
+ else if (c == '\\')
state = VALUE_ESCAPE;
- else if (c == '\"' && (flags & EXTRACT_QUOTES)) {
- if (!GREEDY_REALLOC(s, allocated, sz+1))
- return -ENOMEM;
-
+ else if (c == '\"' && (flags & EXTRACT_QUOTES))
state = DOUBLE_QUOTE;
- } else if (strchr(separators, c)) {
+ else if (strchr(separators, c)) {
if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
(*p) ++;
goto finish_force_next;
@@ -5891,8 +5901,6 @@ end_escape:
case SEPARATOR:
if (c == 0)
goto finish_force_terminate;
- if (flags & EXTRACT_DONT_COALESCE_SEPARATORS)
- goto finish_force_next;
if (!strchr(separators, c))
goto finish;
break;
@@ -6098,6 +6106,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 1ead7b5419..0fafebd52d 100644
--- a/src/basic/util.h
+++ b/src/basic/util.h
@@ -83,7 +83,7 @@ int strcmp_ptr(const char *a, const char *b) _pure_;
#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
-#define malloc0(n) (calloc((n), 1))
+#define malloc0(n) (calloc(1, (n)))
static inline void *mfree(void *memory) {
free(memory);
@@ -154,7 +154,10 @@ int parse_size(const char *t, off_t base, off_t *size);
int parse_boolean(const char *v) _pure_;
int parse_pid(const char *s, pid_t* ret_pid);
int parse_uid(const char *s, uid_t* ret_uid);
-#define parse_gid(s, ret_uid) parse_uid(s, ret_uid)
+#define parse_gid(s, ret_gid) parse_uid(s, ret_gid)
+
+bool uid_is_valid(uid_t uid);
+#define gid_is_valid(gid) uid_is_valid(gid)
int safe_atou(const char *s, unsigned *ret_u);
int safe_atoi(const char *s, int *ret_i);
@@ -363,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)
+
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))
@@ -561,6 +567,7 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
void *arg);
#define _(String) gettext (String)
+#define N_(String) String
void init_gettext(void);
bool is_locale_utf8(void);
diff --git a/src/basic/virt.c b/src/basic/virt.c
index a8d26716a1..4a4bebd528 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;
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index 4ac193e22a..38b79da886 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -70,16 +70,14 @@ typedef struct {
BOOLEAN no_editor;
} Config;
-static VOID cursor_left(UINTN *cursor, UINTN *first)
-{
+static VOID cursor_left(UINTN *cursor, UINTN *first) {
if ((*cursor) > 0)
(*cursor)--;
else if ((*first) > 0)
(*first)--;
}
-static VOID cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len)
-{
+static VOID cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len) {
if ((*cursor)+1 < x_max)
(*cursor)++;
else if ((*first) + (*cursor) < len)
@@ -856,13 +854,11 @@ static VOID config_entry_free(ConfigEntry *entry) {
FreePool(entry->options);
}
-static BOOLEAN is_digit(CHAR16 c)
-{
+static BOOLEAN is_digit(CHAR16 c) {
return (c >= '0') && (c <= '9');
}
-static UINTN c_order(CHAR16 c)
-{
+static UINTN c_order(CHAR16 c) {
if (c == '\0')
return 0;
if (is_digit(c))
@@ -873,8 +869,7 @@ static UINTN c_order(CHAR16 c)
return c + 0x10000;
}
-static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2)
-{
+static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2) {
CHAR16 *os1 = s1;
CHAR16 *os2 = s2;
diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c
index b8d1d2ccaf..4fb642e7b3 100644
--- a/src/cgls/cgls.c
+++ b/src/cgls/cgls.c
@@ -54,7 +54,7 @@ static void help(void) {
" -a --all Show all groups, including empty\n"
" -l --full Do not ellipsize output\n"
" -k Include kernel threads in output\n"
- " -M --machine Show container\n"
+ " -M --machine= Show container\n"
, program_invocation_short_name);
}
@@ -123,146 +123,161 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-int main(int argc, char *argv[]) {
- int r = 0, retval = EXIT_FAILURE;
- int output_flags;
- _cleanup_free_ char *root = NULL;
+static int get_cgroup_root(char **ret) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ _cleanup_free_ char *unit = NULL, *path = NULL;
+ const char *m;
+ int r;
+
+ if (!arg_machine) {
+ r = cg_get_root_path(ret);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get root control group path: %m");
+
+ return 0;
+ }
+
+ m = strjoina("/run/systemd/machines/", arg_machine);
+ r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load machine data: %m");
+
+ path = unit_dbus_path_from_name(unit);
+ if (!path)
+ return log_oom();
+
+ r = bus_open_transport(BUS_TRANSPORT_LOCAL, NULL, false, &bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus connection: %m");
+
+ r = sd_bus_get_property_string(
+ bus,
+ "org.freedesktop.systemd1",
+ path,
+ unit_dbus_interface_from_name(unit),
+ "ControlGroup",
+ &error,
+ ret);
+ if (r < 0)
+ return log_error_errno(r, "Failed to query unit control group path: %s", bus_error_message(&error, r));
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ int r, output_flags;
log_parse_environment();
log_open();
r = parse_argv(argc, argv);
- if (r < 0)
- goto finish;
- else if (r == 0) {
- retval = EXIT_SUCCESS;
+ if (r <= 0)
goto finish;
- }
if (!arg_no_pager) {
r = pager_open(false);
- if (r > 0) {
- if (arg_full == -1)
- arg_full = true;
- }
+ if (r > 0 && arg_full < 0)
+ arg_full = true;
}
output_flags =
arg_all * OUTPUT_SHOW_ALL |
(arg_full > 0) * OUTPUT_FULL_WIDTH;
- r = bus_open_transport(BUS_TRANSPORT_LOCAL, NULL, false, &bus);
- if (r < 0) {
- log_error_errno(r, "Failed to create bus connection: %m");
- goto finish;
- }
-
if (optind < argc) {
+ _cleanup_free_ char *root = NULL;
int i;
+ r = get_cgroup_root(&root);
+ if (r < 0)
+ goto finish;
+
for (i = optind; i < argc; i++) {
int q;
- fprintf(stdout, "%s:\n", argv[i]);
- fflush(stdout);
+ if (path_startswith(argv[i], "/sys/fs/cgroup")) {
- if (arg_machine)
- root = strjoin("machine/", arg_machine, "/", argv[i], NULL);
- else
- root = strdup(argv[i]);
- if (!root)
- return log_oom();
+ printf("Directory %s:\n", argv[i]);
+ fflush(stdout);
+
+ q = show_cgroup_by_path(argv[i], NULL, 0, arg_kernel_threads, output_flags);
+ } else {
+ _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL;
+ const char *controller, *path;
+
+ r = cg_split_spec(argv[i], &c, &p);
+ if (r < 0) {
+ log_error_errno(r, "Failed to split argument %s: %m", argv[i]);
+ goto finish;
+ }
+
+ controller = c ?: SYSTEMD_CGROUP_CONTROLLER;
+ if (p) {
+ j = strjoin(root, "/", p, NULL);
+ if (!j) {
+ r = log_oom();
+ goto finish;
+ }
+
+ path_kill_slashes(j);
+ path = j;
+ } else
+ path = root;
+
+ if (cg_unified() > 0)
+ printf("Control group %s:\n", path);
+ else
+ printf("Controller %s; control group %s:\n", controller, path);
+ fflush(stdout);
+
+ q = show_cgroup(controller, path, NULL, 0, arg_kernel_threads, output_flags);
+ }
- q = show_cgroup_by_path(root, NULL, 0,
- arg_kernel_threads, output_flags);
if (q < 0)
r = q;
}
} else {
- _cleanup_free_ char *p;
+ bool done = false;
- p = get_current_dir_name();
- if (!p) {
- log_error_errno(errno, "Cannot determine current working directory: %m");
- goto finish;
- }
+ if (!arg_machine) {
+ _cleanup_free_ char *cwd = NULL;
- if (path_startswith(p, "/sys/fs/cgroup") && !arg_machine) {
- printf("Working Directory %s:\n", p);
- r = show_cgroup_by_path(p, NULL, 0,
- arg_kernel_threads, output_flags);
- } else {
- if (arg_machine) {
- char *m;
- const char *cgroup;
- _cleanup_free_ char *unit = NULL;
- _cleanup_free_ char *path = NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-
- m = strjoina("/run/systemd/machines/", arg_machine);
- r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL);
- if (r < 0) {
- log_error_errno(r, "Failed to get machine path: %m");
- goto finish;
- }
-
- path = unit_dbus_path_from_name(unit);
- if (!path) {
- log_oom();
- goto finish;
- }
+ cwd = get_current_dir_name();
+ if (!cwd) {
+ r = log_error_errno(errno, "Cannot determine current working directory: %m");
+ goto finish;
+ }
- r = sd_bus_get_property(
- bus,
- "org.freedesktop.systemd1",
- path,
- endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
- "ControlGroup",
- &error,
- &reply,
- "s");
+ if (path_startswith(cwd, "/sys/fs/cgroup")) {
+ printf("Working directory %s:\n", cwd);
+ fflush(stdout);
- if (r < 0) {
- log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
- goto finish;
- }
-
- r = sd_bus_message_read(reply, "s", &cgroup);
- if (r < 0) {
- bus_log_parse_error(r);
- goto finish;
- }
+ r = show_cgroup_by_path(cwd, NULL, 0, arg_kernel_threads, output_flags);
+ done = true;
+ }
+ }
- root = strdup(cgroup);
- if (!root) {
- log_oom();
- goto finish;
- }
+ if (!done) {
+ _cleanup_free_ char *root = NULL;
- } else
- r = cg_get_root_path(&root);
- if (r < 0) {
- log_error_errno(r, "Failed to get %s path: %m",
- arg_machine ? "machine" : "root");
+ r = get_cgroup_root(&root);
+ if (r < 0)
goto finish;
- }
- r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0,
- arg_kernel_threads, output_flags);
+ printf("Control group %s:\n", isempty(root) ? "/" : root);
+ fflush(stdout);
+
+ r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, arg_kernel_threads, output_flags);
}
}
- if (r < 0) {
- log_error_errno(r, "Failed to list cgroup tree %s: %m", root);
- retval = EXIT_FAILURE;
- } else
- retval = EXIT_SUCCESS;
+ if (r < 0)
+ log_error_errno(r, "Failed to list cgroup tree: %m");
finish:
pager_close();
- return retval;
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
index f953c9e624..1c94bea31a 100644
--- a/src/cgtop/cgtop.c
+++ b/src/cgtop/cgtop.c
@@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#define __STDC_FORMAT_MACROS
#include <errno.h>
#include <string.h>
#include <stdlib.h>
@@ -31,6 +30,7 @@
#include "path-util.h"
#include "terminal-util.h"
+#include "process-util.h"
#include "util.h"
#include "hashmap.h"
#include "cgroup-util.h"
@@ -48,23 +48,25 @@ typedef struct Group {
unsigned n_tasks;
unsigned cpu_iteration;
- uint64_t cpu_usage;
- struct timespec cpu_timestamp;
+ nsec_t cpu_usage;
+ nsec_t cpu_timestamp;
double cpu_fraction;
uint64_t memory;
unsigned io_iteration;
uint64_t io_input, io_output;
- struct timespec io_timestamp;
+ nsec_t io_timestamp;
uint64_t io_input_bps, io_output_bps;
} Group;
static unsigned arg_depth = 3;
-static unsigned arg_iterations = (unsigned)-1;
+static unsigned arg_iterations = (unsigned) -1;
static bool arg_batch = false;
static bool arg_raw = false;
static usec_t arg_delay = 1*USEC_PER_SEC;
+static bool arg_kernel_threads = false;
+static bool arg_recursive = true;
static enum {
ORDER_PATH,
@@ -108,12 +110,16 @@ static const char *maybe_format_bytes(char *buf, size_t l, bool is_valid, off_t
return format_bytes(buf, l, t);
}
-static int process(const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration) {
+static int process(
+ const char *controller,
+ const char *path,
+ Hashmap *a,
+ Hashmap *b,
+ unsigned iteration,
+ Group **ret) {
+
Group *g;
int r;
- FILE *f = NULL;
- pid_t pid;
- unsigned n;
assert(controller);
assert(path);
@@ -142,104 +148,111 @@ static int process(const char *controller, const char *path, Hashmap *a, Hashmap
r = hashmap_move_one(a, b, path);
if (r < 0)
return r;
+
g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false;
}
}
- /* Regardless which controller, let's find the maximum number
- * of processes in any of it */
+ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ _cleanup_fclose_ FILE *f = NULL;
+ pid_t pid;
- r = cg_enumerate_processes(controller, path, &f);
- if (r < 0)
- return r;
+ r = cg_enumerate_processes(controller, path, &f);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return r;
- n = 0;
- while (cg_read_pid(f, &pid) > 0)
- n++;
- fclose(f);
+ g->n_tasks = 0;
+ while (cg_read_pid(f, &pid) > 0) {
- if (n > 0) {
- if (g->n_tasks_valid)
- g->n_tasks = MAX(g->n_tasks, n);
- else
- g->n_tasks = n;
+ if (!arg_kernel_threads && is_kernel_thread(pid) > 0)
+ continue;
- g->n_tasks_valid = true;
- }
+ g->n_tasks++;
+ }
- if (streq(controller, "cpuacct")) {
+ if (g->n_tasks > 0)
+ g->n_tasks_valid = true;
+
+ } else if (streq(controller, "cpuacct") && cg_unified() <= 0) {
+ _cleanup_free_ char *p = NULL, *v = NULL;
uint64_t new_usage;
- char *p, *v;
- struct timespec ts;
+ nsec_t timestamp;
r = cg_get_path(controller, path, "cpuacct.usage", &p);
if (r < 0)
return r;
r = read_one_line_file(p, &v);
- free(p);
+ if (r == -ENOENT)
+ return 0;
if (r < 0)
return r;
r = safe_atou64(v, &new_usage);
- free(v);
if (r < 0)
return r;
- assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
+ timestamp = now_nsec(CLOCK_MONOTONIC);
- if (g->cpu_iteration == iteration - 1) {
- uint64_t x, y;
+ if (g->cpu_iteration == iteration - 1 &&
+ (nsec_t) new_usage > g->cpu_usage) {
- x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) -
- ((uint64_t) g->cpu_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->cpu_timestamp.tv_nsec);
+ nsec_t x, y;
- y = new_usage - g->cpu_usage;
+ x = timestamp - g->cpu_timestamp;
+ if (x < 1)
+ x = 1;
- if (y > 0) {
- g->cpu_fraction = (double) y / (double) x;
- g->cpu_valid = true;
- }
+ y = (nsec_t) new_usage - g->cpu_usage;
+ g->cpu_fraction = (double) y / (double) x;
+ g->cpu_valid = true;
}
- g->cpu_usage = new_usage;
- g->cpu_timestamp = ts;
+ g->cpu_usage = (nsec_t) new_usage;
+ g->cpu_timestamp = timestamp;
g->cpu_iteration = iteration;
} else if (streq(controller, "memory")) {
- char *p, *v;
+ _cleanup_free_ char *p = NULL, *v = NULL;
- r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
+ if (cg_unified() <= 0)
+ r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
+ else
+ r = cg_get_path(controller, path, "memory.current", &p);
if (r < 0)
return r;
r = read_one_line_file(p, &v);
- free(p);
+ if (r == -ENOENT)
+ return 0;
if (r < 0)
return r;
r = safe_atou64(v, &g->memory);
- free(v);
if (r < 0)
return r;
if (g->memory > 0)
g->memory_valid = true;
- } else if (streq(controller, "blkio")) {
- char *p;
+ } else if (streq(controller, "blkio") && cg_unified() <= 0) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *p = NULL;
uint64_t wr = 0, rd = 0;
- struct timespec ts;
+ nsec_t timestamp;
r = cg_get_path(controller, path, "blkio.io_service_bytes", &p);
if (r < 0)
return r;
f = fopen(p, "re");
- free(p);
-
- if (!f)
+ if (!f) {
+ if (errno == ENOENT)
+ return 0;
return -errno;
+ }
for (;;) {
char line[LINE_MAX], *l;
@@ -269,20 +282,26 @@ static int process(const char *controller, const char *path, Hashmap *a, Hashmap
*q += k;
}
- fclose(f);
-
- assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
+ timestamp = now_nsec(CLOCK_MONOTONIC);
if (g->io_iteration == iteration - 1) {
uint64_t x, yr, yw;
- x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) -
- ((uint64_t) g->io_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->io_timestamp.tv_nsec);
+ x = (uint64_t) (timestamp - g->io_timestamp);
+ if (x < 1)
+ x = 1;
- yr = rd - g->io_input;
- yw = wr - g->io_output;
+ if (rd > g->io_input)
+ yr = rd - g->io_input;
+ else
+ yr = 0;
+
+ if (wr > g->io_output)
+ yw = wr - g->io_output;
+ else
+ yw = 0;
- if (g->io_input > 0 || g->io_output > 0) {
+ if (yr > 0 || yw > 0) {
g->io_input_bps = (yr * 1000000000ULL) / x;
g->io_output_bps = (yw * 1000000000ULL) / x;
g->io_valid = true;
@@ -291,10 +310,13 @@ static int process(const char *controller, const char *path, Hashmap *a, Hashmap
g->io_input = rd;
g->io_output = wr;
- g->io_timestamp = ts;
+ g->io_timestamp = timestamp;
g->io_iteration = iteration;
}
+ if (ret)
+ *ret = g;
+
return 0;
}
@@ -304,9 +326,11 @@ static int refresh_one(
Hashmap *a,
Hashmap *b,
unsigned iteration,
- unsigned depth) {
+ unsigned depth,
+ Group **ret) {
- DIR *d = NULL;
+ _cleanup_closedir_ DIR *d = NULL;
+ Group *ours;
int r;
assert(controller);
@@ -316,83 +340,100 @@ static int refresh_one(
if (depth > arg_depth)
return 0;
- r = process(controller, path, a, b, iteration);
+ r = process(controller, path, a, b, iteration, &ours);
if (r < 0)
return r;
r = cg_enumerate_subgroups(controller, path, &d);
- if (r < 0) {
- if (r == -ENOENT)
- return 0;
-
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
return r;
- }
for (;;) {
- char *fn, *p;
+ _cleanup_free_ char *fn = NULL, *p = NULL;
+ Group *child = NULL;
r = cg_read_subgroup(d, &fn);
- if (r <= 0)
- goto finish;
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
p = strjoin(path, "/", fn, NULL);
- free(fn);
-
- if (!p) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!p)
+ return -ENOMEM;
path_kill_slashes(p);
- r = refresh_one(controller, p, a, b, iteration, depth + 1);
- free(p);
-
+ r = refresh_one(controller, p, a, b, iteration, depth + 1, &child);
if (r < 0)
- goto finish;
+ return r;
+
+ if (arg_recursive &&
+ child &&
+ child->n_tasks_valid &&
+ streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+
+ /* Recursively sum up processes */
+
+ if (ours->n_tasks_valid)
+ ours->n_tasks += child->n_tasks;
+ else {
+ ours->n_tasks = child->n_tasks;
+ ours->n_tasks_valid = true;
+ }
+ }
}
-finish:
- if (d)
- closedir(d);
+ if (ret)
+ *ret = ours;
- return r;
+ return 1;
}
-static int refresh(Hashmap *a, Hashmap *b, unsigned iteration) {
+static int refresh(const char *root, Hashmap *a, Hashmap *b, unsigned iteration) {
int r;
assert(a);
- r = refresh_one("name=systemd", "/", a, b, iteration, 0);
+ r = refresh_one(SYSTEMD_CGROUP_CONTROLLER, root, a, b, iteration, 0, NULL);
if (r < 0)
- if (r != -ENOENT)
- return r;
- r = refresh_one("cpuacct", "/", a, b, iteration, 0);
+ return r;
+ r = refresh_one("cpuacct", root, a, b, iteration, 0, NULL);
if (r < 0)
- if (r != -ENOENT)
- return r;
- r = refresh_one("memory", "/", a, b, iteration, 0);
+ return r;
+ r = refresh_one("memory", root, a, b, iteration, 0, NULL);
if (r < 0)
- if (r != -ENOENT)
- return r;
-
- r = refresh_one("blkio", "/", a, b, iteration, 0);
+ return r;
+ r = refresh_one("blkio", root, a, b, iteration, 0, NULL);
if (r < 0)
- if (r != -ENOENT)
- return r;
+ return r;
+
return 0;
}
static int group_compare(const void*a, const void *b) {
const Group *x = *(Group**)a, *y = *(Group**)b;
- if (path_startswith(y->path, x->path))
- return -1;
- if (path_startswith(x->path, y->path))
- return 1;
+ if (arg_order != ORDER_TASKS || arg_recursive) {
+ /* Let's make sure that the parent is always before
+ * the child. Except when ordering by tasks and
+ * recursive summing is off, since that is actually
+ * not accumulative for all children. */
+
+ if (path_startswith(y->path, x->path))
+ return -1;
+ if (path_startswith(x->path, y->path))
+ return 1;
+ }
+
+ switch (arg_order) {
+
+ case ORDER_PATH:
+ break;
- if (arg_order == ORDER_CPU) {
+ case ORDER_CPU:
if (arg_cpu_type == CPU_PERCENT) {
if (x->cpu_valid && y->cpu_valid) {
if (x->cpu_fraction > y->cpu_fraction)
@@ -409,10 +450,10 @@ static int group_compare(const void*a, const void *b) {
else if (x->cpu_usage < y->cpu_usage)
return 1;
}
- }
- if (arg_order == ORDER_TASKS) {
+ break;
+ case ORDER_TASKS:
if (x->n_tasks_valid && y->n_tasks_valid) {
if (x->n_tasks > y->n_tasks)
return -1;
@@ -422,9 +463,10 @@ static int group_compare(const void*a, const void *b) {
return -1;
else if (y->n_tasks_valid)
return 1;
- }
- if (arg_order == ORDER_MEMORY) {
+ break;
+
+ case ORDER_MEMORY:
if (x->memory_valid && y->memory_valid) {
if (x->memory > y->memory)
return -1;
@@ -434,9 +476,10 @@ static int group_compare(const void*a, const void *b) {
return -1;
else if (y->memory_valid)
return 1;
- }
- if (arg_order == ORDER_IO) {
+ break;
+
+ case ORDER_IO:
if (x->io_valid && y->io_valid) {
if (x->io_input_bps + x->io_output_bps > y->io_input_bps + y->io_output_bps)
return -1;
@@ -448,13 +491,13 @@ static int group_compare(const void*a, const void *b) {
return 1;
}
- return strcmp(x->path, y->path);
+ return path_compare(x->path, y->path);
}
#define ON ANSI_HIGHLIGHT_ON
#define OFF ANSI_HIGHLIGHT_OFF
-static int display(Hashmap *a) {
+static void display(Hashmap *a) {
Iterator i;
Group *g;
Group **array;
@@ -481,9 +524,10 @@ static int display(Hashmap *a) {
for (j = 0; j < n; j++) {
unsigned cputlen, pathtlen;
- format_timespan(buffer, sizeof(buffer), (nsec_t) (array[j]->cpu_usage / NSEC_PER_USEC), 0);
+ format_timespan(buffer, sizeof(buffer), (usec_t) (array[j]->cpu_usage / NSEC_PER_USEC), 0);
cputlen = strlen(buffer);
maxtcpu = MAX(maxtcpu, cputlen);
+
pathtlen = strlen(array[j]->path);
maxtpath = MAX(maxtpath, pathtlen);
}
@@ -503,7 +547,7 @@ static int display(Hashmap *a) {
path_columns = 10;
printf("%s%-*s%s %s%7s%s %s%s%s %s%8s%s %s%8s%s %s%8s%s\n\n",
- arg_order == ORDER_PATH ? ON : "", path_columns, "Path",
+ arg_order == ORDER_PATH ? ON : "", path_columns, "Control Group",
arg_order == ORDER_PATH ? OFF : "",
arg_order == ORDER_TASKS ? ON : "", "Tasks",
arg_order == ORDER_TASKS ? OFF : "",
@@ -519,16 +563,17 @@ static int display(Hashmap *a) {
path_columns = maxtpath;
for (j = 0; j < n; j++) {
- char *p;
+ _cleanup_free_ char *ellipsized = NULL;
+ const char *path;
if (on_tty() && j + 5 > rows)
break;
g = array[j];
- p = ellipsize(g->path, path_columns, 33);
- printf("%-*s", path_columns, p ? p : g->path);
- free(p);
+ path = isempty(g->path) ? "/" : g->path;
+ ellipsized = ellipsize(path, path_columns, 33);
+ printf("%-*s", path_columns, ellipsized ?: path);
if (g->n_tasks_valid)
printf(" %7u", g->n_tasks);
@@ -541,7 +586,7 @@ static int display(Hashmap *a) {
else
fputs(" -", stdout);
} else
- printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (nsec_t) (g->cpu_usage / NSEC_PER_USEC), 0));
+ printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (usec_t) (g->cpu_usage / NSEC_PER_USEC), 0));
printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->memory_valid, g->memory));
printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->io_valid, g->io_input_bps));
@@ -549,22 +594,23 @@ static int display(Hashmap *a) {
putchar('\n');
}
-
- return 0;
}
static void help(void) {
printf("%s [OPTIONS...]\n\n"
"Show top control groups by their resource usage.\n\n"
" -h --help Show this help\n"
- " --version Print version and exit\n"
- " -p Order by path\n"
- " -t Order by number of tasks\n"
- " -c Order by CPU load\n"
- " -m Order by memory load\n"
- " -i Order by IO load\n"
+ " --version Show package version\n"
+ " -p --order=path Order by path\n"
+ " -t --order=tasks Order by number of tasks\n"
+ " -c --order=cpu Order by CPU load (default)\n"
+ " -m --order=memory Order by memory load\n"
+ " -i --order=io Order by IO load\n"
" -r --raw Provide raw (not human-readable) numbers\n"
- " --cpu[=TYPE] Show CPU usage as time or percentage (default)\n"
+ " --cpu=percentage Show CPU usage as percentage (default)\n"
+ " --cpu=time Show CPU usage as time\n"
+ " -k Include kernel threads in task count\n"
+ " --recursive=BOOL Sum up task count recursively\n"
" -d --delay=DELAY Delay between updates\n"
" -n --iterations=N Run for N iterations before exiting\n"
" -b --batch Run in batch mode, accepting no input\n"
@@ -577,28 +623,31 @@ static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_DEPTH,
- ARG_CPU_TYPE
+ ARG_CPU_TYPE,
+ ARG_ORDER,
+ ARG_RECURSIVE,
};
static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "delay", required_argument, NULL, 'd' },
- { "iterations", required_argument, NULL, 'n' },
- { "batch", no_argument, NULL, 'b' },
- { "raw", no_argument, NULL, 'r' },
- { "depth", required_argument, NULL, ARG_DEPTH },
- { "cpu", optional_argument, NULL, ARG_CPU_TYPE},
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "delay", required_argument, NULL, 'd' },
+ { "iterations", required_argument, NULL, 'n' },
+ { "batch", no_argument, NULL, 'b' },
+ { "raw", no_argument, NULL, 'r' },
+ { "depth", required_argument, NULL, ARG_DEPTH },
+ { "cpu", optional_argument, NULL, ARG_CPU_TYPE },
+ { "order", required_argument, NULL, ARG_ORDER },
+ { "recursive", required_argument, NULL, ARG_RECURSIVE },
{}
};
- int c;
- int r;
+ int c, r;
assert(argc >= 1);
assert(argv);
- while ((c = getopt_long(argc, argv, "hptcmin:brd:", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hptcmin:brd:k", options, NULL)) >= 0)
switch (c) {
@@ -613,13 +662,17 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_CPU_TYPE:
if (optarg) {
- if (strcmp(optarg, "time") == 0)
+ if (streq(optarg, "time"))
arg_cpu_type = CPU_TIME;
- else if (strcmp(optarg, "percentage") == 0)
+ else if (streq(optarg, "percentage"))
arg_cpu_type = CPU_PERCENT;
- else
+ else {
+ log_error("Unknown argument to --cpu=: %s", optarg);
return -EINVAL;
- }
+ }
+ } else
+ arg_cpu_type = CPU_TIME;
+
break;
case ARG_DEPTH:
@@ -677,6 +730,37 @@ static int parse_argv(int argc, char *argv[]) {
arg_order = ORDER_IO;
break;
+ case ARG_ORDER:
+ if (streq(optarg, "path"))
+ arg_order = ORDER_PATH;
+ else if (streq(optarg, "tasks"))
+ arg_order = ORDER_TASKS;
+ else if (streq(optarg, "cpu"))
+ arg_order = ORDER_CPU;
+ else if (streq(optarg, "memory"))
+ arg_order = ORDER_MEMORY;
+ else if (streq(optarg, "io"))
+ arg_order = ORDER_IO;
+ else {
+ log_error("Invalid argument to --order=: %s", optarg);
+ return -EINVAL;
+ }
+ break;
+
+ case 'k':
+ arg_kernel_threads = true;
+ break;
+
+ case ARG_RECURSIVE:
+ r = parse_boolean(optarg);
+ if (r < 0) {
+ log_error("Failed to parse --recursive= argument: %s", optarg);
+ return r;
+ }
+
+ arg_recursive = r;
+ break;
+
case '?':
return -EINVAL;
@@ -698,6 +782,7 @@ int main(int argc, char *argv[]) {
unsigned iteration = 0;
usec_t last_refresh = 0;
bool quit = false, immediate_refresh = false;
+ _cleanup_free_ char *root = NULL;
log_parse_environment();
log_open();
@@ -706,6 +791,12 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto finish;
+ r = cg_get_root_path(&root);
+ if (r < 0) {
+ log_error_errno(r, "Failed to get root control group path: %m");
+ goto finish;
+ }
+
a = hashmap_new(&string_hash_ops);
b = hashmap_new(&string_hash_ops);
if (!a || !b) {
@@ -715,7 +806,7 @@ int main(int argc, char *argv[]) {
signal(SIGWINCH, columns_lines_cache_reset);
- if (arg_iterations == (unsigned)-1)
+ if (arg_iterations == (unsigned) -1)
arg_iterations = on_tty() ? 0 : 1;
while (!quit) {
@@ -728,9 +819,11 @@ int main(int argc, char *argv[]) {
if (t >= last_refresh + arg_delay || immediate_refresh) {
- r = refresh(a, b, iteration++);
- if (r < 0)
+ r = refresh(root, a, b, iteration++);
+ if (r < 0) {
+ log_error_errno(r, "Failed to refresh: %m");
goto finish;
+ }
group_hashmap_clear(b);
@@ -742,9 +835,7 @@ int main(int argc, char *argv[]) {
immediate_refresh = false;
}
- r = display(b);
- if (r < 0)
- goto finish;
+ display(b);
if (arg_iterations && iteration >= arg_iterations)
break;
@@ -753,11 +844,10 @@ int main(int argc, char *argv[]) {
fputs("\n", stdout);
fflush(stdout);
- if (arg_batch) {
- usleep(last_refresh + arg_delay - t);
- } else {
- r = read_one_char(stdin, &key,
- last_refresh + arg_delay - t, NULL);
+ if (arg_batch)
+ (void) usleep(last_refresh + arg_delay - t);
+ else {
+ r = read_one_char(stdin, &key, last_refresh + arg_delay - t, NULL);
if (r == -ETIMEDOUT)
continue;
if (r < 0) {
@@ -808,6 +898,20 @@ int main(int argc, char *argv[]) {
arg_cpu_type = arg_cpu_type == CPU_TIME ? CPU_PERCENT : CPU_TIME;
break;
+ case 'k':
+ arg_kernel_threads = !arg_kernel_threads;
+ fprintf(stdout, "\nCounting kernel threads: %s.", yes_no(arg_kernel_threads));
+ fflush(stdout);
+ sleep(1);
+ break;
+
+ case 'r':
+ arg_recursive = !arg_recursive;
+ fprintf(stdout, "\nRecursive task counting: %s", yes_no(arg_recursive));
+ fflush(stdout);
+ sleep(1);
+ break;
+
case '+':
if (arg_delay < USEC_PER_SEC)
arg_delay += USEC_PER_MSEC*250;
@@ -836,14 +940,17 @@ int main(int argc, char *argv[]) {
case 'h':
fprintf(stdout,
"\t<" ON "p" OFF "> By path; <" ON "t" OFF "> By tasks; <" ON "c" OFF "> By CPU; <" ON "m" OFF "> By memory; <" ON "i" OFF "> By I/O\n"
- "\t<" ON "+" OFF "> Increase delay; <" ON "-" OFF "> Decrease delay; <" ON "%%" OFF "> Toggle time\n"
- "\t<" ON "q" OFF "> Quit; <" ON "SPACE" OFF "> Refresh");
+ "\t<" ON "+" OFF "> Inc. delay; <" ON "-" OFF "> Dec. delay; <" ON "%%" OFF "> Toggle time; <" ON "SPACE" OFF "> Refresh\n"
+ "\t<" ON "k" OFF "> Count kernel threads; <" ON "r" OFF "> Count recursively; <" ON "q" OFF "> Quit");
fflush(stdout);
sleep(3);
break;
default:
- fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key);
+ if (key < ' ')
+ fprintf(stdout, "\nUnknown key '\\x%x'. Ignoring.", key);
+ else
+ fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key);
fflush(stdout);
sleep(1);
break;
@@ -856,10 +963,5 @@ finish:
group_hashmap_free(a);
group_hashmap_free(b);
- if (r < 0) {
- log_error_errno(r, "Exiting with failure: %m");
- return EXIT_FAILURE;
- }
-
- return EXIT_SUCCESS;
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/core/automount.c b/src/core/automount.c
index 4af381b4b6..b8171ddad7 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -1075,7 +1075,6 @@ const UnitVTable automount_vtable = {
.reset_failed = automount_reset_failed,
- .bus_interface = "org.freedesktop.systemd1.Automount",
.bus_vtable = bus_automount_vtable,
.shutdown = automount_shutdown,
diff --git a/src/core/busname.c b/src/core/busname.c
index 9530a87311..d3c1282239 100644
--- a/src/core/busname.c
+++ b/src/core/busname.c
@@ -1058,7 +1058,6 @@ const UnitVTable busname_vtable = {
.supported = busname_supported,
- .bus_interface = "org.freedesktop.systemd1.BusName",
.bus_vtable = bus_busname_vtable,
.status_message_formats = {
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 6474e08bd2..9a025cf929 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -283,7 +283,7 @@ fail:
return -errno;
}
-void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const char *path, ManagerState state) {
+void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, ManagerState state) {
bool is_root;
int r;
@@ -304,7 +304,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const cha
* cgroup trees (assuming we are running in a container then),
* and missing cgroups, i.e. EROFS and ENOENT. */
- if ((mask & CGROUP_CPU) && !is_root) {
+ if ((mask & CGROUP_MASK_CPU) && !is_root) {
char buf[MAX(DECIMAL_STR_MAX(unsigned long), DECIMAL_STR_MAX(usec_t)) + 1];
sprintf(buf, "%lu\n",
@@ -331,7 +331,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const cha
"Failed to set cpu.cfs_quota_us on %s: %m", path);
}
- if (mask & CGROUP_BLKIO) {
+ if (mask & CGROUP_MASK_BLKIO) {
char buf[MAX3(DECIMAL_STR_MAX(unsigned long)+1,
DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(unsigned long)*1,
DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1)];
@@ -381,21 +381,30 @@ void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const cha
}
}
- if ((mask & CGROUP_MEMORY) && !is_root) {
+ if ((mask & CGROUP_MASK_MEMORY) && !is_root) {
if (c->memory_limit != (uint64_t) -1) {
char buf[DECIMAL_STR_MAX(uint64_t) + 1];
sprintf(buf, "%" PRIu64 "\n", c->memory_limit);
- r = cg_set_attribute("memory", path, "memory.limit_in_bytes", buf);
- } else
- r = cg_set_attribute("memory", path, "memory.limit_in_bytes", "-1");
+
+ if (cg_unified() <= 0)
+ r = cg_set_attribute("memory", path, "memory.limit_in_bytes", buf);
+ else
+ r = cg_set_attribute("memory", path, "memory.max", buf);
+
+ } else {
+ if (cg_unified() <= 0)
+ r = cg_set_attribute("memory", path, "memory.limit_in_bytes", "-1");
+ else
+ r = cg_set_attribute("memory", path, "memory.max", "max");
+ }
if (r < 0)
log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set memory.limit_in_bytes on %s: %m", path);
+ "Failed to set memory.limit_in_bytes/memory.max on %s: %m", path);
}
- if ((mask & CGROUP_DEVICE) && !is_root) {
+ if ((mask & CGROUP_MASK_DEVICE) && !is_root) {
CGroupDeviceAllow *a;
/* Changing the devices list of a populated cgroup
@@ -459,8 +468,8 @@ void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const cha
}
}
-CGroupControllerMask cgroup_context_get_mask(CGroupContext *c) {
- CGroupControllerMask mask = 0;
+CGroupMask cgroup_context_get_mask(CGroupContext *c) {
+ CGroupMask mask = 0;
/* Figure out which controllers we need */
@@ -468,52 +477,62 @@ CGroupControllerMask cgroup_context_get_mask(CGroupContext *c) {
c->cpu_shares != (unsigned long) -1 ||
c->startup_cpu_shares != (unsigned long) -1 ||
c->cpu_quota_per_sec_usec != USEC_INFINITY)
- mask |= CGROUP_CPUACCT | CGROUP_CPU;
+ mask |= CGROUP_MASK_CPUACCT | CGROUP_MASK_CPU;
if (c->blockio_accounting ||
c->blockio_weight != (unsigned long) -1 ||
c->startup_blockio_weight != (unsigned long) -1 ||
c->blockio_device_weights ||
c->blockio_device_bandwidths)
- mask |= CGROUP_BLKIO;
+ mask |= CGROUP_MASK_BLKIO;
if (c->memory_accounting ||
c->memory_limit != (uint64_t) -1)
- mask |= CGROUP_MEMORY;
+ mask |= CGROUP_MASK_MEMORY;
if (c->device_allow ||
c->device_policy != CGROUP_AUTO)
- mask |= CGROUP_DEVICE;
+ mask |= CGROUP_MASK_DEVICE;
return mask;
}
-CGroupControllerMask unit_get_cgroup_mask(Unit *u) {
+CGroupMask unit_get_own_mask(Unit *u) {
CGroupContext *c;
+ /* Returns the mask of controllers the unit needs for itself */
+
c = unit_get_cgroup_context(u);
if (!c)
return 0;
/* If delegation is turned on, then turn on all cgroups,
- * unless the process we fork into it is known to drop
- * privileges anyway, and shouldn't get access to the
- * controllers anyway. */
+ * unless we are on the legacy hierarchy and the process we
+ * fork into it is known to drop privileges, and hence
+ * shouldn't get access to the controllers.
+ *
+ * Note that on the unified hierarchy it is safe to delegate
+ * controllers to unprivileged services. */
if (c->delegate) {
ExecContext *e;
e = unit_get_exec_context(u);
- if (!e || exec_context_maintains_privileges(e))
- return _CGROUP_CONTROLLER_MASK_ALL;
+ if (!e ||
+ exec_context_maintains_privileges(e) ||
+ cg_unified() > 0)
+ return _CGROUP_MASK_ALL;
}
return cgroup_context_get_mask(c);
}
-CGroupControllerMask unit_get_members_mask(Unit *u) {
+CGroupMask unit_get_members_mask(Unit *u) {
assert(u);
+ /* Returns the mask of controllers all of the unit's children
+ * require, merged */
+
if (u->cgroup_members_mask_valid)
return u->cgroup_members_mask;
@@ -532,7 +551,7 @@ CGroupControllerMask unit_get_members_mask(Unit *u) {
continue;
u->cgroup_members_mask |=
- unit_get_cgroup_mask(member) |
+ unit_get_own_mask(member) |
unit_get_members_mask(member);
}
}
@@ -541,19 +560,52 @@ CGroupControllerMask unit_get_members_mask(Unit *u) {
return u->cgroup_members_mask;
}
-CGroupControllerMask unit_get_siblings_mask(Unit *u) {
+CGroupMask unit_get_siblings_mask(Unit *u) {
assert(u);
+ /* Returns the mask of controllers all of the unit's siblings
+ * require, i.e. the members mask of the unit's parent slice
+ * if there is one. */
+
if (UNIT_ISSET(u->slice))
return unit_get_members_mask(UNIT_DEREF(u->slice));
- return unit_get_cgroup_mask(u) | unit_get_members_mask(u);
+ return unit_get_own_mask(u) | unit_get_members_mask(u);
+}
+
+CGroupMask unit_get_subtree_mask(Unit *u) {
+
+ /* Returns the mask of this subtree, meaning of the group
+ * itself and its children. */
+
+ return unit_get_own_mask(u) | unit_get_members_mask(u);
}
-CGroupControllerMask unit_get_target_mask(Unit *u) {
- CGroupControllerMask mask;
+CGroupMask unit_get_target_mask(Unit *u) {
+ CGroupMask mask;
+
+ /* This returns the cgroup mask of all controllers to enable
+ * for a specific cgroup, i.e. everything it needs itself,
+ * plus all that its children need, plus all that its siblings
+ * need. This is primarily useful on the legacy cgroup
+ * hierarchy, where we need to duplicate each cgroup in each
+ * hierarchy that shall be enabled for it. */
- mask = unit_get_cgroup_mask(u) | unit_get_members_mask(u) | unit_get_siblings_mask(u);
+ mask = unit_get_own_mask(u) | unit_get_members_mask(u) | unit_get_siblings_mask(u);
+ mask &= u->manager->cgroup_supported;
+
+ return mask;
+}
+
+CGroupMask unit_get_enable_mask(Unit *u) {
+ CGroupMask mask;
+
+ /* This returns the cgroup mask of all controllers to enable
+ * for the children of a specific cgroup. This is primarily
+ * useful for the unified cgroup hierarchy, where each cgroup
+ * controls which controllers are enabled for its children. */
+
+ mask = unit_get_members_mask(u);
mask &= u->manager->cgroup_supported;
return mask;
@@ -562,13 +614,13 @@ CGroupControllerMask unit_get_target_mask(Unit *u) {
/* Recurse from a unit up through its containing slices, propagating
* mask bits upward. A unit is also member of itself. */
void unit_update_cgroup_members_masks(Unit *u) {
- CGroupControllerMask m;
+ CGroupMask m;
bool more;
assert(u);
/* Calculate subtree mask */
- m = unit_get_cgroup_mask(u) | unit_get_members_mask(u);
+ m = unit_get_subtree_mask(u);
/* See if anything changed from the previous invocation. If
* not, we're done. */
@@ -608,7 +660,7 @@ void unit_update_cgroup_members_masks(Unit *u) {
}
}
-static const char *migrate_callback(CGroupControllerMask mask, void *userdata) {
+static const char *migrate_callback(CGroupMask mask, void *userdata) {
Unit *u = userdata;
assert(mask != 0);
@@ -626,7 +678,115 @@ static const char *migrate_callback(CGroupControllerMask mask, void *userdata) {
return NULL;
}
-static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) {
+char *unit_default_cgroup_path(Unit *u) {
+ _cleanup_free_ char *escaped = NULL, *slice = NULL;
+ int r;
+
+ assert(u);
+
+ if (unit_has_name(u, SPECIAL_ROOT_SLICE))
+ return strdup(u->manager->cgroup_root);
+
+ if (UNIT_ISSET(u->slice) && !unit_has_name(UNIT_DEREF(u->slice), SPECIAL_ROOT_SLICE)) {
+ r = cg_slice_to_path(UNIT_DEREF(u->slice)->id, &slice);
+ if (r < 0)
+ return NULL;
+ }
+
+ escaped = cg_escape(u->id);
+ if (!escaped)
+ return NULL;
+
+ if (slice)
+ return strjoin(u->manager->cgroup_root, "/", slice, "/", escaped, NULL);
+ else
+ return strjoin(u->manager->cgroup_root, "/", escaped, NULL);
+}
+
+int unit_set_cgroup_path(Unit *u, const char *path) {
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ assert(u);
+
+ if (path) {
+ p = strdup(path);
+ if (!p)
+ return -ENOMEM;
+ } else
+ p = NULL;
+
+ if (streq_ptr(u->cgroup_path, p))
+ return 0;
+
+ if (p) {
+ r = hashmap_put(u->manager->cgroup_unit, p, u);
+ if (r < 0)
+ return r;
+ }
+
+ unit_release_cgroup(u);
+
+ u->cgroup_path = p;
+ p = NULL;
+
+ return 1;
+}
+
+int unit_watch_cgroup(Unit *u) {
+ _cleanup_free_ char *populated = NULL;
+ int r;
+
+ assert(u);
+
+ if (!u->cgroup_path)
+ return 0;
+
+ if (u->cgroup_inotify_wd >= 0)
+ return 0;
+
+ /* Only applies to the unified hierarchy */
+ r = cg_unified();
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed detect wether the unified hierarchy is used: %m");
+ if (r == 0)
+ return 0;
+
+ /* Don't watch the root slice, it's pointless. */
+ if (unit_has_name(u, SPECIAL_ROOT_SLICE))
+ return 0;
+
+ r = hashmap_ensure_allocated(&u->manager->cgroup_inotify_wd_unit, &trivial_hash_ops);
+ if (r < 0)
+ return log_oom();
+
+ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.populated", &populated);
+ if (r < 0)
+ return log_oom();
+
+ u->cgroup_inotify_wd = inotify_add_watch(u->manager->cgroup_inotify_fd, populated, IN_MODIFY);
+ if (u->cgroup_inotify_wd < 0) {
+
+ if (errno == ENOENT) /* If the directory is already
+ * gone we don't need to track
+ * it, so this is not an error */
+ return 0;
+
+ return log_unit_error_errno(u, errno, "Failed to add inotify watch descriptor for control group %s: %m", u->cgroup_path);
+ }
+
+ r = hashmap_put(u->manager->cgroup_inotify_wd_unit, INT_TO_PTR(u->cgroup_inotify_wd), u);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to add inotify watch descriptor to hash map: %m");
+
+ return 0;
+}
+
+static int unit_create_cgroup(
+ Unit *u,
+ CGroupMask target_mask,
+ CGroupMask enable_mask) {
+
CGroupContext *c;
int r;
@@ -643,25 +803,29 @@ static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) {
if (!path)
return log_oom();
- r = hashmap_put(u->manager->cgroup_unit, path, u);
- if (r < 0) {
- log_error(r == -EEXIST ? "cgroup %s exists already: %s" : "hashmap_put failed for %s: %s", path, strerror(-r));
- return r;
- }
- if (r > 0) {
- u->cgroup_path = path;
- path = NULL;
- }
+ r = unit_set_cgroup_path(u, path);
+ if (r == -EEXIST)
+ return log_unit_error_errno(u, r, "Control group %s exists already.", path);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to set unit's control group path to %s: %m", path);
}
/* First, create our own group */
- r = cg_create_everywhere(u->manager->cgroup_supported, mask, u->cgroup_path);
+ r = cg_create_everywhere(u->manager->cgroup_supported, target_mask, u->cgroup_path);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to create cgroup %s: %m", u->cgroup_path);
+
+ /* Start watching it */
+ (void) unit_watch_cgroup(u);
+
+ /* Enable all controllers we need */
+ r = cg_enable_everywhere(u->manager->cgroup_supported, enable_mask, u->cgroup_path);
if (r < 0)
- return log_error_errno(r, "Failed to create cgroup %s: %m", u->cgroup_path);
+ log_unit_warning_errno(u, r, "Failed to enable controllers on cgroup %s, ignoring: %m", u->cgroup_path);
/* Keep track that this is now realized */
u->cgroup_realized = true;
- u->cgroup_realized_mask = mask;
+ u->cgroup_realized_mask = target_mask;
if (u->type != UNIT_SLICE && !c->delegate) {
@@ -670,7 +834,7 @@ static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) {
* for slice and delegation units. */
r = cg_migrate_everywhere(u->manager->cgroup_supported, u->cgroup_path, u->cgroup_path, migrate_callback, u);
if (r < 0)
- log_warning_errno(r, "Failed to migrate cgroup from to %s: %m", u->cgroup_path);
+ log_unit_warning_errno(u, r, "Failed to migrate cgroup from to %s, ignoring: %m", u->cgroup_path);
}
return 0;
@@ -691,10 +855,10 @@ int unit_attach_pids_to_cgroup(Unit *u) {
return 0;
}
-static bool unit_has_mask_realized(Unit *u, CGroupControllerMask mask) {
+static bool unit_has_mask_realized(Unit *u, CGroupMask target_mask) {
assert(u);
- return u->cgroup_realized && u->cgroup_realized_mask == mask;
+ return u->cgroup_realized && u->cgroup_realized_mask == target_mask;
}
/* Check if necessary controllers and attributes for a unit are in place.
@@ -704,7 +868,7 @@ static bool unit_has_mask_realized(Unit *u, CGroupControllerMask mask) {
*
* Returns 0 on success and < 0 on failure. */
static int unit_realize_cgroup_now(Unit *u, ManagerState state) {
- CGroupControllerMask mask;
+ CGroupMask target_mask, enable_mask;
int r;
assert(u);
@@ -714,9 +878,8 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) {
u->in_cgroup_queue = false;
}
- mask = unit_get_target_mask(u);
-
- if (unit_has_mask_realized(u, mask))
+ target_mask = unit_get_target_mask(u);
+ if (unit_has_mask_realized(u, target_mask))
return 0;
/* First, realize parents */
@@ -727,12 +890,13 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) {
}
/* And then do the real work */
- r = unit_create_cgroups(u, mask);
+ enable_mask = unit_get_enable_mask(u);
+ r = unit_create_cgroup(u, target_mask, enable_mask);
if (r < 0)
return r;
/* Finally, apply the necessary attributes. */
- cgroup_context_apply(unit_get_cgroup_context(u), mask, u->cgroup_path, state);
+ cgroup_context_apply(unit_get_cgroup_context(u), target_mask, u->cgroup_path, state);
return 0;
}
@@ -759,7 +923,7 @@ unsigned manager_dispatch_cgroup_queue(Manager *m) {
r = unit_realize_cgroup_now(i, state);
if (r < 0)
- log_warning_errno(r, "Failed to realize cgroups for queued unit %s: %m", i->id);
+ log_warning_errno(r, "Failed to realize cgroups for queued unit %s, ignoring: %m", i->id);
n++;
}
@@ -806,12 +970,9 @@ static void unit_queue_siblings(Unit *u) {
}
int unit_realize_cgroup(Unit *u) {
- CGroupContext *c;
-
assert(u);
- c = unit_get_cgroup_context(u);
- if (!c)
+ if (!UNIT_HAS_CGROUP_CONTEXT(u))
return 0;
/* So, here's the deal: when realizing the cgroups for this
@@ -832,39 +993,67 @@ int unit_realize_cgroup(Unit *u) {
return unit_realize_cgroup_now(u, manager_state(u->manager));
}
-void unit_destroy_cgroup_if_empty(Unit *u) {
+void unit_release_cgroup(Unit *u) {
+ assert(u);
+
+ /* Forgets all cgroup details for this cgroup */
+
+ if (u->cgroup_path) {
+ (void) hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
+ u->cgroup_path = mfree(u->cgroup_path);
+ }
+
+ if (u->cgroup_inotify_wd >= 0) {
+ if (inotify_rm_watch(u->manager->cgroup_inotify_fd, u->cgroup_inotify_wd) < 0)
+ log_unit_debug_errno(u, errno, "Failed to remove cgroup inotify watch %i for %s, ignoring", u->cgroup_inotify_wd, u->id);
+
+ (void) hashmap_remove(u->manager->cgroup_inotify_wd_unit, INT_TO_PTR(u->cgroup_inotify_wd));
+ u->cgroup_inotify_wd = -1;
+ }
+}
+
+void unit_prune_cgroup(Unit *u) {
int r;
+ bool is_root_slice;
assert(u);
+ /* Removes the cgroup, if empty and possible, and stops watching it. */
+
if (!u->cgroup_path)
return;
- r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !unit_has_name(u, SPECIAL_ROOT_SLICE));
+ is_root_slice = unit_has_name(u, SPECIAL_ROOT_SLICE);
+
+ r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !is_root_slice);
if (r < 0) {
- log_debug_errno(r, "Failed to destroy cgroup %s: %m", u->cgroup_path);
+ log_debug_errno(r, "Failed to destroy cgroup %s, ignoring: %m", u->cgroup_path);
return;
}
- hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
+ if (is_root_slice)
+ return;
+
+ unit_release_cgroup(u);
- free(u->cgroup_path);
- u->cgroup_path = NULL;
u->cgroup_realized = false;
u->cgroup_realized_mask = 0;
}
-pid_t unit_search_main_pid(Unit *u) {
+int unit_search_main_pid(Unit *u, pid_t *ret) {
_cleanup_fclose_ FILE *f = NULL;
pid_t pid = 0, npid, mypid;
+ int r;
assert(u);
+ assert(ret);
if (!u->cgroup_path)
- return 0;
+ return -ENXIO;
- if (cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &f) < 0)
- return 0;
+ r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &f);
+ if (r < 0)
+ return r;
mypid = getpid();
while (cg_read_pid(f, &npid) > 0) {
@@ -877,90 +1066,274 @@ pid_t unit_search_main_pid(Unit *u) {
if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid)
continue;
- if (pid != 0) {
+ if (pid != 0)
/* Dang, there's more than one daemonized PID
in this group, so we don't know what process
is the main process. */
- pid = 0;
- break;
- }
+
+ return -ENODATA;
pid = npid;
}
- return pid;
+ *ret = pid;
+ return 0;
+}
+
+static int unit_watch_pids_in_path(Unit *u, const char *path) {
+ _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int ret = 0, r;
+
+ assert(u);
+ assert(path);
+
+ r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, path, &f);
+ if (r < 0)
+ ret = r;
+ else {
+ pid_t pid;
+
+ while ((r = cg_read_pid(f, &pid)) > 0) {
+ r = unit_watch_pid(u, pid);
+ if (r < 0 && ret >= 0)
+ ret = r;
+ }
+
+ if (r < 0 && ret >= 0)
+ ret = r;
+ }
+
+ r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, path, &d);
+ if (r < 0) {
+ if (ret >= 0)
+ ret = r;
+ } else {
+ char *fn;
+
+ while ((r = cg_read_subgroup(d, &fn)) > 0) {
+ _cleanup_free_ char *p = NULL;
+
+ p = strjoin(path, "/", fn, NULL);
+ free(fn);
+
+ if (!p)
+ return -ENOMEM;
+
+ r = unit_watch_pids_in_path(u, p);
+ if (r < 0 && ret >= 0)
+ ret = r;
+ }
+
+ if (r < 0 && ret >= 0)
+ ret = r;
+ }
+
+ return ret;
+}
+
+int unit_watch_all_pids(Unit *u) {
+ assert(u);
+
+ /* Adds all PIDs from our cgroup to the set of PIDs we
+ * watch. This is a fallback logic for cases where we do not
+ * get reliable cgroup empty notifications: we try to use
+ * SIGCHLD as replacement. */
+
+ if (!u->cgroup_path)
+ return -ENOENT;
+
+ if (cg_unified() > 0) /* On unified we can use proper notifications */
+ return 0;
+
+ return unit_watch_pids_in_path(u, u->cgroup_path);
+}
+
+int unit_notify_cgroup_empty(Unit *u) {
+ int r;
+
+ assert(u);
+
+ if (!u->cgroup_path)
+ return 0;
+
+ r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
+ if (r <= 0)
+ return r;
+
+ unit_add_to_gc_queue(u);
+
+ if (UNIT_VTABLE(u)->notify_cgroup_empty)
+ UNIT_VTABLE(u)->notify_cgroup_empty(u);
+
+ return 0;
+}
+
+static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ Manager *m = userdata;
+
+ assert(s);
+ assert(fd >= 0);
+ assert(m);
+
+ for (;;) {
+ union inotify_event_buffer buffer;
+ struct inotify_event *e;
+ ssize_t l;
+
+ l = read(fd, &buffer, sizeof(buffer));
+ if (l < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ return 0;
+
+ return log_error_errno(errno, "Failed to read control group inotify events: %m");
+ }
+
+ FOREACH_INOTIFY_EVENT(e, buffer, l) {
+ Unit *u;
+
+ if (e->wd < 0)
+ /* Queue overflow has no watch descriptor */
+ continue;
+
+ if (e->mask & IN_IGNORED)
+ /* The watch was just removed */
+ continue;
+
+ u = hashmap_get(m->cgroup_inotify_wd_unit, INT_TO_PTR(e->wd));
+ if (!u) /* Not that inotify might deliver
+ * events for a watch even after it
+ * was removed, because it was queued
+ * before the removal. Let's ignore
+ * this here safely. */
+ continue;
+
+ (void) unit_notify_cgroup_empty(u);
+ }
+ }
}
int manager_setup_cgroup(Manager *m) {
_cleanup_free_ char *path = NULL;
- int r;
+ CGroupController c;
+ int r, unified;
+ char *e;
assert(m);
/* 1. Determine hierarchy */
- free(m->cgroup_root);
- m->cgroup_root = NULL;
-
+ m->cgroup_root = mfree(m->cgroup_root);
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &m->cgroup_root);
if (r < 0)
return log_error_errno(r, "Cannot determine cgroup we are running in: %m");
- /* LEGACY: Already in /system.slice? If so, let's cut this
- * off. This is to support live upgrades from older systemd
- * versions where PID 1 was moved there. */
- if (m->running_as == MANAGER_SYSTEM) {
- char *e;
+ /* Chop off the init scope, if we are already located in it */
+ e = endswith(m->cgroup_root, "/" SPECIAL_INIT_SCOPE);
+ /* LEGACY: Also chop off the system slice if we are in
+ * it. This is to support live upgrades from older systemd
+ * versions where PID 1 was moved there. Also see
+ * cg_get_root_path(). */
+ if (!e && m->running_as == MANAGER_SYSTEM) {
e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
if (!e)
- e = endswith(m->cgroup_root, "/system");
- if (e)
- *e = 0;
+ e = endswith(m->cgroup_root, "/system"); /* even more legacy */
}
+ if (e)
+ *e = 0;
/* And make sure to store away the root value without trailing
* slash, even for the root dir, so that we can easily prepend
* it everywhere. */
- if (streq(m->cgroup_root, "/"))
- m->cgroup_root[0] = 0;
+ while ((e = endswith(m->cgroup_root, "/")))
+ *e = 0;
/* 2. Show data */
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, NULL, &path);
if (r < 0)
return log_error_errno(r, "Cannot find cgroup mount point: %m");
- log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
+ unified = cg_unified();
+ if (unified < 0)
+ return log_error_errno(r, "Couldn't determine if we are running in the unified hierarchy: %m");
+ if (unified > 0)
+ log_debug("Unified cgroup hierarchy is located at %s.", path);
+ else
+ log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
+
if (!m->test_run) {
+ const char *scope_path;
/* 3. Install agent */
- if (m->running_as == MANAGER_SYSTEM) {
+ if (unified) {
+
+ /* In the unified hierarchy we can can get
+ * cgroup empty notifications via inotify. */
+
+ m->cgroup_inotify_event_source = sd_event_source_unref(m->cgroup_inotify_event_source);
+ safe_close(m->cgroup_inotify_fd);
+
+ m->cgroup_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
+ if (m->cgroup_inotify_fd < 0)
+ return log_error_errno(errno, "Failed to create control group inotify object: %m");
+
+ r = sd_event_add_io(m->event, &m->cgroup_inotify_event_source, m->cgroup_inotify_fd, EPOLLIN, on_cgroup_inotify_event, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to watch control group inotify object: %m");
+
+ r = sd_event_source_set_priority(m->cgroup_inotify_event_source, SD_EVENT_PRIORITY_IDLE - 5);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set priority of inotify event source: %m");
+
+ (void) sd_event_source_set_description(m->cgroup_inotify_event_source, "cgroup-inotify");
+
+ } else if (m->running_as == MANAGER_SYSTEM) {
+
+ /* On the legacy hierarchy we only get
+ * notifications via cgroup agents. (Which
+ * isn't really reliable, since it does not
+ * generate events when control groups with
+ * children run empty. */
+
r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
if (r < 0)
log_warning_errno(r, "Failed to install release agent, ignoring: %m");
else if (r > 0)
log_debug("Installed release agent.");
- else
+ else if (r == 0)
log_debug("Release agent already installed.");
}
- /* 4. Make sure we are in the root cgroup */
- r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, 0);
+ /* 4. Make sure we are in the special "init.scope" unit in the root slice. */
+ scope_path = strjoina(m->cgroup_root, "/" SPECIAL_INIT_SCOPE);
+ r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, scope_path, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create %s control group: %m", scope_path);
+
+ /* also, move all other userspace processes remaining
+ * in the root cgroup into that scope. */
+ r = cg_migrate(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, SYSTEMD_CGROUP_CONTROLLER, scope_path, false);
if (r < 0)
- return log_error_errno(r, "Failed to create root cgroup hierarchy: %m");
+ log_warning_errno(r, "Couldn't move remaining userspace processes, ignoring: %m");
/* 5. And pin it, so that it cannot be unmounted */
safe_close(m->pin_cgroupfs_fd);
-
m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK);
if (m->pin_cgroupfs_fd < 0)
return log_error_errno(errno, "Failed to open pin file: %m");
/* 6. Always enable hierarchical support if it exists... */
- cg_set_attribute("memory", "/", "memory.use_hierarchy", "1");
+ if (!unified)
+ (void) cg_set_attribute("memory", "/", "memory.use_hierarchy", "1");
}
/* 7. Figure out which controllers are supported */
- m->cgroup_supported = cg_mask_supported();
+ r = cg_mask_supported(&m->cgroup_supported);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine supported controllers: %m");
+
+ for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++)
+ log_debug("Controller '%s' supported: %s", cgroup_controller_to_string(c), yes_no(m->cgroup_supported & c));
return 0;
}
@@ -971,12 +1344,16 @@ void manager_shutdown_cgroup(Manager *m, bool delete) {
/* We can't really delete the group, since we are in it. But
* let's trim it. */
if (delete && m->cgroup_root)
- cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, false);
+ (void) cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, false);
+
+ m->cgroup_inotify_wd_unit = hashmap_free(m->cgroup_inotify_wd_unit);
+
+ m->cgroup_inotify_event_source = sd_event_source_unref(m->cgroup_inotify_event_source);
+ m->cgroup_inotify_fd = safe_close(m->cgroup_inotify_fd);
m->pin_cgroupfs_fd = safe_close(m->pin_cgroupfs_fd);
- free(m->cgroup_root);
- m->cgroup_root = NULL;
+ m->cgroup_root = mfree(m->cgroup_root);
}
Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) {
@@ -995,8 +1372,8 @@ Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) {
char *e;
e = strrchr(p, '/');
- if (e == p || !e)
- return NULL;
+ if (!e || e == p)
+ return hashmap_get(m->cgroup_unit, SPECIAL_ROOT_SLICE);
*e = 0;
@@ -1006,13 +1383,13 @@ Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) {
}
}
-Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) {
+Unit *manager_get_unit_by_pid_cgroup(Manager *m, pid_t pid) {
_cleanup_free_ char *cgroup = NULL;
int r;
assert(m);
- if (pid <= 1)
+ if (pid <= 0)
return NULL;
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
@@ -1022,9 +1399,30 @@ Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) {
return manager_get_unit_by_cgroup(m, cgroup);
}
+Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) {
+ Unit *u;
+
+ assert(m);
+
+ if (pid <= 0)
+ return NULL;
+
+ if (pid == 1)
+ return hashmap_get(m->units, SPECIAL_INIT_SCOPE);
+
+ u = hashmap_get(m->watch_pids1, PID_TO_PTR(pid));
+ if (u)
+ return u;
+
+ u = hashmap_get(m->watch_pids2, PID_TO_PTR(pid));
+ if (u)
+ return u;
+
+ return manager_get_unit_by_pid_cgroup(m, pid);
+}
+
int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
Unit *u;
- int r;
assert(m);
assert(cgroup);
@@ -1033,15 +1431,7 @@ int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
if (!u)
return 0;
- r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
- if (r <= 0)
- return r;
-
- if (UNIT_VTABLE(u)->notify_cgroup_empty)
- UNIT_VTABLE(u)->notify_cgroup_empty(u);
-
- unit_add_to_gc_queue(u);
- return 0;
+ return unit_notify_cgroup_empty(u);
}
int unit_get_memory_current(Unit *u, uint64_t *ret) {
@@ -1054,10 +1444,13 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) {
if (!u->cgroup_path)
return -ENODATA;
- if ((u->cgroup_realized_mask & CGROUP_MEMORY) == 0)
+ if ((u->cgroup_realized_mask & CGROUP_MASK_MEMORY) == 0)
return -ENODATA;
- r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v);
+ if (cg_unified() <= 0)
+ r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v);
+ else
+ r = cg_get_attribute("memory", u->cgroup_path, "memory.current", &v);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
@@ -1077,7 +1470,7 @@ static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) {
if (!u->cgroup_path)
return -ENODATA;
- if ((u->cgroup_realized_mask & CGROUP_CPUACCT) == 0)
+ if ((u->cgroup_realized_mask & CGROUP_MASK_CPUACCT) == 0)
return -ENODATA;
r = cg_get_attribute("cpuacct", u->cgroup_path, "cpuacct.usage", &v);
@@ -1127,6 +1520,18 @@ int unit_reset_cpu_usage(Unit *u) {
return 0;
}
+bool unit_cgroup_delegate(Unit *u) {
+ CGroupContext *c;
+
+ assert(u);
+
+ c = unit_get_cgroup_context(u);
+ if (!c)
+ return false;
+
+ return c->delegate;
+}
+
static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = {
[CGROUP_AUTO] = "auto",
[CGROUP_CLOSED] = "closed",
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 869ddae8c4..438f5bf50f 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -96,22 +96,32 @@ struct CGroupContext {
void cgroup_context_init(CGroupContext *c);
void cgroup_context_done(CGroupContext *c);
void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix);
-void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const char *path, ManagerState state);
+void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, ManagerState state);
-CGroupControllerMask cgroup_context_get_mask(CGroupContext *c);
+CGroupMask cgroup_context_get_mask(CGroupContext *c);
void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a);
void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w);
void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b);
-CGroupControllerMask unit_get_cgroup_mask(Unit *u);
-CGroupControllerMask unit_get_siblings_mask(Unit *u);
-CGroupControllerMask unit_get_members_mask(Unit *u);
-CGroupControllerMask unit_get_target_mask(Unit *u);
+CGroupMask unit_get_own_mask(Unit *u);
+CGroupMask unit_get_siblings_mask(Unit *u);
+CGroupMask unit_get_members_mask(Unit *u);
+CGroupMask unit_get_subtree_mask(Unit *u);
+
+CGroupMask unit_get_target_mask(Unit *u);
+CGroupMask unit_get_enable_mask(Unit *u);
void unit_update_cgroup_members_masks(Unit *u);
+
+char *unit_default_cgroup_path(Unit *u);
+int unit_set_cgroup_path(Unit *u, const char *path);
+
int unit_realize_cgroup(Unit *u);
-void unit_destroy_cgroup_if_empty(Unit *u);
+void unit_release_cgroup(Unit *u);
+void unit_prune_cgroup(Unit *u);
+int unit_watch_cgroup(Unit *u);
+
int unit_attach_pids_to_cgroup(Unit *u);
int manager_setup_cgroup(Manager *m);
@@ -120,15 +130,20 @@ void manager_shutdown_cgroup(Manager *m, bool delete);
unsigned manager_dispatch_cgroup_queue(Manager *m);
Unit *manager_get_unit_by_cgroup(Manager *m, const char *cgroup);
+Unit *manager_get_unit_by_pid_cgroup(Manager *m, pid_t pid);
Unit* manager_get_unit_by_pid(Manager *m, pid_t pid);
-pid_t unit_search_main_pid(Unit *u);
-
-int manager_notify_cgroup_empty(Manager *m, const char *group);
+int unit_search_main_pid(Unit *u, pid_t *ret);
+int unit_watch_all_pids(Unit *u);
int unit_get_memory_current(Unit *u, uint64_t *ret);
int unit_get_cpu_usage(Unit *u, nsec_t *ret);
int unit_reset_cpu_usage(Unit *u);
+bool unit_cgroup_delegate(Unit *u);
+
+int unit_notify_cgroup_empty(Unit *u);
+int manager_notify_cgroup_empty(Manager *m, const char *group);
+
const char* cgroup_device_policy_to_string(CGroupDevicePolicy i) _const_;
CGroupDevicePolicy cgroup_device_policy_from_string(const char *s) _pure_;
diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
index 9dcc51f240..e8fd44e294 100644
--- a/src/core/dbus-cgroup.c
+++ b/src/core/dbus-cgroup.c
@@ -228,7 +228,7 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->cpu_accounting = b;
- u->cgroup_realized_mask &= ~CGROUP_CPUACCT;
+ u->cgroup_realized_mask &= ~CGROUP_MASK_CPUACCT;
unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no");
}
@@ -252,7 +252,7 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->cpu_shares = ul;
- u->cgroup_realized_mask &= ~CGROUP_CPU;
+ u->cgroup_realized_mask &= ~CGROUP_MASK_CPU;
unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
}
@@ -276,7 +276,7 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->startup_cpu_shares = ul;
- u->cgroup_realized_mask &= ~CGROUP_CPU;
+ u->cgroup_realized_mask &= ~CGROUP_MASK_CPU;
unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%lu", ul);
}
@@ -294,7 +294,7 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->cpu_quota_per_sec_usec = u64;
- u->cgroup_realized_mask &= ~CGROUP_CPU;
+ u->cgroup_realized_mask &= ~CGROUP_MASK_CPU;
unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%0.f%%", (double) (c->cpu_quota_per_sec_usec / 10000));
}
@@ -309,7 +309,7 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->blockio_accounting = b;
- u->cgroup_realized_mask &= ~CGROUP_BLKIO;
+ u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO;
unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
}
@@ -333,7 +333,7 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->blockio_weight = ul;
- u->cgroup_realized_mask &= ~CGROUP_BLKIO;
+ u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO;
unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
}
@@ -357,7 +357,7 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->startup_blockio_weight = ul;
- u->cgroup_realized_mask &= ~CGROUP_BLKIO;
+ u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO;
unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%lu", ul);
}
@@ -427,7 +427,7 @@ int bus_cgroup_set_property(
cgroup_context_free_blockio_device_bandwidth(c, a);
}
- u->cgroup_realized_mask &= ~CGROUP_BLKIO;
+ u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO;
f = open_memstream(&buf, &size);
if (!f)
@@ -510,7 +510,7 @@ int bus_cgroup_set_property(
cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
}
- u->cgroup_realized_mask &= ~CGROUP_BLKIO;
+ u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO;
f = open_memstream(&buf, &size);
if (!f)
@@ -535,7 +535,7 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->memory_accounting = b;
- u->cgroup_realized_mask &= ~CGROUP_MEMORY;
+ u->cgroup_realized_mask &= ~CGROUP_MASK_MEMORY;
unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
}
@@ -550,7 +550,7 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->memory_limit = limit;
- u->cgroup_realized_mask &= ~CGROUP_MEMORY;
+ u->cgroup_realized_mask &= ~CGROUP_MASK_MEMORY;
unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
}
@@ -572,7 +572,7 @@ int bus_cgroup_set_property(
char *buf;
c->device_policy = p;
- u->cgroup_realized_mask &= ~CGROUP_DEVICE;
+ u->cgroup_realized_mask &= ~CGROUP_MASK_DEVICE;
buf = strjoina("DevicePolicy=", policy);
unit_write_drop_in_private(u, mode, name, buf);
@@ -651,7 +651,7 @@ int bus_cgroup_set_property(
cgroup_context_free_device_allow(c, c->device_allow);
}
- u->cgroup_realized_mask &= ~CGROUP_DEVICE;
+ u->cgroup_realized_mask &= ~CGROUP_MASK_DEVICE;
f = open_memstream(&buf, &size);
if (!f)
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 0a9effda71..f9275ed935 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -25,6 +25,7 @@
#include "cgroup-util.h"
#include "strv.h"
#include "bus-common-errors.h"
+#include "special.h"
#include "dbus.h"
#include "dbus-unit.h"
@@ -390,6 +391,29 @@ static int property_get_load_error(
return sd_bus_message_append(reply, "(ss)", e.name, e.message);
}
+static int bus_verify_manage_units_async_full(
+ Unit *u,
+ const char *verb,
+ int capability,
+ const char *polkit_message,
+ sd_bus_message *call,
+ sd_bus_error *error) {
+
+ const char *details[9] = {
+ "unit", u->id,
+ "verb", verb,
+ };
+
+ if (polkit_message) {
+ details[4] = "polkit.message";
+ details[5] = polkit_message;
+ details[6] = "polkit.gettext_domain";
+ details[7] = GETTEXT_PACKAGE;
+ }
+
+ return bus_verify_polkit_async(call, capability, "org.freedesktop.systemd1.manage-units", details, false, UID_INVALID, &u->manager->polkit_registry, error);
+}
+
int bus_unit_method_start_generic(
sd_bus_message *message,
Unit *u,
@@ -399,6 +423,14 @@ int bus_unit_method_start_generic(
const char *smode;
JobMode mode;
+ _cleanup_free_ char *verb = NULL;
+ static const char *const polkit_message_for_job[_JOB_TYPE_MAX] = {
+ [JOB_START] = N_("Authentication is required to start '$(unit)'."),
+ [JOB_STOP] = N_("Authentication is required to stop '$(unit)'."),
+ [JOB_RELOAD] = N_("Authentication is required to reload '$(unit)'."),
+ [JOB_RESTART] = N_("Authentication is required to restart '$(unit)'."),
+ [JOB_TRY_RESTART] = N_("Authentication is required to restart '$(unit)'."),
+ };
int r;
assert(message);
@@ -417,7 +449,20 @@ int bus_unit_method_start_generic(
if (mode < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s invalid", smode);
- r = bus_verify_manage_units_async(u->manager, message, error);
+ if (reload_if_possible)
+ verb = strjoin("reload-or-", job_type_to_string(job_type), NULL);
+ else
+ verb = strdup(job_type_to_string(job_type));
+ if (!verb)
+ return -ENOMEM;
+
+ r = bus_verify_manage_units_async_full(
+ u,
+ verb,
+ CAP_SYS_ADMIN,
+ job_type < _JOB_TYPE_MAX ? polkit_message_for_job[job_type] : NULL,
+ message,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -483,7 +528,13 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
if (signo <= 0 || signo >= _NSIG)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range.");
- r = bus_verify_manage_units_async_for_kill(u->manager, message, error);
+ r = bus_verify_manage_units_async_full(
+ u,
+ "kill",
+ CAP_KILL,
+ N_("Authentication is required to kill '$(unit)'."),
+ message,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -507,7 +558,13 @@ int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus
if (r < 0)
return r;
- r = bus_verify_manage_units_async(u->manager, message, error);
+ r = bus_verify_manage_units_async_full(
+ u,
+ "reset-failed",
+ CAP_SYS_ADMIN,
+ N_("Authentication is required to reset the \"failed\" state of '$(unit)'."),
+ message,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -533,7 +590,13 @@ int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_b
if (r < 0)
return r;
- r = bus_verify_manage_units_async(u->manager, message, error);
+ r = bus_verify_manage_units_async_full(
+ u,
+ "set-property",
+ CAP_SYS_ADMIN,
+ N_("Authentication is required to set properties on '$(unit)'."),
+ message,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -783,7 +846,7 @@ static int send_changed_signal(sd_bus *bus, void *userdata) {
r = sd_bus_emit_properties_changed_strv(
bus, p,
- UNIT_VTABLE(u)->bus_interface,
+ unit_dbus_interface_from_type(u->type),
NULL);
if (r < 0)
return r;
@@ -965,38 +1028,41 @@ static int bus_unit_set_transient_property(
return 1;
- } else if (streq(name, "Slice") && unit_get_cgroup_context(u)) {
+ } else if (streq(name, "Slice")) {
+ Unit *slice;
const char *s;
+ if (!UNIT_HAS_CGROUP_CONTEXT(u))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "The slice property is only available for units with control groups.");
+ if (u->type == UNIT_SLICE)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Slice may not be set for slice units.");
+ if (unit_has_name(u, SPECIAL_INIT_SCOPE))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot set slice for init.scope");
+
r = sd_bus_message_read(message, "s", &s);
if (r < 0)
return r;
- if (!unit_name_is_valid(s, UNIT_NAME_PLAIN) || !endswith(s, ".slice"))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid slice name %s", s);
+ if (!unit_name_is_valid(s, UNIT_NAME_PLAIN))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name '%s'", s);
- if (isempty(s)) {
- if (mode != UNIT_CHECK) {
- unit_ref_unset(&u->slice);
- unit_remove_drop_in(u, mode, name);
- }
- } else {
- Unit *slice;
+ r = manager_load_unit(u->manager, s, NULL, error, &slice);
+ if (r < 0)
+ return r;
+
+ if (slice->type != UNIT_SLICE)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit name '%s' is not a slice", s);
- r = manager_load_unit(u->manager, s, NULL, error, &slice);
+ if (mode != UNIT_CHECK) {
+ r = unit_set_slice(u, slice);
if (r < 0)
return r;
- if (slice->type != UNIT_SLICE)
- return -EINVAL;
-
- if (mode != UNIT_CHECK) {
- unit_ref_set(&u->slice, slice);
- unit_write_drop_in_private_format(u, mode, name, "Slice=%s\n", s);
- }
+ unit_write_drop_in_private_format(u, mode, name, "Slice=%s\n", s);
}
return 1;
+
} else if (STR_IN_SET(name,
"Requires", "RequiresOverridable",
"Requisite", "RequisiteOverridable",
diff --git a/src/core/dbus.c b/src/core/dbus.c
index 44bf5cab28..0a2180c6a7 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -356,7 +356,7 @@ static int bus_unit_interface_find(sd_bus *bus, const char *path, const char *in
if (r <= 0)
return r;
- if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface))
+ if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type)))
return 0;
*found = u;
@@ -378,10 +378,10 @@ static int bus_unit_cgroup_find(sd_bus *bus, const char *path, const char *inter
if (r <= 0)
return r;
- if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface))
+ if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type)))
return 0;
- if (!unit_get_cgroup_context(u))
+ if (!UNIT_HAS_CGROUP_CONTEXT(u))
return 0;
*found = u;
@@ -404,7 +404,7 @@ static int bus_cgroup_context_find(sd_bus *bus, const char *path, const char *in
if (r <= 0)
return r;
- if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface))
+ if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type)))
return 0;
c = unit_get_cgroup_context(u);
@@ -431,7 +431,7 @@ static int bus_exec_context_find(sd_bus *bus, const char *path, const char *inte
if (r <= 0)
return r;
- if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface))
+ if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type)))
return 0;
c = unit_get_exec_context(u);
@@ -458,7 +458,7 @@ static int bus_kill_context_find(sd_bus *bus, const char *path, const char *inte
if (r <= 0)
return r;
- if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface))
+ if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type)))
return 0;
c = unit_get_kill_context(u);
@@ -555,30 +555,34 @@ static int bus_setup_api_vtables(Manager *m, sd_bus *bus) {
return log_error_errno(r, "Failed to add job enumerator: %m");
for (t = 0; t < _UNIT_TYPE_MAX; t++) {
- r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, unit_vtable[t]->bus_vtable, bus_unit_interface_find, m);
+ const char *interface;
+
+ assert_se(interface = unit_dbus_interface_from_type(t));
+
+ r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, unit_vtable[t]->bus_vtable, bus_unit_interface_find, m);
if (r < 0)
- return log_error_errno(r, "Failed to register type specific vtable for %s: %m", unit_vtable[t]->bus_interface);
+ return log_error_errno(r, "Failed to register type specific vtable for %s: %m", interface);
if (unit_vtable[t]->cgroup_context_offset > 0) {
- r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_unit_cgroup_vtable, bus_unit_cgroup_find, m);
+ r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_unit_cgroup_vtable, bus_unit_cgroup_find, m);
if (r < 0)
- return log_error_errno(r, "Failed to register control group unit vtable for %s: %m", unit_vtable[t]->bus_interface);
+ return log_error_errno(r, "Failed to register control group unit vtable for %s: %m", interface);
- r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_cgroup_vtable, bus_cgroup_context_find, m);
+ r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_cgroup_vtable, bus_cgroup_context_find, m);
if (r < 0)
- return log_error_errno(r, "Failed to register control group vtable for %s: %m", unit_vtable[t]->bus_interface);
+ return log_error_errno(r, "Failed to register control group vtable for %s: %m", interface);
}
if (unit_vtable[t]->exec_context_offset > 0) {
- r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_exec_vtable, bus_exec_context_find, m);
+ r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_exec_vtable, bus_exec_context_find, m);
if (r < 0)
- return log_error_errno(r, "Failed to register execute vtable for %s: %m", unit_vtable[t]->bus_interface);
+ return log_error_errno(r, "Failed to register execute vtable for %s: %m", interface);
}
if (unit_vtable[t]->kill_context_offset > 0) {
- r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_kill_vtable, bus_kill_context_find, m);
+ r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_kill_vtable, bus_kill_context_find, m);
if (r < 0)
- return log_error_errno(r, "Failed to register kill vtable for %s: %m", unit_vtable[t]->bus_interface);
+ return log_error_errno(r, "Failed to register kill vtable for %s: %m", interface);
}
}
@@ -1194,22 +1198,17 @@ int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l) {
}
int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-units", false, UID_INVALID, &m->polkit_registry, error);
-}
-
-/* Same as bus_verify_manage_unit_async(), but checks for CAP_KILL instead of CAP_SYS_ADMIN */
-int bus_verify_manage_units_async_for_kill(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_KILL, "org.freedesktop.systemd1.manage-units", false, UID_INVALID, &m->polkit_registry, error);
+ return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-units", NULL, false, UID_INVALID, &m->polkit_registry, error);
}
int bus_verify_manage_unit_files_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-unit-files", false, UID_INVALID, &m->polkit_registry, error);
+ return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-unit-files", NULL, false, UID_INVALID, &m->polkit_registry, error);
}
int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.reload-daemon", false, UID_INVALID, &m->polkit_registry, error);
+ return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.reload-daemon", NULL, false, UID_INVALID, &m->polkit_registry, error);
}
int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.set-environment", false, UID_INVALID, &m->polkit_registry, error);
+ return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.set-environment", NULL, false, UID_INVALID, &m->polkit_registry, error);
}
diff --git a/src/core/dbus.h b/src/core/dbus.h
index 4832722069..4f06ad11c4 100644
--- a/src/core/dbus.h
+++ b/src/core/dbus.h
@@ -37,7 +37,6 @@ int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l);
int bus_foreach_bus(Manager *m, sd_bus_track *subscribed2, int (*send_message)(sd_bus *bus, void *userdata), void *userdata);
int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error);
-int bus_verify_manage_units_async_for_kill(Manager *m, sd_bus_message *call, sd_bus_error *error);
int bus_verify_manage_unit_files_async(Manager *m, sd_bus_message *call, sd_bus_error *error);
int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_error *error);
int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *error);
diff --git a/src/core/device.c b/src/core/device.c
index e7efcf0f0a..3f574b1832 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -849,7 +849,6 @@ const UnitVTable device_vtable = {
.active_state = device_active_state,
.sub_state_to_string = device_sub_state_to_string,
- .bus_interface = "org.freedesktop.systemd1.Device",
.bus_vtable = bus_device_vtable,
.following = device_following,
diff --git a/src/core/execute.h b/src/core/execute.h
index 8d14fe23d0..a750246a89 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -214,7 +214,7 @@ struct ExecParameters {
bool apply_tty_stdin;
bool confirm_spawn;
bool selinux_context_net;
- CGroupControllerMask cgroup_supported;
+ CGroupMask cgroup_supported;
const char *cgroup_path;
bool cgroup_delegate;
const char *runtime_prefix;
diff --git a/src/core/killall.c b/src/core/killall.c
index 2a9d72c901..ee5d388560 100644
--- a/src/core/killall.c
+++ b/src/core/killall.c
@@ -108,7 +108,7 @@ static void wait_for_children(Set *pids, sigset_t *mask) {
return;
}
- set_remove(pids, ULONG_TO_PTR(pid));
+ (void) set_remove(pids, PID_TO_PTR(pid));
}
/* Now explicitly check who might be remaining, who
@@ -117,7 +117,7 @@ static void wait_for_children(Set *pids, sigset_t *mask) {
/* We misuse getpgid as a check whether a
* process still exists. */
- if (getpgid((pid_t) PTR_TO_ULONG(p)) >= 0)
+ if (getpgid(PTR_TO_PID(p)) >= 0)
continue;
if (errno != ESRCH)
@@ -179,7 +179,7 @@ static int killall(int sig, Set *pids, bool send_sighup) {
if (kill(pid, sig) >= 0) {
if (pids) {
- r = set_put(pids, ULONG_TO_PTR(pid));
+ r = set_put(pids, PID_TO_PTR(pid));
if (r < 0)
log_oom();
}
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 60b97722db..edd55b9e45 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -106,7 +106,7 @@ m4_define(`KILL_CONTEXT_CONFIG_ITEMS',
`$1.SendSIGKILL, config_parse_bool, 0, offsetof($1, kill_context.send_sigkill)
$1.SendSIGHUP, config_parse_bool, 0, offsetof($1, kill_context.send_sighup)
$1.KillMode, config_parse_kill_mode, 0, offsetof($1, kill_context.kill_mode)
-$1.KillSignal, config_parse_kill_signal, 0, offsetof($1, kill_context.kill_signal)'
+$1.KillSignal, config_parse_signal, 0, offsetof($1, kill_context.kill_signal)'
)m4_dnl
m4_define(`CGROUP_CONTEXT_CONFIG_ITEMS',
`$1.Slice, config_parse_unit_slice, 0, 0
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index b3bf8bdb40..542bf8eb26 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -1048,7 +1048,7 @@ int config_parse_bounding_set(const char *unit,
cap = capability_from_name(t);
if (cap < 0) {
- log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse capability in bounding set, ignoring: %s", t);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse capability in bounding set, ignoring: %s", t);
continue;
}
@@ -1143,39 +1143,8 @@ int config_parse_sysv_priority(const char *unit,
#endif
DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
-
DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
-int config_parse_kill_signal(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- int *sig = data;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(sig);
-
- r = signal_from_string_try_harder(rvalue);
- if (r <= 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse kill signal, ignoring: %s", rvalue);
- return 0;
- }
-
- *sig = r;
- return 0;
-}
-
int config_parse_exec_mount_flags(const char *unit,
const char *filename,
unsigned line,
@@ -2602,7 +2571,7 @@ int config_parse_unit_slice(
void *userdata) {
_cleanup_free_ char *k = NULL;
- Unit *u = userdata, *slice;
+ Unit *u = userdata, *slice = NULL;
int r;
assert(filename);
@@ -2611,29 +2580,23 @@ int config_parse_unit_slice(
assert(u);
r = unit_name_printf(u, rvalue, &k);
- if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
- if (!k) {
- k = strdup(rvalue);
- if (!k)
- return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
+ return 0;
}
r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to load slice unit %s. Ignoring.", k);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load slice unit %s. Ignoring.", k);
return 0;
}
- if (slice->type != UNIT_SLICE) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Slice unit %s is not a slice. Ignoring.", k);
+ r = unit_set_slice(u, slice);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to assign slice %s to unit %s. Ignoring.", slice->id, u->id);
return 0;
}
- unit_ref_set(&u->slice, slice);
return 0;
}
@@ -3027,36 +2990,6 @@ int config_parse_job_mode_isolate(
return 0;
}
-int config_parse_personality(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- unsigned long *personality = data, p;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(personality);
-
- p = personality_from_string(rvalue);
- if (p == PERSONALITY_INVALID) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse personality, ignoring: %s", rvalue);
- return 0;
- }
-
- *personality = p;
- return 0;
-}
-
int config_parse_runtime_directory(
const char *unit,
const char *filename,
@@ -3603,6 +3536,11 @@ int unit_load_fragment(Unit *u) {
assert(u->load_state == UNIT_STUB);
assert(u->id);
+ if (u->transient) {
+ u->load_state = UNIT_LOADED;
+ return 0;
+ }
+
/* First, try to find the unit under its id. We always look
* for unit files in the default directories, to make it easy
* to override things by placing things in /etc/systemd/system */
@@ -3721,7 +3659,7 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_sysv_priority, "SYSVPRIORITY" },
#endif
{ config_parse_kill_mode, "KILLMODE" },
- { config_parse_kill_signal, "SIGNAL" },
+ { config_parse_signal, "SIGNAL" },
{ config_parse_socket_listen, "SOCKET [...]" },
{ config_parse_socket_bind, "SOCKETBIND" },
{ config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index fcca2b0221..1d128716c4 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -92,7 +92,6 @@ int config_parse_blockio_bandwidth(const char *unit, const char *filename, unsig
int config_parse_job_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_job_mode_isolate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_selinux_context(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_personality(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_apparmor_profile(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_smack_process_label(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_address_families(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/core/main.c b/src/core/main.c
index 87b97aa883..4cd2b08c38 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -433,25 +433,28 @@ static int config_parse_cpu_affinity2(
void *data,
void *userdata) {
- const char *word, *state;
- size_t l;
- cpu_set_t *c = NULL;
+ const char *whole_rvalue = rvalue;
+ _cleanup_cpu_free_ cpu_set_t *c = NULL;
unsigned ncpus = 0;
assert(filename);
assert(lvalue);
assert(rvalue);
- FOREACH_WORD_QUOTED(word, l, rvalue, state) {
- char *t;
- int r;
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
unsigned cpu;
+ int r;
- if (!(t = strndup(word, l)))
- return log_oom();
+ r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
+ return r;
+ }
+ if (r == 0)
+ break;
- r = safe_atou(t, &cpu);
- free(t);
+ r = safe_atou(word, &cpu);
if (!c)
if (!(c = cpu_set_malloc(&ncpus)))
@@ -460,23 +463,19 @@ static int config_parse_cpu_affinity2(
if (r < 0 || cpu >= ncpus) {
log_syntax(unit, LOG_ERR, filename, line, -r,
"Failed to parse CPU affinity '%s'", rvalue);
- CPU_FREE(c);
return -EBADMSG;
}
CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c);
}
- if (!isempty(state))
+ if (!isempty(rvalue))
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Trailing garbage, ignoring.");
- if (c) {
+ if (c)
if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0)
log_warning("Failed to set CPU affinity: %m");
- CPU_FREE(c);
- }
-
return 0;
}
@@ -538,9 +537,8 @@ static int config_parse_join_controllers(const char *unit,
void *data,
void *userdata) {
+ const char *whole_rvalue = rvalue;
unsigned n = 0;
- const char *word, *state;
- size_t length;
assert(filename);
assert(lvalue);
@@ -548,16 +546,22 @@ static int config_parse_join_controllers(const char *unit,
free_join_controllers();
- FOREACH_WORD_QUOTED(word, length, rvalue, state) {
- char *s, **l;
-
- s = strndup(word, length);
- if (!s)
- return log_oom();
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ char **l;
+ int r;
- l = strv_split(s, ",");
- free(s);
+ r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
+ return r;
+ }
+ if (r == 0)
+ break;
+ l = strv_split(word, ",");
+ if (!l)
+ log_oom();
strv_uniq(l);
if (strv_length(l) <= 1) {
@@ -617,7 +621,7 @@ static int config_parse_join_controllers(const char *unit,
arg_join_controllers = t;
}
}
- if (!isempty(state))
+ if (!isempty(rvalue))
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Trailing garbage, ignoring.");
@@ -1391,8 +1395,7 @@ int main(int argc, char *argv[]) {
/* clear the kernel timestamp,
* because we are not PID 1 */
- kernel_timestamp.monotonic = 0ULL;
- kernel_timestamp.realtime = 0ULL;
+ kernel_timestamp = DUAL_TIMESTAMP_NULL;
}
/* Initialize default unit */
@@ -2044,7 +2047,7 @@ finish:
* kernel; at this point, we will not listen to the
* signals anyway */
if (detect_container(NULL) <= 0)
- cg_uninstall_release_agent(SYSTEMD_CGROUP_CONTROLLER);
+ (void) cg_uninstall_release_agent(SYSTEMD_CGROUP_CONTROLLER);
execve(SYSTEMD_SHUTDOWN_BINARY_PATH, (char **) command_line, env_block);
log_error_errno(errno, "Failed to execute shutdown binary, %s: %m",
diff --git a/src/core/manager.c b/src/core/manager.c
index ecea89c377..fc10ddb5d9 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -250,8 +250,8 @@ static int manager_dispatch_ask_password_fd(sd_event_source *source,
static void manager_close_ask_password(Manager *m) {
assert(m);
- m->ask_password_inotify_fd = safe_close(m->ask_password_inotify_fd);
m->ask_password_event_source = sd_event_source_unref(m->ask_password_event_source);
+ m->ask_password_inotify_fd = safe_close(m->ask_password_inotify_fd);
m->have_ask_password = -EINVAL;
}
@@ -568,11 +568,14 @@ int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) {
m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
- m->pin_cgroupfs_fd = m->notify_fd = m->signal_fd = m->time_change_fd = m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->utab_inotify_fd = -1;
+ m->pin_cgroupfs_fd = m->notify_fd = m->signal_fd = m->time_change_fd =
+ m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->utab_inotify_fd =
+ m->cgroup_inotify_fd = -1;
m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
m->ask_password_inotify_fd = -1;
m->have_ask_password = -EINVAL; /* we don't know */
+ m->first_boot = -1;
m->test_run = test_run;
@@ -1582,19 +1585,19 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
/* Notify every unit that might be interested, but try
* to avoid notifying the same one multiple times. */
- u1 = manager_get_unit_by_pid(m, ucred->pid);
+ u1 = manager_get_unit_by_pid_cgroup(m, ucred->pid);
if (u1) {
manager_invoke_notify_message(m, u1, ucred->pid, buf, n, fds);
found = true;
}
- u2 = hashmap_get(m->watch_pids1, LONG_TO_PTR(ucred->pid));
+ u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(ucred->pid));
if (u2 && u2 != u1) {
manager_invoke_notify_message(m, u2, ucred->pid, buf, n, fds);
found = true;
}
- u3 = hashmap_get(m->watch_pids2, LONG_TO_PTR(ucred->pid));
+ u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(ucred->pid));
if (u3 && u3 != u2 && u3 != u1) {
manager_invoke_notify_message(m, u3, ucred->pid, buf, n, fds);
found = true;
@@ -1660,13 +1663,13 @@ static int manager_dispatch_sigchld(Manager *m) {
/* And now figure out the unit this belongs
* to, it might be multiple... */
- u1 = manager_get_unit_by_pid(m, si.si_pid);
+ u1 = manager_get_unit_by_pid_cgroup(m, si.si_pid);
if (u1)
invoke_sigchld_event(m, u1, &si);
- u2 = hashmap_get(m->watch_pids1, LONG_TO_PTR(si.si_pid));
+ u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(si.si_pid));
if (u2 && u2 != u1)
invoke_sigchld_event(m, u2, &si);
- u3 = hashmap_get(m->watch_pids2, LONG_TO_PTR(si.si_pid));
+ u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(si.si_pid));
if (u3 && u3 != u2 && u3 != u1)
invoke_sigchld_event(m, u3, &si);
}
@@ -2721,7 +2724,7 @@ void manager_check_finished(Manager *m) {
SET_FOREACH(u, m->startup_units, i)
if (u->cgroup_path)
- cgroup_context_apply(unit_get_cgroup_context(u), unit_get_cgroup_mask(u), u->cgroup_path, manager_state(m));
+ cgroup_context_apply(unit_get_cgroup_context(u), unit_get_own_mask(u), u->cgroup_path, manager_state(m));
}
static int create_generator_dir(Manager *m, char **generator, const char *name) {
@@ -2998,12 +3001,14 @@ void manager_set_first_boot(Manager *m, bool b) {
if (m->running_as != MANAGER_SYSTEM)
return;
- m->first_boot = b;
+ if (m->first_boot != (int) b) {
+ if (b)
+ (void) touch("/run/systemd/first-boot");
+ else
+ (void) unlink("/run/systemd/first-boot");
+ }
- if (m->first_boot)
- touch("/run/systemd/first-boot");
- else
- unlink("/run/systemd/first-boot");
+ m->first_boot = b;
}
void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) {
diff --git a/src/core/manager.h b/src/core/manager.h
index 1e01f2bdef..9956cb7700 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -215,16 +215,22 @@ struct Manager {
/* Data specific to the cgroup subsystem */
Hashmap *cgroup_unit;
- CGroupControllerMask cgroup_supported;
+ CGroupMask cgroup_supported;
char *cgroup_root;
- int gc_marker;
- unsigned n_in_gc_queue;
+ /* Notifications from cgroups, when the unified hierarchy is
+ * used is done via inotify. */
+ int cgroup_inotify_fd;
+ sd_event_source *cgroup_inotify_event_source;
+ Hashmap *cgroup_inotify_wd_unit;
/* Make sure the user cannot accidentally unmount our cgroup
* file system */
int pin_cgroupfs_fd;
+ int gc_marker;
+ unsigned n_in_gc_queue;
+
/* Flags */
ManagerRunningAs running_as;
ManagerExitCode exit_code:5;
@@ -233,7 +239,6 @@ struct Manager {
bool dispatching_dbus_queue:1;
bool taint_usr:1;
- bool first_boot:1;
bool test_run:1;
@@ -295,6 +300,8 @@ struct Manager {
const char *unit_log_field;
const char *unit_log_format_string;
+
+ int first_boot;
};
int manager_new(ManagerRunningAs running_as, bool test_run, Manager **m);
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index 1782d40720..c6f3569915 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -93,12 +93,14 @@ static const MountPoint mount_table[] = {
#endif
{ "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
NULL, MNT_FATAL|MNT_IN_CONTAINER },
+ { "cgroup", "/sys/fs/cgroup", "cgroup", "__DEVEL__sane_behavior", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ cg_is_unified_wanted, MNT_FATAL|MNT_IN_CONTAINER },
{ "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
- NULL, MNT_FATAL|MNT_IN_CONTAINER },
+ cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
{ "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV,
- NULL, MNT_IN_CONTAINER },
+ cg_is_legacy_wanted, MNT_IN_CONTAINER },
{ "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV,
- NULL, MNT_FATAL|MNT_IN_CONTAINER },
+ cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
{ "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
NULL, MNT_NONE },
#ifdef ENABLE_EFI
@@ -217,6 +219,9 @@ int mount_cgroup_controllers(char ***join_controllers) {
_cleanup_set_free_free_ Set *controllers = NULL;
int r;
+ if (!cg_is_legacy_wanted())
+ return 0;
+
/* Mount all available cgroup controllers that are built into the kernel. */
controllers = set_new(&string_hash_ops);
diff --git a/src/core/mount.c b/src/core/mount.c
index c0d1cdfbd4..2b81d17b9c 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -521,7 +521,7 @@ static int mount_add_extras(Mount *m) {
if (r < 0)
return r;
- r = unit_add_default_slice(u, &m->cgroup_context);
+ r = unit_set_default_slice(u);
if (r < 0)
return r;
@@ -1871,7 +1871,6 @@ const UnitVTable mount_vtable = {
.reset_failed = mount_reset_failed,
- .bus_interface = "org.freedesktop.systemd1.Mount",
.bus_vtable = bus_mount_vtable,
.bus_set_property = bus_mount_set_property,
.bus_commit_properties = bus_mount_commit_properties,
diff --git a/src/core/path.c b/src/core/path.c
index 20995d920c..e9111d0612 100644
--- a/src/core/path.c
+++ b/src/core/path.c
@@ -770,6 +770,5 @@ const UnitVTable path_vtable = {
.reset_failed = path_reset_failed,
- .bus_interface = "org.freedesktop.systemd1.Path",
.bus_vtable = bus_path_vtable
};
diff --git a/src/core/scope.c b/src/core/scope.c
index ab1769b46b..35cf0621be 100644
--- a/src/core/scope.c
+++ b/src/core/scope.c
@@ -22,12 +22,13 @@
#include <errno.h>
#include <unistd.h>
-#include "unit.h"
-#include "scope.h"
#include "log.h"
-#include "dbus-scope.h"
+#include "strv.h"
#include "special.h"
#include "unit-name.h"
+#include "unit.h"
+#include "scope.h"
+#include "dbus-scope.h"
#include "load-dropin.h"
static const UnitActiveState state_translation_table[_SCOPE_STATE_MAX] = {
@@ -136,7 +137,9 @@ static int scope_verify(Scope *s) {
if (UNIT(s)->load_state != UNIT_LOADED)
return 0;
- if (set_isempty(UNIT(s)->pids) && UNIT(s)->manager->n_reloading <= 0) {
+ if (set_isempty(UNIT(s)->pids) &&
+ !manager_is_reloading_or_reexecuting(UNIT(s)->manager) &&
+ !unit_has_name(UNIT(s), SPECIAL_INIT_SCOPE)) {
log_unit_error(UNIT(s), "Scope has no PIDs. Refusing.");
return -EINVAL;
}
@@ -151,7 +154,7 @@ static int scope_load(Unit *u) {
assert(s);
assert(u->load_state == UNIT_STUB);
- if (!u->transient && UNIT(s)->manager->n_reloading <= 0)
+ if (!u->transient && !manager_is_reloading_or_reexecuting(u->manager))
return -ENOENT;
u->load_state = UNIT_LOADED;
@@ -164,7 +167,7 @@ static int scope_load(Unit *u) {
if (r < 0)
return r;
- r = unit_add_default_slice(u, &s->cgroup_context);
+ r = unit_set_default_slice(u);
if (r < 0)
return r;
@@ -279,6 +282,9 @@ static int scope_start(Unit *u) {
assert(s);
+ if (unit_has_name(u, SPECIAL_INIT_SCOPE))
+ return -EPERM;
+
if (s->state == SCOPE_FAILED)
return -EPERM;
@@ -289,7 +295,7 @@ static int scope_start(Unit *u) {
assert(s->state == SCOPE_DEAD);
- if (!u->transient && UNIT(s)->manager->n_reloading <= 0)
+ if (!u->transient && !manager_is_reloading_or_reexecuting(u->manager))
return -ENOENT;
(void) unit_realize_cgroup(u);
@@ -396,7 +402,7 @@ static bool scope_check_gc(Unit *u) {
if (u->cgroup_path) {
int r;
- r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
+ r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
if (r <= 0)
return true;
}
@@ -464,6 +470,9 @@ static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *user
int scope_abandon(Scope *s) {
assert(s);
+ if (unit_has_name(UNIT(s), SPECIAL_INIT_SCOPE))
+ return -EPERM;
+
if (!IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED))
return -ESTALE;
@@ -499,6 +508,48 @@ _pure_ static const char *scope_sub_state_to_string(Unit *u) {
return scope_state_to_string(SCOPE(u)->state);
}
+static int scope_enumerate(Manager *m) {
+ Unit *u;
+ int r;
+
+ assert(m);
+
+ /* Let's unconditionally add the "init.scope" special unit
+ * that encapsulates PID 1. Note that PID 1 already is in the
+ * cgroup for this, we hence just need to allocate the object
+ * for it and that's it. */
+
+ u = manager_get_unit(m, SPECIAL_INIT_SCOPE);
+ if (!u) {
+ u = unit_new(m, sizeof(Scope));
+ if (!u)
+ return log_oom();
+
+ r = unit_add_name(u, SPECIAL_INIT_SCOPE);
+ if (r < 0) {
+ unit_free(u);
+ return log_error_errno(r, "Failed to add init.scope name");
+ }
+ }
+
+ u->transient = true;
+ u->default_dependencies = false;
+ u->no_gc = true;
+ SCOPE(u)->deserialized_state = SCOPE_RUNNING;
+ SCOPE(u)->kill_context.kill_signal = SIGRTMIN+14;
+
+ /* Prettify things, if we can. */
+ if (!u->description)
+ u->description = strdup("System and Service Manager");
+ if (!u->documentation)
+ (void) strv_extend(&u->documentation, "man:systemd(1)");
+
+ unit_add_to_load_queue(u);
+ unit_add_to_dbus_queue(u);
+
+ return 0;
+}
+
static const char* const scope_state_table[_SCOPE_STATE_MAX] = {
[SCOPE_DEAD] = "dead",
[SCOPE_RUNNING] = "running",
@@ -561,10 +612,11 @@ const UnitVTable scope_vtable = {
.notify_cgroup_empty = scope_notify_cgroup_empty_event,
- .bus_interface = "org.freedesktop.systemd1.Scope",
.bus_vtable = bus_scope_vtable,
.bus_set_property = bus_scope_set_property,
.bus_commit_properties = bus_scope_commit_properties,
- .can_transient = true
+ .can_transient = true,
+
+ .enumerate = scope_enumerate,
};
diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c
index f920c2e2cd..40ca0c6166 100644
--- a/src/core/selinux-access.c
+++ b/src/core/selinux-access.c
@@ -246,7 +246,7 @@ int mac_selinux_generic_access_check(
if (path) {
/* Get the file context of the unit file */
- r = getfilecon(path, &fcon);
+ r = getfilecon_raw(path, &fcon);
if (r < 0) {
r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
goto finish;
@@ -254,7 +254,7 @@ int mac_selinux_generic_access_check(
tclass = "service";
} else {
- r = getcon(&fcon);
+ r = getcon_raw(&fcon);
if (r < 0) {
r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
goto finish;
diff --git a/src/core/selinux-setup.c b/src/core/selinux-setup.c
index a4678500e6..e5b457643b 100644
--- a/src/core/selinux-setup.c
+++ b/src/core/selinux-setup.c
@@ -34,6 +34,7 @@
#include "log.h"
#ifdef HAVE_SELINUX
+_printf_(2,3)
static int null_log(int type, const char *fmt, ...) {
return 0;
}
diff --git a/src/core/service.c b/src/core/service.c
index b790ec98be..292fe50de8 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -556,7 +556,7 @@ static int service_add_extras(Service *s) {
if (r < 0)
return r;
- r = unit_add_default_slice(UNIT(s), &s->cgroup_context);
+ r = unit_set_default_slice(UNIT(s));
if (r < 0)
return r;
@@ -767,7 +767,7 @@ static int service_load_pid_file(Service *s, bool may_warn) {
}
static int service_search_main_pid(Service *s) {
- pid_t pid;
+ pid_t pid = 0;
int r;
assert(s);
@@ -782,9 +782,9 @@ static int service_search_main_pid(Service *s) {
assert(s->main_pid <= 0);
- pid = unit_search_main_pid(UNIT(s));
- if (pid <= 0)
- return -ENOENT;
+ r = unit_search_main_pid(UNIT(s), &pid);
+ if (r < 0)
+ return r;
log_unit_debug(UNIT(s), "Main PID guessed: "PID_FMT, pid);
r = service_set_main_pid(s, pid);
@@ -860,7 +860,7 @@ static void service_set_state(Service *s, ServiceState state) {
/* For the inactive states unit_notify() will trim the cgroup,
* but for exit we have to do that ourselves... */
if (state == SERVICE_EXITED && UNIT(s)->manager->n_reloading <= 0)
- unit_destroy_cgroup_if_empty(UNIT(s));
+ unit_prune_cgroup(UNIT(s));
/* For remain_after_exit services, let's see if we can "release" the
* hold on the console, since unit_notify() only does that in case of
@@ -1269,7 +1269,7 @@ static int cgroup_good(Service *s) {
if (!UNIT(s)->cgroup_path)
return 0;
- r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path, true);
+ r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path);
if (r < 0)
return r;
@@ -1520,18 +1520,33 @@ fail:
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES);
}
+static bool service_good(Service *s) {
+ int main_pid_ok;
+ assert(s);
+
+ if (s->type == SERVICE_DBUS && !s->bus_name_good)
+ return false;
+
+ main_pid_ok = main_pid_good(s);
+ if (main_pid_ok > 0) /* It's alive */
+ return true;
+ if (main_pid_ok == 0) /* It's dead */
+ return false;
+
+ /* OK, we don't know anything about the main PID, maybe
+ * because there is none. Let's check the control group
+ * instead. */
+
+ return cgroup_good(s) != 0;
+}
+
static void service_enter_running(Service *s, ServiceResult f) {
- int main_pid_ok, cgroup_ok;
assert(s);
if (f != SERVICE_SUCCESS)
s->result = f;
- main_pid_ok = main_pid_good(s);
- cgroup_ok = cgroup_good(s);
-
- if ((main_pid_ok > 0 || (main_pid_ok < 0 && cgroup_ok != 0)) &&
- (s->bus_name_good || s->type != SERVICE_DBUS)) {
+ if (service_good(s)) {
/* If there are any queued up sd_notify()
* notifications, process them now */
@@ -2629,7 +2644,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
break;
}
} else
- service_search_main_pid(s);
+ (void) service_search_main_pid(s);
service_enter_start_post(s);
break;
@@ -2651,7 +2666,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
break;
}
} else
- service_search_main_pid(s);
+ (void) service_search_main_pid(s);
service_enter_running(s, SERVICE_SUCCESS);
break;
@@ -2659,7 +2674,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
case SERVICE_RELOAD:
if (f == SERVICE_SUCCESS) {
service_load_pid_file(s, true);
- service_search_main_pid(s);
+ (void) service_search_main_pid(s);
}
s->reload_result = f;
@@ -3214,7 +3229,6 @@ const UnitVTable service_vtable = {
.bus_name_owner_change = service_bus_name_owner_change,
- .bus_interface = "org.freedesktop.systemd1.Service",
.bus_vtable = bus_service_vtable,
.bus_set_property = bus_service_set_property,
.bus_commit_properties = bus_service_commit_properties,
diff --git a/src/core/slice.c b/src/core/slice.c
index 064eb5d933..b414462066 100644
--- a/src/core/slice.c
+++ b/src/core/slice.c
@@ -21,12 +21,13 @@
#include <errno.h>
-#include "unit.h"
-#include "slice.h"
#include "log.h"
-#include "dbus-slice.h"
+#include "strv.h"
#include "special.h"
#include "unit-name.h"
+#include "unit.h"
+#include "slice.h"
+#include "dbus-slice.h"
static const UnitActiveState state_translation_table[_SLICE_STATE_MAX] = {
[SLICE_DEAD] = UNIT_INACTIVE,
@@ -252,6 +253,40 @@ _pure_ static const char *slice_sub_state_to_string(Unit *u) {
return slice_state_to_string(SLICE(u)->state);
}
+static int slice_enumerate(Manager *m) {
+ Unit *u;
+ int r;
+
+ assert(m);
+
+ u = manager_get_unit(m, SPECIAL_ROOT_SLICE);
+ if (!u) {
+ u = unit_new(m, sizeof(Slice));
+ if (!u)
+ return log_oom();
+
+ r = unit_add_name(u, SPECIAL_ROOT_SLICE);
+ if (r < 0) {
+ unit_free(u);
+ return log_error_errno(r, "Failed to add -.slice name");
+ }
+ }
+
+ u->default_dependencies = false;
+ u->no_gc = true;
+ SLICE(u)->deserialized_state = SLICE_ACTIVE;
+
+ if (!u->description)
+ u->description = strdup("Root Slice");
+ if (!u->documentation)
+ (void) strv_extend(&u->documentation, "man:systemd.special(7)");
+
+ unit_add_to_load_queue(u);
+ unit_add_to_dbus_queue(u);
+
+ return 0;
+}
+
static const char* const slice_state_table[_SLICE_STATE_MAX] = {
[SLICE_DEAD] = "dead",
[SLICE_ACTIVE] = "active"
@@ -289,11 +324,12 @@ const UnitVTable slice_vtable = {
.active_state = slice_active_state,
.sub_state_to_string = slice_sub_state_to_string,
- .bus_interface = "org.freedesktop.systemd1.Slice",
.bus_vtable = bus_slice_vtable,
.bus_set_property = bus_slice_set_property,
.bus_commit_properties = bus_slice_commit_properties,
+ .enumerate = slice_enumerate,
+
.status_message_formats = {
.finished_start_job = {
[JOB_DONE] = "Created slice %s.",
diff --git a/src/core/snapshot.c b/src/core/snapshot.c
index 9518e21f36..336ff20f84 100644
--- a/src/core/snapshot.c
+++ b/src/core/snapshot.c
@@ -302,6 +302,5 @@ const UnitVTable snapshot_vtable = {
.active_state = snapshot_active_state,
.sub_state_to_string = snapshot_sub_state_to_string,
- .bus_interface = "org.freedesktop.systemd1.Snapshot",
.bus_vtable = bus_snapshot_vtable
};
diff --git a/src/core/socket.c b/src/core/socket.c
index a387057473..1014fad626 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -345,7 +345,7 @@ static int socket_add_extras(Socket *s) {
if (r < 0)
return r;
- r = unit_add_default_slice(u, &s->cgroup_context);
+ r = unit_set_default_slice(u);
if (r < 0)
return r;
}
@@ -839,7 +839,7 @@ static void socket_apply_socket_options(Socket *s, int fd) {
if (s->keep_alive_cnt) {
int value = s->keep_alive_cnt;
- if (setsockopt(fd, SOL_SOCKET, TCP_KEEPCNT, &value, sizeof(value)) < 0)
+ if (setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &value, sizeof(value)) < 0)
log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPCNT failed: %m");
}
@@ -2709,7 +2709,6 @@ const UnitVTable socket_vtable = {
.reset_failed = socket_reset_failed,
- .bus_interface = "org.freedesktop.systemd1.Socket",
.bus_vtable = bus_socket_vtable,
.bus_set_property = bus_socket_set_property,
.bus_commit_properties = bus_socket_commit_properties,
diff --git a/src/core/swap.c b/src/core/swap.c
index 0bc3827ff0..4f3ddc9f04 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -326,7 +326,7 @@ static int swap_load(Unit *u) {
if (r < 0)
return r;
- r = unit_add_default_slice(u, &s->cgroup_context);
+ r = unit_set_default_slice(u);
if (r < 0)
return r;
@@ -1485,7 +1485,6 @@ const UnitVTable swap_vtable = {
.reset_failed = swap_reset_failed,
- .bus_interface = "org.freedesktop.systemd1.Swap",
.bus_vtable = bus_swap_vtable,
.bus_set_property = bus_swap_set_property,
.bus_commit_properties = bus_swap_commit_properties,
diff --git a/src/core/target.c b/src/core/target.c
index b492a7c4c7..f714cb31c2 100644
--- a/src/core/target.c
+++ b/src/core/target.c
@@ -221,7 +221,6 @@ const UnitVTable target_vtable = {
.active_state = target_active_state,
.sub_state_to_string = target_sub_state_to_string,
- .bus_interface = "org.freedesktop.systemd1.Target",
.bus_vtable = bus_target_vtable,
.status_message_formats = {
diff --git a/src/core/timer.c b/src/core/timer.c
index 89758c6b19..eb6567bbfa 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -772,7 +772,6 @@ const UnitVTable timer_vtable = {
.reset_failed = timer_reset_failed,
.time_change = timer_time_change,
- .bus_interface = "org.freedesktop.systemd1.Timer",
.bus_vtable = bus_timer_vtable,
.bus_set_property = bus_timer_set_property,
diff --git a/src/core/unit.c b/src/core/unit.c
index 43a5ca1064..1aa0321b23 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -28,27 +28,28 @@
#include "sd-id128.h"
#include "sd-messages.h"
#include "set.h"
-#include "unit.h"
#include "macro.h"
#include "strv.h"
#include "path-util.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
#include "log.h"
-#include "unit-name.h"
-#include "dbus-unit.h"
-#include "special.h"
#include "cgroup-util.h"
#include "missing.h"
#include "mkdir.h"
#include "fileio-label.h"
-#include "bus-common-errors.h"
-#include "dbus.h"
-#include "execute.h"
-#include "dropin.h"
#include "formats-util.h"
#include "process-util.h"
+#include "virt.h"
+#include "bus-common-errors.h"
#include "bus-util.h"
+#include "dropin.h"
+#include "unit-name.h"
+#include "special.h"
+#include "unit.h"
+#include "load-fragment.h"
+#include "load-dropin.h"
+#include "dbus.h"
+#include "dbus-unit.h"
+#include "execute.h"
const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
[UNIT_SERVICE] = &service_vtable,
@@ -90,6 +91,7 @@ Unit *unit_new(Manager *m, size_t size) {
u->unit_file_state = _UNIT_FILE_STATE_INVALID;
u->unit_file_preset = -1;
u->on_failure_job_mode = JOB_REPLACE;
+ u->cgroup_inotify_wd = -1;
RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16);
@@ -405,17 +407,17 @@ static void unit_remove_transient(Unit *u) {
return;
if (u->fragment_path)
- unlink(u->fragment_path);
+ (void) unlink(u->fragment_path);
STRV_FOREACH(i, u->dropin_paths) {
_cleanup_free_ char *p = NULL;
int r;
- unlink(*i);
+ (void) unlink(*i);
r = path_get_parent(*i, &p);
if (r >= 0)
- rmdir(p);
+ (void) rmdir(p);
}
}
@@ -524,10 +526,7 @@ void unit_free(Unit *u) {
if (u->in_cgroup_queue)
LIST_REMOVE(cgroup_queue, u->manager->cgroup_queue, u);
- if (u->cgroup_path) {
- hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
- free(u->cgroup_path);
- }
+ unit_release_cgroup(u);
manager_update_failed_units(u->manager, u, false);
set_remove(u->manager->startup_units, u);
@@ -1122,7 +1121,7 @@ static int unit_add_target_dependencies(Unit *u) {
static int unit_add_slice_dependencies(Unit *u) {
assert(u);
- if (!unit_get_cgroup_context(u))
+ if (!UNIT_HAS_CGROUP_CONTEXT(u))
return 0;
if (UNIT_ISSET(u->slice))
@@ -1417,6 +1416,7 @@ int unit_start(Unit *u) {
assert(u);
+ /* Units that aren't loaded cannot be started */
if (u->load_state != UNIT_LOADED)
return -EINVAL;
@@ -1445,6 +1445,15 @@ int unit_start(Unit *u) {
return -EPROTO;
}
+ /* Units of types that aren't supported cannot be
+ * started. Note that we do this test only after the condition
+ * checks, so that we rather return condition check errors
+ * (which are usually not considered a true failure) than "not
+ * supported" errors (which are considered a failure).
+ */
+ if (!unit_supported(u))
+ return -EOPNOTSUPP;
+
/* Forward to the main object, if we aren't it. */
following = unit_following(u);
if (following) {
@@ -1452,9 +1461,6 @@ int unit_start(Unit *u) {
return unit_start(following);
}
- if (!unit_supported(u))
- return -EOPNOTSUPP;
-
/* If it is stopped, but we cannot start it, then fail */
if (!UNIT_VTABLE(u)->start)
return -EBADR;
@@ -1473,6 +1479,12 @@ int unit_start(Unit *u) {
bool unit_can_start(Unit *u) {
assert(u);
+ if (u->load_state != UNIT_LOADED)
+ return false;
+
+ if (!unit_supported(u))
+ return false;
+
return !!UNIT_VTABLE(u)->start;
}
@@ -1800,7 +1812,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
/* Make sure the cgroup is always removed when we become inactive */
if (UNIT_IS_INACTIVE_OR_FAILED(ns))
- unit_destroy_cgroup_if_empty(u);
+ unit_prune_cgroup(u);
/* Note that this doesn't apply to RemainAfterExit services exiting
* successfully, since there's no change of state in that case. Which is
@@ -1996,16 +2008,16 @@ int unit_watch_pid(Unit *u, pid_t pid) {
if (r < 0)
return r;
- r = hashmap_put(u->manager->watch_pids1, LONG_TO_PTR(pid), u);
+ r = hashmap_put(u->manager->watch_pids1, PID_TO_PTR(pid), u);
if (r == -EEXIST) {
r = hashmap_ensure_allocated(&u->manager->watch_pids2, NULL);
if (r < 0)
return r;
- r = hashmap_put(u->manager->watch_pids2, LONG_TO_PTR(pid), u);
+ r = hashmap_put(u->manager->watch_pids2, PID_TO_PTR(pid), u);
}
- q = set_put(u->pids, LONG_TO_PTR(pid));
+ q = set_put(u->pids, PID_TO_PTR(pid));
if (q < 0)
return q;
@@ -2016,81 +2028,18 @@ void unit_unwatch_pid(Unit *u, pid_t pid) {
assert(u);
assert(pid >= 1);
- hashmap_remove_value(u->manager->watch_pids1, LONG_TO_PTR(pid), u);
- hashmap_remove_value(u->manager->watch_pids2, LONG_TO_PTR(pid), u);
- set_remove(u->pids, LONG_TO_PTR(pid));
+ (void) hashmap_remove_value(u->manager->watch_pids1, PID_TO_PTR(pid), u);
+ (void) hashmap_remove_value(u->manager->watch_pids2, PID_TO_PTR(pid), u);
+ (void) set_remove(u->pids, PID_TO_PTR(pid));
}
void unit_unwatch_all_pids(Unit *u) {
assert(u);
while (!set_isempty(u->pids))
- unit_unwatch_pid(u, PTR_TO_LONG(set_first(u->pids)));
+ unit_unwatch_pid(u, PTR_TO_PID(set_first(u->pids)));
- set_free(u->pids);
- u->pids = NULL;
-}
-
-static int unit_watch_pids_in_path(Unit *u, const char *path) {
- _cleanup_closedir_ DIR *d = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- int ret = 0, r;
-
- assert(u);
- assert(path);
-
- /* Adds all PIDs from a specific cgroup path to the set of PIDs we watch. */
-
- r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, path, &f);
- if (r >= 0) {
- pid_t pid;
-
- while ((r = cg_read_pid(f, &pid)) > 0) {
- r = unit_watch_pid(u, pid);
- if (r < 0 && ret >= 0)
- ret = r;
- }
- if (r < 0 && ret >= 0)
- ret = r;
-
- } else if (ret >= 0)
- ret = r;
-
- r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, path, &d);
- if (r >= 0) {
- char *fn;
-
- while ((r = cg_read_subgroup(d, &fn)) > 0) {
- _cleanup_free_ char *p = NULL;
-
- p = strjoin(path, "/", fn, NULL);
- free(fn);
-
- if (!p)
- return -ENOMEM;
-
- r = unit_watch_pids_in_path(u, p);
- if (r < 0 && ret >= 0)
- ret = r;
- }
- if (r < 0 && ret >= 0)
- ret = r;
-
- } else if (ret >= 0)
- ret = r;
-
- return ret;
-}
-
-int unit_watch_all_pids(Unit *u) {
- assert(u);
-
- /* Adds all PIDs from our cgroup to the set of PIDs we watch */
-
- if (!u->cgroup_path)
- return -ENOENT;
-
- return unit_watch_pids_in_path(u, u->cgroup_path);
+ u->pids = set_free(u->pids);
}
void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2) {
@@ -2102,7 +2051,7 @@ void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2) {
/* Cleans dead PIDs from our list */
SET_FOREACH(e, u->pids, i) {
- pid_t pid = PTR_TO_LONG(e);
+ pid_t pid = PTR_TO_PID(e);
if (pid == except1 || pid == except2)
continue;
@@ -2399,39 +2348,49 @@ char *unit_dbus_path(Unit *u) {
return unit_dbus_path_from_name(u->id);
}
-char *unit_default_cgroup_path(Unit *u) {
- _cleanup_free_ char *escaped = NULL, *slice = NULL;
- int r;
-
+int unit_set_slice(Unit *u, Unit *slice) {
assert(u);
+ assert(slice);
- if (unit_has_name(u, SPECIAL_ROOT_SLICE))
- return strdup(u->manager->cgroup_root);
+ /* Sets the unit slice if it has not been set before. Is extra
+ * careful, to only allow this for units that actually have a
+ * cgroup context. Also, we don't allow to set this for slices
+ * (since the parent slice is derived from the name). Make
+ * sure the unit we set is actually a slice. */
- if (UNIT_ISSET(u->slice) && !unit_has_name(UNIT_DEREF(u->slice), SPECIAL_ROOT_SLICE)) {
- r = cg_slice_to_path(UNIT_DEREF(u->slice)->id, &slice);
- if (r < 0)
- return NULL;
- }
+ if (!UNIT_HAS_CGROUP_CONTEXT(u))
+ return -EOPNOTSUPP;
- escaped = cg_escape(u->id);
- if (!escaped)
- return NULL;
+ if (u->type == UNIT_SLICE)
+ return -EINVAL;
- if (slice)
- return strjoin(u->manager->cgroup_root, "/", slice, "/", escaped, NULL);
- else
- return strjoin(u->manager->cgroup_root, "/", escaped, NULL);
+ if (unit_active_state(u) != UNIT_INACTIVE)
+ return -EBUSY;
+
+ if (slice->type != UNIT_SLICE)
+ return -EINVAL;
+
+ if (unit_has_name(u, SPECIAL_INIT_SCOPE) &&
+ !unit_has_name(slice, SPECIAL_ROOT_SLICE))
+ return -EPERM;
+
+ if (UNIT_DEREF(u->slice) == slice)
+ return 0;
+
+ if (UNIT_ISSET(u->slice))
+ return -EBUSY;
+
+ unit_ref_set(&u->slice, slice);
+ return 1;
}
-int unit_add_default_slice(Unit *u, CGroupContext *c) {
+int unit_set_default_slice(Unit *u) {
_cleanup_free_ char *b = NULL;
const char *slice_name;
Unit *slice;
int r;
assert(u);
- assert(c);
if (UNIT_ISSET(u->slice))
return 0;
@@ -2463,7 +2422,7 @@ int unit_add_default_slice(Unit *u, CGroupContext *c) {
slice_name = b;
} else
slice_name =
- u->manager->running_as == MANAGER_SYSTEM
+ u->manager->running_as == MANAGER_SYSTEM && !unit_has_name(u, SPECIAL_INIT_SCOPE)
? SPECIAL_SYSTEM_SLICE
: SPECIAL_ROOT_SLICE;
@@ -2471,8 +2430,7 @@ int unit_add_default_slice(Unit *u, CGroupContext *c) {
if (r < 0)
return r;
- unit_ref_set(&u->slice, slice);
- return 0;
+ return unit_set_slice(u, slice);
}
const char *unit_slice_name(Unit *u) {
@@ -2673,40 +2631,6 @@ void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) {
fprintf(f, "%s=%s\n", key, value);
}
-static int unit_set_cgroup_path(Unit *u, const char *path) {
- _cleanup_free_ char *p = NULL;
- int r;
-
- assert(u);
-
- if (path) {
- p = strdup(path);
- if (!p)
- return -ENOMEM;
- } else
- p = NULL;
-
- if (streq_ptr(u->cgroup_path, p))
- return 0;
-
- if (p) {
- r = hashmap_put(u->manager->cgroup_unit, p, u);
- if (r < 0)
- return r;
- }
-
- if (u->cgroup_path) {
- log_unit_debug(u, "Changing cgroup path from %s to %s.", u->cgroup_path, strna(p));
- hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
- free(u->cgroup_path);
- }
-
- u->cgroup_path = p;
- p = NULL;
-
- return 0;
-}
-
int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
ExecRuntime **rt = NULL;
size_t offset;
@@ -2837,6 +2761,8 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
if (r < 0)
log_unit_debug_errno(u, r, "Failed to set cgroup path %s, ignoring: %m", v);
+ (void) unit_watch_cgroup(u);
+
continue;
} else if (streq(l, "cgroup-realized")) {
int b;
@@ -3080,13 +3006,13 @@ static Set *unit_pid_set(pid_t main_pid, pid_t control_pid) {
/* Exclude the main/control pids from being killed via the cgroup */
if (main_pid > 0) {
- r = set_put(pid_set, LONG_TO_PTR(main_pid));
+ r = set_put(pid_set, PID_TO_PTR(main_pid));
if (r < 0)
goto fail;
}
if (control_pid > 0) {
- r = set_put(pid_set, LONG_TO_PTR(control_pid));
+ r = set_put(pid_set, PID_TO_PTR(control_pid));
if (r < 0)
goto fail;
}
@@ -3108,17 +3034,17 @@ int unit_kill_common(
int r = 0;
- if (who == KILL_MAIN && main_pid <= 0) {
+ if (who == KILL_MAIN) {
if (main_pid < 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type));
- else
+ else if (main_pid == 0)
return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill");
}
- if (who == KILL_CONTROL && control_pid <= 0) {
+ if (who == KILL_CONTROL) {
if (control_pid < 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type));
- else
+ else if (control_pid == 0)
return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
}
@@ -3141,7 +3067,7 @@ int unit_kill_common(
if (!pid_set)
return -ENOMEM;
- q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
+ q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, false, false, pid_set);
if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT)
r = q;
}
@@ -3317,6 +3243,8 @@ ExecRuntime *unit_get_exec_runtime(Unit *u) {
}
static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient, char **dir) {
+ assert(u);
+
if (u->manager->running_as == MANAGER_USER) {
int r;
@@ -3324,9 +3252,9 @@ static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient,
r = user_config_home(dir);
else
r = user_runtime_dir(dir);
-
if (r == 0)
return -ENOENT;
+
return r;
}
@@ -3340,8 +3268,7 @@ static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient,
return 0;
}
-static int unit_drop_in_file(Unit *u,
- UnitSetPropertiesMode mode, const char *name, char **p, char **q) {
+static int unit_drop_in_file(Unit *u, UnitSetPropertiesMode mode, const char *name, char **p, char **q) {
_cleanup_free_ char *dir = NULL;
int r;
@@ -3475,40 +3402,17 @@ int unit_remove_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name) {
}
int unit_make_transient(Unit *u) {
- int r;
-
assert(u);
+ if (!UNIT_VTABLE(u)->can_transient)
+ return -EOPNOTSUPP;
+
u->load_state = UNIT_STUB;
u->load_error = 0;
u->transient = true;
+ u->fragment_path = mfree(u->fragment_path);
- free(u->fragment_path);
- u->fragment_path = NULL;
-
- if (u->manager->running_as == MANAGER_USER) {
- _cleanup_free_ char *c = NULL;
-
- r = user_runtime_dir(&c);
- if (r < 0)
- return r;
- if (r == 0)
- return -ENOENT;
-
- u->fragment_path = strjoin(c, "/", u->id, NULL);
- if (!u->fragment_path)
- return -ENOMEM;
-
- mkdir_p(c, 0755);
- } else {
- u->fragment_path = strappend("/run/systemd/system/", u->id);
- if (!u->fragment_path)
- return -ENOMEM;
-
- mkdir_p("/run/systemd/system", 0755);
- }
-
- return write_string_file_atomic_label(u->fragment_path, "# Transient stub");
+ return 0;
}
int unit_kill_context(
@@ -3519,7 +3423,8 @@ int unit_kill_context(
pid_t control_pid,
bool main_pid_alien) {
- int sig, wait_for_exit = false, r;
+ bool wait_for_exit = false;
+ int sig, r;
assert(u);
assert(c);
@@ -3548,13 +3453,13 @@ int unit_kill_context(
_cleanup_free_ char *comm = NULL;
get_process_comm(main_pid, &comm);
- log_unit_warning_errno(u, r, "Failed to kill main process " PID_FMT " (%s): %m", main_pid, strna(comm));
+ log_unit_warning_errno(u, r, "Failed to kill main process " PID_FMT " (%s), ignoring: %m", main_pid, strna(comm));
} else {
if (!main_pid_alien)
wait_for_exit = true;
- if (c->send_sighup && k != KILL_KILL)
- kill(main_pid, SIGHUP);
+ if (c->send_sighup && k == KILL_TERMINATE)
+ (void) kill(main_pid, SIGHUP);
}
}
@@ -3565,16 +3470,17 @@ int unit_kill_context(
_cleanup_free_ char *comm = NULL;
get_process_comm(control_pid, &comm);
- log_unit_warning_errno(u, r, "Failed to kill control process " PID_FMT " (%s): %m", control_pid, strna(comm));
+ log_unit_warning_errno(u, r, "Failed to kill control process " PID_FMT " (%s), ignoring: %m", control_pid, strna(comm));
} else {
wait_for_exit = true;
- if (c->send_sighup && k != KILL_KILL)
- kill(control_pid, SIGHUP);
+ if (c->send_sighup && k == KILL_TERMINATE)
+ (void) kill(control_pid, SIGHUP);
}
}
- if ((c->kill_mode == KILL_CONTROL_GROUP || (c->kill_mode == KILL_MIXED && k == KILL_KILL)) && u->cgroup_path) {
+ if (u->cgroup_path &&
+ (c->kill_mode == KILL_CONTROL_GROUP || (c->kill_mode == KILL_MIXED && k == KILL_KILL))) {
_cleanup_set_free_ Set *pid_set = NULL;
/* Exclude the main/control pids from being killed via the cgroup */
@@ -3582,21 +3488,30 @@ int unit_kill_context(
if (!pid_set)
return -ENOMEM;
- r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, sig, true, true, false, pid_set);
+ r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, sig, true, k != KILL_TERMINATE, false, pid_set);
if (r < 0) {
if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
- log_unit_warning_errno(u, r, "Failed to kill control group: %m");
- } else if (r > 0) {
+ log_unit_warning_errno(u, r, "Failed to kill control group %s, ignoring: %m", u->cgroup_path);
- /* FIXME: For now, we will not wait for the
- * cgroup members to die, simply because
- * cgroup notification is unreliable. It
- * doesn't work at all in containers, and
- * outside of containers it can be confused
- * easily by leaving directories in the
- * cgroup. */
+ } else if (r > 0) {
- /* wait_for_exit = true; */
+ /* FIXME: For now, on the legacy hierarchy, we
+ * will not wait for the cgroup members to die
+ * if we are running in a container or if this
+ * is a delegation unit, simply because cgroup
+ * notification is unreliable in these
+ * cases. It doesn't work at all in
+ * containers, and outside of containers it
+ * can be confused easily by left-over
+ * directories in the cgroup -- which however
+ * should not exist in non-delegated units. On
+ * the unified hierarchy that's different,
+ * there we get proper events. Hence rely on
+ * them.*/
+
+ if (cg_unified() > 0 ||
+ (detect_container(NULL) == 0 && !unit_cgroup_delegate(u)))
+ wait_for_exit = true;
if (c->send_sighup && k != KILL_KILL) {
set_free(pid_set);
diff --git a/src/core/unit.h b/src/core/unit.h
index 9df5a7e6fb..3c7684411b 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -184,9 +184,10 @@ struct Unit {
/* Counterparts in the cgroup filesystem */
char *cgroup_path;
- CGroupControllerMask cgroup_realized_mask;
- CGroupControllerMask cgroup_subtree_mask;
- CGroupControllerMask cgroup_members_mask;
+ CGroupMask cgroup_realized_mask;
+ CGroupMask cgroup_subtree_mask;
+ CGroupMask cgroup_members_mask;
+ int cgroup_inotify_wd;
/* How to start OnFailure units */
JobMode on_failure_job_mode;
@@ -404,9 +405,6 @@ struct UnitVTable {
* of this type will immediately fail. */
bool (*supported)(void);
- /* The interface name */
- const char *bus_interface;
-
/* The bus vtable */
const sd_bus_vtable *bus_vtable;
@@ -442,6 +440,10 @@ extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX];
/* For casting the various unit types into a unit */
#define UNIT(u) (&(u)->meta)
+#define UNIT_HAS_EXEC_CONTEXT(u) (UNIT_VTABLE(u)->exec_context_offset > 0)
+#define UNIT_HAS_CGROUP_CONTEXT(u) (UNIT_VTABLE(u)->cgroup_context_offset > 0)
+#define UNIT_HAS_KILL_CONTEXT(u) (UNIT_VTABLE(u)->kill_context_offset > 0)
+
#define UNIT_TRIGGER(u) ((Unit*) set_first((u)->dependencies[UNIT_TRIGGERS]))
DEFINE_CAST(SERVICE, Service);
@@ -493,7 +495,8 @@ int unit_load_fragment_and_dropin(Unit *u);
int unit_load_fragment_and_dropin_optional(Unit *u);
int unit_load(Unit *unit);
-int unit_add_default_slice(Unit *u, CGroupContext *c);
+int unit_set_slice(Unit *u, Unit *slice);
+int unit_set_default_slice(Unit *u);
const char *unit_description(Unit *u) _pure_;
@@ -520,7 +523,6 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
int unit_watch_pid(Unit *u, pid_t pid);
void unit_unwatch_pid(Unit *u, pid_t pid);
-int unit_watch_all_pids(Unit *u);
void unit_unwatch_all_pids(Unit *u);
void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2);
@@ -565,8 +567,6 @@ bool unit_active_or_pending(Unit *u);
int unit_add_default_target_dependency(Unit *u, Unit *target);
-char *unit_default_cgroup_path(Unit *u);
-
void unit_start_on_failure(Unit *u);
void unit_trigger_notify(Unit *u);
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index 0a34f86be7..cf25d9847f 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -666,6 +666,7 @@ static int enumerate_partitions(dev_t devnum) {
first = udev_enumerate_get_list_entry(e);
udev_list_entry_foreach(item, first) {
_cleanup_udev_device_unref_ struct udev_device *q;
+ unsigned long long flags;
const char *stype, *subnode;
sd_id128_t type_id;
blkid_partition pp;
@@ -705,10 +706,10 @@ static int enumerate_partitions(dev_t devnum) {
if (sd_id128_from_string(stype, &type_id) < 0)
continue;
+ flags = blkid_partition_get_flags(pp);
+
if (sd_id128_equal(type_id, GPT_SWAP)) {
- unsigned long long flags;
- flags = blkid_partition_get_flags(pp);
if (flags & GPT_FLAG_NO_AUTO)
continue;
@@ -727,6 +728,10 @@ static int enumerate_partitions(dev_t devnum) {
if (boot && nr >= boot_nr)
continue;
+ /* Note that we do not honour the "no-auto"
+ * flag for the ESP, as it is often unset, to
+ * hide it from Windows. */
+
boot_nr = nr;
r = free_and_strdup(&boot, subnode);
@@ -734,9 +739,7 @@ static int enumerate_partitions(dev_t devnum) {
return log_oom();
} else if (sd_id128_equal(type_id, GPT_HOME)) {
- unsigned long long flags;
- flags = blkid_partition_get_flags(pp);
if (flags & GPT_FLAG_NO_AUTO)
continue;
@@ -752,9 +755,7 @@ static int enumerate_partitions(dev_t devnum) {
return log_oom();
} else if (sd_id128_equal(type_id, GPT_SRV)) {
- unsigned long long flags;
- flags = blkid_partition_get_flags(pp);
if (flags & GPT_FLAG_NO_AUTO)
continue;
@@ -799,6 +800,10 @@ static int get_block_device(const char *path, dev_t *dev) {
assert(path);
assert(dev);
+ /* Get's the block device directly backing a file system. If
+ * the block device is encrypted, returns the device mapper
+ * block device. */
+
if (lstat(path, &st))
return -errno;
@@ -816,6 +821,77 @@ static int get_block_device(const char *path, dev_t *dev) {
return 0;
}
+static int get_block_device_harder(const char *path, dev_t *dev) {
+ _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_free_ char *p = NULL, *t = NULL;
+ struct dirent *de, *found = NULL;
+ const char *q;
+ unsigned maj, min;
+ dev_t dt;
+ int r;
+
+ assert(path);
+ assert(dev);
+
+ /* Gets the backing block device for a file system, and
+ * handles LUKS encrypted file systems, looking for its
+ * immediate parent, if there is one. */
+
+ r = get_block_device(path, &dt);
+ if (r <= 0)
+ return r;
+
+ if (asprintf(&p, "/sys/dev/block/%u:%u/slaves", major(dt), minor(dt)) < 0)
+ return -ENOMEM;
+
+ d = opendir(p);
+ if (!d) {
+ if (errno == ENOENT)
+ goto fallback;
+
+ return -errno;
+ }
+
+ FOREACH_DIRENT_ALL(de, d, return -errno) {
+
+ if (STR_IN_SET(de->d_name, ".", ".."))
+ continue;
+
+ if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
+ continue;
+
+ if (found) /* Don't try to support multiple backing block devices */
+ goto fallback;
+
+ found = de;
+ break;
+ }
+
+ if (!found)
+ goto fallback;
+
+ q = strjoina(p, "/", found->d_name, "/dev");
+
+ r = read_one_line_file(q, &t);
+ if (r == -ENOENT)
+ goto fallback;
+ if (r < 0)
+ return r;
+
+ if (sscanf(t, "%u:%u", &maj, &min) != 2)
+ return -EINVAL;
+
+ if (maj == 0)
+ goto fallback;
+
+ *dev = makedev(maj, min);
+ return 1;
+
+fallback:
+ *dev = dt;
+ return 1;
+}
+
static int parse_proc_cmdline_item(const char *key, const char *value) {
int r;
@@ -883,11 +959,11 @@ static int add_mounts(void) {
dev_t devno;
int r;
- r = get_block_device("/", &devno);
+ r = get_block_device_harder("/", &devno);
if (r < 0)
return log_error_errno(r, "Failed to determine block device of root file system: %m");
else if (r == 0) {
- r = get_block_device("/usr", &devno);
+ r = get_block_device_harder("/usr", &devno);
if (r < 0)
return log_error_errno(r, "Failed to determine block device of /usr file system: %m");
else if (r == 0) {
diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c
index a78516c8b5..c423be3767 100644
--- a/src/hostname/hostnamed.c
+++ b/src/hostname/hostnamed.c
@@ -434,6 +434,7 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *
m,
CAP_SYS_ADMIN,
"org.freedesktop.hostname1.set-hostname",
+ NULL,
interactive,
UID_INVALID,
&c->polkit_registry,
@@ -486,6 +487,7 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_
m,
CAP_SYS_ADMIN,
"org.freedesktop.hostname1.set-static-hostname",
+ NULL,
interactive,
UID_INVALID,
&c->polkit_registry,
@@ -557,6 +559,7 @@ static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_mess
m,
CAP_SYS_ADMIN,
prop == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : "org.freedesktop.hostname1.set-machine-info",
+ NULL,
interactive,
UID_INVALID,
&c->polkit_registry,
diff --git a/src/import/importd.c b/src/import/importd.c
index 8b508eaeec..ffff94ee72 100644
--- a/src/import/importd.c
+++ b/src/import/importd.c
@@ -735,6 +735,7 @@ static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
msg,
CAP_SYS_ADMIN,
"org.freedesktop.import1.import",
+ NULL,
false,
UID_INVALID,
&m->polkit_registry,
@@ -799,6 +800,7 @@ static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
msg,
CAP_SYS_ADMIN,
"org.freedesktop.import1.export",
+ NULL,
false,
UID_INVALID,
&m->polkit_registry,
@@ -864,6 +866,7 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er
msg,
CAP_SYS_ADMIN,
"org.freedesktop.import1.pull",
+ NULL,
false,
UID_INVALID,
&m->polkit_registry,
@@ -945,6 +948,7 @@ static int method_pull_dkr(sd_bus_message *msg, void *userdata, sd_bus_error *er
msg,
CAP_SYS_ADMIN,
"org.freedesktop.import1.pull",
+ NULL,
false,
UID_INVALID,
&m->polkit_registry,
@@ -1079,6 +1083,7 @@ static int method_cancel(sd_bus_message *msg, void *userdata, sd_bus_error *erro
msg,
CAP_SYS_ADMIN,
"org.freedesktop.import1.pull",
+ NULL,
false,
UID_INVALID,
&t->manager->polkit_registry,
@@ -1108,6 +1113,7 @@ static int method_cancel_transfer(sd_bus_message *msg, void *userdata, sd_bus_er
msg,
CAP_SYS_ADMIN,
"org.freedesktop.import1.pull",
+ NULL,
false,
UID_INVALID,
&m->polkit_registry,
diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c
index e5be7f7766..51fe3aa50a 100644
--- a/src/journal/journald-kmsg.c
+++ b/src/journal/journald-kmsg.c
@@ -190,7 +190,7 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) {
for (j = 0; l > 0 && j < N_IOVEC_KERNEL_FIELDS; j++) {
char *m;
- /* Meta data fields attached */
+ /* Metadata fields attached */
if (*k != ' ')
break;
diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h
index 7c60ef123c..df6f882af5 100644
--- a/src/libsystemd-network/dhcp-internal.h
+++ b/src/libsystemd-network/dhcp-internal.h
@@ -45,10 +45,10 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_
uint8_t code, size_t optlen, const void *optval);
typedef int (*dhcp_option_cb_t)(uint8_t code, uint8_t len,
- const uint8_t *option, void *user_data);
+ const void *option, void *userdata);
int dhcp_option_parse(DHCPMessage *message, size_t len,
- dhcp_option_cb_t cb, void *user_data);
+ dhcp_option_cb_t cb, void *userdata);
int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
uint8_t type, uint16_t arp_type, size_t optlen,
diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h
index 5a3fcddb1b..c6b97ca8f7 100644
--- a/src/libsystemd-network/dhcp-lease-internal.h
+++ b/src/libsystemd-network/dhcp-lease-internal.h
@@ -25,7 +25,6 @@
#include <stdint.h>
#include <linux/if_packet.h>
-#include "refcnt.h"
#include "util.h"
#include "list.h"
@@ -48,54 +47,62 @@ struct sd_dhcp_raw_option {
};
struct sd_dhcp_lease {
- RefCount n_ref;
+ unsigned n_ref;
- int32_t time_offset;
+ /* each 0 if unset */
uint32_t t1;
uint32_t t2;
uint32_t lifetime;
- uint32_t mtu_aging_timeout;
+
+ /* each 0 if unset */
be32_t address;
be32_t server_address;
- be32_t subnet_mask;
be32_t router;
be32_t next_server;
+
+ bool have_subnet_mask;
+ be32_t subnet_mask;
+
+ bool have_broadcast;
be32_t broadcast;
+
struct in_addr *dns;
size_t dns_size;
+
struct in_addr *ntp;
size_t ntp_size;
- struct in_addr *policy_filter;
- size_t policy_filter_size;
+
struct sd_dhcp_route *static_route;
- size_t static_route_size;
- size_t static_route_allocated;
- uint16_t boot_file_size;
- uint16_t mdr;
- uint16_t mtu;
- uint8_t ttl;
- bool ip_forward;
- bool ip_forward_non_local;
+ size_t static_route_size, static_route_allocated;
+
+ uint16_t mtu; /* 0 if unset */
+
char *domainname;
char *hostname;
char *root_path;
- uint8_t *client_id;
+
+ void *client_id;
size_t client_id_len;
- uint8_t *vendor_specific;
+
+ void *vendor_specific;
size_t vendor_specific_len;
+
+ char *timezone;
+
LIST_HEAD(struct sd_dhcp_raw_option, private_options);
};
int dhcp_lease_new(sd_dhcp_lease **ret);
-int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
- void *user_data);
-int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag,
- const uint8_t *data, uint8_t len);
+
+int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata);
+int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len);
int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease);
-int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const uint8_t *client_id,
- size_t client_id_len);
+int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len);
+
+int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file);
+int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file);
DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_lease*, sd_dhcp_lease_unref);
#define _cleanup_dhcp_lease_unref_ _cleanup_(sd_dhcp_lease_unrefp)
diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c
index b6110c5f16..36be7d54ed 100644
--- a/src/libsystemd-network/dhcp-option.c
+++ b/src/libsystemd-network/dhcp-option.c
@@ -140,7 +140,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload,
uint8_t *message_type, dhcp_option_cb_t cb,
- void *user_data) {
+ void *userdata) {
uint8_t code, len;
size_t offset = 0;
@@ -199,7 +199,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
return -EINVAL;
if (cb)
- cb(code, len, &options[offset], user_data);
+ cb(code, len, &options[offset], userdata);
offset += len;
@@ -214,7 +214,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
}
int dhcp_option_parse(DHCPMessage *message, size_t len,
- dhcp_option_cb_t cb, void *user_data) {
+ dhcp_option_cb_t cb, void *userdata) {
uint8_t overload = 0;
uint8_t message_type = 0;
int r;
@@ -228,20 +228,20 @@ int dhcp_option_parse(DHCPMessage *message, size_t len,
len -= sizeof(DHCPMessage);
r = parse_options(message->options, len, &overload, &message_type,
- cb, user_data);
+ cb, userdata);
if (r < 0)
return r;
if (overload & DHCP_OVERLOAD_FILE) {
r = parse_options(message->file, sizeof(message->file),
- NULL, &message_type, cb, user_data);
+ NULL, &message_type, cb, userdata);
if (r < 0)
return r;
}
if (overload & DHCP_OVERLOAD_SNAME) {
r = parse_options(message->sname, sizeof(message->sname),
- NULL, &message_type, cb, user_data);
+ NULL, &message_type, cb, userdata);
if (r < 0)
return r;
}
diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h
index 4308723f91..88a81d2866 100644
--- a/src/libsystemd-network/dhcp-protocol.h
+++ b/src/libsystemd-network/dhcp-protocol.h
@@ -137,6 +137,8 @@ enum {
DHCP_OPTION_REBINDING_T2_TIME = 59,
DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60,
DHCP_OPTION_CLIENT_IDENTIFIER = 61,
+ DHCP_OPTION_NEW_POSIX_TIMEZONE = 100,
+ DHCP_OPTION_NEW_TZDB_TIMEZONE = 101,
DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121,
DHCP_OPTION_PRIVATE_BASE = 224,
DHCP_OPTION_PRIVATE_LAST = 254,
diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h
index 58750c4418..5dc3c7aa26 100644
--- a/src/libsystemd-network/dhcp-server-internal.h
+++ b/src/libsystemd-network/dhcp-server-internal.h
@@ -26,7 +26,6 @@
#include "sd-dhcp-server.h"
#include "hashmap.h"
-#include "refcnt.h"
#include "util.h"
#include "log.h"
@@ -34,7 +33,7 @@
typedef struct DHCPClientId {
size_t length;
- uint8_t *data;
+ void *data;
} DHCPClientId;
typedef struct DHCPLease {
@@ -47,7 +46,7 @@ typedef struct DHCPLease {
} DHCPLease;
struct sd_dhcp_server {
- RefCount n_ref;
+ unsigned n_ref;
sd_event *event;
int event_priority;
@@ -55,15 +54,23 @@ struct sd_dhcp_server {
int fd;
int fd_raw;
- int index;
+ int ifindex;
be32_t address;
be32_t netmask;
- be32_t pool_start;
- size_t pool_size;
- size_t next_offer;
+ be32_t subnet;
+ uint32_t pool_offset;
+ uint32_t pool_size;
+
+ char *timezone;
+
+ struct in_addr *ntp, *dns;
+ unsigned n_ntp, n_dns;
Hashmap *leases_by_client_id;
DHCPLease **bound_leases;
+ DHCPLease invalid_lease;
+
+ uint32_t max_lease_time, default_lease_time;
};
typedef struct DHCPRequest {
@@ -75,7 +82,7 @@ typedef struct DHCPRequest {
size_t max_optlen;
be32_t server_id;
be32_t requested_ip;
- int lifetime;
+ uint32_t lifetime;
} DHCPRequest;
DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_server*, sd_dhcp_server_unref);
diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h
index 037f580eb6..4edecf7711 100644
--- a/src/libsystemd-network/dhcp6-lease-internal.h
+++ b/src/libsystemd-network/dhcp6-lease-internal.h
@@ -24,13 +24,11 @@
#include <stdint.h>
-#include "refcnt.h"
-
#include "sd-dhcp6-lease.h"
#include "dhcp6-internal.h"
struct sd_dhcp6_lease {
- RefCount n_ref;
+ unsigned n_ref;
uint8_t *serverid;
size_t serverid_len;
diff --git a/src/libsystemd-network/dhcp6-network.c b/src/libsystemd-network/dhcp6-network.c
index fe56c10273..187975364b 100644
--- a/src/libsystemd-network/dhcp6-network.c
+++ b/src/libsystemd-network/dhcp6-network.c
@@ -41,8 +41,7 @@
{ { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
-int dhcp_network_icmp6_bind_router_solicitation(int index)
-{
+int dhcp_network_icmp6_bind_router_solicitation(int index) {
struct icmp6_filter filter = { };
struct ipv6_mreq mreq = {
.ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
@@ -92,8 +91,7 @@ int dhcp_network_icmp6_bind_router_solicitation(int index)
return r;
}
-int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr)
-{
+int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
struct sockaddr_in6 dst = {
.sin6_family = AF_INET6,
.sin6_addr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c
index 2fa4d5fac8..f41bebced0 100644
--- a/src/libsystemd-network/dhcp6-option.c
+++ b/src/libsystemd-network/dhcp6-option.c
@@ -338,9 +338,7 @@ int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
return count;
}
-int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen,
- char ***str_arr)
-{
+int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char ***str_arr) {
size_t pos = 0, idx = 0;
_cleanup_free_ char **names = NULL;
int r;
diff --git a/src/libsystemd-network/lldp-internal.c b/src/libsystemd-network/lldp-internal.c
index 0f354461f7..3c04898e92 100644
--- a/src/libsystemd-network/lldp-internal.c
+++ b/src/libsystemd-network/lldp-internal.c
@@ -374,9 +374,8 @@ int lldp_mib_add_objects(Prioq *by_expiry,
}
/* Admission Control: Can this port attached to the existing chassis ? */
- if (REFCNT_GET(c->n_ref) >= LLDP_MIB_MAX_PORT_PER_CHASSIS) {
- log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...",
- REFCNT_GET(c->n_ref));
+ if (c->n_ref >= LLDP_MIB_MAX_PORT_PER_CHASSIS) {
+ log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...", c->n_ref);
c = NULL;
goto drop;
@@ -394,7 +393,7 @@ int lldp_mib_add_objects(Prioq *by_expiry,
/* Attach new port to chassis */
LIST_PREPEND(port, c->ports, p);
- REFCNT_INC(c->n_ref);
+ c->n_ref ++;
p = NULL;
c = NULL;
@@ -424,7 +423,8 @@ void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p) {
lldp_neighbour_port_free(p);
/* Drop the Chassis if no port is attached */
- if (REFCNT_DEC(c->n_ref) <= 1) {
+ c->n_ref --;
+ if (c->n_ref <= 1) {
hashmap_remove(c->neighbour_mib, &c->chassis_id);
lldp_chassis_free(c);
}
@@ -486,7 +486,7 @@ void lldp_chassis_free(lldp_chassis *c) {
if (!c)
return;
- if (REFCNT_GET(c->n_ref) > 1)
+ if (c->n_ref > 1)
return;
free(c->chassis_id.data);
@@ -513,7 +513,7 @@ int lldp_chassis_new(tlv_packet *tlv,
if (!c)
return -ENOMEM;
- c->n_ref = REFCNT_INIT;
+ c->n_ref = 1;
c->chassis_id.type = type;
c->chassis_id.length = length;
diff --git a/src/libsystemd-network/lldp-internal.h b/src/libsystemd-network/lldp-internal.h
index 8e09ee8f3a..f4eadbb87e 100644
--- a/src/libsystemd-network/lldp-internal.h
+++ b/src/libsystemd-network/lldp-internal.h
@@ -24,7 +24,6 @@
#include "log.h"
#include "list.h"
-#include "refcnt.h"
#include "lldp-tlv.h"
#include "prioq.h"
@@ -63,7 +62,7 @@ struct lldp_chassis_id {
};
struct lldp_chassis {
- RefCount n_ref;
+ unsigned n_ref;
lldp_chassis_id chassis_id;
diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c
index d8357c687e..26bd4088d9 100644
--- a/src/libsystemd-network/network-internal.c
+++ b/src/libsystemd-network/network-internal.c
@@ -525,7 +525,7 @@ int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t
return 0;
}
-int serialize_dhcp_option(FILE *f, const char *key, const uint8_t *data, size_t size) {
+int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size) {
_cleanup_free_ char *hex_buf = NULL;
assert(f);
@@ -541,7 +541,7 @@ int serialize_dhcp_option(FILE *f, const char *key, const uint8_t *data, size_t
return 0;
}
-int deserialize_dhcp_option(uint8_t **data, size_t *data_len, const char *string) {
+int deserialize_dhcp_option(void **data, size_t *data_len, const char *string) {
assert(data);
assert(data_len);
assert(string);
diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h
index dca82646ce..d5d4ef42f2 100644
--- a/src/libsystemd-network/network-internal.h
+++ b/src/libsystemd-network/network-internal.h
@@ -77,5 +77,5 @@ struct sd_dhcp_route;
void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *routes, size_t size);
int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string);
-int serialize_dhcp_option(FILE *f, const char *key, const uint8_t *data, size_t size);
-int deserialize_dhcp_option(uint8_t **data, size_t *data_len, const char *string);
+int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size);
+int deserialize_dhcp_option(void **data, size_t *data_len, const char *string);
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index 46104afded..c12768cf0e 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -27,7 +27,6 @@
#include <sys/ioctl.h>
#include "util.h"
-#include "refcnt.h"
#include "random-util.h"
#include "async.h"
@@ -41,7 +40,7 @@
#define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
struct sd_dhcp_client {
- RefCount n_ref;
+ unsigned n_ref;
DHCPState state;
sd_event *event;
@@ -106,7 +105,6 @@ static const uint8_t default_req_opts[] = {
DHCP_OPTION_HOST_NAME,
DHCP_OPTION_DOMAIN_NAME,
DHCP_OPTION_DOMAIN_NAME_SERVER,
- DHCP_OPTION_NTP_SERVER,
};
static int client_receive_message_raw(sd_event_source *s, int fd,
@@ -377,8 +375,7 @@ static int client_initialize(sd_dhcp_client *client) {
client->state = DHCP_STATE_INIT;
client->xid = 0;
- if (client->lease)
- client->lease = sd_dhcp_lease_unref(client->lease);
+ client->lease = sd_dhcp_lease_unref(client->lease);
return 0;
}
@@ -1055,18 +1052,16 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
}
lease->next_server = offer->siaddr;
-
lease->address = offer->yiaddr;
- if (lease->address == INADDR_ANY ||
- lease->server_address == INADDR_ANY ||
+ if (lease->address == 0 ||
+ lease->server_address == 0 ||
lease->lifetime == 0) {
- log_dhcp_client(client, "received lease lacks address, server "
- "address or lease lifetime, ignoring");
+ log_dhcp_client(client, "received lease lacks address, server address or lease lifetime, ignoring");
return -ENOMSG;
}
- if (lease->subnet_mask == INADDR_ANY) {
+ if (!lease->have_subnet_mask) {
r = dhcp_lease_set_default_subnet_mask(lease);
if (r < 0) {
log_dhcp_client(client, "received lease lacks subnet "
@@ -1168,13 +1163,17 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
return r;
}
-static uint64_t client_compute_timeout(sd_dhcp_client *client,
- uint32_t lifetime, double factor) {
+static uint64_t client_compute_timeout(sd_dhcp_client *client, uint32_t lifetime, double factor) {
assert(client);
assert(client->request_sent);
- assert(lifetime);
+ assert(lifetime > 0);
- return client->request_sent + ((lifetime - 3) * USEC_PER_SEC * factor) +
+ if (lifetime > 3)
+ lifetime -= 3;
+ else
+ lifetime = 0;
+
+ return client->request_sent + (lifetime * USEC_PER_SEC * factor) +
+ (random_u32() & 0x1fffff);
}
@@ -1206,7 +1205,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
/* convert the various timeouts from relative (secs) to absolute (usecs) */
lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1);
- if (client->lease->t1 && client->lease->t2) {
+ if (client->lease->t1 > 0 && client->lease->t2 > 0) {
/* both T1 and T2 are given */
if (client->lease->t1 < client->lease->t2 &&
client->lease->t2 < client->lease->lifetime) {
@@ -1220,7 +1219,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
client->lease->t1 = client->lease->lifetime / 2;
}
- } else if (client->lease->t2 && client->lease->t2 < client->lease->lifetime) {
+ } else if (client->lease->t2 > 0 && client->lease->t2 < client->lease->lifetime) {
/* only T2 is given, and it is valid */
t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
@@ -1230,7 +1229,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
client->lease->t2 = (client->lease->lifetime * 7) / 8;
}
- } else if (client->lease->t1 && client->lease->t1 < client->lease->lifetime) {
+ } else if (client->lease->t1 > 0 && client->lease->t1 < client->lease->lifetime) {
/* only T1 is given, and it is valid */
t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
@@ -1676,30 +1675,41 @@ sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
}
sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
- if (client)
- assert_se(REFCNT_INC(client->n_ref) >= 2);
+
+ if (!client)
+ return NULL;
+
+ assert(client->n_ref >= 1);
+ client->n_ref++;
return client;
}
sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
- if (client && REFCNT_DEC(client->n_ref) == 0) {
- log_dhcp_client(client, "FREE");
- client_initialize(client);
+ if (!client)
+ return NULL;
- client->receive_message =
- sd_event_source_unref(client->receive_message);
+ assert(client->n_ref >= 1);
+ client->n_ref--;
- sd_dhcp_client_detach_event(client);
+ if (client->n_ref > 0)
+ return NULL;
- sd_dhcp_lease_unref(client->lease);
+ log_dhcp_client(client, "FREE");
- free(client->req_opts);
- free(client->hostname);
- free(client->vendor_class_identifier);
- free(client);
- }
+ client_initialize(client);
+
+ client->receive_message = sd_event_source_unref(client->receive_message);
+
+ sd_dhcp_client_detach_event(client);
+
+ sd_dhcp_lease_unref(client->lease);
+
+ free(client->req_opts);
+ free(client->hostname);
+ free(client->vendor_class_identifier);
+ free(client);
return NULL;
}
@@ -1713,7 +1723,7 @@ int sd_dhcp_client_new(sd_dhcp_client **ret) {
if (!client)
return -ENOMEM;
- client->n_ref = REFCNT_INIT;
+ client->n_ref = 1;
client->state = DHCP_STATE_INIT;
client->index = -1;
client->fd = -1;
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index f5b9e22589..aa07846693 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -28,18 +28,31 @@
#include "unaligned.h"
#include "in-addr-util.h"
#include "hostname-util.h"
+#include "dns-domain.h"
+#include "network-internal.h"
#include "dhcp-protocol.h"
#include "dhcp-lease-internal.h"
#include "sd-dhcp-lease.h"
-#include "network-internal.h"
-#include "dns-domain.h"
int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
+ if (lease->address == 0)
+ return -ENODATA;
+
addr->s_addr = lease->address;
+ return 0;
+}
+int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr) {
+ assert_return(lease, -EINVAL);
+ assert_return(addr, -EINVAL);
+
+ if (!lease->have_broadcast)
+ return -ENODATA;
+
+ addr->s_addr = lease->broadcast;
return 0;
}
@@ -47,8 +60,32 @@ int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) {
assert_return(lease, -EINVAL);
assert_return(lifetime, -EINVAL);
+ if (lease->lifetime <= 0)
+ return -ENODATA;
+
*lifetime = lease->lifetime;
+ return 0;
+}
+
+int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1) {
+ assert_return(lease, -EINVAL);
+ assert_return(t1, -EINVAL);
+
+ if (lease->t1 <= 0)
+ return -ENODATA;
+ *t1 = lease->t1;
+ return 0;
+}
+
+int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2) {
+ assert_return(lease, -EINVAL);
+ assert_return(t2, -EINVAL);
+
+ if (lease->t2 <= 0)
+ return -ENODATA;
+
+ *t2 = lease->t2;
return 0;
}
@@ -56,11 +93,10 @@ int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
assert_return(lease, -EINVAL);
assert_return(mtu, -EINVAL);
- if (lease->mtu)
- *mtu = lease->mtu;
- else
- return -ENOENT;
+ if (lease->mtu <= 0)
+ return -ENODATA;
+ *mtu = lease->mtu;
return 0;
}
@@ -68,37 +104,32 @@ int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
- if (lease->dns_size) {
- *addr = lease->dns;
- return lease->dns_size;
- } else
- return -ENOENT;
+ if (lease->dns_size <= 0)
+ return -ENODATA;
- return 0;
+ *addr = lease->dns;
+ return (int) lease->dns_size;
}
int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
- if (lease->ntp_size) {
- *addr = lease->ntp;
- return lease->ntp_size;
- } else
- return -ENOENT;
+ if (lease->ntp_size <= 0)
+ return -ENODATA;
- return 0;
+ *addr = lease->ntp;
+ return (int) lease->ntp_size;
}
int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
assert_return(lease, -EINVAL);
assert_return(domainname, -EINVAL);
- if (lease->domainname)
- *domainname = lease->domainname;
- else
- return -ENOENT;
+ if (!lease->domainname)
+ return -ENODATA;
+ *domainname = lease->domainname;
return 0;
}
@@ -106,11 +137,10 @@ int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
assert_return(lease, -EINVAL);
assert_return(hostname, -EINVAL);
- if (lease->hostname)
- *hostname = lease->hostname;
- else
- return -ENOENT;
+ if (!lease->hostname)
+ return -ENODATA;
+ *hostname = lease->hostname;
return 0;
}
@@ -118,11 +148,10 @@ int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) {
assert_return(lease, -EINVAL);
assert_return(root_path, -EINVAL);
- if (lease->root_path)
- *root_path = lease->root_path;
- else
- return -ENOENT;
+ if (!lease->root_path)
+ return -ENODATA;
+ *root_path = lease->root_path;
return 0;
}
@@ -130,11 +159,10 @@ int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
- if (lease->router != INADDR_ANY)
- addr->s_addr = lease->router;
- else
- return -ENOENT;
+ if (lease->router == 0)
+ return -ENODATA;
+ addr->s_addr = lease->router;
return 0;
}
@@ -142,8 +170,10 @@ int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
- addr->s_addr = lease->subnet_mask;
+ if (!lease->have_subnet_mask)
+ return -ENODATA;
+ addr->s_addr = lease->subnet_mask;
return 0;
}
@@ -151,8 +181,10 @@ int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *ad
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
- addr->s_addr = lease->server_address;
+ if (lease->server_address == 0)
+ return -ENODATA;
+ addr->s_addr = lease->server_address;
return 0;
}
@@ -160,136 +192,134 @@ int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
- addr->s_addr = lease->next_server;
+ if (lease->next_server == 0)
+ return -ENODATA;
+ addr->s_addr = lease->next_server;
return 0;
}
int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes) {
-
assert_return(lease, -EINVAL);
assert_return(routes, -EINVAL);
- if (lease->static_route_size) {
- *routes = lease->static_route;
- return lease->static_route_size;
- } else
- return -ENOENT;
+ if (lease->static_route_size <= 0)
+ return -ENODATA;
- return 0;
+ *routes = lease->static_route;
+ return (int) lease->static_route_size;
}
-int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const uint8_t **data,
- size_t *data_len) {
+int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) {
assert_return(lease, -EINVAL);
assert_return(data, -EINVAL);
assert_return(data_len, -EINVAL);
- if (!lease->vendor_specific)
- return -ENOENT;
+ if (lease->vendor_specific_len <= 0)
+ return -ENODATA;
*data = lease->vendor_specific;
*data_len = lease->vendor_specific_len;
-
return 0;
}
sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
- if (lease)
- assert_se(REFCNT_INC(lease->n_ref) >= 2);
+
+ if (!lease)
+ return NULL;
+
+ assert(lease->n_ref >= 1);
+ lease->n_ref++;
return lease;
}
sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
- if (lease && REFCNT_DEC(lease->n_ref) == 0) {
- while (lease->private_options) {
- struct sd_dhcp_raw_option *option = lease->private_options;
- LIST_REMOVE(options, lease->private_options, option);
+ if (!lease)
+ return NULL;
- free(option->data);
- free(option);
- }
- free(lease->hostname);
- free(lease->domainname);
- free(lease->dns);
- free(lease->ntp);
- free(lease->static_route);
- free(lease->client_id);
- free(lease->vendor_specific);
- free(lease);
- }
+ assert(lease->n_ref >= 1);
+ lease->n_ref--;
- return NULL;
-}
+ if (lease->n_ref > 0)
+ return NULL;
-static void lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) {
- assert(option);
- assert(ret);
+ while (lease->private_options) {
+ struct sd_dhcp_raw_option *option = lease->private_options;
- if (len == 4) {
- *ret = unaligned_read_be32((be32_t*) option);
+ LIST_REMOVE(options, lease->private_options, option);
- if (*ret < min)
- *ret = min;
+ free(option->data);
+ free(option);
}
-}
-static void lease_parse_s32(const uint8_t *option, size_t len, int32_t *ret) {
- lease_parse_u32(option, len, (uint32_t *)ret, 0);
+ free(lease->hostname);
+ free(lease->domainname);
+ free(lease->dns);
+ free(lease->ntp);
+ free(lease->static_route);
+ free(lease->client_id);
+ free(lease->vendor_specific);
+ free(lease);
+
+ return NULL;
}
-static void lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) {
+static int lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) {
assert(option);
assert(ret);
- if (len == 2) {
- *ret = unaligned_read_be16((be16_t*) option);
+ if (len != 4)
+ return -EINVAL;
- if (*ret < min)
- *ret = min;
- }
+ *ret = unaligned_read_be32((be32_t*) option);
+ if (*ret < min)
+ *ret = min;
+
+ return 0;
}
-static void lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
+static int lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) {
assert(option);
assert(ret);
- if (len == 4)
- memcpy(ret, option, 4);
-}
+ if (len != 2)
+ return -EINVAL;
-static void lease_parse_bool(const uint8_t *option, size_t len, bool *ret) {
- assert(option);
- assert(ret);
+ *ret = unaligned_read_be16((be16_t*) option);
+ if (*ret < min)
+ *ret = min;
- if (len == 1)
- *ret = !!(*option);
+ return 0;
}
-static void lease_parse_u8(const uint8_t *option, size_t len, uint8_t *ret, uint8_t min) {
+static int lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
assert(option);
assert(ret);
- if (len == 1) {
- *ret = *option;
+ if (len != 4)
+ return -EINVAL;
- if (*ret < min)
- *ret = min;
- }
+ memcpy(ret, option, 4);
+ return 0;
}
static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
assert(option);
assert(ret);
- if (len >= 1) {
+ if (len <= 0)
+ *ret = mfree(*ret);
+ else {
char *string;
+ if (memchr(option, 0, len))
+ return -EINVAL;
+
string = strndup((const char *)option, len);
if (!string)
- return -errno;
+ return -ENOMEM;
free(*ret);
*ret = string;
@@ -298,48 +328,47 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
return 0;
}
-static int lease_parse_in_addrs_aux(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size, size_t mult) {
+static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) {
assert(option);
assert(ret);
- assert(ret_size);
+ assert(n_ret);
- if (len && !(len % (4 * mult))) {
- size_t size;
+ if (len <= 0) {
+ *ret = mfree(*ret);
+ *n_ret = 0;
+ } else {
+ size_t n_addresses;
struct in_addr *addresses;
- size = len / 4;
+ if (len % 4 != 0)
+ return -EINVAL;
+
+ n_addresses = len / 4;
- addresses = newdup(struct in_addr, option, size);
+ addresses = newdup(struct in_addr, option, n_addresses);
if (!addresses)
return -ENOMEM;
free(*ret);
*ret = addresses;
- *ret_size = size;
+ *n_ret = n_addresses;
}
return 0;
}
-static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
- return lease_parse_in_addrs_aux(option, len, ret, ret_size, 1);
-}
-
-static int lease_parse_in_addrs_pairs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
- return lease_parse_in_addrs_aux(option, len, ret, ret_size, 2);
-}
-
-static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
- size_t *routes_size, size_t *routes_allocated) {
+static int lease_parse_routes(
+ const uint8_t *option, size_t len,
+ struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) {
struct in_addr addr;
- assert(option);
+ assert(option || len <= 0);
assert(routes);
assert(routes_size);
assert(routes_allocated);
- if (!len)
+ if (len <= 0)
return 0;
if (len % 8 != 0)
@@ -354,15 +383,15 @@ static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_
r = in_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
if (r < 0) {
- log_error("Failed to determine destination prefix length from class based IP, ignoring");
+ log_debug("Failed to determine destination prefix length from class based IP, ignoring");
continue;
}
- lease_parse_be32(option, 4, &addr.s_addr);
+ assert_se(lease_parse_be32(option, 4, &addr.s_addr) >= 0);
route->dst_addr = inet_makeaddr(inet_netof(addr), 0);
option += 4;
- lease_parse_be32(option, 4, &route->gw_addr.s_addr);
+ assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0);
option += 4;
len -= 8;
@@ -373,14 +402,18 @@ static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_
}
/* parses RFC3442 Classless Static Route Option */
-static int lease_parse_classless_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
- size_t *routes_size, size_t *routes_allocated) {
+static int lease_parse_classless_routes(
+ const uint8_t *option, size_t len,
+ struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) {
- assert(option);
+ assert(option || len <= 0);
assert(routes);
assert(routes_size);
assert(routes_allocated);
+ if (len <= 0)
+ return 0;
+
/* option format: (subnet-mask-width significant-subnet-octets gateway-ip)* */
while (len > 0) {
@@ -388,7 +421,7 @@ static int lease_parse_classless_routes(const uint8_t *option, size_t len, struc
struct sd_dhcp_route *route;
if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + 1))
- return -ENOMEM;
+ return -ENOMEM;
route = *routes + *routes_size;
@@ -419,223 +452,222 @@ static int lease_parse_classless_routes(const uint8_t *option, size_t len, struc
return 0;
}
-int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
- void *user_data) {
- sd_dhcp_lease *lease = user_data;
+int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata) {
+ sd_dhcp_lease *lease = userdata;
int r;
assert(lease);
switch(code) {
- case DHCP_OPTION_TIME_OFFSET:
- lease_parse_s32(option, len, &lease->time_offset);
-
- break;
-
- case DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT:
- lease_parse_u32(option, len, &lease->mtu_aging_timeout, 0);
-
- break;
-
case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
- lease_parse_u32(option, len, &lease->lifetime, 1);
+ r = lease_parse_u32(option, len, &lease->lifetime, 1);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse lease time, ignoring: %m");
break;
case DHCP_OPTION_SERVER_IDENTIFIER:
- lease_parse_be32(option, len, &lease->server_address);
+ r = lease_parse_be32(option, len, &lease->server_address);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse server identifier, ignoring: %m");
break;
case DHCP_OPTION_SUBNET_MASK:
- lease_parse_be32(option, len, &lease->subnet_mask);
-
+ r = lease_parse_be32(option, len, &lease->subnet_mask);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse subnet mask, ignoring: %m");
+ else
+ lease->have_subnet_mask = true;
break;
case DHCP_OPTION_BROADCAST:
- lease_parse_be32(option, len, &lease->broadcast);
-
+ r = lease_parse_be32(option, len, &lease->broadcast);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse broadcast address, ignoring: %m");
+ else
+ lease->have_broadcast = true;
break;
case DHCP_OPTION_ROUTER:
- if(len >= 4)
- lease_parse_be32(option, 4, &lease->router);
-
+ if (len >= 4) {
+ r = lease_parse_be32(option, 4, &lease->router);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse router address, ignoring: %m");
+ }
break;
case DHCP_OPTION_DOMAIN_NAME_SERVER:
r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size);
if (r < 0)
- return r;
-
+ log_debug_errno(r, "Failed to parse DNS server, ignoring: %m");
break;
case DHCP_OPTION_NTP_SERVER:
r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size);
if (r < 0)
- return r;
-
- break;
-
- case DHCP_OPTION_POLICY_FILTER:
- r = lease_parse_in_addrs_pairs(option, len, &lease->policy_filter, &lease->policy_filter_size);
- if (r < 0)
- return r;
-
+ log_debug_errno(r, "Failed to parse NTP server, ignoring: %m");
break;
case DHCP_OPTION_STATIC_ROUTE:
- r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size,
- &lease->static_route_allocated);
+ r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, &lease->static_route_allocated);
if (r < 0)
- return r;
-
+ log_debug_errno(r, "Failed to parse static routes, ignoring: %m");
break;
case DHCP_OPTION_INTERFACE_MTU:
- lease_parse_u16(option, len, &lease->mtu, 68);
-
- break;
-
- case DHCP_OPTION_INTERFACE_MDR:
- lease_parse_u16(option, len, &lease->mdr, 576);
-
- break;
-
- case DHCP_OPTION_INTERFACE_TTL:
- lease_parse_u8(option, len, &lease->ttl, 1);
-
- break;
-
- case DHCP_OPTION_BOOT_FILE_SIZE:
- lease_parse_u16(option, len, &lease->boot_file_size, 0);
-
+ r = lease_parse_u16(option, len, &lease->mtu, 68);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse MTU, ignoring: %m");
break;
- case DHCP_OPTION_DOMAIN_NAME:
- {
- _cleanup_free_ char *domainname = NULL;
- char *e;
+ case DHCP_OPTION_DOMAIN_NAME: {
+ _cleanup_free_ char *domainname = NULL, *normalized = NULL;
r = lease_parse_string(option, len, &domainname);
- if (r < 0)
- return r;
-
- /* Chop off trailing dot of domain name that some DHCP
- * servers send us back. Internally we want to store
- * host names without trailing dots and
- * host_name_is_valid() doesn't accept them. */
- e = endswith(domainname, ".");
- if (e)
- *e = 0;
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse domain name, ignoring: %m");
+ return 0;
+ }
- if (is_localhost(domainname))
- break;
+ r = dns_name_normalize(domainname, &normalized);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to normalize domain name '%s': %m", domainname);
+ return 0;
+ }
- r = dns_name_is_valid(domainname);
- if (r <= 0) {
- if (r < 0)
- log_error_errno(r, "Failed to validate domain name: %s: %m", domainname);
- if (r == 0)
- log_warning("Domain name is not valid, ignoring: %s", domainname);
+ if (is_localhost(normalized)) {
+ log_debug_errno(r, "Detected 'localhost' as suggested domain name, ignoring.");
break;
}
free(lease->domainname);
- lease->domainname = domainname;
- domainname = NULL;
+ lease->domainname = normalized;
+ normalized = NULL;
break;
}
- case DHCP_OPTION_HOST_NAME:
- {
- _cleanup_free_ char *hostname = NULL;
- char *e;
+
+ case DHCP_OPTION_HOST_NAME: {
+ _cleanup_free_ char *hostname = NULL, *normalized = NULL;
r = lease_parse_string(option, len, &hostname);
- if (r < 0)
- return r;
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse host name, ignoring: %m");
+ return 0;
+ }
- e = endswith(hostname, ".");
- if (e)
- *e = 0;
+ r = dns_name_normalize(hostname, &normalized);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to normalize host name '%s', ignoring: %m", hostname);
+ return 0;
+ }
- if (!hostname_is_valid(hostname, false) || is_localhost(hostname))
- break;
+ if (is_localhost(normalized)) {
+ log_debug_errno(r, "Detected 'localhost' as suggested host name, ignoring.");
+ return 0;
+ }
- free_and_replace(&lease->hostname, hostname);
- hostname = NULL;
+ free(lease->hostname);
+ lease->hostname = normalized;
+ normalized = NULL;
break;
}
+
case DHCP_OPTION_ROOT_PATH:
r = lease_parse_string(option, len, &lease->root_path);
if (r < 0)
- return r;
-
+ log_debug_errno(r, "Failed to parse root path, ignoring: %m");
break;
case DHCP_OPTION_RENEWAL_T1_TIME:
- lease_parse_u32(option, len, &lease->t1, 1);
-
+ r = lease_parse_u32(option, len, &lease->t1, 1);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse T1 time, ignoring: %m");
break;
case DHCP_OPTION_REBINDING_T2_TIME:
- lease_parse_u32(option, len, &lease->t2, 1);
-
+ r = lease_parse_u32(option, len, &lease->t2, 1);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse T2 time, ignoring: %m");
break;
- case DHCP_OPTION_ENABLE_IP_FORWARDING:
- lease_parse_bool(option, len, &lease->ip_forward);
-
+ case DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
+ r = lease_parse_classless_routes(
+ option, len,
+ &lease->static_route,
+ &lease->static_route_size,
+ &lease->static_route_allocated);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse classless routes, ignoring: %m");
break;
- case DHCP_OPTION_ENABLE_IP_FORWARDING_NL:
- lease_parse_bool(option, len, &lease->ip_forward_non_local);
+ case DHCP_OPTION_NEW_TZDB_TIMEZONE: {
+ _cleanup_free_ char *tz = NULL;
- break;
+ r = lease_parse_string(option, len, &tz);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse timezone option, ignoring: %m");
+ return 0;
+ }
- case DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
- r = lease_parse_classless_routes(option, len, &lease->static_route, &lease->static_route_size,
- &lease->static_route_allocated);
- if (r < 0)
- return r;
+ if (!timezone_is_valid(tz)) {
+ log_debug_errno(r, "Timezone is not valid, ignoring: %m");
+ return 0;
+ }
+
+ free(lease->timezone);
+ lease->timezone = tz;
+ tz = NULL;
break;
+ }
case DHCP_OPTION_VENDOR_SPECIFIC:
- if (len >= 1) {
- free(lease->vendor_specific);
- lease->vendor_specific = memdup(option, len);
- if (!lease->vendor_specific)
+
+ if (len <= 0)
+ lease->vendor_specific = mfree(lease->vendor_specific);
+ else {
+ void *p;
+
+ p = memdup(option, len);
+ if (!p)
return -ENOMEM;
- lease->vendor_specific_len = len;
- }
- break;
+ free(lease->vendor_specific);
+ lease->vendor_specific = p;
+ }
- default:
- if (code < DHCP_OPTION_PRIVATE_BASE || code > DHCP_OPTION_PRIVATE_LAST)
- break;
+ lease->vendor_specific_len = len;
+ break;
+ case DHCP_OPTION_PRIVATE_BASE ... DHCP_OPTION_PRIVATE_LAST:
r = dhcp_lease_insert_private_option(lease, code, option, len);
if (r < 0)
return r;
+
+ break;
+
+ default:
+ log_debug("Ignoring option DHCP option %i while parsing.", code);
+ break;
}
return 0;
}
-int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag,
- const uint8_t *data, uint8_t len) {
+int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len) {
struct sd_dhcp_raw_option *cur, *option;
+ assert(lease);
+
LIST_FOREACH(options, cur, lease->private_options) {
if (tag < cur->tag)
break;
- else if (tag == cur->tag) {
- log_error("Ignoring duplicate option, tagged %d.", tag);
+ if (tag == cur->tag) {
+ log_debug("Ignoring duplicate option, tagged %i.", tag);
return 0;
}
}
@@ -653,7 +685,6 @@ int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag,
}
LIST_INSERT_BEFORE(options, lease->private_options, cur, option);
-
return 0;
}
@@ -665,24 +696,24 @@ int dhcp_lease_new(sd_dhcp_lease **ret) {
return -ENOMEM;
lease->router = INADDR_ANY;
- lease->n_ref = REFCNT_INIT;
- LIST_HEAD_INIT(lease->private_options);
+ lease->n_ref = 1;
*ret = lease;
return 0;
}
-int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
+int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
_cleanup_free_ char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
struct sd_dhcp_raw_option *option;
struct in_addr address;
const struct in_addr *addresses;
- const uint8_t *client_id, *data;
+ const void *client_id, *data;
size_t client_id_len, data_len;
const char *string;
uint16_t mtu;
struct sd_dhcp_route *routes;
+ uint32_t t1, t2, lifetime;
int r;
assert(lease);
@@ -694,19 +725,16 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
fchmod(fileno(f), 0644);
- r = sd_dhcp_lease_get_address(lease, &address);
- if (r < 0)
- goto fail;
-
fprintf(f,
- "# This is private data. Do not parse.\n"
- "ADDRESS=%s\n", inet_ntoa(address));
+ "# This is private data. Do not parse.\n");
- r = sd_dhcp_lease_get_netmask(lease, &address);
- if (r < 0)
- goto fail;
+ r = sd_dhcp_lease_get_address(lease, &address);
+ if (r >= 0)
+ fprintf(f, "ADDRESS=%s\n", inet_ntoa(address));
- fprintf(f, "NETMASK=%s\n", inet_ntoa(address));
+ r = sd_dhcp_lease_get_netmask(lease, &address);
+ if (r >= 0)
+ fprintf(f, "NETMASK=%s\n", inet_ntoa(address));
r = sd_dhcp_lease_get_router(lease, &address);
if (r >= 0)
@@ -714,28 +742,45 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
r = sd_dhcp_lease_get_server_identifier(lease, &address);
if (r >= 0)
- fprintf(f, "SERVER_ADDRESS=%s\n",
- inet_ntoa(address));
+ fprintf(f, "SERVER_ADDRESS=%s\n", inet_ntoa(address));
r = sd_dhcp_lease_get_next_server(lease, &address);
if (r >= 0)
fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address));
+ r = sd_dhcp_lease_get_broadcast(lease, &address);
+ if (r >= 0)
+ fprintf(f, "BROADCAST=%s\n", inet_ntoa(address));
+
r = sd_dhcp_lease_get_mtu(lease, &mtu);
if (r >= 0)
fprintf(f, "MTU=%" PRIu16 "\n", mtu);
- fputs("DNS=", f);
- r = sd_dhcp_lease_get_dns(lease, &addresses);
+ r = sd_dhcp_lease_get_t1(lease, &t1);
+ if (r >= 0)
+ fprintf(f, "T1=%" PRIu32 "\n", t1);
+
+ r = sd_dhcp_lease_get_t2(lease, &t2);
+ if (r >= 0)
+ fprintf(f, "T2=%" PRIu32 "\n", t2);
+
+ r = sd_dhcp_lease_get_lifetime(lease, &lifetime);
if (r >= 0)
+ fprintf(f, "LIFETIME=%" PRIu32 "\n", lifetime);
+
+ r = sd_dhcp_lease_get_dns(lease, &addresses);
+ if (r > 0) {
+ fputs("DNS=", f);
serialize_in_addrs(f, addresses, r);
- fputs("\n", f);
+ fputs("\n", f);
+ }
- fputs("NTP=", f);
r = sd_dhcp_lease_get_ntp(lease, &addresses);
- if (r >= 0)
+ if (r > 0) {
+ fputs("NTP=", f);
serialize_in_addrs(f, addresses, r);
- fputs("\n", f);
+ fputs("\n", f);
+ }
r = sd_dhcp_lease_get_domainname(lease, &string);
if (r >= 0)
@@ -750,9 +795,13 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
fprintf(f, "ROOT_PATH=%s\n", string);
r = sd_dhcp_lease_get_routes(lease, &routes);
- if (r >= 0)
+ if (r > 0)
serialize_dhcp_routes(f, "ROUTES", routes, r);
+ r = sd_dhcp_lease_get_timezone(lease, &string);
+ if (r >= 0)
+ fprintf(f, "TIMEZONE=%s\n", string);
+
r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
if (r >= 0) {
_cleanup_free_ char *client_id_hex;
@@ -779,6 +828,7 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
LIST_FOREACH(options, option, lease->private_options) {
char key[strlen("OPTION_000")+1];
+
snprintf(key, sizeof(key), "OPTION_%"PRIu8, option->tag);
r = serialize_dhcp_option(f, key, option->data, option->length);
if (r < 0)
@@ -803,16 +853,27 @@ fail:
return log_error_errno(r, "Failed to save lease data %s: %m", lease_file);
}
-int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
+int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
+
_cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
- _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL,
- *server_address = NULL, *next_server = NULL,
- *dns = NULL, *ntp = NULL, *mtu = NULL,
- *routes = NULL, *client_id_hex = NULL,
- *vendor_specific_hex = NULL,
- *options[DHCP_OPTION_PRIVATE_LAST -
- DHCP_OPTION_PRIVATE_BASE + 1] = { NULL };
- struct in_addr addr;
+ _cleanup_free_ char
+ *address = NULL,
+ *router = NULL,
+ *netmask = NULL,
+ *server_address = NULL,
+ *next_server = NULL,
+ *broadcast = NULL,
+ *dns = NULL,
+ *ntp = NULL,
+ *mtu = NULL,
+ *routes = NULL,
+ *client_id_hex = NULL,
+ *vendor_specific_hex = NULL,
+ *lifetime = NULL,
+ *t1 = NULL,
+ *t2 = NULL,
+ *options[DHCP_OPTION_PRIVATE_LAST - DHCP_OPTION_PRIVATE_BASE + 1] = {};
+
int r, i;
assert(lease_file);
@@ -828,6 +889,7 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
"NETMASK", &netmask,
"SERVER_IDENTIFIER", &server_address,
"NEXT_SERVER", &next_server,
+ "BROADCAST", &broadcast,
"DNS", &dns,
"NTP", &ntp,
"MTU", &mtu,
@@ -836,7 +898,11 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
"ROOT_PATH", &lease->root_path,
"ROUTES", &routes,
"CLIENTID", &client_id_hex,
+ "TIMEZONE", &lease->timezone,
"VENDOR_SPECIFIC", &vendor_specific_hex,
+ "LIFETIME", &lifetime,
+ "T1", &t1,
+ "T2", &t2,
"OPTION_224", &options[0],
"OPTION_225", &options[1],
"OPTION_226", &options[2],
@@ -869,100 +935,123 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
"OPTION_253", &options[29],
"OPTION_254", &options[30],
NULL);
- if (r < 0) {
- if (r == -ENOENT)
- return 0;
-
- return log_error_errno(r, "Failed to read %s: %m", lease_file);
- }
-
- r = inet_pton(AF_INET, address, &addr);
if (r < 0)
return r;
- lease->address = addr.s_addr;
+ if (address) {
+ r = inet_pton(AF_INET, address, &lease->address);
+ if (r <= 0)
+ log_debug_errno(errno, "Failed to parse address %s, ignoring: %m", address);
+ }
if (router) {
- r = inet_pton(AF_INET, router, &addr);
- if (r < 0)
- return r;
-
- lease->router = addr.s_addr;
+ r = inet_pton(AF_INET, router, &lease->router);
+ if (r <= 0)
+ log_debug_errno(errno, "Failed to parse router %s, ignoring: %m", router);
}
- r = inet_pton(AF_INET, netmask, &addr);
- if (r < 0)
- return r;
-
- lease->subnet_mask = addr.s_addr;
+ if (netmask) {
+ r = inet_pton(AF_INET, netmask, &lease->subnet_mask);
+ if (r <= 0)
+ log_debug_errno(errno, "Failed to parse netmask %s, ignoring: %m", netmask);
+ else
+ lease->have_subnet_mask = true;
+ }
if (server_address) {
- r = inet_pton(AF_INET, server_address, &addr);
- if (r < 0)
- return r;
-
- lease->server_address = addr.s_addr;
+ r = inet_pton(AF_INET, server_address, &lease->server_address);
+ if (r <= 0)
+ log_debug_errno(errno, "Failed to parse netmask %s, ignoring: %m", server_address);
}
if (next_server) {
- r = inet_pton(AF_INET, next_server, &addr);
- if (r < 0)
- return r;
+ r = inet_pton(AF_INET, next_server, &lease->next_server);
+ if (r <= 0)
+ log_debug_errno(errno, "Failed to parse next server %s, ignoring: %m", next_server);
+ }
- lease->next_server = addr.s_addr;
+ if (broadcast) {
+ r = inet_pton(AF_INET, broadcast, &lease->broadcast);
+ if (r <= 0)
+ log_debug_errno(errno, "Failed to parse broadcast address %s, ignoring: %m", broadcast);
+ else
+ lease->have_broadcast = true;
}
if (dns) {
r = deserialize_in_addrs(&lease->dns, dns);
if (r < 0)
- return r;
-
- lease->dns_size = r;
+ log_debug_errno(r, "Failed to deserialize DNS servers %s, ignoring: %m", dns);
+ else
+ lease->dns_size = r;
}
if (ntp) {
r = deserialize_in_addrs(&lease->ntp, ntp);
if (r < 0)
- return r;
-
- lease->ntp_size = r;
+ log_debug_errno(r, "Failed to deserialize NTP servers %s, ignoring: %m", ntp);
+ else
+ lease->ntp_size = r;
}
if (mtu) {
- uint16_t u;
- if (sscanf(mtu, "%" SCNu16, &u) > 0)
- lease->mtu = u;
+ r = safe_atou16(mtu, &lease->mtu);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse MTU %s, ignoring: %m", mtu);
}
if (routes) {
- r = deserialize_dhcp_routes(&lease->static_route, &lease->static_route_size,
- &lease->static_route_allocated, routes);
+ r = deserialize_dhcp_routes(
+ &lease->static_route,
+ &lease->static_route_size,
+ &lease->static_route_allocated,
+ routes);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse DHCP routes %s, ignoring: %m", routes);
+ }
+
+ if (lifetime) {
+ r = safe_atou32(lifetime, &lease->lifetime);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse lifetime %s, ignoring: %m", lifetime);
+ }
+
+ if (t1) {
+ r = safe_atou32(t1, &lease->t1);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse T1 %s, ignoring: %m", t1);
+ }
+
+ if (t2) {
+ r = safe_atou32(t2, &lease->t2);
if (r < 0)
- return r;
+ log_debug_errno(r, "Failed to parse T2 %s, ignoring: %m", t2);
}
if (client_id_hex) {
r = deserialize_dhcp_option(&lease->client_id, &lease->client_id_len, client_id_hex);
if (r < 0)
- return r;
+ log_debug_errno(r, "Failed to parse client ID %s, ignoring: %m", client_id_hex);
}
if (vendor_specific_hex) {
r = deserialize_dhcp_option(&lease->vendor_specific, &lease->vendor_specific_len, vendor_specific_hex);
if (r < 0)
- return r;
+ log_debug_errno(r, "Failed to parse vendor specific data %s, ignoring: %m", vendor_specific_hex);
}
for (i = 0; i <= DHCP_OPTION_PRIVATE_LAST - DHCP_OPTION_PRIVATE_BASE; i++) {
- _cleanup_free_ uint8_t *data = NULL;
+ _cleanup_free_ void *data = NULL;
size_t len;
if (!options[i])
continue;
r = deserialize_dhcp_option(&data, &len, options[i]);
- if (r < 0)
- return r;
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse private DHCP option %s, ignoring: %m", options[i]);
+ continue;
+ }
r = dhcp_lease_insert_private_option(lease, DHCP_OPTION_PRIVATE_BASE + i, data, len);
if (r < 0)
@@ -976,12 +1065,14 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
}
int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
- struct in_addr address;
- struct in_addr mask;
+ struct in_addr address, mask;
int r;
assert(lease);
+ if (lease->address == 0)
+ return -ENODATA;
+
address.s_addr = lease->address;
/* fall back to the default subnet masks based on address class */
@@ -990,35 +1081,53 @@ int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
return r;
lease->subnet_mask = mask.s_addr;
+ lease->have_subnet_mask = true;
return 0;
}
-int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const uint8_t **client_id,
- size_t *client_id_len) {
+int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len) {
assert_return(lease, -EINVAL);
assert_return(client_id, -EINVAL);
assert_return(client_id_len, -EINVAL);
+ if (!lease->client_id)
+ return -ENODATA;
+
*client_id = lease->client_id;
*client_id_len = lease->client_id_len;
+
return 0;
}
-int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const uint8_t *client_id,
- size_t client_id_len) {
+int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len) {
assert_return(lease, -EINVAL);
- assert_return((!client_id && !client_id_len) ||
- (client_id && client_id_len), -EINVAL);
+ assert_return(client_id || client_id_len <= 0, -EINVAL);
+
+ if (client_id_len <= 0)
+ lease->client_id = mfree(lease->client_id);
+ else {
+ void *p;
- free (lease->client_id);
- lease->client_id = NULL;
- lease->client_id_len = 0;
+ p = memdup(client_id, client_id_len);
+ if (!p)
+ return -ENOMEM;
- if (client_id) {
- lease->client_id = memdup (client_id, client_id_len);
+ free(lease->client_id);
+ lease->client_id = p;
lease->client_id_len = client_id_len;
}
return 0;
}
+
+int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **tz) {
+ assert_return(lease, -EINVAL);
+ assert_return(tz, -EINVAL);
+
+ if (!lease->timezone)
+ return -ENODATA;
+
+ *tz = lease->timezone;
+ return 0;
+}
diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c
index faeab0fd30..1f167485e3 100644
--- a/src/libsystemd-network/sd-dhcp-server.c
+++ b/src/libsystemd-network/sd-dhcp-server.c
@@ -22,46 +22,73 @@
#include <sys/ioctl.h>
+#include "in-addr-util.h"
#include "siphash24.h"
#include "sd-dhcp-server.h"
#include "dhcp-server-internal.h"
#include "dhcp-internal.h"
-#define DHCP_DEFAULT_LEASE_TIME 3600 /* one hour */
+#define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR
+#define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12)
+
+/* configures the server's address and subnet, and optionally the pool's size and offset into the subnet
+ * the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address
+ * moreover, the server's own address may be in the pool, and is in that case reserved in order not to
+ * accidentally hand it out */
+int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size) {
+ struct in_addr netmask_addr;
+ be32_t netmask;
+ uint32_t server_off, broadcast_off, size_max;
-int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server,
- struct in_addr *address,
- size_t size) {
assert_return(server, -EINVAL);
assert_return(address, -EINVAL);
- assert_return(address->s_addr, -EINVAL);
- assert_return(size, -EINVAL);
- assert_return(server->pool_start == htobe32(INADDR_ANY), -EBUSY);
- assert_return(!server->pool_size, -EBUSY);
- assert_return(!server->bound_leases, -EBUSY);
+ assert_return(address->s_addr != INADDR_ANY, -EINVAL);
+ assert_return(prefixlen <= 32, -ERANGE);
+ assert_return(server->address == INADDR_ANY, -EBUSY);
+
+ assert_se(in_addr_prefixlen_to_netmask(&netmask_addr, prefixlen));
+ netmask = netmask_addr.s_addr;
+
+ server_off = be32toh(address->s_addr & ~netmask);
+ broadcast_off = be32toh(~netmask);
+
+ /* the server address cannot be the subnet address */
+ assert_return(server_off != 0, -ERANGE);
+
+ /* nor the broadcast address */
+ assert_return(server_off != broadcast_off, -ERANGE);
+
+ /* 0 offset means we should set a default, we skip the first (subnet) address
+ and take the next one */
+ if (offset == 0)
+ offset = 1;
+
+ size_max = (broadcast_off + 1) /* the number of addresses in the subnet */
+ - offset /* exclude the addresses before the offset */
+ - 1; /* exclude the last (broadcast) address */
+
+ /* The pool must contain at least one address */
+ assert_return(size_max >= 1, -ERANGE);
+
+ if (size != 0)
+ assert_return(size <= size_max, -ERANGE);
+ else
+ size = size_max;
server->bound_leases = new0(DHCPLease*, size);
if (!server->bound_leases)
return -ENOMEM;
- server->pool_start = address->s_addr;
+ server->pool_offset = offset;
server->pool_size = size;
- return 0;
-}
-
-int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address,
- unsigned char prefixlen) {
- assert_return(server, -EINVAL);
- assert_return(address, -EINVAL);
- assert_return(address->s_addr, -EINVAL);
- assert_return(prefixlen <= 32, -ERANGE);
- assert_return(server->address == htobe32(INADDR_ANY), -EBUSY);
- assert_return(server->netmask == htobe32(INADDR_ANY), -EBUSY);
-
server->address = address->s_addr;
- server->netmask = htobe32(0xfffffffflu << (32 - prefixlen));
+ server->netmask = netmask;
+ server->subnet = address->s_addr & netmask;
+
+ if (server_off >= offset && server_off - offset < size)
+ server->bound_leases[server_off - offset] = &server->invalid_lease;
return 0;
}
@@ -73,8 +100,12 @@ bool sd_dhcp_server_is_running(sd_dhcp_server *server) {
}
sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
- if (server)
- assert_se(REFCNT_INC(server->n_ref) >= 2);
+
+ if (!server)
+ return NULL;
+
+ assert(server->n_ref >= 1);
+ server->n_ref++;
return server;
}
@@ -127,7 +158,10 @@ sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
if (!server)
return NULL;
- if (REFCNT_DEC(server->n_ref) > 0)
+ assert(server->n_ref >= 1);
+ server->n_ref--;
+
+ if (server->n_ref > 0)
return NULL;
log_dhcp_server(server, "UNREF");
@@ -136,6 +170,10 @@ sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
sd_event_unref(server->event);
+ free(server->timezone);
+ free(server->dns);
+ free(server->ntp);
+
while ((lease = hashmap_steal_first(server->leases_by_client_id)))
dhcp_lease_free(lease);
hashmap_free(server->leases_by_client_id);
@@ -156,13 +194,15 @@ int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
if (!server)
return -ENOMEM;
- server->n_ref = REFCNT_INIT;
+ server->n_ref = 1;
server->fd_raw = -1;
server->fd = -1;
server->address = htobe32(INADDR_ANY);
server->netmask = htobe32(INADDR_ANY);
- server->index = ifindex;
+ server->ifindex = ifindex;
server->leases_by_client_id = hashmap_new(&client_id_hash_ops);
+ server->default_lease_time = DIV_ROUND_UP(DHCP_DEFAULT_LEASE_TIME_USEC, USEC_PER_SEC);
+ server->max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC);
*ret = server;
server = NULL;
@@ -223,13 +263,12 @@ static int dhcp_server_send_unicast_raw(sd_dhcp_server *server,
union sockaddr_union link = {
.ll.sll_family = AF_PACKET,
.ll.sll_protocol = htons(ETH_P_IP),
- .ll.sll_ifindex = server->index,
+ .ll.sll_ifindex = server->ifindex,
.ll.sll_halen = ETH_ALEN,
};
- int r;
assert(server);
- assert(server->index > 0);
+ assert(server->ifindex > 0);
assert(server->address);
assert(packet);
assert(len > sizeof(DHCPPacket));
@@ -240,11 +279,7 @@ static int dhcp_server_send_unicast_raw(sd_dhcp_server *server,
packet->dhcp.yiaddr,
DHCP_PORT_CLIENT, len);
- r = dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
- if (r < 0)
- return r;
-
- return 0;
+ return dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
}
static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
@@ -290,7 +325,7 @@ static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
assert(pktinfo);
- pktinfo->ipi_ifindex = server->index;
+ pktinfo->ipi_ifindex = server->ifindex;
pktinfo->ipi_spec_dst.s_addr = server->address;
r = sendmsg(server->fd, &msg, 0);
@@ -474,6 +509,33 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
if (r < 0)
return r;
+ if (server->n_dns > 0) {
+ r = dhcp_option_append(
+ &packet->dhcp, req->max_optlen, &offset, 0,
+ DHCP_OPTION_DOMAIN_NAME_SERVER,
+ sizeof(struct in_addr) * server->n_dns, server->dns);
+ if (r < 0)
+ return r;
+ }
+
+ if (server->n_ntp > 0) {
+ r = dhcp_option_append(
+ &packet->dhcp, req->max_optlen, &offset, 0,
+ DHCP_OPTION_NTP_SERVER,
+ sizeof(struct in_addr) * server->n_ntp, server->ntp);
+ if (r < 0)
+ return r;
+ }
+
+ if (server->timezone) {
+ r = dhcp_option_append(
+ &packet->dhcp, req->max_optlen, &offset, 0,
+ DHCP_OPTION_NEW_TZDB_TIMEZONE,
+ strlen(server->timezone), server->timezone);
+ if (r < 0)
+ return r;
+ }
+
r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
if (r < 0)
return r;
@@ -490,11 +552,7 @@ static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
if (r < 0)
return r;
- r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
- if (r < 0)
- return r;
-
- return 0;
+ return dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
}
static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
@@ -532,9 +590,8 @@ static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
return 0;
}
-static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
- void *user_data) {
- DHCPRequest *req = user_data;
+static int parse_request(uint8_t code, uint8_t len, const void *option, void *userdata) {
+ DHCPRequest *req = userdata;
assert(req);
@@ -590,7 +647,7 @@ static void dhcp_request_free(DHCPRequest *req) {
DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
#define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
-static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
+static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMessage *message) {
assert(req);
assert(message);
@@ -599,23 +656,27 @@ static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
/* set client id based on MAC address if client did not send an explicit
one */
if (!req->client_id.data) {
- uint8_t *data;
+ void *data;
- data = new0(uint8_t, ETH_ALEN + 1);
+ data = malloc0(ETH_ALEN + 1);
if (!data)
return -ENOMEM;
+ ((uint8_t*) data)[0] = 0x01;
+ memcpy((uint8_t*) data + 1, &message->chaddr, ETH_ALEN);
+
req->client_id.length = ETH_ALEN + 1;
req->client_id.data = data;
- req->client_id.data[0] = 0x01;
- memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
}
if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
- if (!req->lifetime)
- req->lifetime = DHCP_DEFAULT_LEASE_TIME;
+ if (req->lifetime <= 0)
+ req->lifetime = MAX(1ULL, server->default_lease_time);
+
+ if (server->max_lease_time > 0 && req->lifetime > server->max_lease_time)
+ req->lifetime = server->max_lease_time;
return 0;
}
@@ -626,14 +687,15 @@ static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
if (!server->pool_size)
return -EINVAL;
- if (be32toh(requested_ip) < be32toh(server->pool_start) ||
- be32toh(requested_ip) >= be32toh(server->pool_start) +
- + server->pool_size)
- return -EINVAL;
+ if (be32toh(requested_ip) < (be32toh(server->subnet) | server->pool_offset) ||
+ be32toh(requested_ip) >= (be32toh(server->subnet) | (server->pool_offset + server->pool_size)))
+ return -ERANGE;
- return be32toh(requested_ip) - be32toh(server->pool_start);
+ return be32toh(requested_ip & ~server->netmask) - server->pool_offset;
}
+#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
+
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
size_t length) {
_cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
@@ -656,7 +718,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
if (type < 0)
return 0;
- r = ensure_sane_request(req, message);
+ r = ensure_sane_request(server, req, message);
if (r < 0)
/* this only fails on critical errors */
return r;
@@ -665,8 +727,8 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
&req->client_id);
switch(type) {
- case DHCP_DISCOVER:
- {
+
+ case DHCP_DISCOVER: {
be32_t address = INADDR_ANY;
unsigned i;
@@ -681,12 +743,20 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
if (existing_lease)
address = existing_lease->address;
else {
+ uint32_t next_offer;
+
+ /* even with no persistence of leases, we try to offer the same client
+ the same IP address. we do this by using the hash of the client id
+ as the offset into the pool of leases when finding the next free one */
+
+ next_offer = client_id_hash_func(&req->client_id, HASH_KEY.bytes) % server->pool_size;
+
for (i = 0; i < server->pool_size; i++) {
- if (!server->bound_leases[server->next_offer]) {
- address = htobe32(be32toh(server->pool_start) + server->next_offer);
+ if (!server->bound_leases[next_offer]) {
+ address = server->subnet | htobe32(server->pool_offset + next_offer);
break;
} else
- server->next_offer = (server->next_offer + 1) % server->pool_size;
+ next_offer = (next_offer + 1) % server->pool_size;
}
}
@@ -716,9 +786,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
return 1;
- break;
- case DHCP_REQUEST:
- {
+ case DHCP_REQUEST: {
be32_t address;
bool init_reboot = false;
int pool_offset;
@@ -840,6 +908,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
break;
}
+
case DHCP_RELEASE: {
int pool_offset;
@@ -883,13 +952,12 @@ static int server_receive_message(sd_event_source *s, int fd,
.msg_controllen = sizeof(cmsgbuf),
};
struct cmsghdr *cmsg;
- int buflen = 0, len, r;
+ int buflen = 0, len;
assert(server);
- r = ioctl(fd, FIONREAD, &buflen);
- if (r < 0)
- return r;
+ if (ioctl(fd, FIONREAD, &buflen) < 0)
+ return -errno;
if (buflen < 0)
return -EIO;
@@ -914,7 +982,7 @@ static int server_receive_message(sd_event_source *s, int fd,
/* TODO figure out if this can be done as a filter on
* the socket, like for IPv6 */
- if (server->index != info->ipi_ifindex)
+ if (server->ifindex != info->ipi_ifindex)
return 0;
break;
@@ -979,7 +1047,7 @@ int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
for (i = 0; i < server->pool_size; i++) {
DHCPLease *lease = server->bound_leases[i];
- if (!lease)
+ if (!lease || lease == &server->invalid_lease)
continue;
r = server_send_forcerenew(server, lease->address,
@@ -993,3 +1061,91 @@ int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
return r;
}
+
+int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *tz) {
+ int r;
+
+ assert_return(server, -EINVAL);
+ assert_return(timezone_is_valid(tz), -EINVAL);
+
+ if (streq_ptr(tz, server->timezone))
+ return 0;
+
+ r = free_and_strdup(&server->timezone, tz);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t) {
+ assert_return(server, -EINVAL);
+
+ if (t == server->max_lease_time)
+ return 0;
+
+ server->max_lease_time = t;
+ return 1;
+}
+
+int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t) {
+ assert_return(server, -EINVAL);
+
+ if (t == server->default_lease_time)
+ return 0;
+
+ server->default_lease_time = t;
+ return 1;
+}
+
+int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], unsigned n) {
+ assert_return(server, -EINVAL);
+ assert_return(dns || n <= 0, -EINVAL);
+
+ if (server->n_dns == n &&
+ memcmp(server->dns, dns, sizeof(struct in_addr) * n) == 0)
+ return 0;
+
+ if (n <= 0) {
+ server->dns = mfree(server->dns);
+ server->n_dns = 0;
+ } else {
+ struct in_addr *c;
+
+ c = newdup(struct in_addr, dns, n);
+ if (!c)
+ return -ENOMEM;
+
+ free(server->dns);
+ server->dns = c;
+ server->n_dns = n;
+ }
+
+ return 1;
+}
+
+int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n) {
+ assert_return(server, -EINVAL);
+ assert_return(ntp || n <= 0, -EINVAL);
+
+ if (server->n_ntp == n &&
+ memcmp(server->ntp, ntp, sizeof(struct in_addr) * n) == 0)
+ return 0;
+
+ if (n <= 0) {
+ server->ntp = mfree(server->ntp);
+ server->n_ntp = 0;
+ } else {
+ struct in_addr *c;
+
+ c = newdup(struct in_addr, ntp, n);
+ if (!c)
+ return -ENOMEM;
+
+ free(server->ntp);
+ server->ntp = c;
+ server->n_ntp = n;
+ }
+
+ return 1;
+}
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index 10c3654020..5489c77864 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -27,7 +27,6 @@
#include "udev.h"
#include "udev-util.h"
#include "util.h"
-#include "refcnt.h"
#include "random-util.h"
#include "network-internal.h"
@@ -40,7 +39,7 @@
#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
struct sd_dhcp6_client {
- RefCount n_ref;
+ unsigned n_ref;
enum DHCP6State state;
sd_event *event;
@@ -113,9 +112,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
-int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
- sd_dhcp6_client_cb_t cb, void *userdata)
-{
+int sd_dhcp6_client_set_callback(sd_dhcp6_client *client, sd_dhcp6_client_cb_t cb, void *userdata) {
assert_return(client, -EINVAL);
client->cb = cb;
@@ -124,8 +121,7 @@ int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
return 0;
}
-int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
-{
+int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index) {
assert_return(client, -EINVAL);
assert_return(interface_index >= -1, -EINVAL);
@@ -134,9 +130,11 @@ int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
return 0;
}
-int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
- size_t addr_len, uint16_t arp_type)
-{
+int sd_dhcp6_client_set_mac(
+ sd_dhcp6_client *client,
+ const uint8_t *addr, size_t addr_len,
+ uint16_t arp_type) {
+
assert_return(client, -EINVAL);
assert_return(addr, -EINVAL);
assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
@@ -160,16 +158,17 @@ int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
return 0;
}
-static int client_ensure_duid(sd_dhcp6_client *client)
-{
+static int client_ensure_duid(sd_dhcp6_client *client) {
if (client->duid_len != 0)
return 0;
+
return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
}
-int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
- size_t duid_len)
-{
+int sd_dhcp6_client_set_duid(
+ sd_dhcp6_client *client,
+ uint16_t type,
+ uint8_t *duid, size_t duid_len) {
assert_return(client, -EINVAL);
assert_return(duid, -EINVAL);
assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
@@ -203,8 +202,7 @@ int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *du
return 0;
}
-int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client,
- bool enabled) {
+int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, bool enabled) {
assert_return(client, -EINVAL);
client->information_request = enabled;
@@ -212,8 +210,7 @@ int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client,
return 0;
}
-int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client,
- bool *enabled) {
+int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, bool *enabled) {
assert_return(client, -EINVAL);
assert_return(enabled, -EINVAL);
@@ -222,8 +219,7 @@ int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client,
return 0;
}
-int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
- uint16_t option) {
+int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) {
size_t t;
assert_return(client, -EINVAL);
@@ -805,9 +801,7 @@ static int client_parse_message(sd_dhcp6_client *client,
return r;
}
-static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
- size_t len)
-{
+static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
int r;
_cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
bool rapid_commit;
@@ -843,8 +837,7 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
return DHCP6_STATE_BOUND;
}
-static int client_receive_advertise(sd_dhcp6_client *client,
- DHCP6Message *advertise, size_t len) {
+static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) {
int r;
_cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
uint8_t pref_advertise = 0, pref_lease = 0;
@@ -879,8 +872,7 @@ static int client_receive_advertise(sd_dhcp6_client *client,
return r;
}
-static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
- void *userdata) {
+static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
sd_dhcp6_client *client = userdata;
DHCP6_CLIENT_DONT_DESTROY(client);
_cleanup_free_ DHCP6Message *message;
@@ -991,8 +983,7 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
return 0;
}
-static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
-{
+static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
int r;
usec_t timeout, time_now;
char time_string[FORMAT_TIMESPAN_MAX];
@@ -1121,15 +1112,13 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
return 0;
}
-int sd_dhcp6_client_stop(sd_dhcp6_client *client)
-{
+int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
client_stop(client, DHCP6_EVENT_STOP);
return 0;
}
-int sd_dhcp6_client_start(sd_dhcp6_client *client)
-{
+int sd_dhcp6_client_start(sd_dhcp6_client *client) {
int r = 0;
enum DHCP6State state = DHCP6_STATE_SOLICITATION;
@@ -1185,9 +1174,7 @@ error:
return r;
}
-int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
- int priority)
-{
+int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int priority) {
int r;
assert_return(client, -EINVAL);
@@ -1222,30 +1209,39 @@ sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
}
sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
- if (client)
- assert_se(REFCNT_INC(client->n_ref) >= 2);
+
+ if (!client)
+ return NULL;
+
+ assert(client->n_ref >= 1);
+ client->n_ref++;
return client;
}
sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
- if (client && REFCNT_DEC(client->n_ref) == 0) {
- client_reset(client);
- sd_dhcp6_client_detach_event(client);
- sd_dhcp6_lease_unref(client->lease);
+ if (!client)
+ return NULL;
- free(client->req_opts);
- free(client);
+ assert(client->n_ref >= 1);
+ client->n_ref--;
+ if (client->n_ref > 0)
return NULL;
- }
- return client;
+ client_reset(client);
+
+ sd_dhcp6_client_detach_event(client);
+ sd_dhcp6_lease_unref(client->lease);
+
+ free(client->req_opts);
+ free(client);
+
+ return NULL;
}
-int sd_dhcp6_client_new(sd_dhcp6_client **ret)
-{
+int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
_cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
size_t t;
@@ -1255,7 +1251,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret)
if (!client)
return -ENOMEM;
- client->n_ref = REFCNT_INIT;
+ client->n_ref = 1;
client->ia_na.type = DHCP6_OPTION_IA_NA;
diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c
index f0494b3c91..f34af6eaba 100644
--- a/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/libsystemd-network/sd-dhcp6-lease.c
@@ -247,8 +247,7 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains) {
return -ENOENT;
}
-int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen)
-{
+int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
int r;
uint16_t subopt;
size_t sublen;
@@ -361,26 +360,38 @@ int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) {
}
sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) {
- if (lease)
- assert_se(REFCNT_INC(lease->n_ref) >= 2);
+
+ if (!lease)
+ return NULL;
+
+ assert(lease->n_ref >= 1);
+ lease->n_ref++;
return lease;
}
sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) {
- if (lease && REFCNT_DEC(lease->n_ref) == 0) {
- free(lease->serverid);
- dhcp6_lease_free_ia(&lease->ia);
- free(lease->dns);
+ if (!lease)
+ return NULL;
- lease->domains = strv_free(lease->domains);
+ assert(lease->n_ref >= 1);
+ lease->n_ref--;
- free(lease->ntp);
+ if (lease->n_ref > 0)
+ return NULL;
- lease->ntp_fqdn = strv_free(lease->ntp_fqdn);
- free(lease);
- }
+ free(lease->serverid);
+ dhcp6_lease_free_ia(&lease->ia);
+
+ free(lease->dns);
+
+ lease->domains = strv_free(lease->domains);
+
+ free(lease->ntp);
+
+ lease->ntp_fqdn = strv_free(lease->ntp_fqdn);
+ free(lease);
return NULL;
}
@@ -392,7 +403,7 @@ int dhcp6_lease_new(sd_dhcp6_lease **ret) {
if (!lease)
return -ENOMEM;
- lease->n_ref = REFCNT_INIT;
+ lease->n_ref = 1;
LIST_HEAD_INIT(lease->ia.addresses);
diff --git a/src/libsystemd-network/sd-icmp6-nd.c b/src/libsystemd-network/sd-icmp6-nd.c
index 2f867e8562..e80232a7e0 100644
--- a/src/libsystemd-network/sd-icmp6-nd.c
+++ b/src/libsystemd-network/sd-icmp6-nd.c
@@ -25,7 +25,6 @@
#include <sys/ioctl.h>
#include "socket-util.h"
-#include "refcnt.h"
#include "async.h"
#include "dhcp6-internal.h"
@@ -47,7 +46,7 @@ enum icmp6_nd_state {
typedef struct ICMP6Prefix ICMP6Prefix;
struct ICMP6Prefix {
- RefCount n_ref;
+ unsigned n_ref;
LIST_FIELDS(ICMP6Prefix, prefixes);
@@ -57,7 +56,7 @@ struct ICMP6Prefix {
};
struct sd_icmp6_nd {
- RefCount n_ref;
+ unsigned n_ref;
enum icmp6_nd_state state;
sd_event *event;
@@ -78,13 +77,18 @@ struct sd_icmp6_nd {
#define log_icmp6_nd(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "ICMPv6 CLIENT: " fmt, ##__VA_ARGS__)
static ICMP6Prefix *icmp6_prefix_unref(ICMP6Prefix *prefix) {
- if (prefix && REFCNT_DEC(prefix->n_ref) <= 0) {
- prefix->timeout_valid =
- sd_event_source_unref(prefix->timeout_valid);
- free(prefix);
- }
+ if (!prefix)
+ return NULL;
+
+ assert(prefix->n_ref > 0);
+ prefix->n_ref--;
+ if (prefix->n_ref > 0)
+ return NULL;
+
+ prefix->timeout_valid = sd_event_source_unref(prefix->timeout_valid);
+ free(prefix);
return NULL;
}
@@ -97,7 +101,7 @@ static int icmp6_prefix_new(ICMP6Prefix **ret) {
if (!prefix)
return -ENOMEM;
- prefix->n_ref = REFCNT_INIT;
+ prefix->n_ref = 1;
LIST_INIT(prefixes, prefix);
*ret = prefix;
@@ -106,8 +110,7 @@ static int icmp6_prefix_new(ICMP6Prefix **ret) {
return 0;
}
-static void icmp6_nd_notify(sd_icmp6_nd *nd, int event)
-{
+static void icmp6_nd_notify(sd_icmp6_nd *nd, int event) {
if (nd->callback)
nd->callback(nd, event, nd->userdata);
}
@@ -177,9 +180,12 @@ sd_event *sd_icmp6_nd_get_event(sd_icmp6_nd *nd) {
}
sd_icmp6_nd *sd_icmp6_nd_ref(sd_icmp6_nd *nd) {
- assert (nd);
- assert_se(REFCNT_INC(nd->n_ref) >= 2);
+ if (!nd)
+ return NULL;
+
+ assert(nd->n_ref > 0);
+ nd->n_ref++;
return nd;
}
@@ -195,21 +201,28 @@ static int icmp6_nd_init(sd_icmp6_nd *nd) {
}
sd_icmp6_nd *sd_icmp6_nd_unref(sd_icmp6_nd *nd) {
- if (nd && REFCNT_DEC(nd->n_ref) == 0) {
- ICMP6Prefix *prefix, *p;
+ ICMP6Prefix *prefix, *p;
- icmp6_nd_init(nd);
- sd_icmp6_nd_detach_event(nd);
+ if (!nd)
+ return NULL;
- LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) {
- LIST_REMOVE(prefixes, nd->prefixes, prefix);
+ assert(nd->n_ref > 0);
+ nd->n_ref--;
- prefix = icmp6_prefix_unref(prefix);
- }
+ if (nd->n_ref > 0)
+ return NULL;
+
+ icmp6_nd_init(nd);
+ sd_icmp6_nd_detach_event(nd);
- free(nd);
+ LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) {
+ LIST_REMOVE(prefixes, nd->prefixes, prefix);
+
+ prefix = icmp6_prefix_unref(prefix);
}
+ free(nd);
+
return NULL;
}
@@ -225,7 +238,7 @@ int sd_icmp6_nd_new(sd_icmp6_nd **ret) {
if (!nd)
return -ENOMEM;
- nd->n_ref = REFCNT_INIT;
+ nd->n_ref = 1;
nd->index = -1;
nd->fd = -1;
@@ -376,9 +389,7 @@ int sd_icmp6_ra_get_prefixlen(sd_icmp6_nd *nd, const struct in6_addr *addr,
return 0;
}
-int sd_icmp6_ra_get_expired_prefix(sd_icmp6_nd *nd, struct in6_addr **addr,
- uint8_t *prefixlen)
-{
+int sd_icmp6_ra_get_expired_prefix(sd_icmp6_nd *nd, struct in6_addr **addr, uint8_t *prefixlen) {
assert_return(nd, -EINVAL);
assert_return(addr, -EINVAL);
assert_return(prefixlen, -EINVAL);
@@ -525,9 +536,7 @@ static int icmp6_ra_parse(sd_icmp6_nd *nd, struct nd_router_advert *ra,
return 0;
}
-static int icmp6_router_advertisment_recv(sd_event_source *s, int fd,
- uint32_t revents, void *userdata)
-{
+static int icmp6_router_advertisment_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
sd_icmp6_nd *nd = userdata;
int r, buflen = 0;
ssize_t len;
@@ -586,9 +595,7 @@ static int icmp6_router_advertisment_recv(sd_event_source *s, int fd,
return 0;
}
-static int icmp6_router_solicitation_timeout(sd_event_source *s, uint64_t usec,
- void *userdata)
-{
+static int icmp6_router_solicitation_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
sd_icmp6_nd *nd = userdata;
uint64_t time_now, next_timeout;
struct ether_addr unset = { };
diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c
index f080c5c0a7..0fc05e1484 100644
--- a/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/libsystemd-network/sd-ipv4ll.c
@@ -26,7 +26,6 @@
#include "util.h"
#include "siphash24.h"
#include "list.h"
-#include "refcnt.h"
#include "random-util.h"
#include "ipv4ll-internal.h"
@@ -68,7 +67,7 @@ typedef enum IPv4LLState {
} IPv4LLState;
struct sd_ipv4ll {
- RefCount n_ref;
+ unsigned n_ref;
IPv4LLState state;
int index;
@@ -598,30 +597,39 @@ int sd_ipv4ll_stop(sd_ipv4ll *ll) {
}
sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
- if (ll)
- assert_se(REFCNT_INC(ll->n_ref) >= 2);
+
+ if (!ll)
+ return NULL;
+
+ assert(ll->n_ref >= 1);
+ ll->n_ref++;
return ll;
}
sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
- if (ll && REFCNT_DEC(ll->n_ref) == 0) {
- ll->receive_message =
- sd_event_source_unref(ll->receive_message);
- ll->fd = safe_close(ll->fd);
- ll->timer = sd_event_source_unref(ll->timer);
+ if (!ll)
+ return NULL;
- sd_ipv4ll_detach_event(ll);
+ assert(ll->n_ref >= 1);
+ ll->n_ref--;
- free(ll->random_data);
- free(ll->random_data_state);
- free(ll);
+ if (ll->n_ref > 0)
+ return ll;
- return NULL;
- }
+ ll->receive_message = sd_event_source_unref(ll->receive_message);
+ ll->fd = safe_close(ll->fd);
- return ll;
+ ll->timer = sd_event_source_unref(ll->timer);
+
+ sd_ipv4ll_detach_event(ll);
+
+ free(ll->random_data);
+ free(ll->random_data_state);
+ free(ll);
+
+ return NULL;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref);
@@ -636,7 +644,7 @@ int sd_ipv4ll_new(sd_ipv4ll **ret) {
if (!ll)
return -ENOMEM;
- ll->n_ref = REFCNT_INIT;
+ ll->n_ref = 1;
ll->state = IPV4LL_STATE_INIT;
ll->index = -1;
ll->fd = -1;
diff --git a/src/libsystemd-network/sd-pppoe.c b/src/libsystemd-network/sd-pppoe.c
index ff064f563f..c6c9da812b 100644
--- a/src/libsystemd-network/sd-pppoe.c
+++ b/src/libsystemd-network/sd-pppoe.c
@@ -36,7 +36,6 @@
#include "random-util.h"
#include "socket-util.h"
#include "async.h"
-#include "refcnt.h"
#include "utf8.h"
#define PPPOE_MAX_PACKET_SIZE 1484
@@ -68,7 +67,7 @@ typedef struct PPPoETags {
} PPPoETags;
struct sd_pppoe {
- RefCount n_ref;
+ unsigned n_ref;
PPPoEState state;
uint64_t host_uniq;
@@ -202,23 +201,34 @@ int sd_pppoe_detach_event(sd_pppoe *ppp) {
}
sd_pppoe *sd_pppoe_ref(sd_pppoe *ppp) {
- if (ppp)
- assert_se(REFCNT_INC(ppp->n_ref) >= 2);
+
+ if (!ppp)
+ return NULL;
+
+ assert(ppp->n_ref > 0);
+ ppp->n_ref++;
return ppp;
}
sd_pppoe *sd_pppoe_unref(sd_pppoe *ppp) {
- if (ppp && REFCNT_DEC(ppp->n_ref) <= 0) {
- pppoe_tags_clear(&ppp->tags);
- free(ppp->ifname);
- free(ppp->service_name);
- sd_pppoe_stop(ppp);
- sd_pppoe_detach_event(ppp);
-
- free(ppp);
- }
+ if (!ppp)
+ return NULL;
+
+ assert(ppp->n_ref > 0);
+ ppp->n_ref--;
+
+ if (ppp->n_ref > 0)
+ return NULL;
+
+ pppoe_tags_clear(&ppp->tags);
+ free(ppp->ifname);
+ free(ppp->service_name);
+ sd_pppoe_stop(ppp);
+ sd_pppoe_detach_event(ppp);
+
+ free(ppp);
return NULL;
}
@@ -231,7 +241,7 @@ int sd_pppoe_new (sd_pppoe **ret) {
if (!ppp)
return -ENOMEM;
- ppp->n_ref = REFCNT_INIT;
+ ppp->n_ref = 1;
ppp->state = _PPPOE_STATE_INVALID;
ppp->ifindex = -1;
ppp->fd = -1;
diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c
index 200499d613..29c20b77e3 100644
--- a/src/libsystemd-network/test-dhcp-client.c
+++ b/src/libsystemd-network/test-dhcp-client.c
@@ -43,16 +43,13 @@ static test_callback_recv_t callback_recv;
static be32_t xid;
static sd_event_source *test_hangcheck;
-static int test_dhcp_hangcheck(sd_event_source *s, uint64_t usec,
- void *userdata)
-{
+static int test_dhcp_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) {
assert_not_reached("Test case should have completed in 2 seconds");
return 0;
}
-static void test_request_basic(sd_event *e)
-{
+static void test_request_basic(sd_event *e) {
int r;
sd_dhcp_client *client;
@@ -87,10 +84,7 @@ static void test_request_basic(sd_event *e)
assert_se(sd_dhcp_client_set_request_option(client,
DHCP_OPTION_DOMAIN_NAME) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_DOMAIN_NAME_SERVER)
- == -EEXIST);
- assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_NTP_SERVER) == -EEXIST);
+ DHCP_OPTION_DOMAIN_NAME_SERVER) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
DHCP_OPTION_PAD) == -EINVAL);
@@ -112,8 +106,7 @@ static void test_request_basic(sd_event *e)
sd_dhcp_client_unref(client);
}
-static void test_checksum(void)
-{
+static void test_checksum(void) {
uint8_t buf[20] = {
0x45, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
0x40, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -126,9 +119,7 @@ static void test_checksum(void)
assert_se(dhcp_packet_checksum((uint8_t*)&buf, 20) == be16toh(0x78ae));
}
-static int check_options(uint8_t code, uint8_t len, const uint8_t *option,
- void *user_data)
-{
+static int check_options(uint8_t code, uint8_t len, const void *option, void *userdata) {
switch(code) {
case DHCP_OPTION_CLIENT_IDENTIFIER:
{
@@ -141,10 +132,10 @@ static int check_options(uint8_t code, uint8_t len, const uint8_t *option,
assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid_len);
assert_se(len == 19);
- assert_se(option[0] == 0xff);
+ assert_se(((uint8_t*) option)[0] == 0xff);
- assert_se(memcmp(&option[1], &iaid, sizeof(iaid)) == 0);
- assert_se(memcmp(&option[5], &duid, duid_len) == 0);
+ assert_se(memcmp((uint8_t*) option + 1, &iaid, sizeof(iaid)) == 0);
+ assert_se(memcmp((uint8_t*) option + 5, &duid, duid_len) == 0);
break;
}
@@ -155,9 +146,7 @@ static int check_options(uint8_t code, uint8_t len, const uint8_t *option,
return 0;
}
-int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
- const void *packet, size_t len)
-{
+int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, const void *packet, size_t len) {
size_t size;
_cleanup_free_ DHCPPacket *discover;
uint16_t ip_check, udp_check;
@@ -202,18 +191,20 @@ int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
return 575;
}
-int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link,
- uint32_t id, const uint8_t *addr,
- size_t addr_len, uint16_t arp_type)
-{
+int dhcp_network_bind_raw_socket(
+ int index,
+ union sockaddr_union *link,
+ uint32_t id,
+ const uint8_t *addr, size_t addr_len,
+ uint16_t arp_type) {
+
if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0)
return -errno;
return test_fd[0];
}
-int dhcp_network_bind_udp_socket(be32_t address, uint16_t port)
-{
+int dhcp_network_bind_udp_socket(be32_t address, uint16_t port) {
int fd;
fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0);
@@ -223,14 +214,11 @@ int dhcp_network_bind_udp_socket(be32_t address, uint16_t port)
return fd;
}
-int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
- const void *packet, size_t len)
-{
+int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, const void *packet, size_t len) {
return 0;
}
-static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp)
-{
+static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp) {
int res;
res = dhcp_option_parse(dhcp, size, check_options, NULL);
@@ -242,8 +230,7 @@ static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp)
return 0;
}
-static void test_discover_message(sd_event *e)
-{
+static void test_discover_message(sd_event *e) {
sd_dhcp_client *client;
int res, r;
diff --git a/src/libsystemd-network/test-dhcp-option.c b/src/libsystemd-network/test-dhcp-option.c
index a63a4ea738..b1ef174849 100644
--- a/src/libsystemd-network/test-dhcp-option.c
+++ b/src/libsystemd-network/test-dhcp-option.c
@@ -145,8 +145,8 @@ static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen) {
}
}
-static int test_options_cb(uint8_t code, uint8_t len, const uint8_t *option, void *user_data) {
- struct option_desc *desc = user_data;
+static int test_options_cb(uint8_t code, uint8_t len, const void *option, void *userdata) {
+ struct option_desc *desc = userdata;
uint8_t *descoption = NULL;
int *desclen = NULL, *descpos = NULL;
uint8_t optcode = 0;
@@ -207,10 +207,10 @@ static int test_options_cb(uint8_t code, uint8_t len, const uint8_t *option, voi
for (i = 0; i < len; i++) {
if (verbose)
- printf("0x%02x(0x%02x) ", option[i],
+ printf("0x%02x(0x%02x) ", ((uint8_t*) option)[i],
descoption[*descpos + 2 + i]);
- assert_se(option[i] == descoption[*descpos + 2 + i]);
+ assert_se(((uint8_t*) option)[i] == descoption[*descpos + 2 + i]);
}
if (verbose)
diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c
index 9f60ab761e..7d8a1f6bd9 100644
--- a/src/libsystemd-network/test-dhcp-server.c
+++ b/src/libsystemd-network/test-dhcp-server.c
@@ -28,6 +28,14 @@
#include "sd-dhcp-server.h"
#include "dhcp-server-internal.h"
+static void test_pool(struct in_addr *address, unsigned size, int ret) {
+ _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
+
+ assert_se(sd_dhcp_server_new(&server, 1) >= 0);
+
+ assert_se(sd_dhcp_server_configure_pool(server, address, 8, 0, size) == ret);
+}
+
static int test_basic(sd_event *event) {
_cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
struct in_addr address_lo = {
@@ -54,15 +62,14 @@ static int test_basic(sd_event *event) {
assert_se(!sd_dhcp_server_unref(server));
assert_se(sd_dhcp_server_start(server) == -EUNATCH);
- assert_se(sd_dhcp_server_set_address(server, &address_any, 28) == -EINVAL);
- assert_se(sd_dhcp_server_set_address(server, &address_lo, 38) == -ERANGE);
- assert_se(sd_dhcp_server_set_address(server, &address_lo, 8) >= 0);
- assert_se(sd_dhcp_server_set_address(server, &address_lo, 8) == -EBUSY);
- assert_se(sd_dhcp_server_set_lease_pool(server, &address_any, 1) == -EINVAL);
- assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 0) == -EINVAL);
- assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 1) >= 0);
- assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 1) == -EBUSY);
+ assert_se(sd_dhcp_server_configure_pool(server, &address_any, 28, 0, 0) == -EINVAL);
+ assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 38, 0, 0) == -ERANGE);
+ assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0);
+ assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) == -EBUSY);
+
+ test_pool(&address_any, 1, -EINVAL);
+ test_pool(&address_lo, 1, 0);
r = sd_dhcp_server_start(server);
@@ -119,12 +126,10 @@ static void test_message_handler(void) {
};
assert_se(sd_dhcp_server_new(&server, 1) >= 0);
- assert_se(sd_dhcp_server_set_address(server, &address_lo, 8) >= 0);
+ assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0);
assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0);
assert_se(sd_dhcp_server_start(server) >= 0);
- assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
- assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 10) >= 0);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
test.end = 0;
diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index 6e62262443..12daac3211 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -501,8 +501,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
return 0;
}
-static int test_client_send_advertise(DHCP6Message *solicit)
-{
+static int test_client_send_advertise(DHCP6Message *solicit) {
DHCP6Message advertise;
advertise.transaction_id = solicit->transaction_id;
diff --git a/src/libsystemd-network/test-ipv4ll.c b/src/libsystemd-network/test-ipv4ll.c
index 5677bfb2d2..d60ee98b25 100644
--- a/src/libsystemd-network/test-ipv4ll.c
+++ b/src/libsystemd-network/test-ipv4ll.c
@@ -39,9 +39,9 @@ static int test_fd[2];
static int basic_request_handler_bind = 0;
static int basic_request_handler_stop = 0;
-static void* basic_request_handler_user_data = (void*)0xCABCAB;
+static void* basic_request_handler_userdata = (void*)0xCABCAB;
static void basic_request_handler(sd_ipv4ll *ll, int event, void *userdata) {
- assert_se(userdata == basic_request_handler_user_data);
+ assert_se(userdata == basic_request_handler_userdata);
switch(event) {
case IPV4LL_EVENT_STOP:
@@ -175,7 +175,7 @@ static void test_basic_request(sd_event *e) {
assert_se(sd_ipv4ll_start(ll) == -EINVAL);
assert_se(sd_ipv4ll_set_callback(ll, basic_request_handler,
- basic_request_handler_user_data) == 0);
+ basic_request_handler_userdata) == 0);
assert_se(sd_ipv4ll_start(ll) == -EINVAL);
assert_se(sd_ipv4ll_set_index(ll, 1) == 0);
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index 7bf1d66dde..d5ad127bcb 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -467,3 +467,9 @@ global:
sd_bus_emit_object_removed;
sd_bus_flush_close_unref;
} LIBSYSTEMD_221;
+
+LIBSYSTEMD_226 {
+global:
+ sd_pid_get_cgroup;
+ sd_peer_get_cgroup;
+} LIBSYSTEMD_222;
diff --git a/src/libsystemd/sd-bus/bus-container.c b/src/libsystemd/sd-bus/bus-container.c
index 101e4af18d..5c607f49b1 100644
--- a/src/libsystemd/sd-bus/bus-container.c
+++ b/src/libsystemd/sd-bus/bus-container.c
@@ -29,10 +29,12 @@
#include "bus-container.h"
int bus_container_connect_socket(sd_bus *b) {
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
_cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
pid_t child;
siginfo_t si;
- int r;
+ int r, error_buf = 0;
+ ssize_t n;
assert(b);
assert(b->input_fd < 0);
@@ -57,6 +59,9 @@ int bus_container_connect_socket(sd_bus *b) {
bus_socket_setup(b);
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
+ return -errno;
+
child = fork();
if (child < 0)
return -errno;
@@ -64,9 +69,11 @@ int bus_container_connect_socket(sd_bus *b) {
if (child == 0) {
pid_t grandchild;
+ pair[0] = safe_close(pair[0]);
+
r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
if (r < 0)
- _exit(255);
+ _exit(EXIT_FAILURE);
/* We just changed PID namespace, however it will only
* take effect on the children we now fork. Hence,
@@ -77,16 +84,16 @@ int bus_container_connect_socket(sd_bus *b) {
grandchild = fork();
if (grandchild < 0)
- _exit(255);
+ _exit(EXIT_FAILURE);
if (grandchild == 0) {
r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
if (r < 0) {
- if (errno == EINPROGRESS)
- _exit(1);
-
- _exit(255);
+ /* Try to send error up */
+ error_buf = errno;
+ (void) write(pair[1], &error_buf, sizeof(error_buf));
+ _exit(EXIT_FAILURE);
}
_exit(EXIT_SUCCESS);
@@ -94,24 +101,41 @@ int bus_container_connect_socket(sd_bus *b) {
r = wait_for_terminate(grandchild, &si);
if (r < 0)
- _exit(255);
+ _exit(EXIT_FAILURE);
if (si.si_code != CLD_EXITED)
- _exit(255);
+ _exit(EXIT_FAILURE);
_exit(si.si_status);
}
+ pair[1] = safe_close(pair[1]);
+
r = wait_for_terminate(child, &si);
if (r < 0)
return r;
+ n = read(pair[0], &error_buf, sizeof(error_buf));
+ if (n < 0)
+ return -errno;
+
+ if (n > 0) {
+ if (n != sizeof(error_buf))
+ return -EIO;
+
+ if (error_buf < 0)
+ return -EIO;
+
+ if (error_buf == EINPROGRESS)
+ return 1;
+
+ if (error_buf > 0)
+ return -error_buf;
+ }
+
if (si.si_code != CLD_EXITED)
return -EIO;
- if (si.si_status == 1)
- return 1;
-
if (si.si_status != EXIT_SUCCESS)
return -EIO;
@@ -125,15 +149,22 @@ int bus_container_connect_kernel(sd_bus *b) {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
} control = {};
+ int error_buf = 0;
+ struct iovec iov = {
+ .iov_base = &error_buf,
+ .iov_len = sizeof(error_buf),
+ };
struct msghdr mh = {
.msg_control = &control,
.msg_controllen = sizeof(control),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
};
struct cmsghdr *cmsg;
pid_t child;
siginfo_t si;
- int r;
- _cleanup_close_ int fd = -1;
+ int r, fd = -1;
+ ssize_t n;
assert(b);
assert(b->input_fd < 0);
@@ -150,7 +181,7 @@ int bus_container_connect_kernel(sd_bus *b) {
if (r < 0)
return r;
- if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
return -errno;
child = fork();
@@ -178,10 +209,13 @@ int bus_container_connect_kernel(sd_bus *b) {
_exit(EXIT_FAILURE);
if (grandchild == 0) {
-
fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
+ if (fd < 0) {
+ /* Try to send error up */
+ error_buf = errno;
+ (void) write(pair[1], &error_buf, sizeof(error_buf));
_exit(EXIT_FAILURE);
+ }
cmsg = CMSG_FIRSTHDR(&mh);
cmsg->cmsg_level = SOL_SOCKET;
@@ -213,20 +247,17 @@ int bus_container_connect_kernel(sd_bus *b) {
if (r < 0)
return r;
- if (si.si_code != CLD_EXITED)
- return -EIO;
-
- if (si.si_status != EXIT_SUCCESS)
- return -EIO;
-
- if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
+ n = recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC);
+ if (n < 0)
return -errno;
- CMSG_FOREACH(cmsg, &mh)
+ CMSG_FOREACH(cmsg, &mh) {
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
int *fds;
unsigned n_fds;
+ assert(fd < 0);
+
fds = (int*) CMSG_DATA(cmsg);
n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
@@ -237,9 +268,18 @@ int bus_container_connect_kernel(sd_bus *b) {
fd = fds[0];
}
+ }
+
+ /* If there's an fd passed, we are good. */
+ if (fd >= 0) {
+ b->input_fd = b->output_fd = fd;
+ return bus_kernel_take_fd(b);
+ }
- b->input_fd = b->output_fd = fd;
- fd = -1;
+ /* If there's an error passed, use it */
+ if (n == sizeof(error_buf) && error_buf > 0)
+ return -error_buf;
- return bus_kernel_take_fd(b);
+ /* Otherwise, we have no clue */
+ return -EIO;
}
diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c
index 1c365b7fcd..c3cc2b7212 100644
--- a/src/libsystemd/sd-bus/bus-creds.c
+++ b/src/libsystemd/sd-bus/bus-creds.c
@@ -1062,8 +1062,8 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
if (missing & SD_BUS_CREDS_AUDIT_SESSION_ID) {
r = audit_session_from_pid(pid, &c->audit_session_id);
- if (r == -ENXIO) {
- /* ENXIO means: no audit session id assigned */
+ if (r == -ENODATA) {
+ /* ENODATA means: no audit session id assigned */
c->audit_session_id = AUDIT_SESSION_INVALID;
c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID;
} else if (r < 0) {
@@ -1075,8 +1075,8 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
if (missing & SD_BUS_CREDS_AUDIT_LOGIN_UID) {
r = audit_loginuid_from_pid(pid, &c->audit_login_uid);
- if (r == -ENXIO) {
- /* ENXIO means: no audit login uid assigned */
+ if (r == -ENODATA) {
+ /* ENODATA means: no audit login uid assigned */
c->audit_login_uid = UID_INVALID;
c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID;
} else if (r < 0) {
diff --git a/src/libsystemd/sd-bus/bus-match.c b/src/libsystemd/sd-bus/bus-match.c
index 2b83f439a7..7234e7926a 100644
--- a/src/libsystemd/sd-bus/bus-match.c
+++ b/src/libsystemd/sd-bus/bus-match.c
@@ -932,7 +932,7 @@ fail:
}
char *bus_match_to_string(struct bus_match_component *components, unsigned n_components) {
- _cleanup_free_ FILE *f = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
char *buffer = NULL;
size_t size = 0;
unsigned i;
diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c
index a21ed8cb62..366a026426 100644
--- a/src/libsystemd/sd-bus/bus-message.c
+++ b/src/libsystemd/sd-bus/bus-message.c
@@ -1024,7 +1024,9 @@ _public_ const char *sd_bus_message_get_sender(sd_bus_message *m) {
_public_ const sd_bus_error *sd_bus_message_get_error(sd_bus_message *m) {
assert_return(m, NULL);
- assert_return(sd_bus_error_is_set(&m->error), NULL);
+
+ if (!sd_bus_error_is_set(&m->error))
+ return NULL;
return &m->error;
}
diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c
index 0593aa658a..1d061cb9cf 100644
--- a/src/libsystemd/sd-bus/bus-objects.c
+++ b/src/libsystemd/sd-bus/bus-objects.c
@@ -169,11 +169,18 @@ static int add_enumerated_to_set(
return 0;
}
+enum {
+ /* if set, add_subtree() works recursively */
+ CHILDREN_RECURSIVE = (1U << 1),
+ /* if set, add_subtree() scans object-manager hierarchies recursively */
+ CHILDREN_SUBHIERARCHIES = (1U << 0),
+};
+
static int add_subtree_to_set(
sd_bus *bus,
const char *prefix,
struct node *n,
- bool skip_subhierarchies,
+ unsigned int flags,
Set *s,
sd_bus_error *error) {
@@ -205,8 +212,9 @@ static int add_subtree_to_set(
if (r < 0 && r != -EEXIST)
return r;
- if (!skip_subhierarchies || !i->object_managers) {
- r = add_subtree_to_set(bus, prefix, i, skip_subhierarchies, s, error);
+ if ((flags & CHILDREN_RECURSIVE) &&
+ ((flags & CHILDREN_SUBHIERARCHIES) || !i->object_managers)) {
+ r = add_subtree_to_set(bus, prefix, i, flags, s, error);
if (r < 0)
return r;
if (bus->nodes_modified)
@@ -221,7 +229,7 @@ static int get_child_nodes(
sd_bus *bus,
const char *prefix,
struct node *n,
- bool skip_subhierarchies,
+ unsigned int flags,
Set **_s,
sd_bus_error *error) {
@@ -237,7 +245,7 @@ static int get_child_nodes(
if (!s)
return -ENOMEM;
- r = add_subtree_to_set(bus, prefix, n, skip_subhierarchies, s, error);
+ r = add_subtree_to_set(bus, prefix, n, flags, s, error);
if (r < 0) {
set_free_free(s);
return r;
@@ -907,7 +915,7 @@ static int process_introspect(
assert(n);
assert(found_object);
- r = get_child_nodes(bus, m->path, n, false, &s, &error);
+ r = get_child_nodes(bus, m->path, n, 0, &s, &error);
if (r < 0)
return bus_maybe_reply_error(m, r, &error);
if (bus->nodes_modified)
@@ -1173,7 +1181,7 @@ static int process_get_managed_objects(
if (require_fallback || !n->object_managers)
return 0;
- r = get_child_nodes(bus, m->path, n, true, &s, &error);
+ r = get_child_nodes(bus, m->path, n, CHILDREN_RECURSIVE, &s, &error);
if (r < 0)
return r;
if (bus->nodes_modified)
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index 5285278d92..4a9181613a 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -1025,19 +1025,30 @@ static int bus_start_address(sd_bus *b) {
if (b->exec_path)
r = bus_socket_exec(b);
+
else if ((b->nspid > 0 || b->machine) && b->kernel) {
r = bus_container_connect_kernel(b);
if (r < 0 && !IN_SET(r, -ENOENT, -ESOCKTNOSUPPORT))
container_kdbus_available = true;
- } else if (!container_kdbus_available && (b->nspid > 0 || b->machine) && b->sockaddr.sa.sa_family != AF_UNSPEC)
- r = bus_container_connect_socket(b);
- else if (b->kernel) {
+
+ } else if ((b->nspid > 0 || b->machine) && b->sockaddr.sa.sa_family != AF_UNSPEC) {
+ if (!container_kdbus_available)
+ r = bus_container_connect_socket(b);
+ else
+ skipped = true;
+
+ } else if (b->kernel) {
r = bus_kernel_connect(b);
if (r < 0 && !IN_SET(r, -ENOENT, -ESOCKTNOSUPPORT))
kdbus_available = true;
- } else if (!kdbus_available && b->sockaddr.sa.sa_family != AF_UNSPEC)
- r = bus_socket_connect(b);
- else
+
+ } else if (b->sockaddr.sa.sa_family != AF_UNSPEC) {
+ if (!kdbus_available)
+ r = bus_socket_connect(b);
+ else
+ skipped = true;
+
+ } else
skipped = true;
if (!skipped) {
@@ -1230,6 +1241,8 @@ fail:
int bus_set_address_user(sd_bus *b) {
const char *e;
+ uid_t uid;
+ int r;
assert(b);
@@ -1237,6 +1250,10 @@ int bus_set_address_user(sd_bus *b) {
if (e)
return sd_bus_set_address(b, e);
+ r = cg_pid_get_owner_uid(0, &uid);
+ if (r < 0)
+ uid = getuid();
+
e = secure_getenv("XDG_RUNTIME_DIR");
if (e) {
_cleanup_free_ char *ee = NULL;
@@ -1245,9 +1262,9 @@ int bus_set_address_user(sd_bus *b) {
if (!ee)
return -ENOMEM;
- (void) asprintf(&b->address, KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT, getuid(), ee);
+ (void) asprintf(&b->address, KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT, uid, ee);
} else
- (void) asprintf(&b->address, KERNEL_USER_BUS_ADDRESS_FMT, getuid());
+ (void) asprintf(&b->address, KERNEL_USER_BUS_ADDRESS_FMT, uid);
if (!b->address)
return -ENOMEM;
diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c
index edd5033db2..580117165a 100644
--- a/src/libsystemd/sd-bus/test-bus-creds.c
+++ b/src/libsystemd/sd-bus/test-bus-creds.c
@@ -22,11 +22,17 @@
#include "sd-bus.h"
#include "bus-dump.h"
#include "bus-util.h"
+#include "cgroup-util.h"
int main(int argc, char *argv[]) {
_cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
int r;
+ if (cg_unified() == -ENOEXEC) {
+ puts("Skipping test: /sys/fs/cgroup/ not available");
+ return EXIT_TEST_SKIP;
+ }
+
r = sd_bus_creds_new_from_pid(&creds, 0, _SD_BUS_CREDS_ALL);
assert_se(r >= 0);
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index 0e33ced342..fd39a56225 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -56,9 +56,22 @@ typedef enum EventSourceType {
_SOURCE_EVENT_SOURCE_TYPE_INVALID = -1
} EventSourceType;
+/* All objects we use in epoll events start with this value, so that
+ * we know how to dispatch it */
+typedef enum WakeupType {
+ WAKEUP_NONE,
+ WAKEUP_EVENT_SOURCE,
+ WAKEUP_CLOCK_DATA,
+ WAKEUP_SIGNAL_DATA,
+ _WAKEUP_TYPE_MAX,
+ _WAKEUP_TYPE_INVALID = -1,
+} WakeupType;
+
#define EVENT_SOURCE_IS_TIME(t) IN_SET((t), SOURCE_TIME_REALTIME, SOURCE_TIME_BOOTTIME, SOURCE_TIME_MONOTONIC, SOURCE_TIME_REALTIME_ALARM, SOURCE_TIME_BOOTTIME_ALARM)
struct sd_event_source {
+ WakeupType wakeup;
+
unsigned n_ref;
sd_event *event;
@@ -120,6 +133,7 @@ struct sd_event_source {
};
struct clock_data {
+ WakeupType wakeup;
int fd;
/* For all clocks we maintain two priority queues each, one
@@ -136,11 +150,23 @@ struct clock_data {
bool needs_rearm:1;
};
+struct signal_data {
+ WakeupType wakeup;
+
+ /* For each priority we maintain one signal fd, so that we
+ * only have to dequeue a single event per priority at a
+ * time. */
+
+ int fd;
+ int64_t priority;
+ sigset_t sigset;
+ sd_event_source *current;
+};
+
struct sd_event {
unsigned n_ref;
int epoll_fd;
- int signal_fd;
int watchdog_fd;
Prioq *pending;
@@ -157,8 +183,8 @@ struct sd_event {
usec_t perturb;
- sigset_t sigset;
- sd_event_source **signal_sources;
+ sd_event_source **signal_sources; /* indexed by signal number */
+ Hashmap *signal_data; /* indexed by priority */
Hashmap *child_sources;
unsigned n_enabled_child_sources;
@@ -355,6 +381,7 @@ static int exit_prioq_compare(const void *a, const void *b) {
static void free_clock_data(struct clock_data *d) {
assert(d);
+ assert(d->wakeup == WAKEUP_CLOCK_DATA);
safe_close(d->fd);
prioq_free(d->earliest);
@@ -378,7 +405,6 @@ static void event_free(sd_event *e) {
*(e->default_event_ptr) = NULL;
safe_close(e->epoll_fd);
- safe_close(e->signal_fd);
safe_close(e->watchdog_fd);
free_clock_data(&e->realtime);
@@ -392,6 +418,7 @@ static void event_free(sd_event *e) {
prioq_free(e->exit);
free(e->signal_sources);
+ hashmap_free(e->signal_data);
hashmap_free(e->child_sources);
set_free(e->post_sources);
@@ -409,13 +436,12 @@ _public_ int sd_event_new(sd_event** ret) {
return -ENOMEM;
e->n_ref = 1;
- e->signal_fd = e->watchdog_fd = e->epoll_fd = e->realtime.fd = e->boottime.fd = e->monotonic.fd = e->realtime_alarm.fd = e->boottime_alarm.fd = -1;
+ e->watchdog_fd = e->epoll_fd = e->realtime.fd = e->boottime.fd = e->monotonic.fd = e->realtime_alarm.fd = e->boottime_alarm.fd = -1;
e->realtime.next = e->boottime.next = e->monotonic.next = e->realtime_alarm.next = e->boottime_alarm.next = USEC_INFINITY;
+ e->realtime.wakeup = e->boottime.wakeup = e->monotonic.wakeup = e->realtime_alarm.wakeup = e->boottime_alarm.wakeup = WAKEUP_CLOCK_DATA;
e->original_pid = getpid();
e->perturb = USEC_INFINITY;
- assert_se(sigemptyset(&e->sigset) == 0);
-
e->pending = prioq_new(pending_prioq_compare);
if (!e->pending) {
r = -ENOMEM;
@@ -481,7 +507,8 @@ static void source_io_unregister(sd_event_source *s) {
return;
r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, NULL);
- assert_log(r >= 0);
+ if (r < 0)
+ log_debug_errno(errno, "Failed to remove source %s from epoll: %m", strna(s->description));
s->io.registered = false;
}
@@ -508,7 +535,6 @@ static int source_io_register(
r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_MOD, s->io.fd, &ev);
else
r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_ADD, s->io.fd, &ev);
-
if (r < 0)
return -errno;
@@ -590,45 +616,171 @@ static struct clock_data* event_get_clock_data(sd_event *e, EventSourceType t) {
}
}
-static bool need_signal(sd_event *e, int signal) {
- return (e->signal_sources && e->signal_sources[signal] &&
- e->signal_sources[signal]->enabled != SD_EVENT_OFF)
- ||
- (signal == SIGCHLD &&
- e->n_enabled_child_sources > 0);
-}
+static int event_make_signal_data(
+ sd_event *e,
+ int sig,
+ struct signal_data **ret) {
-static int event_update_signal_fd(sd_event *e) {
struct epoll_event ev = {};
- bool add_to_epoll;
+ struct signal_data *d;
+ bool added = false;
+ sigset_t ss_copy;
+ int64_t priority;
int r;
assert(e);
if (event_pid_changed(e))
- return 0;
+ return -ECHILD;
- add_to_epoll = e->signal_fd < 0;
+ if (e->signal_sources && e->signal_sources[sig])
+ priority = e->signal_sources[sig]->priority;
+ else
+ priority = 0;
- r = signalfd(e->signal_fd, &e->sigset, SFD_NONBLOCK|SFD_CLOEXEC);
- if (r < 0)
- return -errno;
+ d = hashmap_get(e->signal_data, &priority);
+ if (d) {
+ if (sigismember(&d->sigset, sig) > 0) {
+ if (ret)
+ *ret = d;
+ return 0;
+ }
+ } else {
+ r = hashmap_ensure_allocated(&e->signal_data, &uint64_hash_ops);
+ if (r < 0)
+ return r;
+
+ d = new0(struct signal_data, 1);
+ if (!d)
+ return -ENOMEM;
+
+ d->wakeup = WAKEUP_SIGNAL_DATA;
+ d->fd = -1;
+ d->priority = priority;
+
+ r = hashmap_put(e->signal_data, &d->priority, d);
+ if (r < 0)
+ return r;
+
+ added = true;
+ }
+
+ ss_copy = d->sigset;
+ assert_se(sigaddset(&ss_copy, sig) >= 0);
+
+ r = signalfd(d->fd, &ss_copy, SFD_NONBLOCK|SFD_CLOEXEC);
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
- e->signal_fd = r;
+ d->sigset = ss_copy;
- if (!add_to_epoll)
+ if (d->fd >= 0) {
+ if (ret)
+ *ret = d;
return 0;
+ }
+
+ d->fd = r;
ev.events = EPOLLIN;
- ev.data.ptr = INT_TO_PTR(SOURCE_SIGNAL);
+ ev.data.ptr = d;
- r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, e->signal_fd, &ev);
- if (r < 0) {
- e->signal_fd = safe_close(e->signal_fd);
- return -errno;
+ r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, d->fd, &ev);
+ if (r < 0) {
+ r = -errno;
+ goto fail;
}
+ if (ret)
+ *ret = d;
+
return 0;
+
+fail:
+ if (added) {
+ d->fd = safe_close(d->fd);
+ hashmap_remove(e->signal_data, &d->priority);
+ free(d);
+ }
+
+ return r;
+}
+
+static void event_unmask_signal_data(sd_event *e, struct signal_data *d, int sig) {
+ assert(e);
+ assert(d);
+
+ /* Turns off the specified signal in the signal data
+ * object. If the signal mask of the object becomes empty that
+ * way removes it. */
+
+ if (sigismember(&d->sigset, sig) == 0)
+ return;
+
+ assert_se(sigdelset(&d->sigset, sig) >= 0);
+
+ if (sigisemptyset(&d->sigset)) {
+
+ /* If all the mask is all-zero we can get rid of the structure */
+ hashmap_remove(e->signal_data, &d->priority);
+ assert(!d->current);
+ safe_close(d->fd);
+ free(d);
+ return;
+ }
+
+ assert(d->fd >= 0);
+
+ if (signalfd(d->fd, &d->sigset, SFD_NONBLOCK|SFD_CLOEXEC) < 0)
+ log_debug_errno(errno, "Failed to unset signal bit, ignoring: %m");
+}
+
+static void event_gc_signal_data(sd_event *e, const int64_t *priority, int sig) {
+ struct signal_data *d;
+ static const int64_t zero_priority = 0;
+
+ assert(e);
+
+ /* Rechecks if the specified signal is still something we are
+ * interested in. If not, we'll unmask it, and possibly drop
+ * the signalfd for it. */
+
+ if (sig == SIGCHLD &&
+ e->n_enabled_child_sources > 0)
+ return;
+
+ if (e->signal_sources &&
+ e->signal_sources[sig] &&
+ e->signal_sources[sig]->enabled != SD_EVENT_OFF)
+ return;
+
+ /*
+ * The specified signal might be enabled in three different queues:
+ *
+ * 1) the one that belongs to the priority passed (if it is non-NULL)
+ * 2) the one that belongs to the priority of the event source of the signal (if there is one)
+ * 3) the 0 priority (to cover the SIGCHLD case)
+ *
+ * Hence, let's remove it from all three here.
+ */
+
+ if (priority) {
+ d = hashmap_get(e->signal_data, priority);
+ if (d)
+ event_unmask_signal_data(e, d, sig);
+ }
+
+ if (e->signal_sources && e->signal_sources[sig]) {
+ d = hashmap_get(e->signal_data, &e->signal_sources[sig]->priority);
+ if (d)
+ event_unmask_signal_data(e, d, sig);
+ }
+
+ d = hashmap_get(e->signal_data, &zero_priority);
+ if (d)
+ event_unmask_signal_data(e, d, sig);
}
static void source_disconnect(sd_event_source *s) {
@@ -667,17 +819,11 @@ static void source_disconnect(sd_event_source *s) {
case SOURCE_SIGNAL:
if (s->signal.sig > 0) {
+
if (s->event->signal_sources)
s->event->signal_sources[s->signal.sig] = NULL;
- /* If the signal was on and now it is off... */
- if (s->enabled != SD_EVENT_OFF && !need_signal(s->event, s->signal.sig)) {
- assert_se(sigdelset(&s->event->sigset, s->signal.sig) == 0);
-
- (void) event_update_signal_fd(s->event);
- /* If disabling failed, we might get a spurious event,
- * but otherwise nothing bad should happen. */
- }
+ event_gc_signal_data(s->event, &s->priority, s->signal.sig);
}
break;
@@ -687,18 +833,10 @@ static void source_disconnect(sd_event_source *s) {
if (s->enabled != SD_EVENT_OFF) {
assert(s->event->n_enabled_child_sources > 0);
s->event->n_enabled_child_sources--;
-
- /* We know the signal was on, if it is off now... */
- if (!need_signal(s->event, SIGCHLD)) {
- assert_se(sigdelset(&s->event->sigset, SIGCHLD) == 0);
-
- (void) event_update_signal_fd(s->event);
- /* If disabling failed, we might get a spurious event,
- * but otherwise nothing bad should happen. */
- }
}
- hashmap_remove(s->event->child_sources, INT_TO_PTR(s->child.pid));
+ (void) hashmap_remove(s->event->child_sources, INT_TO_PTR(s->child.pid));
+ event_gc_signal_data(s->event, &s->priority, SIGCHLD);
}
break;
@@ -777,6 +915,14 @@ static int source_set_pending(sd_event_source *s, bool b) {
d->needs_rearm = true;
}
+ if (s->type == SOURCE_SIGNAL && !b) {
+ struct signal_data *d;
+
+ d = hashmap_get(s->event->signal_data, &s->priority);
+ if (d && d->current == s)
+ d->current = NULL;
+ }
+
return 0;
}
@@ -826,6 +972,7 @@ _public_ int sd_event_add_io(
if (!s)
return -ENOMEM;
+ s->wakeup = WAKEUP_EVENT_SOURCE;
s->io.fd = fd;
s->io.events = events;
s->io.callback = callback;
@@ -882,7 +1029,7 @@ static int event_setup_timer_fd(
return -errno;
ev.events = EPOLLIN;
- ev.data.ptr = INT_TO_PTR(clock_to_event_source_type(clock));
+ ev.data.ptr = d;
r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, fd, &ev);
if (r < 0) {
@@ -992,9 +1139,9 @@ _public_ int sd_event_add_signal(
void *userdata) {
sd_event_source *s;
+ struct signal_data *d;
sigset_t ss;
int r;
- bool previous;
assert_return(e, -EINVAL);
assert_return(sig > 0, -EINVAL);
@@ -1019,8 +1166,6 @@ _public_ int sd_event_add_signal(
} else if (e->signal_sources[sig])
return -EBUSY;
- previous = need_signal(e, sig);
-
s = source_new(e, !ret, SOURCE_SIGNAL);
if (!s)
return -ENOMEM;
@@ -1032,14 +1177,10 @@ _public_ int sd_event_add_signal(
e->signal_sources[sig] = s;
- if (!previous) {
- assert_se(sigaddset(&e->sigset, sig) == 0);
-
- r = event_update_signal_fd(e);
- if (r < 0) {
- source_free(s);
- return r;
- }
+ r = event_make_signal_data(e, sig, &d);
+ if (r < 0) {
+ source_free(s);
+ return r;
}
/* Use the signal name as description for the event source by default */
@@ -1061,7 +1202,6 @@ _public_ int sd_event_add_child(
sd_event_source *s;
int r;
- bool previous;
assert_return(e, -EINVAL);
assert_return(pid > 1, -EINVAL);
@@ -1078,8 +1218,6 @@ _public_ int sd_event_add_child(
if (hashmap_contains(e->child_sources, INT_TO_PTR(pid)))
return -EBUSY;
- previous = need_signal(e, SIGCHLD);
-
s = source_new(e, !ret, SOURCE_CHILD);
if (!s)
return -ENOMEM;
@@ -1098,14 +1236,11 @@ _public_ int sd_event_add_child(
e->n_enabled_child_sources ++;
- if (!previous) {
- assert_se(sigaddset(&e->sigset, SIGCHLD) == 0);
-
- r = event_update_signal_fd(e);
- if (r < 0) {
- source_free(s);
- return r;
- }
+ r = event_make_signal_data(e, SIGCHLD, NULL);
+ if (r < 0) {
+ e->n_enabled_child_sources--;
+ source_free(s);
+ return r;
}
e->need_process_child = true;
@@ -1405,6 +1540,8 @@ _public_ int sd_event_source_get_priority(sd_event_source *s, int64_t *priority)
}
_public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority) {
+ int r;
+
assert_return(s, -EINVAL);
assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(s->event), -ECHILD);
@@ -1412,7 +1549,25 @@ _public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority)
if (s->priority == priority)
return 0;
- s->priority = priority;
+ if (s->type == SOURCE_SIGNAL && s->enabled != SD_EVENT_OFF) {
+ struct signal_data *old, *d;
+
+ /* Move us from the signalfd belonging to the old
+ * priority to the signalfd of the new priority */
+
+ assert_se(old = hashmap_get(s->event->signal_data, &s->priority));
+
+ s->priority = priority;
+
+ r = event_make_signal_data(s->event, s->signal.sig, &d);
+ if (r < 0) {
+ s->priority = old->priority;
+ return r;
+ }
+
+ event_unmask_signal_data(s->event, old, s->signal.sig);
+ } else
+ s->priority = priority;
if (s->pending)
prioq_reshuffle(s->event->pending, s, &s->pending_index);
@@ -1477,34 +1632,18 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) {
}
case SOURCE_SIGNAL:
- assert(need_signal(s->event, s->signal.sig));
-
s->enabled = m;
- if (!need_signal(s->event, s->signal.sig)) {
- assert_se(sigdelset(&s->event->sigset, s->signal.sig) == 0);
-
- (void) event_update_signal_fd(s->event);
- /* If disabling failed, we might get a spurious event,
- * but otherwise nothing bad should happen. */
- }
-
+ event_gc_signal_data(s->event, &s->priority, s->signal.sig);
break;
case SOURCE_CHILD:
- assert(need_signal(s->event, SIGCHLD));
-
s->enabled = m;
assert(s->event->n_enabled_child_sources > 0);
s->event->n_enabled_child_sources--;
- if (!need_signal(s->event, SIGCHLD)) {
- assert_se(sigdelset(&s->event->sigset, SIGCHLD) == 0);
-
- (void) event_update_signal_fd(s->event);
- }
-
+ event_gc_signal_data(s->event, &s->priority, SIGCHLD);
break;
case SOURCE_EXIT:
@@ -1550,37 +1689,33 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) {
}
case SOURCE_SIGNAL:
- /* Check status before enabling. */
- if (!need_signal(s->event, s->signal.sig)) {
- assert_se(sigaddset(&s->event->sigset, s->signal.sig) == 0);
-
- r = event_update_signal_fd(s->event);
- if (r < 0) {
- s->enabled = SD_EVENT_OFF;
- return r;
- }
- }
s->enabled = m;
+
+ r = event_make_signal_data(s->event, s->signal.sig, NULL);
+ if (r < 0) {
+ s->enabled = SD_EVENT_OFF;
+ event_gc_signal_data(s->event, &s->priority, s->signal.sig);
+ return r;
+ }
+
break;
case SOURCE_CHILD:
- /* Check status before enabling. */
- if (s->enabled == SD_EVENT_OFF) {
- if (!need_signal(s->event, SIGCHLD)) {
- assert_se(sigaddset(&s->event->sigset, s->signal.sig) == 0);
-
- r = event_update_signal_fd(s->event);
- if (r < 0) {
- s->enabled = SD_EVENT_OFF;
- return r;
- }
- }
+ if (s->enabled == SD_EVENT_OFF)
s->event->n_enabled_child_sources++;
- }
s->enabled = m;
+
+ r = event_make_signal_data(s->event, SIGCHLD, NULL);
+ if (r < 0) {
+ s->enabled = SD_EVENT_OFF;
+ s->event->n_enabled_child_sources--;
+ event_gc_signal_data(s->event, &s->priority, SIGCHLD);
+ return r;
+ }
+
break;
case SOURCE_EXIT:
@@ -2024,20 +2159,35 @@ static int process_child(sd_event *e) {
return 0;
}
-static int process_signal(sd_event *e, uint32_t events) {
+static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) {
bool read_one = false;
int r;
assert(e);
-
assert_return(events == EPOLLIN, -EIO);
+ /* If there's a signal queued on this priority and SIGCHLD is
+ on this priority too, then make sure to recheck the
+ children we watch. This is because we only ever dequeue
+ the first signal per priority, and if we dequeue one, and
+ SIGCHLD might be enqueued later we wouldn't know, but we
+ might have higher priority children we care about hence we
+ need to check that explicitly. */
+
+ if (sigismember(&d->sigset, SIGCHLD))
+ e->need_process_child = true;
+
+ /* If there's already an event source pending for this
+ * priority we don't read another */
+ if (d->current)
+ return 0;
+
for (;;) {
struct signalfd_siginfo si;
ssize_t n;
sd_event_source *s = NULL;
- n = read(e->signal_fd, &si, sizeof(si));
+ n = read(d->fd, &si, sizeof(si));
if (n < 0) {
if (errno == EAGAIN || errno == EINTR)
return read_one;
@@ -2052,24 +2202,21 @@ static int process_signal(sd_event *e, uint32_t events) {
read_one = true;
- if (si.ssi_signo == SIGCHLD) {
- r = process_child(e);
- if (r < 0)
- return r;
- if (r > 0)
- continue;
- }
-
if (e->signal_sources)
s = e->signal_sources[si.ssi_signo];
-
if (!s)
continue;
+ if (s->pending)
+ continue;
s->signal.siginfo = si;
+ d->current = s;
+
r = source_set_pending(s, true);
if (r < 0)
return r;
+
+ return 1;
}
}
@@ -2387,23 +2534,31 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
for (i = 0; i < m; i++) {
- if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_TIME_REALTIME))
- r = flush_timer(e, e->realtime.fd, ev_queue[i].events, &e->realtime.next);
- else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_TIME_BOOTTIME))
- r = flush_timer(e, e->boottime.fd, ev_queue[i].events, &e->boottime.next);
- else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_TIME_MONOTONIC))
- r = flush_timer(e, e->monotonic.fd, ev_queue[i].events, &e->monotonic.next);
- else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_TIME_REALTIME_ALARM))
- r = flush_timer(e, e->realtime_alarm.fd, ev_queue[i].events, &e->realtime_alarm.next);
- else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_TIME_BOOTTIME_ALARM))
- r = flush_timer(e, e->boottime_alarm.fd, ev_queue[i].events, &e->boottime_alarm.next);
- else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_SIGNAL))
- r = process_signal(e, ev_queue[i].events);
- else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_WATCHDOG))
+ if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_WATCHDOG))
r = flush_timer(e, e->watchdog_fd, ev_queue[i].events, NULL);
- else
- r = process_io(e, ev_queue[i].data.ptr, ev_queue[i].events);
+ else {
+ WakeupType *t = ev_queue[i].data.ptr;
+
+ switch (*t) {
+
+ case WAKEUP_EVENT_SOURCE:
+ r = process_io(e, ev_queue[i].data.ptr, ev_queue[i].events);
+ break;
+ case WAKEUP_CLOCK_DATA: {
+ struct clock_data *d = ev_queue[i].data.ptr;
+ r = flush_timer(e, d->fd, ev_queue[i].events, &d->next);
+ break;
+ }
+
+ case WAKEUP_SIGNAL_DATA:
+ r = process_signal(e, ev_queue[i].data.ptr, ev_queue[i].events);
+ break;
+
+ default:
+ assert_not_reached("Invalid wake-up pointer");
+ }
+ }
if (r < 0)
goto finish;
}
diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c
index 408e1679a2..c092e56b7a 100644
--- a/src/libsystemd/sd-event/test-event.c
+++ b/src/libsystemd/sd-event/test-event.c
@@ -156,7 +156,7 @@ static int exit_handler(sd_event_source *s, void *userdata) {
return 3;
}
-int main(int argc, char *argv[]) {
+static void test_basic(void) {
sd_event *e = NULL;
sd_event_source *w = NULL, *x = NULL, *y = NULL, *z = NULL, *q = NULL, *t = NULL;
static const char ch = 'x';
@@ -244,6 +244,70 @@ int main(int argc, char *argv[]) {
safe_close_pair(b);
safe_close_pair(d);
safe_close_pair(k);
+}
+
+static int last_rtqueue_sigval = 0;
+static int n_rtqueue = 0;
+
+static int rtqueue_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ last_rtqueue_sigval = si->ssi_int;
+ n_rtqueue ++;
+ return 0;
+}
+
+static void test_rtqueue(void) {
+ sd_event_source *u = NULL, *v = NULL, *s = NULL;
+ sd_event *e = NULL;
+
+ assert_se(sd_event_default(&e) >= 0);
+
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN+2, SIGRTMIN+3, SIGUSR2, -1) >= 0);
+ assert_se(sd_event_add_signal(e, &u, SIGRTMIN+2, rtqueue_handler, NULL) >= 0);
+ assert_se(sd_event_add_signal(e, &v, SIGRTMIN+3, rtqueue_handler, NULL) >= 0);
+ assert_se(sd_event_add_signal(e, &s, SIGUSR2, rtqueue_handler, NULL) >= 0);
+
+ assert_se(sd_event_source_set_priority(v, -10) >= 0);
+
+ assert(sigqueue(getpid(), SIGRTMIN+2, (union sigval) { .sival_int = 1 }) >= 0);
+ assert(sigqueue(getpid(), SIGRTMIN+3, (union sigval) { .sival_int = 2 }) >= 0);
+ assert(sigqueue(getpid(), SIGUSR2, (union sigval) { .sival_int = 3 }) >= 0);
+ assert(sigqueue(getpid(), SIGRTMIN+3, (union sigval) { .sival_int = 4 }) >= 0);
+ assert(sigqueue(getpid(), SIGUSR2, (union sigval) { .sival_int = 5 }) >= 0);
+
+ assert_se(n_rtqueue == 0);
+ assert_se(last_rtqueue_sigval == 0);
+
+ assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+ assert_se(n_rtqueue == 1);
+ assert_se(last_rtqueue_sigval == 2); /* first SIGRTMIN+3 */
+
+ assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+ assert_se(n_rtqueue == 2);
+ assert_se(last_rtqueue_sigval == 4); /* second SIGRTMIN+3 */
+
+ assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+ assert_se(n_rtqueue == 3);
+ assert_se(last_rtqueue_sigval == 3); /* first SIGUSR2 */
+
+ assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
+ assert_se(n_rtqueue == 4);
+ assert_se(last_rtqueue_sigval == 1); /* SIGRTMIN+2 */
+
+ assert_se(sd_event_run(e, 0) == 0); /* the other SIGUSR2 is dropped, because the first one was still queued */
+ assert_se(n_rtqueue == 4);
+ assert_se(last_rtqueue_sigval == 1);
+
+ sd_event_source_unref(u);
+ sd_event_source_unref(v);
+ sd_event_source_unref(s);
+
+ sd_event_unref(e);
+}
+
+int main(int argc, char *argv[]) {
+
+ test_basic();
+ test_rtqueue();
return 0;
}
diff --git a/src/libsystemd/sd-hwdb/sd-hwdb.c b/src/libsystemd/sd-hwdb/sd-hwdb.c
index 40aa77ee5c..06c98314e8 100644
--- a/src/libsystemd/sd-hwdb/sd-hwdb.c
+++ b/src/libsystemd/sd-hwdb/sd-hwdb.c
@@ -79,8 +79,7 @@ static bool linebuf_add(struct linebuf *buf, const char *s, size_t len) {
return true;
}
-static bool linebuf_add_char(struct linebuf *buf, char c)
-{
+static bool linebuf_add_char(struct linebuf *buf, char c) {
if (buf->len + 1 >= sizeof(buf->bytes))
return false;
buf->bytes[buf->len++] = c;
@@ -269,13 +268,13 @@ static int trie_search_f(sd_hwdb *hwdb, const char *search) {
}
static const char hwdb_bin_paths[] =
- "/etc/systemd/hwdb/hwdb.bin\0"
- "/etc/udev/hwdb.bin\0"
- "/usr/lib/systemd/hwdb/hwdb.bin\0"
+ "/etc/systemd/hwdb/hwdb.bin\0"
+ "/etc/udev/hwdb.bin\0"
+ "/usr/lib/systemd/hwdb/hwdb.bin\0"
#ifdef HAVE_SPLIT_USR
- "/lib/systemd/hwdb/hwdb.bin\0"
+ "/lib/systemd/hwdb/hwdb.bin\0"
#endif
- UDEVLIBEXECDIR "/hwdb.bin\0";
+ UDEVLIBEXECDIR "/hwdb.bin\0";
_public_ int sd_hwdb_new(sd_hwdb **ret) {
_cleanup_hwdb_unref_ sd_hwdb *hwdb = NULL;
diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c
index 0eadc8c747..265c7c7db2 100644
--- a/src/libsystemd/sd-login/sd-login.c
+++ b/src/libsystemd/sd-login/sd-login.c
@@ -35,6 +35,16 @@
#include "hostname-util.h"
#include "sd-login.h"
+/* Error codes:
+ *
+ * invalid input parameters → -EINVAL
+ * invalid fd → -EBADF
+ * process does not exist → -ESRCH
+ * cgroup does not exist → -ENOENT
+ * machine, session does not exist → -ENXIO
+ * requested metadata on object is missing → -ENODATA
+ */
+
_public_ int sd_pid_get_session(pid_t pid, char **session) {
assert_return(pid >= 0, -EINVAL);
@@ -91,6 +101,32 @@ _public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) {
return cg_pid_get_owner_uid(pid, uid);
}
+_public_ int sd_pid_get_cgroup(pid_t pid, char **cgroup) {
+ char *c;
+ int r;
+
+ assert_return(pid >= 0, -EINVAL);
+ assert_return(cgroup, -EINVAL);
+
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &c);
+ if (r < 0)
+ return r;
+
+ /* The internal APIs return the empty string for the root
+ * cgroup, let's return the "/" in the public APIs instead, as
+ * that's easier and less ambigious for people to grok. */
+ if (isempty(c)) {
+ free(c);
+ c = strdup("/");
+ if (!c)
+ return -ENOMEM;
+
+ }
+
+ *cgroup = c;
+ return 0;
+}
+
_public_ int sd_peer_get_session(int fd, char **session) {
struct ucred ucred = {};
int r;
@@ -189,7 +225,23 @@ _public_ int sd_peer_get_user_slice(int fd, char **slice) {
return cg_pid_get_user_slice(ucred.pid, slice);
}
+_public_ int sd_peer_get_cgroup(int fd, char **cgroup) {
+ struct ucred ucred;
+ int r;
+
+ assert_return(fd >= 0, -EBADF);
+ assert_return(cgroup, -EINVAL);
+
+ r = getpeercred(fd, &ucred);
+ if (r < 0)
+ return r;
+
+ return sd_pid_get_cgroup(ucred.pid, cgroup);
+}
+
static int file_of_uid(uid_t uid, char **p) {
+
+ assert_return(uid_is_valid(uid), -EINVAL);
assert(p);
if (asprintf(p, "/run/systemd/users/" UID_FMT, uid) < 0)
@@ -216,11 +268,15 @@ _public_ int sd_uid_get_state(uid_t uid, char**state) {
if (!s)
return -ENOMEM;
- } else if (r < 0) {
+ }
+ if (r < 0) {
free(s);
return r;
- } else if (!s)
+ }
+ if (isempty(s)) {
+ free(s);
return -EIO;
+ }
*state = s;
return 0;
@@ -237,11 +293,12 @@ _public_ int sd_uid_get_display(uid_t uid, char **session) {
return r;
r = parse_env_file(p, NEWLINE, "DISPLAY", &s, NULL);
+ if (r == -ENOENT)
+ return -ENODATA;
if (r < 0)
return r;
-
if (isempty(s))
- return -ENOENT;
+ return -ENODATA;
*session = s;
s = NULL;
@@ -249,35 +306,63 @@ _public_ int sd_uid_get_display(uid_t uid, char **session) {
return 0;
}
+static int file_of_seat(const char *seat, char **_p) {
+ char *p;
+ int r;
+
+ assert(_p);
+
+ if (seat) {
+ if (!filename_is_valid(seat))
+ return -EINVAL;
+
+ p = strappend("/run/systemd/seats/", seat);
+ } else {
+ _cleanup_free_ char *buf = NULL;
+
+ r = sd_session_get_seat(NULL, &buf);
+ if (r < 0)
+ return r;
+
+ p = strappend("/run/systemd/seats/", buf);
+ }
+
+ if (!p)
+ return -ENOMEM;
+
+ *_p = p;
+ p = NULL;
+ return 0;
+}
+
_public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat) {
_cleanup_free_ char *t = NULL, *s = NULL, *p = NULL;
size_t l;
int r;
const char *word, *variable, *state;
- assert_return(seat, -EINVAL);
+ assert_return(uid_is_valid(uid), -EINVAL);
- variable = require_active ? "ACTIVE_UID" : "UIDS";
+ r = file_of_seat(seat, &p);
+ if (r < 0)
+ return r;
- p = strappend("/run/systemd/seats/", seat);
- if (!p)
- return -ENOMEM;
+ variable = require_active ? "ACTIVE_UID" : "UIDS";
r = parse_env_file(p, NEWLINE, variable, &s, NULL);
-
+ if (r == -ENOENT)
+ return 0;
if (r < 0)
return r;
-
- if (!s)
- return -EIO;
+ if (isempty(s))
+ return 0;
if (asprintf(&t, UID_FMT, uid) < 0)
return -ENOMEM;
- FOREACH_WORD(word, l, s, state) {
+ FOREACH_WORD(word, l, s, state)
if (strneq(t, word, l))
return 1;
- }
return 0;
}
@@ -287,31 +372,22 @@ static int uid_get_array(uid_t uid, const char *variable, char ***array) {
char **a;
int r;
+ assert(variable);
+
r = file_of_uid(uid, &p);
if (r < 0)
return r;
- r = parse_env_file(p, NEWLINE,
- variable, &s,
- NULL);
- if (r < 0) {
- if (r == -ENOENT) {
- if (array)
- *array = NULL;
- return 0;
- }
-
- return r;
- }
-
- if (!s) {
+ r = parse_env_file(p, NEWLINE, variable, &s, NULL);
+ if (r == -ENOENT || (r >= 0 && isempty(s))) {
if (array)
*array = NULL;
return 0;
}
+ if (r < 0)
+ return r;
a = strv_split(s, " ");
-
if (!a)
return -ENOMEM;
@@ -373,37 +449,39 @@ static int file_of_session(const char *session, char **_p) {
}
_public_ int sd_session_is_active(const char *session) {
- int r;
_cleanup_free_ char *p = NULL, *s = NULL;
+ int r;
r = file_of_session(session, &p);
if (r < 0)
return r;
r = parse_env_file(p, NEWLINE, "ACTIVE", &s, NULL);
+ if (r == -ENOENT)
+ return -ENXIO;
if (r < 0)
return r;
-
- if (!s)
+ if (isempty(s))
return -EIO;
return parse_boolean(s);
}
_public_ int sd_session_is_remote(const char *session) {
- int r;
_cleanup_free_ char *p = NULL, *s = NULL;
+ int r;
r = file_of_session(session, &p);
if (r < 0)
return r;
r = parse_env_file(p, NEWLINE, "REMOTE", &s, NULL);
+ if (r == -ENOENT)
+ return -ENXIO;
if (r < 0)
return r;
-
- if (!s)
- return -EIO;
+ if (isempty(s))
+ return -ENODATA;
return parse_boolean(s);
}
@@ -419,9 +497,11 @@ _public_ int sd_session_get_state(const char *session, char **state) {
return r;
r = parse_env_file(p, NEWLINE, "STATE", &s, NULL);
+ if (r == -ENOENT)
+ return -ENXIO;
if (r < 0)
return r;
- else if (!s)
+ if (isempty(s))
return -EIO;
*state = s;
@@ -441,10 +521,11 @@ _public_ int sd_session_get_uid(const char *session, uid_t *uid) {
return r;
r = parse_env_file(p, NEWLINE, "UID", &s, NULL);
+ if (r == -ENOENT)
+ return -ENXIO;
if (r < 0)
return r;
-
- if (!s)
+ if (isempty(s))
return -EIO;
return parse_uid(s, uid);
@@ -455,17 +536,19 @@ static int session_get_string(const char *session, const char *field, char **val
int r;
assert_return(value, -EINVAL);
+ assert(field);
r = file_of_session(session, &p);
if (r < 0)
return r;
r = parse_env_file(p, NEWLINE, field, &s, NULL);
+ if (r == -ENOENT)
+ return -ENXIO;
if (r < 0)
return r;
-
if (isempty(s))
- return -ENOENT;
+ return -ENODATA;
*value = s;
s = NULL;
@@ -485,6 +568,8 @@ _public_ int sd_session_get_vt(const char *session, unsigned *vtnr) {
unsigned u;
int r;
+ assert_return(vtnr, -EINVAL);
+
r = session_get_string(session, "VTNR", &vtnr_string);
if (r < 0)
return r;
@@ -540,32 +625,6 @@ _public_ int sd_session_get_remote_host(const char *session, char **remote_host)
return session_get_string(session, "REMOTE_HOST", remote_host);
}
-static int file_of_seat(const char *seat, char **_p) {
- char *p;
- int r;
-
- assert(_p);
-
- if (seat)
- p = strappend("/run/systemd/seats/", seat);
- else {
- _cleanup_free_ char *buf = NULL;
-
- r = sd_session_get_seat(NULL, &buf);
- if (r < 0)
- return r;
-
- p = strappend("/run/systemd/seats/", buf);
- }
-
- if (!p)
- return -ENOMEM;
-
- *_p = p;
- p = NULL;
- return 0;
-}
-
_public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
_cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
int r;
@@ -580,14 +639,16 @@ _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
"ACTIVE", &s,
"ACTIVE_UID", &t,
NULL);
+ if (r == -ENOENT)
+ return -ENXIO;
if (r < 0)
return r;
if (session && !s)
- return -ENOENT;
+ return -ENODATA;
if (uid && !t)
- return -ENOENT;
+ return -ENODATA;
if (uid && t) {
r = parse_uid(t, uid);
@@ -618,7 +679,8 @@ _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **ui
"SESSIONS", &s,
"ACTIVE_SESSIONS", &t,
NULL);
-
+ if (r == -ENOENT)
+ return -ENXIO;
if (r < 0)
return r;
@@ -650,7 +712,6 @@ _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **ui
return -ENOMEM;
r = parse_uid(k, b + i);
-
if (r < 0)
continue;
@@ -681,7 +742,7 @@ static int seat_get_can(const char *seat, const char *variable) {
_cleanup_free_ char *p = NULL, *s = NULL;
int r;
- assert_return(variable, -EINVAL);
+ assert(variable);
r = file_of_seat(seat, &p);
if (r < 0)
@@ -690,10 +751,12 @@ static int seat_get_can(const char *seat, const char *variable) {
r = parse_env_file(p, NEWLINE,
variable, &s,
NULL);
+ if (r == -ENOENT)
+ return -ENXIO;
if (r < 0)
return r;
- if (!s)
- return 0;
+ if (isempty(s))
+ return -ENODATA;
return parse_boolean(s);
}
@@ -817,6 +880,8 @@ _public_ int sd_machine_get_class(const char *machine, char **class) {
p = strjoina("/run/systemd/machines/", machine);
r = parse_env_file(p, NEWLINE, "CLASS", &c, NULL);
+ if (r == -ENOENT)
+ return -ENXIO;
if (r < 0)
return r;
if (!c)
@@ -840,6 +905,8 @@ _public_ int sd_machine_get_ifindices(const char *machine, int **ifindices) {
p = strjoina("/run/systemd/machines/", machine);
r = parse_env_file(p, NEWLINE, "NETIF", &netif, NULL);
+ if (r == -ENOENT)
+ return -ENXIO;
if (r < 0)
return r;
if (!netif) {
diff --git a/src/libsystemd/sd-login/test-login.c b/src/libsystemd/sd-login/test-login.c
index 05affa442d..f734ce9eee 100644
--- a/src/libsystemd/sd-login/test-login.c
+++ b/src/libsystemd/sd-login/test-login.c
@@ -33,7 +33,7 @@ static void test_login(void) {
_cleanup_free_ char *pp = NULL, *qq = NULL;
int r, k;
uid_t u, u2;
- char *seat, *type, *class, *display, *remote_user, *remote_host;
+ char *seat, *type, *class, *display, *remote_user, *remote_host, *display_session, *cgroup;
char *session;
char *state;
char *session2;
@@ -50,6 +50,16 @@ static void test_login(void) {
assert_se(sd_pid_get_owner_uid(0, &u2) == 0);
printf("user = "UID_FMT"\n", u2);
+ assert_se(sd_pid_get_cgroup(0, &cgroup) == 0);
+ printf("cgroup = %s\n", cgroup);
+ free(cgroup);
+
+ display_session = NULL;
+ r = sd_uid_get_display(u2, &display_session);
+ assert_se(r >= 0 || r == -ENODATA);
+ printf("user's display session = %s\n", strna(display_session));
+ free(display_session);
+
assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == 0);
sd_peer_get_session(pair[0], &pp);
sd_peer_get_session(pair[1], &qq);
@@ -100,16 +110,22 @@ static void test_login(void) {
printf("class = %s\n", class);
free(class);
- assert_se(sd_session_get_display(session, &display) >= 0);
- printf("display = %s\n", display);
+ display = NULL;
+ r = sd_session_get_display(session, &display);
+ assert_se(r >= 0 || r == -ENODATA);
+ printf("display = %s\n", strna(display));
free(display);
- assert_se(sd_session_get_remote_user(session, &remote_user) >= 0);
- printf("remote_user = %s\n", remote_user);
+ remote_user = NULL;
+ r = sd_session_get_remote_user(session, &remote_user);
+ assert_se(r >= 0 || r == -ENODATA);
+ printf("remote_user = %s\n", strna(remote_user));
free(remote_user);
- assert_se(sd_session_get_remote_host(session, &remote_host) >= 0);
- printf("remote_host = %s\n", remote_host);
+ remote_host = NULL;
+ r = sd_session_get_remote_host(session, &remote_host);
+ assert_se(r >= 0 || r == -ENODATA);
+ printf("remote_host = %s\n", strna(remote_host));
free(remote_host);
assert_se(sd_session_get_seat(session, &seat) >= 0);
diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c
index b63fdf8fcb..87d87359b8 100644
--- a/src/libsystemd/sd-network/sd-network.c
+++ b/src/libsystemd/sd-network/sd-network.c
@@ -214,6 +214,28 @@ _public_ int sd_network_link_get_lldp(int ifindex, char **lldp) {
return 0;
}
+int sd_network_link_get_timezone(int ifindex, char **ret) {
+ _cleanup_free_ char *s = NULL, *p = NULL;
+ int r;
+
+ assert_return(ifindex > 0, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0)
+ return -ENOMEM;
+
+ r = parse_env_file(p, NEWLINE, "TIMEZONE", &s, NULL);
+ if (r == -ENOENT)
+ return -ENODATA;
+ if (r < 0)
+ return r;
+ if (isempty(s))
+ return -ENODATA;
+
+ *ret = s;
+ s = NULL;
+ return 0;
+}
static int network_get_link_strv(const char *key, int ifindex, char ***ret) {
_cleanup_free_ char *p = NULL, *s = NULL;
diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c
index b0dc822591..888b372c99 100644
--- a/src/libsystemd/sd-resolve/sd-resolve.c
+++ b/src/libsystemd/sd-resolve/sd-resolve.c
@@ -48,9 +48,6 @@ typedef enum {
RESPONSE_ADDRINFO,
REQUEST_NAMEINFO,
RESPONSE_NAMEINFO,
- REQUEST_RES_QUERY,
- REQUEST_RES_SEARCH,
- RESPONSE_RES,
REQUEST_TERMINATE,
RESPONSE_DIED
} QueryType;
@@ -104,12 +101,10 @@ struct sd_resolve_query {
int _h_errno;
struct addrinfo *addrinfo;
char *serv, *host;
- unsigned char *answer;
union {
sd_resolve_getaddrinfo_handler_t getaddrinfo_handler;
sd_resolve_getnameinfo_handler_t getnameinfo_handler;
- sd_resolve_res_handler_t res_handler;
};
void *userdata;
@@ -166,33 +161,16 @@ typedef struct NameInfoResponse {
int _h_errno;
} NameInfoResponse;
-typedef struct ResRequest {
- struct RHeader header;
- int class;
- int type;
- size_t dname_len;
-} ResRequest;
-
-typedef struct ResResponse {
- struct RHeader header;
- int ret;
- int _errno;
- int _h_errno;
-} ResResponse;
-
typedef union Packet {
RHeader rheader;
AddrInfoRequest addrinfo_request;
AddrInfoResponse addrinfo_response;
NameInfoRequest nameinfo_request;
NameInfoResponse nameinfo_response;
- ResRequest res_request;
- ResResponse res_response;
} Packet;
static int getaddrinfo_done(sd_resolve_query* q);
static int getnameinfo_done(sd_resolve_query *q);
-static int res_query_done(sd_resolve_query* q);
static void resolve_query_disconnect(sd_resolve_query *q);
@@ -343,38 +321,6 @@ static int send_nameinfo_reply(
return 0;
}
-static int send_res_reply(int out_fd, unsigned id, const unsigned char *answer, int ret, int _errno, int _h_errno) {
-
- ResResponse resp = {
- .header.type = RESPONSE_RES,
- .header.id = id,
- .ret = ret,
- ._errno = _errno,
- ._h_errno = _h_errno,
- };
-
- struct msghdr mh = {};
- struct iovec iov[2];
- size_t l;
-
- assert(out_fd >= 0);
-
- l = ret > 0 ? (size_t) ret : 0;
-
- resp.header.length = sizeof(ResResponse) + l;
-
- iov[0] = (struct iovec) { .iov_base = &resp, .iov_len = sizeof(ResResponse) };
- iov[1] = (struct iovec) { .iov_base = (void*) answer, .iov_len = l };
-
- mh.msg_iov = iov;
- mh.msg_iovlen = ELEMENTSOF(iov);
-
- if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0)
- return -errno;
-
- return 0;
-}
-
static int handle_request(int out_fd, const Packet *packet, size_t length) {
const RHeader *req;
@@ -437,29 +383,6 @@ static int handle_request(int out_fd, const Packet *packet, size_t length) {
errno, h_errno);
}
- case REQUEST_RES_QUERY:
- case REQUEST_RES_SEARCH: {
- const ResRequest *res_req = &packet->res_request;
- union {
- HEADER header;
- uint8_t space[BUFSIZE];
- } answer;
- const char *dname;
- int ret;
-
- assert(length >= sizeof(ResRequest));
- assert(length == sizeof(ResRequest) + res_req->dname_len);
-
- dname = (const char *) res_req + sizeof(ResRequest);
-
- if (req->type == REQUEST_RES_QUERY)
- ret = res_query(dname, res_req->class, res_req->type, (unsigned char *) &answer, BUFSIZE);
- else
- ret = res_search(dname, res_req->class, res_req->type, (unsigned char *) &answer, BUFSIZE);
-
- return send_res_reply(out_fd, req->id, (unsigned char *) &answer, ret, errno, h_errno);
- }
-
case REQUEST_TERMINATE:
/* Quit */
return -ECONNRESET;
@@ -752,11 +675,6 @@ static int complete_query(sd_resolve *resolve, sd_resolve_query *q) {
r = getnameinfo_done(q);
break;
- case REQUEST_RES_QUERY:
- case REQUEST_RES_SEARCH:
- r = res_query_done(q);
- break;
-
default:
assert_not_reached("Cannot complete unknown query type");
}
@@ -923,28 +841,6 @@ static int handle_response(sd_resolve *resolve, const Packet *packet, size_t len
return complete_query(resolve, q);
}
- case RESPONSE_RES: {
- const ResResponse *res_resp = &packet->res_response;
-
- assert(length >= sizeof(ResResponse));
- assert(q->type == REQUEST_RES_QUERY || q->type == REQUEST_RES_SEARCH);
-
- q->ret = res_resp->ret;
- q->_errno = res_resp->_errno;
- q->_h_errno = res_resp->_h_errno;
-
- if (res_resp->ret >= 0) {
- q->answer = memdup((const char *)resp + sizeof(ResResponse), res_resp->ret);
- if (!q->answer) {
- q->ret = -1;
- q->_errno = ENOMEM;
- q->_h_errno = 0;
- }
- }
-
- return complete_query(resolve, q);
- }
-
default:
return 0;
}
@@ -1181,79 +1077,6 @@ static int getnameinfo_done(sd_resolve_query *q) {
return q->getnameinfo_handler(q, q->ret, q->host, q->serv, q->userdata);
}
-static int resolve_res(
- sd_resolve *resolve,
- sd_resolve_query **_q,
- QueryType qtype,
- const char *dname,
- int class, int type,
- sd_resolve_res_handler_t callback, void *userdata) {
-
- struct msghdr mh = {};
- struct iovec iov[2];
- ResRequest req = {};
- sd_resolve_query *q;
- int r;
-
- assert_return(resolve, -EINVAL);
- assert_return(dname, -EINVAL);
- assert_return(callback, -EINVAL);
- assert_return(!resolve_pid_changed(resolve), -ECHILD);
-
- r = alloc_query(resolve, !_q, &q);
- if (r < 0)
- return r;
-
- q->type = qtype;
- q->res_handler = callback;
- q->userdata = userdata;
-
- req.dname_len = strlen(dname) + 1;
- req.class = class;
- req.type = type;
-
- req.header.id = q->id;
- req.header.type = qtype;
- req.header.length = sizeof(ResRequest) + req.dname_len;
-
- iov[0] = (struct iovec) { .iov_base = &req, .iov_len = sizeof(ResRequest) };
- iov[1] = (struct iovec) { .iov_base = (void*) dname, .iov_len = req.dname_len };
-
- mh.msg_iov = iov;
- mh.msg_iovlen = 2;
-
- if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) {
- sd_resolve_query_unref(q);
- return -errno;
- }
-
- resolve->n_outstanding++;
-
- if (_q)
- *_q = q;
-
- return 0;
-}
-
-_public_ int sd_resolve_res_query(sd_resolve *resolve, sd_resolve_query** q, const char *dname, int class, int type, sd_resolve_res_handler_t callback, void *userdata) {
- return resolve_res(resolve, q, REQUEST_RES_QUERY, dname, class, type, callback, userdata);
-}
-
-_public_ int sd_resolve_res_search(sd_resolve *resolve, sd_resolve_query** q, const char *dname, int class, int type, sd_resolve_res_handler_t callback, void *userdata) {
- return resolve_res(resolve, q, REQUEST_RES_SEARCH, dname, class, type, callback, userdata);
-}
-
-static int res_query_done(sd_resolve_query* q) {
- assert(q);
- assert(q->done);
- assert(q->res_handler);
-
- errno = q->_errno;
- h_errno = q->_h_errno;
-
- return q->res_handler(q, q->ret, q->answer, q->userdata);
-}
-
_public_ sd_resolve_query* sd_resolve_query_ref(sd_resolve_query *q) {
assert_return(q, NULL);
@@ -1310,7 +1133,6 @@ static void resolve_query_free(sd_resolve_query *q) {
resolve_freeaddrinfo(q->addrinfo);
free(q->host);
free(q->serv);
- free(q->answer);
free(q);
}
diff --git a/src/libsystemd/sd-resolve/test-resolve.c b/src/libsystemd/sd-resolve/test-resolve.c
index 354a4071b7..e8056529f5 100644
--- a/src/libsystemd/sd-resolve/test-resolve.c
+++ b/src/libsystemd/sd-resolve/test-resolve.c
@@ -67,59 +67,8 @@ static int getnameinfo_handler(sd_resolve_query *q, int ret, const char *host, c
return 0;
}
-static int res_handler(sd_resolve_query *q, int ret, unsigned char *answer, void *userdata) {
- int qdcount, ancount, len;
- const unsigned char *pos = answer + sizeof(HEADER);
- unsigned char *end = answer + ret;
- HEADER *head = (HEADER *) answer;
- char name[256];
- assert_se(q);
-
- if (ret < 0) {
- log_error("res_query() error: %s %i", strerror(errno), errno);
- return 0;
- }
-
- if (ret == 0) {
- log_error("No reply for SRV lookup");
- return 0;
- }
-
- qdcount = ntohs(head->qdcount);
- ancount = ntohs(head->ancount);
-
- printf("%d answers for srv lookup:\n", ancount);
-
- /* Ignore the questions */
- while (qdcount-- > 0 && (len = dn_expand(answer, end, pos, name, 255)) >= 0) {
- assert_se(len >= 0);
- pos += len + QFIXEDSZ;
- }
-
- /* Parse the answers */
- while (ancount-- > 0 && (len = dn_expand(answer, end, pos, name, 255)) >= 0) {
- /* Ignore the initial string */
- uint16_t pref, weight, port;
- assert_se(len >= 0);
- pos += len;
- /* Ignore type, ttl, class and dlen */
- pos += 10;
-
- GETSHORT(pref, pos);
- GETSHORT(weight, pos);
- GETSHORT(port, pos);
- len = dn_expand(answer, end, pos, name, 255);
- printf("\tpreference: %2d weight: %2d port: %d host: %s\n",
- pref, weight, port, name);
-
- pos += len;
- }
-
- return 0;
-}
-
int main(int argc, char *argv[]) {
- _cleanup_resolve_query_unref_ sd_resolve_query *q1 = NULL, *q2 = NULL, *q3 = NULL;
+ _cleanup_resolve_query_unref_ sd_resolve_query *q1 = NULL, *q2 = NULL;
_cleanup_resolve_unref_ sd_resolve *resolve = NULL;
int r = 0;
@@ -150,15 +99,9 @@ int main(int argc, char *argv[]) {
if (r < 0)
log_error_errno(r, "sd_resolve_getnameinfo(): %m");
- /* Make a res_query() call */
- r = sd_resolve_res_query(resolve, &q3, "_xmpp-client._tcp.gmail.com", C_IN, T_SRV, res_handler, NULL);
- if (r < 0)
- log_error_errno(r, "sd_resolve_res_query(): %m");
-
- /* Wait until the three queries are completed */
+ /* Wait until the two queries are completed */
while (sd_resolve_query_is_done(q1) == 0 ||
- sd_resolve_query_is_done(q2) == 0 ||
- sd_resolve_query_is_done(q3) == 0) {
+ sd_resolve_query_is_done(q2) == 0) {
r = sd_resolve_wait(resolve, (uint64_t) -1);
if (r < 0) {
diff --git a/src/locale/localed.c b/src/locale/localed.c
index e9489f04c2..e304588c58 100644
--- a/src/locale/localed.c
+++ b/src/locale/localed.c
@@ -960,6 +960,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
m,
CAP_SYS_ADMIN,
"org.freedesktop.locale1.set-locale",
+ NULL,
interactive,
UID_INVALID,
&c->polkit_registry,
@@ -1049,6 +1050,7 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro
m,
CAP_SYS_ADMIN,
"org.freedesktop.locale1.set-keyboard",
+ NULL,
interactive,
UID_INVALID,
&c->polkit_registry,
@@ -1092,6 +1094,7 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro
}
#ifdef HAVE_XKBCOMMON
+_printf_(3, 0)
static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) {
const char *fmt;
@@ -1179,6 +1182,7 @@ static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_err
m,
CAP_SYS_ADMIN,
"org.freedesktop.locale1.set-keyboard",
+ NULL,
interactive,
UID_INVALID,
&c->polkit_registry,
diff --git a/src/login/70-power-switch.rules b/src/login/70-power-switch.rules
index 71f9fe6c72..36d2a3eb40 100644
--- a/src/login/70-power-switch.rules
+++ b/src/login/70-power-switch.rules
@@ -11,6 +11,7 @@ SUBSYSTEM=="input", KERNEL=="event*", SUBSYSTEMS=="acpi", TAG+="power-switch"
SUBSYSTEM=="input", KERNEL=="event*", KERNELS=="thinkpad_acpi", TAG+="power-switch"
SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="twl4030_pwrbutton", TAG+="power-switch"
SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="tps65217_pwr_but", TAG+="power-switch"
+SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="* WMI hotkeys", TAG+="power-switch"
SUBSYSTEM=="input", KERNEL=="event*", \
SUBSYSTEMS=="platform", DRIVERS=="gpio-keys", ATTRS{keys}=="116", TAG+="power-switch"
diff --git a/src/login/loginctl.c b/src/login/loginctl.c
index 5fa98e069f..a7e64071cf 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -263,7 +263,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit
if (isempty(cgroup))
return 0;
- if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
+ if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
return 0;
c = columns();
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 5b2b36b9c0..b6fa50aa52 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -942,6 +942,7 @@ static int method_lock_sessions(sd_bus_message *message, void *userdata, sd_bus_
message,
CAP_SYS_ADMIN,
"org.freedesktop.login1.lock-sessions",
+ NULL,
false,
UID_INVALID,
&m->polkit_registry,
@@ -1096,6 +1097,7 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu
message,
CAP_SYS_ADMIN,
"org.freedesktop.login1.set-user-linger",
+ NULL,
interactive,
UID_INVALID,
&m->polkit_registry,
@@ -1268,6 +1270,7 @@ static int method_attach_device(sd_bus_message *message, void *userdata, sd_bus_
message,
CAP_SYS_ADMIN,
"org.freedesktop.login1.attach-device",
+ NULL,
interactive,
UID_INVALID,
&m->polkit_registry,
@@ -1299,6 +1302,7 @@ static int method_flush_devices(sd_bus_message *message, void *userdata, sd_bus_
message,
CAP_SYS_ADMIN,
"org.freedesktop.login1.flush-devices",
+ NULL,
interactive,
UID_INVALID,
&m->polkit_registry,
@@ -1339,8 +1343,7 @@ static int bus_manager_log_shutdown(
InhibitWhat w,
const char *unit_name) {
- const char *p;
- const char *q;
+ const char *p, *q;
assert(m);
assert(unit_name);
@@ -1365,8 +1368,8 @@ static int bus_manager_log_shutdown(
q = NULL;
}
- if (m->wall_message)
- p = strjoina(p, " (", m->wall_message, ")", NULL);
+ if (!isempty(m->wall_message))
+ p = strjoina(p, " (", m->wall_message, ")");
return log_struct(LOG_NOTICE,
LOG_MESSAGE_ID(SD_MESSAGE_SHUTDOWN),
@@ -1650,7 +1653,7 @@ static int verify_shutdown_creds(
blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
if (multiple_sessions && action_multiple_sessions) {
- r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, interactive, UID_INVALID, &m->polkit_registry, error);
+ r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, interactive, UID_INVALID, &m->polkit_registry, error);
if (r < 0)
return r;
if (r == 0)
@@ -1658,7 +1661,7 @@ static int verify_shutdown_creds(
}
if (blocked && action_ignore_inhibit) {
- r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, interactive, UID_INVALID, &m->polkit_registry, error);
+ r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, interactive, UID_INVALID, &m->polkit_registry, error);
if (r < 0)
return r;
if (r == 0)
@@ -1666,7 +1669,7 @@ static int verify_shutdown_creds(
}
if (!multiple_sessions && !blocked && action) {
- r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action, interactive, UID_INVALID, &m->polkit_registry, error);
+ r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action, NULL, interactive, UID_INVALID, &m->polkit_registry, error);
if (r < 0)
return r;
if (r == 0)
@@ -1786,10 +1789,9 @@ static int nologin_timeout_handler(
}
static int update_schedule_file(Manager *m) {
-
- int r;
+ _cleanup_free_ char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
- _cleanup_free_ char *t = NULL, *temp_path = NULL;
+ int r;
assert(m);
@@ -1797,10 +1799,6 @@ static int update_schedule_file(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to create shutdown subdirectory: %m");
- t = cescape(m->wall_message);
- if (!t)
- return log_oom();
-
r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path);
if (r < 0)
return log_error_errno(r, "Failed to save information about scheduled shutdowns: %m");
@@ -1815,8 +1813,17 @@ static int update_schedule_file(Manager *m) {
m->enable_wall_messages,
m->scheduled_shutdown_type);
- if (!isempty(m->wall_message))
+ if (!isempty(m->wall_message)) {
+ _cleanup_free_ char *t;
+
+ t = cescape(m->wall_message);
+ if (!t) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
fprintf(f, "WALL_MESSAGE=%s\n", t);
+ }
r = fflush_and_check(f);
if (r < 0)
@@ -2084,7 +2091,7 @@ static int method_can_shutdown_or_sleep(
blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
if (multiple_sessions) {
- r = bus_test_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, UID_INVALID, &challenge, error);
+ r = bus_test_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, UID_INVALID, &challenge, error);
if (r < 0)
return r;
@@ -2097,7 +2104,7 @@ static int method_can_shutdown_or_sleep(
}
if (blocked) {
- r = bus_test_polkit(message, CAP_SYS_BOOT, action_ignore_inhibit, UID_INVALID, &challenge, error);
+ r = bus_test_polkit(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, UID_INVALID, &challenge, error);
if (r < 0)
return r;
@@ -2113,7 +2120,7 @@ static int method_can_shutdown_or_sleep(
/* If neither inhibit nor multiple sessions
* apply then just check the normal policy */
- r = bus_test_polkit(message, CAP_SYS_BOOT, action, UID_INVALID, &challenge, error);
+ r = bus_test_polkit(message, CAP_SYS_BOOT, action, NULL, UID_INVALID, &challenge, error);
if (r < 0)
return r;
@@ -2232,6 +2239,7 @@ static int method_set_reboot_to_firmware_setup(
r = bus_verify_polkit_async(message,
CAP_SYS_ADMIN,
"org.freedesktop.login1.set-reboot-to-firmware-setup",
+ NULL,
false,
UID_INVALID,
&m->polkit_registry,
@@ -2270,6 +2278,7 @@ static int method_can_reboot_to_firmware_setup(
r = bus_test_polkit(message,
CAP_SYS_ADMIN,
"org.freedesktop.login1.set-reboot-to-firmware-setup",
+ NULL,
UID_INVALID,
&challenge,
error);
@@ -2294,7 +2303,7 @@ static int method_set_wall_message(
int r;
Manager *m = userdata;
char *wall_message;
- bool enable_wall_messages;
+ int enable_wall_messages;
assert(message);
assert(m);
@@ -2306,19 +2315,24 @@ static int method_set_wall_message(
r = bus_verify_polkit_async(message,
CAP_SYS_ADMIN,
"org.freedesktop.login1.set-wall-message",
+ NULL,
false,
UID_INVALID,
&m->polkit_registry,
error);
-
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
- r = free_and_strdup(&m->wall_message, wall_message);
- if (r < 0)
- return log_oom();
+ if (isempty(wall_message))
+ m->wall_message = mfree(m->wall_message);
+ else {
+ r = free_and_strdup(&m->wall_message, wall_message);
+ if (r < 0)
+ return log_oom();
+ }
+
m->enable_wall_messages = enable_wall_messages;
return sd_bus_reply_method_return(message, NULL);
@@ -2373,6 +2387,7 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error
w == INHIBIT_HANDLE_SUSPEND_KEY ? "org.freedesktop.login1.inhibit-handle-suspend-key" :
w == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" :
"org.freedesktop.login1.inhibit-handle-lid-switch",
+ NULL,
false,
UID_INVALID,
&m->polkit_registry,
diff --git a/src/login/logind-seat-dbus.c b/src/login/logind-seat-dbus.c
index ce67ffde37..346e1d2cec 100644
--- a/src/login/logind-seat-dbus.c
+++ b/src/login/logind-seat-dbus.c
@@ -204,6 +204,7 @@ int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_er
message,
CAP_KILL,
"org.freedesktop.login1.manage",
+ NULL,
false,
UID_INVALID,
&s->manager->polkit_registry,
diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c
index 563153e2d9..e6b4ccd7c6 100644
--- a/src/login/logind-session-dbus.c
+++ b/src/login/logind-session-dbus.c
@@ -191,6 +191,7 @@ int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus
message,
CAP_KILL,
"org.freedesktop.login1.manage",
+ NULL,
false,
s->user->uid,
&s->manager->polkit_registry,
@@ -232,6 +233,7 @@ int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_erro
message,
CAP_SYS_ADMIN,
"org.freedesktop.login1.lock-sessions",
+ NULL,
false,
s->user->uid,
&s->manager->polkit_registry,
@@ -306,6 +308,7 @@ int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
message,
CAP_KILL,
"org.freedesktop.login1.manage",
+ NULL,
false,
s->user->uid,
&s->manager->polkit_registry,
diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c
index 36c0e8626d..20ea2fbdc4 100644
--- a/src/login/logind-user-dbus.c
+++ b/src/login/logind-user-dbus.c
@@ -179,6 +179,7 @@ int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_er
message,
CAP_KILL,
"org.freedesktop.login1.manage",
+ NULL,
false,
u->uid,
&u->manager->polkit_registry,
@@ -207,6 +208,7 @@ int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
message,
CAP_KILL,
"org.freedesktop.login1.manage",
+ NULL,
false,
u->uid,
&u->manager->polkit_registry,
diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c
index f83d18b035..f66f1ce842 100644
--- a/src/login/pam_systemd.c
+++ b/src/login/pam_systemd.c
@@ -179,24 +179,37 @@ static int export_legacy_dbus_address(
const char *runtime) {
_cleanup_free_ char *s = NULL;
- int r;
+ int r = PAM_BUF_ERR;
- /* skip export if kdbus is not active */
- if (!is_kdbus_available())
- return PAM_SUCCESS;
+ if (is_kdbus_available()) {
+ if (asprintf(&s, KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT, uid, runtime) < 0)
+ goto error;
+ } else {
+ /* FIXME: We *really* should move the access() check into the
+ * daemons that spawn dbus-daemon, instead of forcing
+ * DBUS_SESSION_BUS_ADDRESS= here. */
+
+ s = strjoin(runtime, "/bus", NULL);
+ if (!s)
+ goto error;
+
+ if (access(s, F_OK) < 0)
+ return PAM_SUCCESS;
- if (asprintf(&s, KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT, uid, runtime) < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to set bus variable.");
- return PAM_BUF_ERR;
+ s = mfree(s);
+ if (asprintf(&s, UNIX_USER_BUS_ADDRESS_FMT, runtime) < 0)
+ goto error;
}
r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", s, 0);
- if (r != PAM_SUCCESS) {
- pam_syslog(handle, LOG_ERR, "Failed to set bus variable.");
- return r;
- }
+ if (r != PAM_SUCCESS)
+ goto error;
return PAM_SUCCESS;
+
+error:
+ pam_syslog(handle, LOG_ERR, "Failed to set bus variable.");
+ return r;
}
_public_ PAM_EXTERN int pam_sm_open_session(
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index 95d7bca4bf..2453a9ff04 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -43,6 +43,7 @@ int bus_image_method_remove(
message,
CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
+ NULL,
false,
UID_INVALID,
&m->polkit_registry,
@@ -83,6 +84,7 @@ int bus_image_method_rename(
message,
CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
+ NULL,
false,
UID_INVALID,
&m->polkit_registry,
@@ -123,6 +125,7 @@ int bus_image_method_clone(
message,
CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
+ NULL,
false,
UID_INVALID,
&m->polkit_registry,
@@ -158,6 +161,7 @@ int bus_image_method_mark_read_only(
message,
CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
+ NULL,
false,
UID_INVALID,
&m->polkit_registry,
@@ -194,6 +198,7 @@ int bus_image_method_set_limit(
message,
CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
+ NULL,
false,
UID_INVALID,
&m->polkit_registry,
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index af2b8eff06..cc38116704 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -45,6 +45,7 @@
#include "formats-util.h"
#include "process-util.h"
#include "env-util.h"
+#include "terminal-util.h"
static int property_get_id(
sd_bus *bus,
@@ -123,6 +124,7 @@ int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus
message,
CAP_KILL,
"org.freedesktop.machine1.manage-machines",
+ NULL,
false,
UID_INVALID,
&m->manager->polkit_registry,
@@ -168,6 +170,7 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
message,
CAP_KILL,
"org.freedesktop.machine1.manage-machines",
+ NULL,
false,
UID_INVALID,
&m->manager->polkit_registry,
@@ -487,6 +490,7 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_
message,
CAP_SYS_ADMIN,
m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty",
+ NULL,
false,
UID_INVALID,
&m->manager->polkit_registry,
@@ -500,7 +504,7 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_
if (master < 0)
return master;
- r = ptsname_malloc(master, &pty_name);
+ r = ptsname_namespace(master, &pty_name);
if (r < 0)
return r;
@@ -515,7 +519,7 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_
return sd_bus_send(NULL, reply, NULL);
}
-static int container_bus_new(Machine *m, sd_bus **ret) {
+static int container_bus_new(Machine *m, sd_bus_error *error, sd_bus **ret) {
int r;
assert(m);
@@ -544,6 +548,8 @@ static int container_bus_new(Machine *m, sd_bus **ret) {
bus->is_system = true;
r = sd_bus_start(bus);
+ if (r == -ENOENT)
+ return sd_bus_error_set_errnof(error, r, "There is no system bus in container %s.", m->name);
if (r < 0)
return r;
@@ -576,6 +582,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
message,
CAP_SYS_ADMIN,
m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login",
+ NULL,
false,
UID_INVALID,
&m->manager->polkit_registry,
@@ -589,7 +596,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
if (master < 0)
return master;
- r = ptsname_malloc(master, &pty_name);
+ r = ptsname_namespace(master, &pty_name);
if (r < 0)
return r;
@@ -597,10 +604,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
if (!p)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name);
- if (unlockpt(master) < 0)
- return -errno;
-
- r = container_bus_new(m, &allocated_bus);
+ r = container_bus_new(m, error, &allocated_bus);
if (r < 0)
return r;
@@ -677,6 +681,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
message,
CAP_SYS_ADMIN,
m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell",
+ NULL,
false,
UID_INVALID,
&m->manager->polkit_registry,
@@ -690,7 +695,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
if (master < 0)
return master;
- r = ptsname_malloc(master, &pty_name);
+ r = ptsname_namespace(master, &pty_name);
if (r < 0)
return r;
@@ -701,10 +706,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
utmp_id = path_startswith(pty_name, "/dev/");
assert(utmp_id);
- if (unlockpt(master) < 0)
- return -errno;
-
- r = container_bus_new(m, &allocated_bus);
+ r = container_bus_new(m, error, &allocated_bus);
if (r < 0)
return r;
@@ -888,6 +890,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
message,
CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
+ NULL,
false,
UID_INVALID,
&m->manager->polkit_registry,
@@ -1150,6 +1153,7 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
message,
CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
+ NULL,
false,
UID_INVALID,
&m->manager->polkit_registry,
diff --git a/src/machine/machine.c b/src/machine/machine.c
index 0d1b119dc1..a056ec3b08 100644
--- a/src/machine/machine.c
+++ b/src/machine/machine.c
@@ -547,8 +547,18 @@ int machine_openpt(Machine *m, int flags) {
switch (m->class) {
- case MACHINE_HOST:
- return posix_openpt(flags);
+ case MACHINE_HOST: {
+ int fd;
+
+ fd = posix_openpt(flags);
+ if (fd < 0)
+ return -errno;
+
+ if (unlockpt(fd) < 0)
+ return -errno;
+
+ return fd;
+ }
case MACHINE_CONTAINER:
if (m->leader <= 0)
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index 926035d185..bb8c5ac64b 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -361,8 +361,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
bus,
"org.freedesktop.systemd1",
path,
- endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" :
- endswith(unit, ".slice") ? "org.freedesktop.systemd1.Slice" : "org.freedesktop.systemd1.Service",
+ unit_dbus_interface_from_name(unit),
"ControlGroup",
&error,
&reply,
@@ -376,7 +375,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
if (r < 0)
return bus_log_parse_error(r);
- if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
+ if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
return 0;
c = columns();
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 0d52c693e4..eef9c5fa5f 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -810,6 +810,7 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus
message,
CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
+ NULL,
false,
UID_INVALID,
&m->polkit_registry,
diff --git a/src/network/networkctl.c b/src/network/networkctl.c
index c9b53dc3f6..786579def0 100644
--- a/src/network/networkctl.c
+++ b/src/network/networkctl.c
@@ -497,7 +497,7 @@ static int link_status_one(
sd_hwdb *hwdb,
const char *name) {
_cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
- _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
+ _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL;
_cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
_cleanup_device_unref_ sd_device *d = NULL;
char devid[2 + DECIMAL_STR_MAX(int)];
@@ -570,7 +570,6 @@ static int link_status_one(
setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
sd_network_link_get_dns(ifindex, &dns);
- sd_network_link_get_ntp(ifindex, &ntp);
sd_network_link_get_domains(ifindex, &domains);
r = sd_network_link_get_wildcard_domain(ifindex);
if (r > 0) {
@@ -652,6 +651,8 @@ static int link_status_one(
dump_list(" DNS: ", dns);
if (!strv_isempty(domains))
dump_list(" Domain: ", domains);
+
+ (void) sd_network_link_get_ntp(ifindex, &ntp);
if (!strv_isempty(ntp))
dump_list(" NTP: ", ntp);
@@ -661,6 +662,10 @@ static int link_status_one(
if (!strv_isempty(carrier_bound_by))
dump_list("Carrier Bound By: ", carrier_bound_by);
+ (void) sd_network_link_get_timezone(ifindex, &tz);
+ if (tz)
+ printf(" Time Zone: %s", tz);
+
return 0;
}
diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c
index 584a956a7e..d609daafde 100644
--- a/src/network/networkd-address-pool.c
+++ b/src/network/networkd-address-pool.c
@@ -19,9 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-
#include "networkd.h"
-#include "networkd-link.h"
+#include "networkd-address-pool.h"
int address_pool_new(
Manager *m,
diff --git a/src/network/networkd-address-pool.h b/src/network/networkd-address-pool.h
new file mode 100644
index 0000000000..e6207ccce6
--- /dev/null
+++ b/src/network/networkd-address-pool.h
@@ -0,0 +1,43 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct AddressPool AddressPool;
+
+#include "networkd.h"
+
+struct AddressPool {
+ Manager *manager;
+
+ int family;
+ unsigned prefixlen;
+
+ union in_addr_union in_addr;
+
+ LIST_FIELDS(AddressPool, address_pools);
+};
+
+int address_pool_new(Manager *m, AddressPool **ret, int family, const union in_addr_union *u, unsigned prefixlen);
+int address_pool_new_from_string(Manager *m, AddressPool **ret, int family, const char *p, unsigned prefixlen);
+void address_pool_free(AddressPool *p);
+
+int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union *found);
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index 172ca43a7d..b0d296941e 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -25,8 +25,10 @@
#include "util.h"
#include "conf-parser.h"
#include "firewall-util.h"
+#include "netlink-util.h"
+
#include "networkd.h"
-#include "networkd-link.h"
+#include "networkd-address.h"
static void address_init(Address *address) {
assert(address);
diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h
new file mode 100644
index 0000000000..39789a2382
--- /dev/null
+++ b/src/network/networkd-address.h
@@ -0,0 +1,73 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include "in-addr-util.h"
+
+typedef struct Address Address;
+
+#include "networkd.h"
+#include "networkd-network.h"
+#include "networkd-link.h"
+
+#define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU
+
+struct Address {
+ Network *network;
+ unsigned section;
+
+ int family;
+ unsigned char prefixlen;
+ unsigned char scope;
+ uint32_t flags;
+ char *label;
+
+ struct in_addr broadcast;
+ struct ifa_cacheinfo cinfo;
+
+ union in_addr_union in_addr;
+ union in_addr_union in_addr_peer;
+
+ bool ip_masquerade_done;
+
+ LIST_FIELDS(Address, addresses);
+};
+
+int address_new_static(Network *network, unsigned section, Address **ret);
+int address_new_dynamic(Address **ret);
+void address_free(Address *address);
+int address_configure(Address *address, Link *link, sd_netlink_message_handler_t callback);
+int address_update(Address *address, Link *link, sd_netlink_message_handler_t callback);
+int address_drop(Address *address, Link *link, sd_netlink_message_handler_t callback);
+int address_establish(Address *address, Link *link);
+int address_release(Address *address, Link *link);
+bool address_equal(Address *a1, Address *a2);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free);
+#define _cleanup_address_free_ _cleanup_(address_freep)
+
+int config_parse_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_broadcast(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_label(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 1b2ff7c769..4ffb01382f 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -39,8 +39,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
- log_link_error(link, "could not set DHCPv4 route: %s",
- strerror(-r));
+ log_link_error_errno(link, r, "Could not set DHCPv4 route: %m");
link_enter_failed(link);
}
@@ -61,40 +60,25 @@ static int link_set_dhcp_routes(Link *link) {
assert(link->dhcp_lease);
r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
- if (r < 0 && r != -ENOENT) {
- log_link_warning(link,
- "DHCP error: could not get gateway: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0 && r != -ENODATA)
+ return log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m");
+
if (r >= 0) {
struct in_addr address;
_cleanup_route_free_ Route *route = NULL;
_cleanup_route_free_ Route *route_gw = NULL;
r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
- if (r < 0) {
- log_link_warning(link,
- "DHCP error: could not get address: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP error: could not get address: %m");
r = route_new_dynamic(&route, RTPROT_DHCP);
- if (r < 0) {
- log_link_error(link,
- "Could not allocate route: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate route: %m");
r = route_new_dynamic(&route_gw, RTPROT_DHCP);
- if (r < 0) {
- log_link_error(link,
- "Could not allocate route: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate route: %m");
/* The dhcp netmask may mask out the gateway. Add an explicit
* route for the gw host so that we can route no matter the
@@ -107,12 +91,8 @@ static int link_set_dhcp_routes(Link *link) {
route_gw->metrics = link->network->dhcp_route_metric;
r = route_configure(route_gw, link, &dhcp4_route_handler);
- if (r < 0) {
- log_link_warning(link,
- "could not set host route: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not set host route: %m");
link->dhcp4_messages ++;
@@ -123,9 +103,7 @@ static int link_set_dhcp_routes(Link *link) {
r = route_configure(route, link, &dhcp4_route_handler);
if (r < 0) {
- log_link_warning(link,
- "could not set routes: %s",
- strerror(-r));
+ log_link_warning_errno(link, r, "Could not set routes: %m");
link_enter_failed(link);
return r;
}
@@ -134,25 +112,17 @@ static int link_set_dhcp_routes(Link *link) {
}
n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes);
- if (n == -ENOENT)
+ if (n == -ENODATA)
return 0;
- if (n < 0) {
- log_link_warning(link,
- "DHCP error: could not get routes: %s",
- strerror(-n));
-
- return n;
- }
+ if (n < 0)
+ return log_link_warning_errno(link, n, "DHCP error: could not get routes: %m");
for (i = 0; i < n; i++) {
_cleanup_route_free_ Route *route = NULL;
r = route_new_dynamic(&route, RTPROT_DHCP);
- if (r < 0) {
- log_link_error(link, "Could not allocate route: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate route: %m");
route->family = AF_INET;
route->in_addr.in = static_routes[i].gw_addr;
@@ -161,12 +131,8 @@ static int link_set_dhcp_routes(Link *link) {
route->metrics = link->network->dhcp_route_metric;
r = route_configure(route, link, &dhcp4_route_handler);
- if (r < 0) {
- log_link_warning(link,
- "could not set host route: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not set host route: %m");
link->dhcp4_messages ++;
}
@@ -270,18 +236,16 @@ static int dhcp_lease_lost(Link *link) {
if (link->network->dhcp_hostname) {
const char *hostname = NULL;
- if (!link->network->hostname)
- r = sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
- else
+ if (link->network->hostname)
hostname = link->network->hostname;
+ else
+ (void) sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
- if (r >= 0 || hostname) {
- r = link_set_hostname(link, hostname);
+ if (hostname) {
+ /* If a hostname was set due to the lease, then unset it now. */
+ r = link_set_hostname(link, NULL);
if (r < 0)
- log_link_error_errno(link, r,
- "Failed to set transient hostname to '%s': %m",
- hostname);
-
+ log_link_warning_errno(link, r, "Failed to reset transient hostname: %m");
}
}
@@ -300,8 +264,7 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
- log_link_error(link, "could not set DHCPv4 address: %s",
- strerror(-r));
+ log_link_error_errno(link, r, "Could not set DHCPv4 address: %m");
link_enter_failed(link);
} else if (r >= 0)
link_rtnl_process_address(rtnl, m, link->manager);
@@ -357,45 +320,30 @@ static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
assert(link->network);
r = sd_dhcp_client_get_lease(client, &lease);
- if (r < 0) {
- log_link_warning(link, "DHCP error: no lease %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP error: no lease: %m");
sd_dhcp_lease_unref(link->dhcp_lease);
link->dhcp4_configured = false;
link->dhcp_lease = sd_dhcp_lease_ref(lease);
r = sd_dhcp_lease_get_address(lease, &address);
- if (r < 0) {
- log_link_warning(link, "DHCP error: no address: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP error: no address: %m");
r = sd_dhcp_lease_get_netmask(lease, &netmask);
- if (r < 0) {
- log_link_warning(link, "DHCP error: no netmask: %s",
- strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP error: no netmask: %m");
if (!link->network->dhcp_critical) {
- r = sd_dhcp_lease_get_lifetime(link->dhcp_lease,
- &lifetime);
- if (r < 0) {
- log_link_warning(link,
- "DHCP error: no lifetime: %s",
- strerror(-r));
- return r;
- }
+ r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
}
r = dhcp4_update_address(link, &address, &netmask, lifetime);
if (r < 0) {
- log_link_warning(link, "could not update IP address: %s",
- strerror(-r));
+ log_link_warning_errno(link, r, "Could not update IP address: %m");
link_enter_failed(link);
return r;
}
@@ -417,21 +365,21 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
r = sd_dhcp_client_get_lease(client, &lease);
if (r < 0)
- return log_link_error_errno(link, r, "DHCP error: no lease: %m");
+ return log_link_error_errno(link, r, "DHCP error: No lease: %m");
r = sd_dhcp_lease_get_address(lease, &address);
if (r < 0)
- return log_link_error_errno(link, r, "DHCP error: no address: %m");
+ return log_link_error_errno(link, r, "DHCP error: No address: %m");
r = sd_dhcp_lease_get_netmask(lease, &netmask);
if (r < 0)
- return log_link_error_errno(link, r, "DHCP error: no netmask: %m");
+ return log_link_error_errno(link, r, "DHCP error: No netmask: %m");
prefixlen = in_addr_netmask_to_prefixlen(&netmask);
r = sd_dhcp_lease_get_router(lease, &gateway);
- if (r < 0 && r != -ENOENT)
- return log_link_error_errno(link, r, "DHCP error: could not get gateway: %m");
+ if (r < 0 && r != -ENODATA)
+ return log_link_error_errno(link, r, "DHCP error: Could not get gateway: %m");
if (r >= 0)
log_struct(LOG_INFO,
@@ -470,18 +418,30 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
if (link->network->dhcp_hostname) {
const char *hostname = NULL;
- if (!link->network->hostname)
- r = sd_dhcp_lease_get_hostname(lease, &hostname);
- else
+ if (link->network->hostname)
hostname = link->network->hostname;
+ else
+ (void) sd_dhcp_lease_get_hostname(lease, &hostname);
- if (r >= 0 || hostname) {
+ if (hostname) {
r = link_set_hostname(link, hostname);
if (r < 0)
log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname);
}
}
+ if (link->network->dhcp_timezone) {
+ const char *tz = NULL;
+
+ (void) sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz);
+
+ if (tz) {
+ r = link_set_timezone(link, tz);
+ if (r < 0)
+ log_link_error_errno(link, r, "Failed to set timezone to '%s': %m", tz);
+ }
+ }
+
if (!link->network->dhcp_critical) {
r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
if (r < 0) {
@@ -515,8 +475,7 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
case DHCP_EVENT_STOP:
case DHCP_EVENT_IP_CHANGE:
if (link->network->dhcp_critical) {
- log_link_error(link,
- "DHCPv4 connection considered system critical, ignoring request to reconfigure it.");
+ log_link_error(link, "DHCPv4 connection considered system critical, ignoring request to reconfigure it.");
return;
}
@@ -553,13 +512,9 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
break;
default:
if (event < 0)
- log_link_warning(link,
- "DHCP error: client failed: %s",
- strerror(-event));
+ log_link_warning_errno(link, event, "DHCP error: Client failed: %m");
else
- log_link_warning(link,
- "DHCP unknown event: %d",
- event);
+ log_link_warning(link, "DHCP unknown event: %i", event);
break;
}
@@ -607,10 +562,10 @@ int dhcp4_configure(Link *link) {
}
if (link->network->dhcp_mtu) {
- r = sd_dhcp_client_set_request_option(link->dhcp_client,
- DHCP_OPTION_INTERFACE_MTU);
- if (r < 0)
- return r;
+ r = sd_dhcp_client_set_request_option(link->dhcp_client,
+ DHCP_OPTION_INTERFACE_MTU);
+ if (r < 0)
+ return r;
}
if (link->network->dhcp_routes) {
@@ -624,6 +579,15 @@ int dhcp4_configure(Link *link) {
return r;
}
+ /* Always acquire the timezone and NTP*/
+ r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_NTP_SERVER);
+ if (r < 0)
+ return r;
+
+ r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_NEW_TZDB_TIMEZONE);
+ if (r < 0)
+ return r;
+
if (link->network->dhcp_sendhost) {
_cleanup_free_ char *hostname = NULL;
const char *hn = NULL;
diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c
index 0f2510e904..22efadb843 100644
--- a/src/network/networkd-fdb.c
+++ b/src/network/networkd-fdb.c
@@ -22,10 +22,12 @@
#include <net/if.h>
#include <net/ethernet.h>
-#include "networkd.h"
-#include "networkd-link.h"
#include "conf-parser.h"
#include "util.h"
+#include "netlink-util.h"
+
+#include "networkd.h"
+#include "networkd-fdb.h"
/* create a new FDB entry or get an existing one. */
int fdb_entry_new_static(Network *const network,
diff --git a/src/network/networkd-fdb.h b/src/network/networkd-fdb.h
new file mode 100644
index 0000000000..f0efb902d0
--- /dev/null
+++ b/src/network/networkd-fdb.h
@@ -0,0 +1,47 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct FdbEntry FdbEntry;
+
+#include "networkd.h"
+#include "networkd-network.h"
+
+struct FdbEntry {
+ Network *network;
+ unsigned section;
+
+ struct ether_addr *mac_addr;
+ uint16_t vlan_id;
+
+ LIST_FIELDS(FdbEntry, static_fdb_entries);
+};
+
+int fdb_entry_new_static(Network *const network, const unsigned section, FdbEntry **ret);
+void fdb_entry_free(FdbEntry *fdb_entry);
+int fdb_entry_configure(Link *const link, FdbEntry *const fdb_entry);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(FdbEntry*, fdb_entry_free);
+#define _cleanup_fdbentry_free_ _cleanup_(fdb_entry_freep)
+
+int config_parse_fdb_hwaddr(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_fdb_vlan_id(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index cc9dc393c6..1dc9db0fca 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -29,7 +29,10 @@
#include "socket-util.h"
#include "bus-util.h"
#include "udev-util.h"
+#include "netlink-util.h"
+#include "dhcp-lease-internal.h"
#include "network-internal.h"
+
#include "networkd-link.h"
#include "networkd-netdev.h"
@@ -613,6 +616,96 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda
return 1;
}
+static int link_push_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) {
+ _cleanup_free_ struct in_addr *addresses = NULL;
+ size_t n_addresses = 0, n_allocated = 0;
+ char **a;
+
+ log_debug("Copying DNS server information from %s", link->ifname);
+
+ if (!link->network)
+ return 0;
+
+ STRV_FOREACH(a, link->network->dns) {
+ struct in_addr ia;
+
+ /* Only look for IPv4 addresses */
+ if (inet_pton(AF_INET, *a, &ia) <= 0)
+ continue;
+
+ if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
+ return log_oom();
+
+ addresses[n_addresses++] = ia;
+ }
+
+ if (link->network->dhcp_dns &&
+ link->dhcp_lease) {
+ const struct in_addr *da = NULL;
+ int n;
+
+ n = sd_dhcp_lease_get_dns(link->dhcp_lease, &da);
+ if (n > 0) {
+
+ if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
+ return log_oom();
+
+ memcpy(addresses + n_addresses, da, n * sizeof(struct in_addr));
+ n_addresses += n;
+ }
+ }
+
+ if (n_addresses <= 0)
+ return 0;
+
+ return sd_dhcp_server_set_dns(s, addresses, n_addresses);
+}
+
+static int link_push_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) {
+ _cleanup_free_ struct in_addr *addresses = NULL;
+ size_t n_addresses = 0, n_allocated = 0;
+ char **a;
+
+ if (!link->network)
+ return 0;
+
+ log_debug("Copying NTP server information from %s", link->ifname);
+
+ STRV_FOREACH(a, link->network->ntp) {
+ struct in_addr ia;
+
+ /* Only look for IPv4 addresses */
+ if (inet_pton(AF_INET, *a, &ia) <= 0)
+ continue;
+
+ if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
+ return log_oom();
+
+ addresses[n_addresses++] = ia;
+ }
+
+ if (link->network->dhcp_ntp &&
+ link->dhcp_lease) {
+ const struct in_addr *da = NULL;
+ int n;
+
+ n = sd_dhcp_lease_get_ntp(link->dhcp_lease, &da);
+ if (n > 0) {
+
+ if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
+ return log_oom();
+
+ memcpy(addresses + n_addresses, da, n * sizeof(struct in_addr));
+ n_addresses += n;
+ }
+ }
+
+ if (n_addresses <= 0)
+ return 0;
+
+ return sd_dhcp_server_set_ntp(s, addresses, n_addresses);
+}
+
static int link_enter_set_addresses(Link *link) {
Address *ad;
int r;
@@ -637,8 +730,9 @@ static int link_enter_set_addresses(Link *link) {
/* now that we can figure out a default address for the dhcp server,
start it */
if (link_dhcp4_server_enabled(link)) {
- struct in_addr pool_start;
Address *address;
+ Link *uplink = NULL;
+ bool acquired_uplink = false;
address = link_find_dhcp_server_address(link);
if (!address) {
@@ -647,16 +741,9 @@ static int link_enter_set_addresses(Link *link) {
return 0;
}
- r = sd_dhcp_server_set_address(link->dhcp_server,
- &address->in_addr.in,
- address->prefixlen);
- if (r < 0)
- return r;
-
- /* offer 32 addresses starting from the address following the server address */
- pool_start.s_addr = htobe32(be32toh(address->in_addr.in.s_addr) + 1);
- r = sd_dhcp_server_set_lease_pool(link->dhcp_server,
- &pool_start, 32);
+ /* use the server address' subnet as the pool */
+ r = sd_dhcp_server_configure_pool(link->dhcp_server, &address->in_addr.in, address->prefixlen,
+ link->network->dhcp_server_pool_offset, link->network->dhcp_server_pool_size);
if (r < 0)
return r;
@@ -665,13 +752,83 @@ static int link_enter_set_addresses(Link *link) {
&main_address->in_addr.in);
if (r < 0)
return r;
-
- r = sd_dhcp_server_set_prefixlen(link->dhcp_server,
- main_address->prefixlen);
- if (r < 0)
- return r;
*/
+ if (link->network->dhcp_server_max_lease_time_usec > 0) {
+ r = sd_dhcp_server_set_max_lease_time(
+ link->dhcp_server,
+ DIV_ROUND_UP(link->network->dhcp_server_max_lease_time_usec, USEC_PER_SEC));
+ if (r < 0)
+ return r;
+ }
+
+ if (link->network->dhcp_server_default_lease_time_usec > 0) {
+ r = sd_dhcp_server_set_default_lease_time(
+ link->dhcp_server,
+ DIV_ROUND_UP(link->network->dhcp_server_default_lease_time_usec, USEC_PER_SEC));
+ if (r < 0)
+ return r;
+ }
+
+ if (link->network->dhcp_server_emit_dns) {
+
+ if (link->network->n_dhcp_server_dns > 0)
+ r = sd_dhcp_server_set_dns(link->dhcp_server, link->network->dhcp_server_dns, link->network->n_dhcp_server_dns);
+ else {
+ uplink = manager_find_uplink(link->manager, link);
+ acquired_uplink = true;
+
+ if (!uplink) {
+ log_link_debug(link, "Not emitting DNS server information on link, couldn't find suitable uplink.");
+ r = 0;
+ } else
+ r = link_push_dns_to_dhcp_server(uplink, link->dhcp_server);
+ }
+ if (r < 0)
+ log_link_warning_errno(link, r, "Failed to set DNS server for DHCP server, ignoring: %m");
+ }
+
+
+ if (link->network->dhcp_server_emit_ntp) {
+
+ if (link->network->n_dhcp_server_ntp > 0)
+ r = sd_dhcp_server_set_ntp(link->dhcp_server, link->network->dhcp_server_ntp, link->network->n_dhcp_server_ntp);
+ else {
+ if (!acquired_uplink)
+ uplink = manager_find_uplink(link->manager, link);
+
+ if (!uplink) {
+ log_link_debug(link, "Not emitting NTP server information on link, couldn't find suitable uplink.");
+ r = 0;
+ } else
+ r = link_push_ntp_to_dhcp_server(uplink, link->dhcp_server);
+
+ }
+ if (r < 0)
+ log_link_warning_errno(link, r, "Failed to set NTP server for DHCP server, ignoring: %m");
+ }
+
+ if (link->network->dhcp_server_emit_timezone) {
+ _cleanup_free_ char *buffer = NULL;
+ const char *tz = NULL;
+
+ if (link->network->dhcp_server_timezone)
+ tz = link->network->dhcp_server_timezone;
+ else {
+ r = get_timezone(&buffer);
+ if (r < 0)
+ log_warning_errno(r, "Failed to determine timezone: %m");
+ else
+ tz = buffer;
+ }
+
+ if (tz) {
+ r = sd_dhcp_server_set_timezone(link->dhcp_server, tz);
+ if (r < 0)
+ return r;
+ }
+ }
+
r = sd_dhcp_server_start(link->dhcp_server);
if (r < 0) {
log_link_warning_errno(link, r, "Could not start DHCPv4 server instance: %m");
@@ -743,7 +900,7 @@ static int link_set_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userd
static int set_hostname_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
_cleanup_link_unref_ Link *link = userdata;
- int r;
+ const sd_bus_error *e;
assert(m);
assert(link);
@@ -751,21 +908,20 @@ static int set_hostname_handler(sd_bus_message *m, void *userdata, sd_bus_error
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 1;
- r = sd_bus_message_get_errno(m);
- if (r > 0)
- log_link_warning_errno(link, r, "Could not set hostname: %m");
+ e = sd_bus_message_get_error(m);
+ if (e)
+ log_link_warning_errno(link, sd_bus_error_get_errno(e), "Could not set hostname: %s", e->message);
return 1;
}
int link_set_hostname(Link *link, const char *hostname) {
- int r = 0;
+ int r;
assert(link);
assert(link->manager);
- assert(hostname);
- log_link_debug(link, "Setting transient hostname: '%s'", hostname);
+ log_link_debug(link, "Setting transient hostname: '%s'", strna(hostname));
if (!link->manager->bus) {
/* TODO: replace by assert when we can rely on kdbus */
@@ -794,6 +950,57 @@ int link_set_hostname(Link *link, const char *hostname) {
return 0;
}
+static int set_timezone_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+ _cleanup_link_unref_ Link *link = userdata;
+ const sd_bus_error *e;
+
+ assert(m);
+ assert(link);
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 1;
+
+ e = sd_bus_message_get_error(m);
+ if (e)
+ log_link_warning_errno(link, sd_bus_error_get_errno(e), "Could not set timezone: %s", e->message);
+
+ return 1;
+}
+
+int link_set_timezone(Link *link, const char *tz) {
+ int r;
+
+ assert(link);
+ assert(link->manager);
+ assert(tz);
+
+ log_link_debug(link, "Setting system timezone: '%s'", tz);
+
+ if (!link->manager->bus) {
+ log_link_info(link, "Not connected to system bus, ignoring timezone.");
+ return 0;
+ }
+
+ r = sd_bus_call_method_async(
+ link->manager->bus,
+ NULL,
+ "org.freedesktop.timedate1",
+ "/org/freedesktop/timedate1",
+ "org.freedesktop.timedate1",
+ "SetTimezone",
+ set_timezone_handler,
+ link,
+ "sb",
+ tz,
+ false);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set timezone: %m");
+
+ link_ref(link);
+
+ return 0;
+}
+
static int set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
_cleanup_link_unref_ Link *link = userdata;
int r;
@@ -2414,9 +2621,17 @@ int link_save(Link *link) {
}
if (link->dhcp_lease) {
+ const char *tz = NULL;
+
+ r = sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz);
+ if (r >= 0)
+ fprintf(f, "TIMEZONE=%s\n", tz);
+ }
+
+ if (link->dhcp_lease) {
assert(link->network);
- r = sd_dhcp_lease_save(link->dhcp_lease, link->lease_file);
+ r = dhcp_lease_save(link->dhcp_lease, link->lease_file);
if (r < 0)
goto fail;
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 2dcbbda607..f588faf209 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -1,5 +1,7 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+#pragma once
+
/***
This file is part of systemd.
@@ -19,11 +21,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#pragma once
-
#include <endian.h>
-#include "networkd.h"
+#include "sd-dhcp-client.h"
+#include "sd-dhcp-server.h"
+#include "sd-ipv4ll.h"
+#include "sd-icmp6-nd.h"
+#include "sd-dhcp6-client.h"
+#include "sd-lldp.h"
+
+typedef struct Link Link;
typedef enum LinkState {
LINK_STATE_PENDING,
@@ -38,6 +45,21 @@ typedef enum LinkState {
_LINK_STATE_INVALID = -1
} LinkState;
+typedef enum LinkOperationalState {
+ LINK_OPERSTATE_OFF,
+ LINK_OPERSTATE_NO_CARRIER,
+ LINK_OPERSTATE_DORMANT,
+ LINK_OPERSTATE_CARRIER,
+ LINK_OPERSTATE_DEGRADED,
+ LINK_OPERSTATE_ROUTABLE,
+ _LINK_OPERSTATE_MAX,
+ _LINK_OPERSTATE_INVALID = -1
+} LinkOperationalState;
+
+#include "networkd.h"
+#include "networkd-network.h"
+#include "networkd-address.h"
+
struct Link {
Manager *manager;
@@ -115,6 +137,7 @@ bool link_has_carrier(Link *link);
int link_set_mtu(Link *link, uint32_t mtu);
int link_set_hostname(Link *link, const char *hostname);
+int link_set_timezone(Link *link, const char *timezone);
int ipv4ll_configure(Link *link);
int dhcp4_configure(Link *link);
@@ -130,6 +153,9 @@ bool link_dhcp6_enabled(Link *link);
const char* link_state_to_string(LinkState s) _const_;
LinkState link_state_from_string(const char *s) _pure_;
+const char* link_operstate_to_string(LinkOperationalState s) _const_;
+LinkOperationalState link_operstate_from_string(const char *s) _pure_;
+
extern const sd_bus_vtable link_vtable[];
int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index 09a929e0da..92b607297d 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -22,20 +22,21 @@
#include <sys/socket.h>
#include <linux/if.h>
+#include "sd-netlink.h"
+#include "sd-daemon.h"
+
#include "conf-parser.h"
#include "path-util.h"
-#include "networkd.h"
-#include "networkd-netdev.h"
-#include "networkd-link.h"
#include "libudev-private.h"
#include "udev-util.h"
#include "netlink-util.h"
#include "bus-util.h"
#include "def.h"
#include "virt.h"
+#include "set.h"
+#include "local-addresses.h"
-#include "sd-netlink.h"
-#include "sd-daemon.h"
+#include "networkd.h"
/* use 8 MB for receive socket kernel queue. */
#define RCVBUF_SIZE (8*1024*1024)
@@ -99,7 +100,7 @@ static int manager_reset_all(Manager *m) {
HASHMAP_FOREACH(link, m->links, i) {
r = link_carrier_reset(link);
if (r < 0)
- log_link_warning_errno(link, r, "could not reset carrier: %m");
+ log_link_warning_errno(link, r, "Could not reset carrier: %m");
}
return 0;
@@ -204,7 +205,7 @@ static int manager_udev_process_link(Manager *m, struct udev_device *device) {
ifindex = udev_device_get_ifindex(device);
if (ifindex <= 0) {
- log_debug("ignoring udev ADD event for device with invalid ifindex");
+ log_debug("Ignoring udev ADD event for device with invalid ifindex");
return 0;
}
@@ -291,23 +292,23 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message);
if (r < 0)
- log_warning_errno(r, "rtnl: could not receive link: %m");
+ log_warning_errno(r, "rtnl: Could not receive link: %m");
return 0;
}
r = sd_netlink_message_get_type(message, &type);
if (r < 0) {
- log_warning_errno(r, "rtnl: could not get message type: %m");
+ log_warning_errno(r, "rtnl: Could not get message type: %m");
return 0;
} else if (type != RTM_NEWLINK && type != RTM_DELLINK) {
- log_warning("rtnl: received unexpected message type when processing link");
+ log_warning("rtnl: Received unexpected message type when processing link");
return 0;
}
r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
if (r < 0) {
- log_warning_errno(r, "rtnl: could not get ifindex from link: %m");
+ log_warning_errno(r, "rtnl: Could not get ifindex from link: %m");
return 0;
} else if (ifindex <= 0) {
log_warning("rtnl: received link message with invalid ifindex: %d", ifindex);
@@ -317,7 +318,7 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
r = sd_netlink_message_read_string(message, IFLA_IFNAME, &name);
if (r < 0) {
- log_warning_errno(r, "rtnl: received link message without ifname: %m");
+ log_warning_errno(r, "rtnl: Received link message without ifname: %m");
return 0;
} else
netdev_get(m, name, &netdev);
@@ -328,7 +329,7 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
/* link is new, so add it */
r = link_add(m, message, &link);
if (r < 0) {
- log_warning_errno(r, "could not add new link: %m");
+ log_warning_errno(r, "Could not add new link: %m");
return 0;
}
}
@@ -337,7 +338,7 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
/* netdev exists, so make sure the ifindex matches */
r = netdev_set_ifindex(netdev, message);
if (r < 0) {
- log_warning_errno(r, "could not set ifindex on netdev: %m");
+ log_warning_errno(r, "Could not set ifindex on netdev: %m");
return 0;
}
}
@@ -476,13 +477,13 @@ void manager_free(Manager *m) {
free(m->state_file);
+ sd_event_source_unref(m->udev_event_source);
udev_monitor_unref(m->udev_monitor);
udev_unref(m->udev);
+
sd_bus_unref(m->bus);
sd_bus_slot_unref(m->prepare_for_sleep_slot);
- sd_event_source_unref(m->udev_event_source);
sd_event_source_unref(m->bus_retry_event_source);
- sd_event_unref(m->event);
while ((link = hashmap_first(m->links)))
link_unref(link);
@@ -501,6 +502,7 @@ void manager_free(Manager *m) {
address_pool_free(pool);
sd_netlink_unref(m->rtnl);
+ sd_event_unref(m->event);
free(m);
}
@@ -755,7 +757,7 @@ int manager_save(Manager *m) {
r = set_put_in_addrv(dns, addresses, r);
if (r < 0)
return r;
- } else if (r < 0 && r != -ENOENT)
+ } else if (r < 0 && r != -ENODATA)
return r;
}
@@ -767,7 +769,7 @@ int manager_save(Manager *m) {
r = set_put_in_addrv(ntp, addresses, r);
if (r < 0)
return r;
- } else if (r < 0 && r != -ENOENT)
+ } else if (r < 0 && r != -ENODATA)
return r;
}
@@ -779,7 +781,7 @@ int manager_save(Manager *m) {
r = set_put_strdup(domains, domainname);
if (r < 0)
return r;
- } else if (r != -ENOENT)
+ } else if (r != -ENODATA)
return r;
}
}
@@ -846,36 +848,39 @@ int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, uni
return 0;
}
-const char *address_family_boolean_to_string(AddressFamilyBoolean b) {
- if (b == ADDRESS_FAMILY_YES ||
- b == ADDRESS_FAMILY_NO)
- return yes_no(b == ADDRESS_FAMILY_YES);
+Link* manager_find_uplink(Manager *m, Link *exclude) {
+ _cleanup_free_ struct local_address *gateways = NULL;
+ int n, i;
- if (b == ADDRESS_FAMILY_IPV4)
- return "ipv4";
- if (b == ADDRESS_FAMILY_IPV6)
- return "ipv6";
+ assert(m);
- return NULL;
-}
+ /* Looks for a suitable "uplink", via black magic: an
+ * interface that is up and where the default route with the
+ * highest priority points to. */
-AddressFamilyBoolean address_family_boolean_from_string(const char *s) {
- int r;
+ n = local_gateways(m->rtnl, 0, AF_UNSPEC, &gateways);
+ if (n < 0) {
+ log_warning_errno(n, "Failed to determine list of default gateways: %m");
+ return NULL;
+ }
+
+ for (i = 0; i < n; i++) {
+ Link *link;
+
+ link = hashmap_get(m->links, INT_TO_PTR(gateways[i].ifindex));
+ if (!link) {
+ log_debug("Weird, found a gateway for a link we don't know. Ignoring.");
+ continue;
+ }
- /* Make this a true superset of a boolean */
+ if (link == exclude)
+ continue;
- r = parse_boolean(s);
- if (r > 0)
- return ADDRESS_FAMILY_YES;
- if (r == 0)
- return ADDRESS_FAMILY_NO;
+ if (link->operstate < LINK_OPERSTATE_ROUTABLE)
+ continue;
- if (streq(s, "ipv4"))
- return ADDRESS_FAMILY_IPV4;
- if (streq(s, "ipv6"))
- return ADDRESS_FAMILY_IPV6;
+ return link;
+ }
- return _ADDRESS_FAMILY_BOOLEAN_INVALID;
+ return NULL;
}
-
-DEFINE_CONFIG_PARSE_ENUM(config_parse_address_family_boolean, address_family_boolean, AddressFamilyBoolean, "Failed to parse option");
diff --git a/src/network/networkd-netdev-bond.h b/src/network/networkd-netdev-bond.h
index 9991fa731f..0cdce1605e 100644
--- a/src/network/networkd-netdev-bond.h
+++ b/src/network/networkd-netdev-bond.h
@@ -1,5 +1,7 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+#pragma once
+
/***
This file is part of systemd.
@@ -19,7 +21,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#pragma once
+#include "in-addr-util.h"
typedef struct Bond Bond;
diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf
index 9469160eba..e0bd0e024a 100644
--- a/src/network/networkd-netdev-gperf.gperf
+++ b/src/network/networkd-netdev-gperf.gperf
@@ -39,6 +39,7 @@ Tunnel.DiscoverPathMTU, config_parse_bool, 0,
Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode)
Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, offsetof(Tunnel, ipv6_flowlabel)
Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp)
+Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit)
Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer)
Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer)
VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id)
diff --git a/src/network/networkd-netdev-tunnel.c b/src/network/networkd-netdev-tunnel.c
index 265e67b7e3..a906e473b6 100644
--- a/src/network/networkd-netdev-tunnel.c
+++ b/src/network/networkd-netdev-tunnel.c
@@ -284,6 +284,12 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl
if (t->copy_dscp)
t->flags |= IP6_TNL_F_RCV_DSCP_COPY;
+ if (t->encap_limit != IPV6_DEFAULT_TNL_ENCAP_LIMIT) {
+ r = sd_netlink_message_append_u8(m, IFLA_IPTUN_ENCAP_LIMIT, t->encap_limit);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_ENCAP_LIMIT attribute: %m");
+ }
+
r = sd_netlink_message_append_u32(m, IFLA_IPTUN_FLAGS, t->flags);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_FLAGS attribute: %m");
@@ -442,6 +448,45 @@ int config_parse_ipv6_flowlabel(const char* unit,
return 0;
}
+int config_parse_encap_limit(const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Tunnel *t = userdata;
+ int k = 0;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (streq(rvalue, "none"))
+ t->flags |= IP6_TNL_F_IGN_ENCAP_LIMIT;
+ else {
+ r = safe_atoi(rvalue, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse Tunnel Encapsulation Limit option, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ if (k > 255 || k < 0)
+ log_syntax(unit, LOG_ERR, filename, line, k, "Invalid Tunnel Encapsulation value, ignoring: %d", k);
+ else {
+ t->encap_limit = k;
+ t->flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT;
+ }
+ }
+
+ return 0;
+}
+
static void ipip_init(NetDev *n) {
Tunnel *t = IPIP(n);
diff --git a/src/network/networkd-netdev-tunnel.h b/src/network/networkd-netdev-tunnel.h
index e4fa74aef4..fa7decce18 100644
--- a/src/network/networkd-netdev-tunnel.h
+++ b/src/network/networkd-netdev-tunnel.h
@@ -95,3 +95,9 @@ int config_parse_ipv6_flowlabel(const char *unit, const char *filename,
unsigned section_line, const char *lvalue,
int ltype, const char *rvalue, void *data,
void *userdata);
+
+int config_parse_encap_limit(const char *unit, const char *filename,
+ unsigned line, const char *section,
+ unsigned section_line, const char *lvalue,
+ int ltype, const char *rvalue, void *data,
+ void *userdata);
diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c
index cd31387b41..ff1edf2c39 100644
--- a/src/network/networkd-netdev.c
+++ b/src/network/networkd-netdev.c
@@ -21,15 +21,18 @@
#include <net/if.h>
-#include "networkd-netdev.h"
-#include "networkd-link.h"
-#include "network-internal.h"
#include "conf-files.h"
#include "conf-parser.h"
#include "list.h"
#include "siphash24.h"
+#include "netlink-util.h"
+#include "network-internal.h"
+
+#include "networkd.h"
+#include "networkd-netdev.h"
const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
+
[NETDEV_KIND_BRIDGE] = &bridge_vtable,
[NETDEV_KIND_BOND] = &bond_vtable,
[NETDEV_KIND_VLAN] = &vlan_vtable,
diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h
index 19fb5bb185..1f8510c4f7 100644
--- a/src/network/networkd-netdev.h
+++ b/src/network/networkd-netdev.h
@@ -21,11 +21,14 @@
#pragma once
-#include "networkd.h"
#include "list.h"
+typedef struct NetDev NetDev;
typedef struct NetDevVTable NetDevVTable;
+#include "networkd.h"
+#include "networkd-link.h"
+
typedef struct netdev_join_callback netdev_join_callback;
struct netdev_join_callback {
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 7ac7ef1ea3..10ca9dae35 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -73,6 +73,17 @@ DHCP.RequestBroadcast, config_parse_bool, 0
DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical)
DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier)
DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric)
+DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_timezone)
+DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec)
+DHCPServer.DefaultLeaseTimeSec,config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec)
+DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns)
+DHCPServer.DNS, config_parse_dhcp_server_dns, 0, 0
+DHCPServer.EmitNTP, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_ntp)
+DHCPServer.NTP, config_parse_dhcp_server_ntp, 0, 0
+DHCPServer.EmitTimezone, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_timezone)
+DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone)
+DHCPServer.PoolOffset, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_offset)
+DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size)
Bridge.Cost, config_parse_unsigned, 0, offsetof(Network, cost)
Bridge.UseBPDU, config_parse_bool, 0, offsetof(Network, use_bpdu)
Bridge.HairPin, config_parse_bool, 0, offsetof(Network, hairpin)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 6587ea994c..ee14401982 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -26,11 +26,11 @@
#include "conf-parser.h"
#include "util.h"
#include "hostname-util.h"
-#include "networkd.h"
-#include "networkd-netdev.h"
-#include "networkd-link.h"
-#include "network-internal.h"
#include "dns-domain.h"
+#include "network-internal.h"
+
+#include "networkd.h"
+#include "networkd-network.h"
static int network_load_one(Manager *manager, const char *filename) {
_cleanup_network_free_ Network *network = NULL;
@@ -107,6 +107,10 @@ static int network_load_one(Manager *manager, const char *filename) {
network->dhcp_route_metric = DHCP_ROUTE_METRIC;
network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID;
+ network->dhcp_server_emit_dns = true;
+ network->dhcp_server_emit_ntp = true;
+ network->dhcp_server_emit_timezone = true;
+
network->use_bpdu = true;
network->allow_port_to_be_root = true;
network->unicast_flood = true;
@@ -124,7 +128,8 @@ static int network_load_one(Manager *manager, const char *filename) {
"Address\0"
"Route\0"
"DHCP\0"
- "DHCPv4\0"
+ "DHCPv4\0" /* compat */
+ "DHCPServer\0"
"Bridge\0"
"BridgeFDB\0",
config_item_perf_lookup, network_network_gperf_lookup,
@@ -258,6 +263,10 @@ void network_free(Network *network) {
condition_free_list(network->match_kernel);
condition_free_list(network->match_arch);
+ free(network->dhcp_server_timezone);
+ free(network->dhcp_server_dns);
+ free(network->dhcp_server_ntp);
+
free(network);
}
@@ -632,57 +641,6 @@ static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DCHPClientIdentifier);
DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DCHPClientIdentifier, "Failed to parse client identifier type");
-static const char* const resolve_support_table[_RESOLVE_SUPPORT_MAX] = {
- [RESOLVE_SUPPORT_NO] = "no",
- [RESOLVE_SUPPORT_YES] = "yes",
- [RESOLVE_SUPPORT_RESOLVE] = "resolve",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(resolve_support, ResolveSupport);
-
-int config_parse_resolve(
- const char* unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- ResolveSupport *resolve = data;
- int k;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(resolve);
-
- /* Our enum shall be a superset of booleans, hence first try
- * to parse as boolean, and then as enum */
-
- k = parse_boolean(rvalue);
- if (k > 0)
- *resolve = RESOLVE_SUPPORT_YES;
- else if (k == 0)
- *resolve = RESOLVE_SUPPORT_NO;
- else {
- ResolveSupport s;
-
- s = resolve_support_from_string(rvalue);
- if (s < 0){
- log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse %s option, ignoring: %s", lvalue, rvalue);
- return 0;
- }
-
- *resolve = s;
- }
-
- return 0;
-}
-
int config_parse_ipv6token(
const char* unit,
const char *filename,
@@ -726,40 +684,6 @@ int config_parse_ipv6token(
return 0;
}
-int config_parse_address_family_boolean_with_kernel(
- const char* unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- AddressFamilyBoolean *fwd = data, s;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- s = address_family_boolean_from_string(rvalue);
- if (s < 0) {
- if (streq(rvalue, "kernel"))
- s = _ADDRESS_FAMILY_BOOLEAN_INVALID;
- else {
- log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse IPForwarding option, ignoring: %s", rvalue);
- return 0;
- }
- }
-
- *fwd = s;
-
- return 0;
-}
-
static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
[IPV6_PRIVACY_EXTENSIONS_NO] = "no",
[IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
@@ -816,37 +740,165 @@ int config_parse_ipv6_privacy_extensions(
return 0;
}
-int config_parse_hostname(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
- char **hostname = data;
- char *hn = NULL;
+int config_parse_hostname(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char **hostname = data, *hn = NULL;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
- r = config_parse_string(unit, filename, line, section, section_line,
- lvalue, ltype, rvalue, &hn, userdata);
+ r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &hn, userdata);
if (r < 0)
return r;
- if (!hostname_is_valid(hn, true)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "hostname is not valid, ignoring assignment: %s", rvalue);
-
+ if (!hostname_is_valid(hn, false)) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Hostname is not valid, ignoring assignment: %s", rvalue);
free(hn);
return 0;
}
+ free(*hostname);
*hostname = hostname_cleanup(hn);
+ return 0;
+}
+
+int config_parse_timezone(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char **datap = data, *tz = NULL;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &tz, userdata);
+ if (r < 0)
+ return r;
+
+ if (!timezone_is_valid(tz)) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Timezone is not valid, ignoring assignment: %s", rvalue);
+ free(tz);
+ return 0;
+ }
+
+ free(*datap);
+ *datap = tz;
return 0;
}
+
+int config_parse_dhcp_server_dns(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *n = data;
+ const char *p = rvalue;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ for (;;) {
+ _cleanup_free_ char *w = NULL;
+ struct in_addr a, *m;
+
+ r = extract_first_word(&p, &w, NULL, 0);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ if (r == 0)
+ return 0;
+
+ if (inet_pton(AF_INET, w, &a) <= 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DNS server address, ignoring: %s", w);
+ continue;
+ }
+
+ m = realloc(n->dhcp_server_dns, (n->n_dhcp_server_dns + 1) * sizeof(struct in_addr));
+ if (!m)
+ return log_oom();
+
+ m[n->n_dhcp_server_dns++] = a;
+ n->dhcp_server_dns = m;
+ }
+}
+
+int config_parse_dhcp_server_ntp(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *n = data;
+ const char *p = rvalue;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ for (;;) {
+ _cleanup_free_ char *w = NULL;
+ struct in_addr a, *m;
+
+ r = extract_first_word(&p, &w, NULL, 0);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, r, line, "Failed to extract word, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ if (r == 0)
+ return 0;
+
+ if (inet_pton(AF_INET, w, &a) <= 0) {
+ log_syntax(unit, LOG_ERR, filename, r, line, "Failed to parse NTP server address, ignoring: %s", w);
+ continue;
+ }
+
+ m = realloc(n->dhcp_server_ntp, (n->n_dhcp_server_ntp + 1) * sizeof(struct in_addr));
+ if (!m)
+ return log_oom();
+
+ m[n->n_dhcp_server_ntp++] = a;
+ n->dhcp_server_ntp = m;
+ }
+}
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
new file mode 100644
index 0000000000..c3439a70ba
--- /dev/null
+++ b/src/network/networkd-network.h
@@ -0,0 +1,181 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "condition.h"
+
+typedef struct Network Network;
+
+#include "networkd.h"
+#include "networkd-netdev.h"
+#include "networkd-address.h"
+#include "networkd-route.h"
+#include "networkd-fdb.h"
+#include "networkd-util.h"
+
+#define DHCP_ROUTE_METRIC 1024
+#define IPV4LL_ROUTE_METRIC 2048
+
+typedef enum DCHPClientIdentifier {
+ DHCP_CLIENT_ID_MAC,
+ DHCP_CLIENT_ID_DUID,
+ _DHCP_CLIENT_ID_MAX,
+ _DHCP_CLIENT_ID_INVALID = -1,
+} DCHPClientIdentifier;
+
+typedef enum IPv6PrivacyExtensions {
+ /* The values map to the kernel's /proc/sys/net/ipv6/conf/xxx/use_tempaddr values */
+ IPV6_PRIVACY_EXTENSIONS_NO,
+ IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC,
+ IPV6_PRIVACY_EXTENSIONS_YES, /* aka prefer-temporary */
+ _IPV6_PRIVACY_EXTENSIONS_MAX,
+ _IPV6_PRIVACY_EXTENSIONS_INVALID = -1,
+} IPv6PrivacyExtensions;
+
+struct Network {
+ Manager *manager;
+
+ char *filename;
+ char *name;
+
+ struct ether_addr *match_mac;
+ char **match_path;
+ char **match_driver;
+ char **match_type;
+ char **match_name;
+
+ Condition *match_host;
+ Condition *match_virt;
+ Condition *match_kernel;
+ Condition *match_arch;
+
+ char *description;
+
+ NetDev *bridge;
+ NetDev *bond;
+ Hashmap *stacked_netdevs;
+
+ /* DHCP Client Support */
+ AddressFamilyBoolean dhcp;
+ DCHPClientIdentifier dhcp_client_identifier;
+ char *dhcp_vendor_class_identifier;
+ char *hostname;
+ bool dhcp_dns;
+ bool dhcp_ntp;
+ bool dhcp_mtu;
+ bool dhcp_hostname;
+ bool dhcp_domains;
+ bool dhcp_sendhost;
+ bool dhcp_broadcast;
+ bool dhcp_critical;
+ bool dhcp_routes;
+ bool dhcp_timezone;
+ unsigned dhcp_route_metric;
+
+ /* DHCP Server Support */
+ bool dhcp_server;
+ bool dhcp_server_emit_dns;
+ struct in_addr *dhcp_server_dns;
+ unsigned n_dhcp_server_dns;
+ bool dhcp_server_emit_ntp;
+ struct in_addr *dhcp_server_ntp;
+ unsigned n_dhcp_server_ntp;
+ bool dhcp_server_emit_timezone;
+ char *dhcp_server_timezone;
+ usec_t dhcp_server_default_lease_time_usec, dhcp_server_max_lease_time_usec;
+ uint32_t dhcp_server_pool_offset;
+ uint32_t dhcp_server_pool_size;
+
+ /* IPV4LL Support */
+ AddressFamilyBoolean link_local;
+ bool ipv4ll_route;
+
+ /* Bridge Support */
+ bool use_bpdu;
+ bool hairpin;
+ bool fast_leave;
+ bool allow_port_to_be_root;
+ bool unicast_flood;
+ unsigned cost;
+
+ AddressFamilyBoolean ip_forward;
+ bool ip_masquerade;
+
+ union in_addr_union ipv6_token;
+ IPv6PrivacyExtensions ipv6_privacy_extensions;
+
+ struct ether_addr *mac;
+ unsigned mtu;
+
+ bool lldp;
+
+ LIST_HEAD(Address, static_addresses);
+ LIST_HEAD(Route, static_routes);
+ LIST_HEAD(FdbEntry, static_fdb_entries);
+
+ Hashmap *addresses_by_section;
+ Hashmap *routes_by_section;
+ Hashmap *fdb_entries_by_section;
+
+ bool wildcard_domain;
+ char **domains, **dns, **ntp, **bind_carrier;
+
+ ResolveSupport llmnr;
+
+ LIST_FIELDS(Network, networks);
+};
+
+void network_free(Network *network);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_free);
+#define _cleanup_network_free_ _cleanup_(network_freep)
+
+int network_load(Manager *manager);
+
+int network_get_by_name(Manager *manager, const char *name, Network **ret);
+int network_get(Manager *manager, struct udev_device *device, const char *ifname, const struct ether_addr *mac, Network **ret);
+int network_apply(Manager *manager, Network *network, Link *link);
+
+int config_parse_netdev(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_domains(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_tunnel(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_dhcp(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_dhcp_client_identifier(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_ipv6token(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_ipv6_privacy_extensions(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_hostname(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_timezone(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_dhcp_server_dns(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_dhcp_server_ntp(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+
+/* Legacy IPv4LL support */
+int config_parse_ipv4ll(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+
+const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, unsigned length);
+
+extern const sd_bus_vtable network_vtable[];
+
+int network_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
+int network_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
+
+const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_;
+IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_;
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index 31b10c458d..fbaad40579 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -19,12 +19,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-
-#include "networkd.h"
-#include "networkd-link.h"
-
#include "util.h"
#include "conf-parser.h"
+#include "netlink-util.h"
+
+#include "networkd.h"
+#include "networkd-route.h"
int route_new_static(Network *network, unsigned section, Route **ret) {
_cleanup_route_free_ Route *route = NULL;
diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h
new file mode 100644
index 0000000000..d090b9c91e
--- /dev/null
+++ b/src/network/networkd-route.h
@@ -0,0 +1,60 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct Route Route;
+
+#include "networkd.h"
+#include "networkd-network.h"
+
+struct Route {
+ Network *network;
+ unsigned section;
+
+ int family;
+ unsigned char dst_prefixlen;
+ unsigned char src_prefixlen;
+ unsigned char scope;
+ uint32_t metrics;
+ unsigned char protocol; /* RTPROT_* */
+
+ union in_addr_union in_addr;
+ union in_addr_union dst_addr;
+ union in_addr_union src_addr;
+ union in_addr_union prefsrc_addr;
+
+ LIST_FIELDS(Route, routes);
+};
+
+int route_new_static(Network *network, unsigned section, Route **ret);
+int route_new_dynamic(Route **ret, unsigned char rtm_protocol);
+void route_free(Route *route);
+int route_configure(Route *route, Link *link, sd_netlink_message_handler_t callback);
+int route_drop(Route *route, Link *link, sd_netlink_message_handler_t callback);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Route*, route_free);
+#define _cleanup_route_free_ _cleanup_(route_freep)
+
+int config_parse_gateway(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_destination(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_route_priority(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_route_scope(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/network/networkd-util.c b/src/network/networkd-util.c
new file mode 100644
index 0000000000..a41cd86239
--- /dev/null
+++ b/src/network/networkd-util.c
@@ -0,0 +1,144 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "conf-parser.h"
+
+#include "networkd-util.h"
+
+const char *address_family_boolean_to_string(AddressFamilyBoolean b) {
+ if (b == ADDRESS_FAMILY_YES ||
+ b == ADDRESS_FAMILY_NO)
+ return yes_no(b == ADDRESS_FAMILY_YES);
+
+ if (b == ADDRESS_FAMILY_IPV4)
+ return "ipv4";
+ if (b == ADDRESS_FAMILY_IPV6)
+ return "ipv6";
+
+ return NULL;
+}
+
+AddressFamilyBoolean address_family_boolean_from_string(const char *s) {
+ int r;
+
+ /* Make this a true superset of a boolean */
+
+ r = parse_boolean(s);
+ if (r > 0)
+ return ADDRESS_FAMILY_YES;
+ if (r == 0)
+ return ADDRESS_FAMILY_NO;
+
+ if (streq(s, "ipv4"))
+ return ADDRESS_FAMILY_IPV4;
+ if (streq(s, "ipv6"))
+ return ADDRESS_FAMILY_IPV6;
+
+ return _ADDRESS_FAMILY_BOOLEAN_INVALID;
+}
+
+DEFINE_CONFIG_PARSE_ENUM(config_parse_address_family_boolean, address_family_boolean, AddressFamilyBoolean, "Failed to parse option");
+
+int config_parse_address_family_boolean_with_kernel(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ AddressFamilyBoolean *fwd = data, s;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ s = address_family_boolean_from_string(rvalue);
+ if (s < 0) {
+ if (streq(rvalue, "kernel"))
+ s = _ADDRESS_FAMILY_BOOLEAN_INVALID;
+ else {
+ log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse IPForwarding= option, ignoring: %s", rvalue);
+ return 0;
+ }
+ }
+
+ *fwd = s;
+
+ return 0;
+}
+
+static const char* const resolve_support_table[_RESOLVE_SUPPORT_MAX] = {
+ [RESOLVE_SUPPORT_NO] = "no",
+ [RESOLVE_SUPPORT_YES] = "yes",
+ [RESOLVE_SUPPORT_RESOLVE] = "resolve",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(resolve_support, ResolveSupport);
+
+int config_parse_resolve(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ResolveSupport *resolve = data;
+ int k;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(resolve);
+
+ /* Our enum shall be a superset of booleans, hence first try
+ * to parse as boolean, and then as enum */
+
+ k = parse_boolean(rvalue);
+ if (k > 0)
+ *resolve = RESOLVE_SUPPORT_YES;
+ else if (k == 0)
+ *resolve = RESOLVE_SUPPORT_NO;
+ else {
+ ResolveSupport s;
+
+ s = resolve_support_from_string(rvalue);
+ if (s < 0){
+ log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse %s= option, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ *resolve = s;
+ }
+
+ return 0;
+}
diff --git a/src/network/networkd-util.h b/src/network/networkd-util.h
new file mode 100644
index 0000000000..cc41aae85a
--- /dev/null
+++ b/src/network/networkd-util.h
@@ -0,0 +1,52 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Tom Gundersen <teg@jklm.no>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "macro.h"
+
+typedef enum AddressFamilyBoolean {
+ /* This is a bitmask, though it usually doesn't feel that way! */
+ ADDRESS_FAMILY_NO = 0,
+ ADDRESS_FAMILY_IPV4 = 1,
+ ADDRESS_FAMILY_IPV6 = 2,
+ ADDRESS_FAMILY_YES = 3,
+ _ADDRESS_FAMILY_BOOLEAN_MAX,
+ _ADDRESS_FAMILY_BOOLEAN_INVALID = -1,
+} AddressFamilyBoolean;
+
+typedef enum ResolveSupport {
+ RESOLVE_SUPPORT_NO,
+ RESOLVE_SUPPORT_YES,
+ RESOLVE_SUPPORT_RESOLVE,
+ _RESOLVE_SUPPORT_MAX,
+ _RESOLVE_SUPPORT_INVALID = -1,
+} ResolveSupport;
+
+int config_parse_resolve(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_address_family_boolean(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_address_family_boolean_with_kernel(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+
+const char* resolve_support_to_string(ResolveSupport i) _const_;
+ResolveSupport resolve_support_from_string(const char *s) _pure_;
+
+const char *address_family_boolean_to_string(AddressFamilyBoolean b) _const_;
+AddressFamilyBoolean address_family_boolean_from_string(const char *s) _const_;
diff --git a/src/network/networkd.h b/src/network/networkd.h
index 5340922bf1..eea57ac158 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -26,213 +26,17 @@
#include "sd-event.h"
#include "sd-netlink.h"
#include "sd-bus.h"
-#include "sd-dhcp-client.h"
-#include "sd-dhcp-server.h"
-#include "sd-ipv4ll.h"
-#include "sd-icmp6-nd.h"
-#include "sd-dhcp6-client.h"
#include "udev.h"
-#include "sd-lldp.h"
-#include "netlink-util.h"
#include "hashmap.h"
#include "list.h"
-#include "set.h"
-#include "condition.h"
-#include "in-addr-util.h"
-#define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU
-#define DHCP_ROUTE_METRIC 1024
-#define IPV4LL_ROUTE_METRIC 2048
-
-typedef struct NetDev NetDev;
-typedef struct Network Network;
-typedef struct Link Link;
-typedef struct Address Address;
-typedef struct Route Route;
typedef struct Manager Manager;
-typedef struct AddressPool AddressPool;
-typedef struct FdbEntry FdbEntry;
-
-typedef enum AddressFamilyBoolean {
- /* This is a bitmask, though it usually doesn't feel that way! */
- ADDRESS_FAMILY_NO = 0,
- ADDRESS_FAMILY_IPV4 = 1,
- ADDRESS_FAMILY_IPV6 = 2,
- ADDRESS_FAMILY_YES = 3,
- _ADDRESS_FAMILY_BOOLEAN_MAX,
- _ADDRESS_FAMILY_BOOLEAN_INVALID = -1,
-} AddressFamilyBoolean;
-
-typedef enum ResolveSupport {
- RESOLVE_SUPPORT_NO,
- RESOLVE_SUPPORT_YES,
- RESOLVE_SUPPORT_RESOLVE,
- _RESOLVE_SUPPORT_MAX,
- _RESOLVE_SUPPORT_INVALID = -1,
-} ResolveSupport;
-
-typedef enum LinkOperationalState {
- LINK_OPERSTATE_OFF,
- LINK_OPERSTATE_NO_CARRIER,
- LINK_OPERSTATE_DORMANT,
- LINK_OPERSTATE_CARRIER,
- LINK_OPERSTATE_DEGRADED,
- LINK_OPERSTATE_ROUTABLE,
- _LINK_OPERSTATE_MAX,
- _LINK_OPERSTATE_INVALID = -1
-} LinkOperationalState;
-
-typedef enum DCHPClientIdentifier {
- DHCP_CLIENT_ID_MAC,
- DHCP_CLIENT_ID_DUID,
- _DHCP_CLIENT_ID_MAX,
- _DHCP_CLIENT_ID_INVALID = -1,
-} DCHPClientIdentifier;
-
-typedef enum IPv6PrivacyExtensions {
- /* The values map to the kernel's /proc/sys/net/ipv6/conf/xxx/use_tempaddr values */
- IPV6_PRIVACY_EXTENSIONS_NO,
- IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC,
- IPV6_PRIVACY_EXTENSIONS_YES, /* aka prefer-temporary */
- _IPV6_PRIVACY_EXTENSIONS_MAX,
- _IPV6_PRIVACY_EXTENSIONS_INVALID = -1,
-} IPv6PrivacyExtensions;
-
-struct FdbEntry {
- Network *network;
- unsigned section;
-
- struct ether_addr *mac_addr;
- uint16_t vlan_id;
-
- LIST_FIELDS(FdbEntry, static_fdb_entries);
-};
-
-struct Network {
- Manager *manager;
-
- char *filename;
- char *name;
-
- struct ether_addr *match_mac;
- char **match_path;
- char **match_driver;
- char **match_type;
- char **match_name;
-
- Condition *match_host;
- Condition *match_virt;
- Condition *match_kernel;
- Condition *match_arch;
-
- char *description;
- NetDev *bridge;
- NetDev *bond;
- Hashmap *stacked_netdevs;
- AddressFamilyBoolean dhcp;
- DCHPClientIdentifier dhcp_client_identifier;
- char *dhcp_vendor_class_identifier;
- char *hostname;
- bool dhcp_dns;
- bool dhcp_ntp;
- bool dhcp_mtu;
- bool dhcp_hostname;
- bool dhcp_domains;
- bool dhcp_sendhost;
- bool dhcp_broadcast;
- bool dhcp_critical;
- bool dhcp_routes;
- unsigned dhcp_route_metric;
- AddressFamilyBoolean link_local;
- bool ipv4ll_route;
- union in_addr_union ipv6_token;
-
- bool dhcp_server;
-
- bool use_bpdu;
- bool hairpin;
- bool fast_leave;
- bool allow_port_to_be_root;
- bool unicast_flood;
- unsigned cost;
-
- AddressFamilyBoolean ip_forward;
- bool ip_masquerade;
-
- IPv6PrivacyExtensions ipv6_privacy_extensions;
-
- struct ether_addr *mac;
- unsigned mtu;
-
- bool lldp;
-
- LIST_HEAD(Address, static_addresses);
- LIST_HEAD(Route, static_routes);
- LIST_HEAD(FdbEntry, static_fdb_entries);
-
- Hashmap *addresses_by_section;
- Hashmap *routes_by_section;
- Hashmap *fdb_entries_by_section;
- bool wildcard_domain;
- char **domains, **dns, **ntp, **bind_carrier;
-
- ResolveSupport llmnr;
-
- LIST_FIELDS(Network, networks);
-};
-
-struct Address {
- Network *network;
- unsigned section;
-
- int family;
- unsigned char prefixlen;
- unsigned char scope;
- uint32_t flags;
- char *label;
-
- struct in_addr broadcast;
- struct ifa_cacheinfo cinfo;
-
- union in_addr_union in_addr;
- union in_addr_union in_addr_peer;
-
- bool ip_masquerade_done;
-
- LIST_FIELDS(Address, addresses);
-};
-
-struct Route {
- Network *network;
- unsigned section;
-
- int family;
- unsigned char dst_prefixlen;
- unsigned char src_prefixlen;
- unsigned char scope;
- uint32_t metrics;
- unsigned char protocol; /* RTPROT_* */
-
- union in_addr_union in_addr;
- union in_addr_union dst_addr;
- union in_addr_union src_addr;
- union in_addr_union prefsrc_addr;
-
- LIST_FIELDS(Route, routes);
-};
-
-struct AddressPool {
- Manager *manager;
-
- int family;
- unsigned prefixlen;
-
- union in_addr_union in_addr;
-
- LIST_FIELDS(AddressPool, address_pools);
-};
+#include "networkd-network.h"
+#include "networkd-address-pool.h"
+#include "networkd-link.h"
+#include "networkd-util.h"
struct Manager {
sd_netlink *rtnl;
@@ -281,183 +85,7 @@ int manager_save(Manager *m);
int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found);
+Link* manager_find_uplink(Manager *m, Link *exclude);
+
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
#define _cleanup_manager_free_ _cleanup_(manager_freep)
-
-/* Network */
-
-int network_load(Manager *manager);
-
-void network_free(Network *network);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_free);
-#define _cleanup_network_free_ _cleanup_(network_freep)
-
-int network_get_by_name(Manager *manager, const char *name, Network **ret);
-int network_get(Manager *manager, struct udev_device *device,
- const char *ifname, const struct ether_addr *mac,
- Network **ret);
-int network_apply(Manager *manager, Network *network, Link *link);
-
-int config_parse_netdev(const char *unit, const char *filename, unsigned line,
- const char *section, unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data, void *userdata);
-
-int config_parse_domains(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata);
-
-int config_parse_tunnel(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata);
-
-extern const sd_bus_vtable network_vtable[];
-
-int network_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
-int network_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
-
-/* gperf */
-const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, unsigned length);
-
-/* Route */
-int route_new_static(Network *network, unsigned section, Route **ret);
-int route_new_dynamic(Route **ret, unsigned char rtm_protocol);
-void route_free(Route *route);
-int route_configure(Route *route, Link *link, sd_netlink_message_handler_t callback);
-int route_drop(Route *route, Link *link, sd_netlink_message_handler_t callback);
-
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Route*, route_free);
-#define _cleanup_route_free_ _cleanup_(route_freep)
-
-int config_parse_gateway(const char *unit, const char *filename, unsigned line,
- const char *section, unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data, void *userdata);
-
-int config_parse_destination(const char *unit, const char *filename, unsigned line,
- const char *section, unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data, void *userdata);
-
-int config_parse_route_priority(const char *unit, const char *filename, unsigned line,
- const char *section, unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data, void *userdata);
-
-int config_parse_route_scope(const char *unit, const char *filename, unsigned line,
- const char *section, unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data, void *userdata);
-/* Address */
-int address_new_static(Network *network, unsigned section, Address **ret);
-int address_new_dynamic(Address **ret);
-void address_free(Address *address);
-int address_configure(Address *address, Link *link, sd_netlink_message_handler_t callback);
-int address_update(Address *address, Link *link, sd_netlink_message_handler_t callback);
-int address_drop(Address *address, Link *link, sd_netlink_message_handler_t callback);
-int address_establish(Address *address, Link *link);
-int address_release(Address *address, Link *link);
-bool address_equal(Address *a1, Address *a2);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free);
-#define _cleanup_address_free_ _cleanup_(address_freep)
-
-int config_parse_address(const char *unit, const char *filename, unsigned line,
- const char *section, unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data, void *userdata);
-
-int config_parse_broadcast(const char *unit, const char *filename, unsigned line,
- const char *section, unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data, void *userdata);
-
-int config_parse_label(const char *unit, const char *filename, unsigned line,
- const char *section, unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data, void *userdata);
-
-/* Forwarding database table. */
-int fdb_entry_configure(Link *const link, FdbEntry *const fdb_entry);
-void fdb_entry_free(FdbEntry *fdb_entry);
-int fdb_entry_new_static(Network *const network, const unsigned section, FdbEntry **ret);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(FdbEntry*, fdb_entry_free);
-#define _cleanup_fdbentry_free_ _cleanup_(fdb_entry_freep)
-
-int config_parse_fdb_hwaddr(const char *unit, const char *filename, unsigned line,
- const char *section, unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data, void *userdata);
-
-int config_parse_fdb_vlan_id(const char *unit, const char *filename, unsigned line,
- const char *section, unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data, void *userdata);
-
-/* DHCP support */
-
-int config_parse_dhcp(const char *unit, const char *filename, unsigned line,
- const char *section, unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_dhcp_client_identifier(const char *unit, const char *filename, unsigned line,
- const char *section, unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data, void *userdata);
-
-/* IPv4LL support (legacy) */
-
-int config_parse_ipv4ll(const char *unit, const char *filename, unsigned line,
- const char *section, unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data, void *userdata);
-
-/* IPv6 support */
-int config_parse_ipv6token(const char *unit, const char *filename, unsigned line,
- const char *section, unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data, void *userdata);
-
-/* Resolve protocols support */
-
-const char* resolve_support_to_string(ResolveSupport i) _const_;
-ResolveSupport resolve_support_from_string(const char *s) _pure_;
-
-int config_parse_resolve(const char *unit, const char *filename, unsigned line,
- const char *section, unsigned section_line, const char *lvalue,
- int ltype, const char *rvalue, void *data, void *userdata);
-
-/* Address Pool */
-
-int address_pool_new(Manager *m, AddressPool **ret, int family, const union in_addr_union *u, unsigned prefixlen);
-int address_pool_new_from_string(Manager *m, AddressPool **ret, int family, const char *p, unsigned prefixlen);
-void address_pool_free(AddressPool *p);
-
-int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union *found);
-
-const char *address_family_boolean_to_string(AddressFamilyBoolean b) _const_;
-AddressFamilyBoolean address_family_boolean_from_string(const char *s) _const_;
-
-int config_parse_address_family_boolean(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-
-/* IPForwarding parser */
-int config_parse_address_family_boolean_with_kernel(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-
-/* Operational State */
-
-const char* link_operstate_to_string(LinkOperationalState s) _const_;
-LinkOperationalState link_operstate_from_string(const char *s) _pure_;
-
-/* IPv6 privacy extensions support */
-
-const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_;
-IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_;
-
-int config_parse_ipv6_privacy_extensions(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-
-
-/* Hostname */
-int config_parse_hostname(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/nspawn/.gitignore b/src/nspawn/.gitignore
new file mode 100644
index 0000000000..85c81fff24
--- /dev/null
+++ b/src/nspawn/.gitignore
@@ -0,0 +1 @@
+/nspawn-gperf.c
diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c
new file mode 100644
index 0000000000..c0e9ccd7a4
--- /dev/null
+++ b/src/nspawn/nspawn-cgroup.c
@@ -0,0 +1,162 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/mount.h>
+
+#include "util.h"
+#include "strv.h"
+#include "mkdir.h"
+#include "fileio.h"
+#include "cgroup-util.h"
+
+#include "nspawn-cgroup.h"
+
+int chown_cgroup(pid_t pid, uid_t uid_shift) {
+ _cleanup_free_ char *path = NULL, *fs = NULL;
+ _cleanup_close_ int fd = -1;
+ const char *fn;
+ int r;
+
+ r = cg_pid_get_path(NULL, pid, &path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get container cgroup path: %m");
+
+ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &fs);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get file system path for container cgroup: %m");
+
+ fd = open(fs, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open %s: %m", fs);
+
+ FOREACH_STRING(fn,
+ ".",
+ "tasks",
+ "notify_on_release",
+ "cgroup.procs",
+ "cgroup.clone_children",
+ "cgroup.controllers",
+ "cgroup.subtree_control",
+ "cgroup.populated")
+ if (fchownat(fd, fn, uid_shift, uid_shift, 0) < 0)
+ log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
+ "Failed to chown() cgroup file %s, ignoring: %m", fn);
+
+ return 0;
+}
+
+int sync_cgroup(pid_t pid, bool unified_requested) {
+ _cleanup_free_ char *cgroup = NULL;
+ char tree[] = "/tmp/unifiedXXXXXX", pid_string[DECIMAL_STR_MAX(pid) + 1];
+ bool undo_mount = false;
+ const char *fn;
+ int unified, r;
+
+ unified = cg_unified();
+ if (unified < 0)
+ return log_error_errno(unified, "Failed to determine whether the unified hierachy is used: %m");
+
+ if ((unified > 0) == unified_requested)
+ return 0;
+
+ /* When the host uses the legacy cgroup setup, but the
+ * container shall use the unified hierarchy, let's make sure
+ * we copy the path from the name=systemd hierarchy into the
+ * unified hierarchy. Similar for the reverse situation. */
+
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get control group of " PID_FMT ": %m", pid);
+
+ /* In order to access the unified hierarchy we need to mount it */
+ if (!mkdtemp(tree))
+ return log_error_errno(errno, "Failed to generate temporary mount point for unified hierarchy: %m");
+
+ if (unified)
+ r = mount("cgroup", tree, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, "none,name=systemd,xattr");
+ else
+ r = mount("cgroup", tree, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, "__DEVEL__sane_behavior");
+ if (r < 0) {
+ r = log_error_errno(errno, "Failed to mount unified hierarchy: %m");
+ goto finish;
+ }
+
+ undo_mount = true;
+
+ fn = strjoina(tree, cgroup, "/cgroup.procs");
+ (void) mkdir_parents(fn, 0755);
+
+ sprintf(pid_string, PID_FMT, pid);
+ r = write_string_file(fn, pid_string, 0);
+ if (r < 0)
+ log_error_errno(r, "Failed to move process: %m");
+
+finish:
+ if (undo_mount)
+ (void) umount(tree);
+
+ (void) rmdir(tree);
+ return r;
+}
+
+int create_subcgroup(pid_t pid, bool unified_requested) {
+ _cleanup_free_ char *cgroup = NULL;
+ const char *child;
+ int unified, r;
+ CGroupMask supported;
+
+ /* In the unified hierarchy inner nodes may only only contain
+ * subgroups, but not processes. Hence, if we running in the
+ * unified hierarchy and the container does the same, and we
+ * did not create a scope unit for the container move us and
+ * the container into two separate subcgroups. */
+
+ if (!unified_requested)
+ return 0;
+
+ unified = cg_unified();
+ if (unified < 0)
+ return log_error_errno(unified, "Failed to determine whether the unified hierachy is used: %m");
+ if (unified == 0)
+ return 0;
+
+ r = cg_mask_supported(&supported);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine supported controllers: %m");
+
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get our control group: %m");
+
+ child = strjoina(cgroup, "/payload");
+ r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, child, pid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create %s subcgroup: %m", child);
+
+ child = strjoina(cgroup, "/supervisor");
+ r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, child, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create %s subcgroup: %m", child);
+
+ /* Try to enable as many controllers as possible for the new payload. */
+ (void) cg_enable_everywhere(supported, supported, cgroup);
+ return 0;
+}
diff --git a/src/nspawn/nspawn-cgroup.h b/src/nspawn/nspawn-cgroup.h
new file mode 100644
index 0000000000..985fdfaad5
--- /dev/null
+++ b/src/nspawn/nspawn-cgroup.h
@@ -0,0 +1,29 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <stdbool.h>
+
+int chown_cgroup(pid_t pid, uid_t uid_shift);
+int sync_cgroup(pid_t pid, bool unified_requested);
+int create_subcgroup(pid_t pid, bool unified_requested);
diff --git a/src/nspawn/nspawn-expose-ports.c b/src/nspawn/nspawn-expose-ports.c
new file mode 100644
index 0000000000..38250b6e02
--- /dev/null
+++ b/src/nspawn/nspawn-expose-ports.c
@@ -0,0 +1,277 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-netlink.h"
+
+#include "util.h"
+#include "in-addr-util.h"
+#include "firewall-util.h"
+#include "local-addresses.h"
+#include "netlink-util.h"
+
+#include "nspawn-expose-ports.h"
+
+int expose_port_parse(ExposePort **l, const char *s) {
+
+ const char *split, *e;
+ uint16_t container_port, host_port;
+ int protocol;
+ ExposePort *p;
+ int r;
+
+ assert(l);
+ assert(s);
+
+ if ((e = startswith(s, "tcp:")))
+ protocol = IPPROTO_TCP;
+ else if ((e = startswith(s, "udp:")))
+ protocol = IPPROTO_UDP;
+ else {
+ e = s;
+ protocol = IPPROTO_TCP;
+ }
+
+ split = strchr(e, ':');
+ if (split) {
+ char v[split - e + 1];
+
+ memcpy(v, e, split - e);
+ v[split - e] = 0;
+
+ r = safe_atou16(v, &host_port);
+ if (r < 0 || host_port <= 0)
+ return -EINVAL;
+
+ r = safe_atou16(split + 1, &container_port);
+ } else {
+ r = safe_atou16(e, &container_port);
+ host_port = container_port;
+ }
+
+ if (r < 0 || container_port <= 0)
+ return -EINVAL;
+
+ LIST_FOREACH(ports, p, *l)
+ if (p->protocol == protocol && p->host_port == host_port)
+ return -EEXIST;
+
+ p = new(ExposePort, 1);
+ if (!p)
+ return -ENOMEM;
+
+ p->protocol = protocol;
+ p->host_port = host_port;
+ p->container_port = container_port;
+
+ LIST_PREPEND(ports, *l, p);
+
+ return 0;
+}
+
+void expose_port_free_all(ExposePort *p) {
+
+ while (p) {
+ ExposePort *q = p;
+ LIST_REMOVE(ports, p, q);
+ free(q);
+ }
+}
+
+int expose_port_flush(ExposePort* l, union in_addr_union *exposed) {
+ ExposePort *p;
+ int r, af = AF_INET;
+
+ assert(exposed);
+
+ if (!l)
+ return 0;
+
+ if (in_addr_is_null(af, exposed))
+ return 0;
+
+ log_debug("Lost IP address.");
+
+ LIST_FOREACH(ports, p, l) {
+ r = fw_add_local_dnat(false,
+ af,
+ p->protocol,
+ NULL,
+ NULL, 0,
+ NULL, 0,
+ p->host_port,
+ exposed,
+ p->container_port,
+ NULL);
+ if (r < 0)
+ log_warning_errno(r, "Failed to modify firewall: %m");
+ }
+
+ *exposed = IN_ADDR_NULL;
+ return 0;
+}
+
+int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *exposed) {
+ _cleanup_free_ struct local_address *addresses = NULL;
+ _cleanup_free_ char *pretty = NULL;
+ union in_addr_union new_exposed;
+ ExposePort *p;
+ bool add;
+ int af = AF_INET, r;
+
+ assert(exposed);
+
+ /* Invoked each time an address is added or removed inside the
+ * container */
+
+ if (!l)
+ return 0;
+
+ r = local_addresses(rtnl, 0, af, &addresses);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate local addresses: %m");
+
+ add = r > 0 &&
+ addresses[0].family == af &&
+ addresses[0].scope < RT_SCOPE_LINK;
+
+ if (!add)
+ return expose_port_flush(l, exposed);
+
+ new_exposed = addresses[0].address;
+ if (in_addr_equal(af, exposed, &new_exposed))
+ return 0;
+
+ in_addr_to_string(af, &new_exposed, &pretty);
+ log_debug("New container IP is %s.", strna(pretty));
+
+ LIST_FOREACH(ports, p, l) {
+
+ r = fw_add_local_dnat(true,
+ af,
+ p->protocol,
+ NULL,
+ NULL, 0,
+ NULL, 0,
+ p->host_port,
+ &new_exposed,
+ p->container_port,
+ in_addr_is_null(af, exposed) ? NULL : exposed);
+ if (r < 0)
+ log_warning_errno(r, "Failed to modify firewall: %m");
+ }
+
+ *exposed = new_exposed;
+ return 0;
+}
+
+int expose_port_send_rtnl(int send_fd) {
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int))];
+ } control = {};
+ struct msghdr mh = {
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg;
+ _cleanup_close_ int fd = -1;
+ ssize_t k;
+
+ assert(send_fd >= 0);
+
+ fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to allocate container netlink: %m");
+
+ cmsg = CMSG_FIRSTHDR(&mh);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+
+ mh.msg_controllen = cmsg->cmsg_len;
+
+ /* Store away the fd in the socket, so that it stays open as
+ * long as we run the child */
+ k = sendmsg(send_fd, &mh, MSG_NOSIGNAL);
+ if (k < 0)
+ return log_error_errno(errno, "Failed to send netlink fd: %m");
+
+ return 0;
+}
+
+int expose_port_watch_rtnl(
+ sd_event *event,
+ int recv_fd,
+ sd_netlink_message_handler_t handler,
+ union in_addr_union *exposed,
+ sd_netlink **ret) {
+
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int))];
+ } control = {};
+ struct msghdr mh = {
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg;
+ _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ int fd, r;
+ ssize_t k;
+
+ assert(event);
+ assert(recv_fd >= 0);
+ assert(ret);
+
+ k = recvmsg(recv_fd, &mh, MSG_NOSIGNAL);
+ if (k < 0)
+ return log_error_errno(errno, "Failed to recv netlink fd: %m");
+
+ cmsg = CMSG_FIRSTHDR(&mh);
+ assert(cmsg->cmsg_level == SOL_SOCKET);
+ assert(cmsg->cmsg_type == SCM_RIGHTS);
+ assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
+ memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
+
+ r = sd_netlink_open_fd(&rtnl, fd);
+ if (r < 0) {
+ safe_close(fd);
+ return log_error_errno(r, "Failed to create rtnl object: %m");
+ }
+
+ r = sd_netlink_add_match(rtnl, RTM_NEWADDR, handler, exposed);
+ if (r < 0)
+ return log_error_errno(r, "Failed to subscribe to RTM_NEWADDR messages: %m");
+
+ r = sd_netlink_add_match(rtnl, RTM_DELADDR, handler, exposed);
+ if (r < 0)
+ return log_error_errno(r, "Failed to subscribe to RTM_DELADDR messages: %m");
+
+ r = sd_netlink_attach_event(rtnl, event, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add to even loop: %m");
+
+ *ret = rtnl;
+ rtnl = NULL;
+
+ return 0;
+}
diff --git a/src/nspawn/nspawn-expose-ports.h b/src/nspawn/nspawn-expose-ports.h
new file mode 100644
index 0000000000..39cec28695
--- /dev/null
+++ b/src/nspawn/nspawn-expose-ports.h
@@ -0,0 +1,45 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+
+#include "sd-event.h"
+#include "sd-netlink.h"
+#include "list.h"
+#include "in-addr-util.h"
+
+typedef struct ExposePort {
+ int protocol;
+ uint16_t host_port;
+ uint16_t container_port;
+ LIST_FIELDS(struct ExposePort, ports);
+} ExposePort;
+
+void expose_port_free_all(ExposePort *p);
+int expose_port_parse(ExposePort **l, const char *s);
+
+int expose_port_watch_rtnl(sd_event *event, int recv_fd, sd_netlink_message_handler_t handler, union in_addr_union *exposed, sd_netlink **ret);
+int expose_port_send_rtnl(int send_fd);
+
+int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *exposed);
+int expose_port_flush(ExposePort* l, union in_addr_union *exposed);
diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf
new file mode 100644
index 0000000000..074b380306
--- /dev/null
+++ b/src/nspawn/nspawn-gperf.gperf
@@ -0,0 +1,38 @@
+%{
+#include <stddef.h>
+#include "conf-parser.h"
+#include "nspawn-settings.h"
+#include "nspawn-expose-ports.h"
+%}
+struct ConfigPerfItem;
+%null_strings
+%language=ANSI-C
+%define slot-name section_and_lvalue
+%define hash-function-name nspawn_gperf_hash
+%define lookup-function-name nspawn_gperf_lookup
+%readonly-tables
+%omit-struct-type
+%struct-type
+%includes
+%%
+Exec.Boot, config_parse_tristate, 0, offsetof(Settings, boot)
+Exec.Parameters, config_parse_strv, 0, offsetof(Settings, parameters)
+Exec.Environment, config_parse_strv, 0, offsetof(Settings, environment)
+Exec.User, config_parse_string, 0, offsetof(Settings, user)
+Exec.Capability, config_parse_capability, 0, offsetof(Settings, capability)
+Exec.DropCapability, config_parse_capability, 0, offsetof(Settings, drop_capability)
+Exec.KillSignal, config_parse_signal, 0, offsetof(Settings, kill_signal)
+Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality)
+Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id)
+Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only)
+Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode)
+Files.Bind, config_parse_bind, 0, 0
+Files.BindReadOnly, config_parse_bind, 1, 0
+Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0
+Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network)
+Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces)
+Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan)
+Network.IPVLAN, config_parse_strv, 0, offsetof(Settings, network_ipvlan)
+Network.VirtualEthernet, config_parse_tristate, 0, offsetof(Settings, network_veth)
+Network.Bridge config_parse_string, 0, offsetof(Settings, network_bridge)
+Network.Port, config_parse_expose_port, 0, 0
diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c
new file mode 100644
index 0000000000..2bca39f45d
--- /dev/null
+++ b/src/nspawn/nspawn-mount.c
@@ -0,0 +1,869 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/mount.h>
+
+#include "util.h"
+#include "rm-rf.h"
+#include "strv.h"
+#include "path-util.h"
+#include "mkdir.h"
+#include "label.h"
+#include "set.h"
+#include "cgroup-util.h"
+
+#include "nspawn-mount.h"
+
+CustomMount* custom_mount_add(CustomMount **l, unsigned *n, CustomMountType t) {
+ CustomMount *c, *ret;
+
+ assert(l);
+ assert(n);
+ assert(t >= 0);
+ assert(t < _CUSTOM_MOUNT_TYPE_MAX);
+
+ c = realloc(*l, (*n + 1) * sizeof(CustomMount));
+ if (!c)
+ return NULL;
+
+ *l = c;
+ ret = *l + *n;
+ (*n)++;
+
+ *ret = (CustomMount) { .type = t };
+
+ return ret;
+}
+
+void custom_mount_free_all(CustomMount *l, unsigned n) {
+ unsigned i;
+
+ for (i = 0; i < n; i++) {
+ CustomMount *m = l + i;
+
+ free(m->source);
+ free(m->destination);
+ free(m->options);
+
+ if (m->work_dir) {
+ (void) rm_rf(m->work_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
+ free(m->work_dir);
+ }
+
+ strv_free(m->lower);
+ }
+
+ free(l);
+}
+
+int custom_mount_compare(const void *a, const void *b) {
+ const CustomMount *x = a, *y = b;
+ int r;
+
+ r = path_compare(x->destination, y->destination);
+ if (r != 0)
+ return r;
+
+ if (x->type < y->type)
+ return -1;
+ if (x->type > y->type)
+ return 1;
+
+ return 0;
+}
+
+int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only) {
+ _cleanup_free_ char *source = NULL, *destination = NULL, *opts = NULL;
+ const char *p = s;
+ CustomMount *m;
+ int r;
+
+ assert(l);
+ assert(n);
+
+ r = extract_many_words(&p, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination, NULL);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
+ if (r == 1) {
+ destination = strdup(source);
+ if (!destination)
+ return -ENOMEM;
+ }
+
+ if (r == 2 && !isempty(p)) {
+ opts = strdup(p);
+ if (!opts)
+ return -ENOMEM;
+ }
+
+ if (!path_is_absolute(source))
+ return -EINVAL;
+
+ if (!path_is_absolute(destination))
+ return -EINVAL;
+
+ m = custom_mount_add(l, n, CUSTOM_MOUNT_BIND);
+ if (!m)
+ return log_oom();
+
+ m->source = source;
+ m->destination = destination;
+ m->read_only = read_only;
+ m->options = opts;
+
+ source = destination = opts = NULL;
+ return 0;
+}
+
+int tmpfs_mount_parse(CustomMount **l, unsigned *n, const char *s) {
+ _cleanup_free_ char *path = NULL, *opts = NULL;
+ const char *p = s;
+ CustomMount *m;
+ int r;
+
+ assert(l);
+ assert(n);
+ assert(s);
+
+ r = extract_first_word(&p, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
+ if (isempty(p))
+ opts = strdup("mode=0755");
+ else
+ opts = strdup(p);
+ if (!opts)
+ return -ENOMEM;
+
+ if (!path_is_absolute(path))
+ return -EINVAL;
+
+ m = custom_mount_add(l, n, CUSTOM_MOUNT_TMPFS);
+ if (!m)
+ return -ENOMEM;
+
+ m->destination = path;
+ m->options = opts;
+
+ path = opts = NULL;
+ return 0;
+}
+
+static int tmpfs_patch_options(
+ const char *options,
+ bool userns, uid_t uid_shift, uid_t uid_range,
+ const char *selinux_apifs_context,
+ char **ret) {
+
+ char *buf = NULL;
+
+ if (userns && uid_shift != 0) {
+ assert(uid_shift != UID_INVALID);
+
+ if (options)
+ (void) asprintf(&buf, "%s,uid=" UID_FMT ",gid=" UID_FMT, options, uid_shift, uid_shift);
+ else
+ (void) asprintf(&buf, "uid=" UID_FMT ",gid=" UID_FMT, uid_shift, uid_shift);
+ if (!buf)
+ return -ENOMEM;
+
+ options = buf;
+ }
+
+#ifdef HAVE_SELINUX
+ if (selinux_apifs_context) {
+ char *t;
+
+ if (options)
+ t = strjoin(options, ",context=\"", selinux_apifs_context, "\"", NULL);
+ else
+ t = strjoin("context=\"", selinux_apifs_context, "\"", NULL);
+ if (!t) {
+ free(buf);
+ return -ENOMEM;
+ }
+
+ free(buf);
+ buf = t;
+ }
+#endif
+
+ *ret = buf;
+ return !!buf;
+}
+
+int mount_all(const char *dest,
+ bool userns, uid_t uid_shift, uid_t uid_range,
+ const char *selinux_apifs_context) {
+
+ typedef struct MountPoint {
+ const char *what;
+ const char *where;
+ const char *type;
+ const char *options;
+ unsigned long flags;
+ bool fatal;
+ bool userns;
+ } MountPoint;
+
+ static const MountPoint mount_table[] = {
+ { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true, true },
+ { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, true, true }, /* Bind mount first */
+ { NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, true, true }, /* Then, make it r/o */
+ { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, true, false },
+ { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true, false },
+ { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false },
+ { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false },
+ { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME, true, false },
+#ifdef HAVE_SELINUX
+ { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, false, false }, /* Bind mount first */
+ { NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, false, false }, /* Then, make it r/o */
+#endif
+ };
+
+ unsigned k;
+ int r;
+
+ for (k = 0; k < ELEMENTSOF(mount_table); k++) {
+ _cleanup_free_ char *where = NULL, *options = NULL;
+ const char *o;
+
+ if (userns != mount_table[k].userns)
+ continue;
+
+ where = prefix_root(dest, mount_table[k].where);
+ if (!where)
+ return log_oom();
+
+ r = path_is_mount_point(where, AT_SYMLINK_FOLLOW);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(r, "Failed to detect whether %s is a mount point: %m", where);
+
+ /* Skip this entry if it is not a remount. */
+ if (mount_table[k].what && r > 0)
+ continue;
+
+ r = mkdir_p(where, 0755);
+ if (r < 0) {
+ if (mount_table[k].fatal)
+ return log_error_errno(r, "Failed to create directory %s: %m", where);
+
+ log_warning_errno(r, "Failed to create directory %s: %m", where);
+ continue;
+ }
+
+ o = mount_table[k].options;
+ if (streq_ptr(mount_table[k].type, "tmpfs")) {
+ r = tmpfs_patch_options(o, userns, uid_shift, uid_range, selinux_apifs_context, &options);
+ if (r < 0)
+ return log_oom();
+ if (r > 0)
+ o = options;
+ }
+
+ if (mount(mount_table[k].what,
+ where,
+ mount_table[k].type,
+ mount_table[k].flags,
+ o) < 0) {
+
+ if (mount_table[k].fatal)
+ return log_error_errno(errno, "mount(%s) failed: %m", where);
+
+ log_warning_errno(errno, "mount(%s) failed, ignoring: %m", where);
+ }
+ }
+
+ return 0;
+}
+
+static int parse_mount_bind_options(const char *options, unsigned long *mount_flags, char **mount_opts) {
+ const char *p = options;
+ unsigned long flags = *mount_flags;
+ char *opts = NULL;
+
+ assert(options);
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ int r = extract_first_word(&p, &word, ",", 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract mount option: %m");
+ if (r == 0)
+ break;
+
+ if (streq(word, "rbind"))
+ flags |= MS_REC;
+ else if (streq(word, "norbind"))
+ flags &= ~MS_REC;
+ else {
+ log_error("Invalid bind mount option: %s", word);
+ return -EINVAL;
+ }
+ }
+
+ *mount_flags = flags;
+ /* in the future mount_opts will hold string options for mount(2) */
+ *mount_opts = opts;
+
+ return 0;
+}
+
+static int mount_bind(const char *dest, CustomMount *m) {
+ struct stat source_st, dest_st;
+ const char *where;
+ unsigned long mount_flags = MS_BIND | MS_REC;
+ _cleanup_free_ char *mount_opts = NULL;
+ int r;
+
+ assert(m);
+
+ if (m->options) {
+ r = parse_mount_bind_options(m->options, &mount_flags, &mount_opts);
+ if (r < 0)
+ return r;
+ }
+
+ if (stat(m->source, &source_st) < 0)
+ return log_error_errno(errno, "Failed to stat %s: %m", m->source);
+
+ where = prefix_roota(dest, m->destination);
+
+ if (stat(where, &dest_st) >= 0) {
+ if (S_ISDIR(source_st.st_mode) && !S_ISDIR(dest_st.st_mode)) {
+ log_error("Cannot bind mount directory %s on file %s.", m->source, where);
+ return -EINVAL;
+ }
+
+ if (!S_ISDIR(source_st.st_mode) && S_ISDIR(dest_st.st_mode)) {
+ log_error("Cannot bind mount file %s on directory %s.", m->source, where);
+ return -EINVAL;
+ }
+
+ } else if (errno == ENOENT) {
+ r = mkdir_parents_label(where, 0755);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make parents of %s: %m", where);
+ } else {
+ log_error_errno(errno, "Failed to stat %s: %m", where);
+ return -errno;
+ }
+
+ /* Create the mount point. Any non-directory file can be
+ * mounted on any non-directory file (regular, fifo, socket,
+ * char, block).
+ */
+ if (S_ISDIR(source_st.st_mode))
+ r = mkdir_label(where, 0755);
+ else
+ r = touch(where);
+ if (r < 0 && r != -EEXIST)
+ return log_error_errno(r, "Failed to create mount point %s: %m", where);
+
+ if (mount(m->source, where, NULL, mount_flags, mount_opts) < 0)
+ return log_error_errno(errno, "mount(%s) failed: %m", where);
+
+ if (m->read_only) {
+ r = bind_remount_recursive(where, true);
+ if (r < 0)
+ return log_error_errno(r, "Read-only bind mount failed: %m");
+ }
+
+ return 0;
+}
+
+static int mount_tmpfs(
+ const char *dest,
+ CustomMount *m,
+ bool userns, uid_t uid_shift, uid_t uid_range,
+ const char *selinux_apifs_context) {
+
+ const char *where, *options;
+ _cleanup_free_ char *buf = NULL;
+ int r;
+
+ assert(dest);
+ assert(m);
+
+ where = prefix_roota(dest, m->destination);
+
+ r = mkdir_p_label(where, 0755);
+ if (r < 0 && r != -EEXIST)
+ return log_error_errno(r, "Creating mount point for tmpfs %s failed: %m", where);
+
+ r = tmpfs_patch_options(m->options, userns, uid_shift, uid_range, selinux_apifs_context, &buf);
+ if (r < 0)
+ return log_oom();
+ options = r > 0 ? buf : m->options;
+
+ if (mount("tmpfs", where, "tmpfs", MS_NODEV|MS_STRICTATIME, options) < 0)
+ return log_error_errno(errno, "tmpfs mount to %s failed: %m", where);
+
+ return 0;
+}
+
+static char *joined_and_escaped_lower_dirs(char * const *lower) {
+ _cleanup_strv_free_ char **sv = NULL;
+
+ sv = strv_copy(lower);
+ if (!sv)
+ return NULL;
+
+ strv_reverse(sv);
+
+ if (!strv_shell_escape(sv, ",:"))
+ return NULL;
+
+ return strv_join(sv, ":");
+}
+
+static int mount_overlay(const char *dest, CustomMount *m) {
+ _cleanup_free_ char *lower = NULL;
+ const char *where, *options;
+ int r;
+
+ assert(dest);
+ assert(m);
+
+ where = prefix_roota(dest, m->destination);
+
+ r = mkdir_label(where, 0755);
+ if (r < 0 && r != -EEXIST)
+ return log_error_errno(r, "Creating mount point for overlay %s failed: %m", where);
+
+ (void) mkdir_p_label(m->source, 0755);
+
+ lower = joined_and_escaped_lower_dirs(m->lower);
+ if (!lower)
+ return log_oom();
+
+ if (m->read_only) {
+ _cleanup_free_ char *escaped_source = NULL;
+
+ escaped_source = shell_escape(m->source, ",:");
+ if (!escaped_source)
+ return log_oom();
+
+ options = strjoina("lowerdir=", escaped_source, ":", lower);
+ } else {
+ _cleanup_free_ char *escaped_source = NULL, *escaped_work_dir = NULL;
+
+ assert(m->work_dir);
+ (void) mkdir_label(m->work_dir, 0700);
+
+ escaped_source = shell_escape(m->source, ",:");
+ if (!escaped_source)
+ return log_oom();
+ escaped_work_dir = shell_escape(m->work_dir, ",:");
+ if (!escaped_work_dir)
+ return log_oom();
+
+ options = strjoina("lowerdir=", lower, ",upperdir=", escaped_source, ",workdir=", escaped_work_dir);
+ }
+
+ if (mount("overlay", where, "overlay", m->read_only ? MS_RDONLY : 0, options) < 0)
+ return log_error_errno(errno, "overlay mount to %s failed: %m", where);
+
+ return 0;
+}
+
+int mount_custom(
+ const char *dest,
+ CustomMount *mounts, unsigned n,
+ bool userns, uid_t uid_shift, uid_t uid_range,
+ const char *selinux_apifs_context) {
+
+ unsigned i;
+ int r;
+
+ assert(dest);
+
+ for (i = 0; i < n; i++) {
+ CustomMount *m = mounts + i;
+
+ switch (m->type) {
+
+ case CUSTOM_MOUNT_BIND:
+ r = mount_bind(dest, m);
+ break;
+
+ case CUSTOM_MOUNT_TMPFS:
+ r = mount_tmpfs(dest, m, userns, uid_shift, uid_range, selinux_apifs_context);
+ break;
+
+ case CUSTOM_MOUNT_OVERLAY:
+ r = mount_overlay(dest, m);
+ break;
+
+ default:
+ assert_not_reached("Unknown custom mount type");
+ }
+
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int mount_legacy_cgroup_hierarchy(const char *dest, const char *controller, const char *hierarchy, bool read_only) {
+ char *to;
+ int r;
+
+ to = strjoina(dest, "/sys/fs/cgroup/", hierarchy);
+
+ r = path_is_mount_point(to, 0);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to);
+ if (r > 0)
+ return 0;
+
+ mkdir_p(to, 0755);
+
+ /* The superblock mount options of the mount point need to be
+ * identical to the hosts', and hence writable... */
+ if (mount("cgroup", to, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, controller) < 0)
+ return log_error_errno(errno, "Failed to mount to %s: %m", to);
+
+ /* ... hence let's only make the bind mount read-only, not the
+ * superblock. */
+ if (read_only) {
+ if (mount(NULL, to, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL) < 0)
+ return log_error_errno(errno, "Failed to remount %s read-only: %m", to);
+ }
+ return 1;
+}
+
+static int mount_legacy_cgroups(
+ const char *dest,
+ bool userns, uid_t uid_shift, uid_t uid_range,
+ const char *selinux_apifs_context) {
+
+ _cleanup_set_free_free_ Set *controllers = NULL;
+ const char *cgroup_root;
+ int r;
+
+ cgroup_root = prefix_roota(dest, "/sys/fs/cgroup");
+
+ /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
+ r = path_is_mount_point(cgroup_root, AT_SYMLINK_FOLLOW);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
+ if (r == 0) {
+ _cleanup_free_ char *options = NULL;
+
+ r = tmpfs_patch_options("mode=755", userns, uid_shift, uid_range, selinux_apifs_context, &options);
+ if (r < 0)
+ return log_oom();
+
+ if (mount("tmpfs", cgroup_root, "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options) < 0)
+ return log_error_errno(errno, "Failed to mount /sys/fs/cgroup: %m");
+ }
+
+ if (cg_unified() > 0)
+ goto skip_controllers;
+
+ controllers = set_new(&string_hash_ops);
+ if (!controllers)
+ return log_oom();
+
+ r = cg_kernel_controllers(controllers);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine cgroup controllers: %m");
+
+ for (;;) {
+ _cleanup_free_ char *controller = NULL, *origin = NULL, *combined = NULL;
+
+ controller = set_steal_first(controllers);
+ if (!controller)
+ break;
+
+ origin = prefix_root("/sys/fs/cgroup/", controller);
+ if (!origin)
+ return log_oom();
+
+ r = readlink_malloc(origin, &combined);
+ if (r == -EINVAL) {
+ /* Not a symbolic link, but directly a single cgroup hierarchy */
+
+ r = mount_legacy_cgroup_hierarchy(dest, controller, controller, true);
+ if (r < 0)
+ return r;
+
+ } else if (r < 0)
+ return log_error_errno(r, "Failed to read link %s: %m", origin);
+ else {
+ _cleanup_free_ char *target = NULL;
+
+ target = prefix_root(dest, origin);
+ if (!target)
+ return log_oom();
+
+ /* A symbolic link, a combination of controllers in one hierarchy */
+
+ if (!filename_is_valid(combined)) {
+ log_warning("Ignoring invalid combined hierarchy %s.", combined);
+ continue;
+ }
+
+ r = mount_legacy_cgroup_hierarchy(dest, combined, combined, true);
+ if (r < 0)
+ return r;
+
+ r = symlink_idempotent(combined, target);
+ if (r == -EINVAL) {
+ log_error("Invalid existing symlink for combined hierarchy");
+ return r;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
+ }
+ }
+
+skip_controllers:
+ r = mount_legacy_cgroup_hierarchy(dest, "none,name=systemd,xattr", "systemd", false);
+ if (r < 0)
+ return r;
+
+ if (mount(NULL, cgroup_root, NULL, MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755") < 0)
+ return log_error_errno(errno, "Failed to remount %s read-only: %m", cgroup_root);
+
+ return 0;
+}
+
+static int mount_unified_cgroups(const char *dest) {
+ const char *p;
+ int r;
+
+ assert(dest);
+
+ p = strjoina(dest, "/sys/fs/cgroup");
+
+ r = path_is_mount_point(p, AT_SYMLINK_FOLLOW);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine if %s is mounted already: %m", p);
+ if (r > 0) {
+ p = strjoina(dest, "/sys/fs/cgroup/cgroup.procs");
+ if (access(p, F_OK) >= 0)
+ return 0;
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m", p);
+
+ log_error("%s is already mounted but not a unified cgroup hierarchy. Refusing.", p);
+ return -EINVAL;
+ }
+
+ if (mount("cgroup", p, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, "__DEVEL__sane_behavior") < 0)
+ return log_error_errno(errno, "Failed to mount unified cgroup hierarchy to %s: %m", p);
+
+ return 0;
+}
+
+int mount_cgroups(
+ const char *dest,
+ bool unified_requested,
+ bool userns, uid_t uid_shift, uid_t uid_range,
+ const char *selinux_apifs_context) {
+
+ if (unified_requested)
+ return mount_unified_cgroups(dest);
+ else
+ return mount_legacy_cgroups(dest, userns, uid_shift, uid_range, selinux_apifs_context);
+}
+
+int mount_systemd_cgroup_writable(
+ const char *dest,
+ bool unified_requested) {
+
+ _cleanup_free_ char *own_cgroup_path = NULL;
+ const char *systemd_root, *systemd_own;
+ int r;
+
+ assert(dest);
+
+ r = cg_pid_get_path(NULL, 0, &own_cgroup_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine our own cgroup path: %m");
+
+ /* If we are living in the top-level, then there's nothing to do... */
+ if (path_equal(own_cgroup_path, "/"))
+ return 0;
+
+ if (unified_requested) {
+ systemd_own = strjoina(dest, "/sys/fs/cgroup", own_cgroup_path);
+ systemd_root = prefix_roota(dest, "/sys/fs/cgroup");
+ } else {
+ systemd_own = strjoina(dest, "/sys/fs/cgroup/systemd", own_cgroup_path);
+ systemd_root = prefix_roota(dest, "/sys/fs/cgroup/systemd");
+ }
+
+ /* Make our own cgroup a (writable) bind mount */
+ if (mount(systemd_own, systemd_own, NULL, MS_BIND, NULL) < 0)
+ return log_error_errno(errno, "Failed to turn %s into a bind mount: %m", own_cgroup_path);
+
+ /* And then remount the systemd cgroup root read-only */
+ if (mount(NULL, systemd_root, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL) < 0)
+ return log_error_errno(errno, "Failed to mount cgroup root read-only: %m");
+
+ return 0;
+}
+
+int setup_volatile_state(
+ const char *directory,
+ VolatileMode mode,
+ bool userns, uid_t uid_shift, uid_t uid_range,
+ const char *selinux_apifs_context) {
+
+ _cleanup_free_ char *buf = NULL;
+ const char *p, *options;
+ int r;
+
+ assert(directory);
+
+ if (mode != VOLATILE_STATE)
+ return 0;
+
+ /* --volatile=state means we simply overmount /var
+ with a tmpfs, and the rest read-only. */
+
+ r = bind_remount_recursive(directory, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to remount %s read-only: %m", directory);
+
+ p = prefix_roota(directory, "/var");
+ r = mkdir(p, 0755);
+ if (r < 0 && errno != EEXIST)
+ return log_error_errno(errno, "Failed to create %s: %m", directory);
+
+ options = "mode=755";
+ r = tmpfs_patch_options(options, userns, uid_shift, uid_range, selinux_apifs_context, &buf);
+ if (r < 0)
+ return log_oom();
+ if (r > 0)
+ options = buf;
+
+ if (mount("tmpfs", p, "tmpfs", MS_STRICTATIME, options) < 0)
+ return log_error_errno(errno, "Failed to mount tmpfs to /var: %m");
+
+ return 0;
+}
+
+int setup_volatile(
+ const char *directory,
+ VolatileMode mode,
+ bool userns, uid_t uid_shift, uid_t uid_range,
+ const char *selinux_apifs_context) {
+
+ bool tmpfs_mounted = false, bind_mounted = false;
+ char template[] = "/tmp/nspawn-volatile-XXXXXX";
+ _cleanup_free_ char *buf = NULL;
+ const char *f, *t, *options;
+ int r;
+
+ assert(directory);
+
+ if (mode != VOLATILE_YES)
+ return 0;
+
+ /* --volatile=yes means we mount a tmpfs to the root dir, and
+ the original /usr to use inside it, and that read-only. */
+
+ if (!mkdtemp(template))
+ return log_error_errno(errno, "Failed to create temporary directory: %m");
+
+ options = "mode=755";
+ r = tmpfs_patch_options(options, userns, uid_shift, uid_range, selinux_apifs_context, &buf);
+ if (r < 0)
+ return log_oom();
+ if (r > 0)
+ options = buf;
+
+ if (mount("tmpfs", template, "tmpfs", MS_STRICTATIME, options) < 0) {
+ r = log_error_errno(errno, "Failed to mount tmpfs for root directory: %m");
+ goto fail;
+ }
+
+ tmpfs_mounted = true;
+
+ f = prefix_roota(directory, "/usr");
+ t = prefix_roota(template, "/usr");
+
+ r = mkdir(t, 0755);
+ if (r < 0 && errno != EEXIST) {
+ r = log_error_errno(errno, "Failed to create %s: %m", t);
+ goto fail;
+ }
+
+ if (mount(f, t, NULL, MS_BIND|MS_REC, NULL) < 0) {
+ r = log_error_errno(errno, "Failed to create /usr bind mount: %m");
+ goto fail;
+ }
+
+ bind_mounted = true;
+
+ r = bind_remount_recursive(t, true);
+ if (r < 0) {
+ log_error_errno(r, "Failed to remount %s read-only: %m", t);
+ goto fail;
+ }
+
+ if (mount(template, directory, NULL, MS_MOVE, NULL) < 0) {
+ r = log_error_errno(errno, "Failed to move root mount: %m");
+ goto fail;
+ }
+
+ (void) rmdir(template);
+
+ return 0;
+
+fail:
+ if (bind_mounted)
+ (void) umount(t);
+
+ if (tmpfs_mounted)
+ (void) umount(template);
+ (void) rmdir(template);
+ return r;
+}
+
+VolatileMode volatile_mode_from_string(const char *s) {
+ int b;
+
+ if (isempty(s))
+ return _VOLATILE_MODE_INVALID;
+
+ b = parse_boolean(s);
+ if (b > 0)
+ return VOLATILE_YES;
+ if (b == 0)
+ return VOLATILE_NO;
+
+ if (streq(s, "state"))
+ return VOLATILE_STATE;
+
+ return _VOLATILE_MODE_INVALID;
+}
diff --git a/src/nspawn/nspawn-mount.h b/src/nspawn/nspawn-mount.h
new file mode 100644
index 0000000000..5abd44cc4b
--- /dev/null
+++ b/src/nspawn/nspawn-mount.h
@@ -0,0 +1,70 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+typedef enum VolatileMode {
+ VOLATILE_NO,
+ VOLATILE_YES,
+ VOLATILE_STATE,
+ _VOLATILE_MODE_MAX,
+ _VOLATILE_MODE_INVALID = -1
+} VolatileMode;
+
+typedef enum CustomMountType {
+ CUSTOM_MOUNT_BIND,
+ CUSTOM_MOUNT_TMPFS,
+ CUSTOM_MOUNT_OVERLAY,
+ _CUSTOM_MOUNT_TYPE_MAX,
+ _CUSTOM_MOUNT_TYPE_INVALID = -1
+} CustomMountType;
+
+typedef struct CustomMount {
+ CustomMountType type;
+ bool read_only;
+ char *source; /* for overlayfs this is the upper directory */
+ char *destination;
+ char *options;
+ char *work_dir;
+ char **lower;
+} CustomMount;
+
+CustomMount* custom_mount_add(CustomMount **l, unsigned *n, CustomMountType t);
+
+void custom_mount_free_all(CustomMount *l, unsigned n);
+int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only);
+int tmpfs_mount_parse(CustomMount **l, unsigned *n, const char *s);
+
+int custom_mount_compare(const void *a, const void *b);
+
+int mount_all(const char *dest, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
+
+int mount_cgroups(const char *dest, bool unified_requested, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
+int mount_systemd_cgroup_writable(const char *dest, bool unified_requested);
+
+int mount_custom(const char *dest, CustomMount *mounts, unsigned n, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
+
+int setup_volatile(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
+int setup_volatile_state(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
+
+VolatileMode volatile_mode_from_string(const char *s);
diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c
new file mode 100644
index 0000000000..74abe5379a
--- /dev/null
+++ b/src/nspawn/nspawn-network.c
@@ -0,0 +1,440 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <linux/veth.h>
+#include <net/if.h>
+
+#include "sd-id128.h"
+#include "sd-netlink.h"
+#include "libudev.h"
+
+#include "util.h"
+#include "ether-addr-util.h"
+#include "siphash24.h"
+#include "netlink-util.h"
+#include "udev-util.h"
+
+#include "nspawn-network.h"
+
+#define HOST_HASH_KEY SD_ID128_MAKE(1a,37,6f,c7,46,ec,45,0b,ad,a3,d5,31,06,60,5d,b1)
+#define CONTAINER_HASH_KEY SD_ID128_MAKE(c3,c4,f9,19,b5,57,b2,1c,e6,cf,14,27,03,9c,ee,a2)
+#define MACVLAN_HASH_KEY SD_ID128_MAKE(00,13,6d,bc,66,83,44,81,bb,0c,f9,51,1f,24,a6,6f)
+
+static int generate_mac(
+ const char *machine_name,
+ struct ether_addr *mac,
+ sd_id128_t hash_key,
+ uint64_t idx) {
+
+ uint8_t result[8];
+ size_t l, sz;
+ uint8_t *v, *i;
+ int r;
+
+ l = strlen(machine_name);
+ sz = sizeof(sd_id128_t) + l;
+ if (idx > 0)
+ sz += sizeof(idx);
+
+ v = alloca(sz);
+
+ /* fetch some persistent data unique to the host */
+ r = sd_id128_get_machine((sd_id128_t*) v);
+ if (r < 0)
+ return r;
+
+ /* combine with some data unique (on this host) to this
+ * container instance */
+ i = mempcpy(v + sizeof(sd_id128_t), machine_name, l);
+ if (idx > 0) {
+ idx = htole64(idx);
+ memcpy(i, &idx, sizeof(idx));
+ }
+
+ /* Let's hash the host machine ID plus the container name. We
+ * use a fixed, but originally randomly created hash key here. */
+ siphash24(result, v, sz, hash_key.bytes);
+
+ assert_cc(ETH_ALEN <= sizeof(result));
+ memcpy(mac->ether_addr_octet, result, ETH_ALEN);
+
+ /* see eth_random_addr in the kernel */
+ mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
+ mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
+
+ return 0;
+}
+
+int setup_veth(const char *machine_name,
+ pid_t pid,
+ char iface_name[IFNAMSIZ],
+ bool bridge) {
+
+ _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
+ _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ struct ether_addr mac_host, mac_container;
+ int r, i;
+
+ /* Use two different interface name prefixes depending whether
+ * we are in bridge mode or not. */
+ snprintf(iface_name, IFNAMSIZ - 1, "%s-%s",
+ bridge ? "vb" : "ve", machine_name);
+
+ r = generate_mac(machine_name, &mac_container, CONTAINER_HASH_KEY, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate predictable MAC address for container side: %m");
+
+ r = generate_mac(machine_name, &mac_host, HOST_HASH_KEY, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate predictable MAC address for host side: %m");
+
+ r = sd_netlink_open(&rtnl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to netlink: %m");
+
+ r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate netlink message: %m");
+
+ r = sd_netlink_message_append_string(m, IFLA_IFNAME, iface_name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink interface name: %m");
+
+ r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac_host);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink MAC address: %m");
+
+ r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open netlink container: %m");
+
+ r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "veth");
+ if (r < 0)
+ return log_error_errno(r, "Failed to open netlink container: %m");
+
+ r = sd_netlink_message_open_container(m, VETH_INFO_PEER);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open netlink container: %m");
+
+ r = sd_netlink_message_append_string(m, IFLA_IFNAME, "host0");
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink interface name: %m");
+
+ r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac_container);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink MAC address: %m");
+
+ r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink namespace field: %m");
+
+ r = sd_netlink_message_close_container(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to close netlink container: %m");
+
+ r = sd_netlink_message_close_container(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to close netlink container: %m");
+
+ r = sd_netlink_message_close_container(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to close netlink container: %m");
+
+ r = sd_netlink_call(rtnl, m, 0, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add new veth interfaces (host0, %s): %m", iface_name);
+
+ i = (int) if_nametoindex(iface_name);
+ if (i <= 0)
+ return log_error_errno(errno, "Failed to resolve interface %s: %m", iface_name);
+
+ return i;
+}
+
+int setup_bridge(const char *veth_name, const char *bridge_name) {
+ _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
+ _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ int r, bridge_ifi;
+
+ assert(veth_name);
+ assert(bridge_name);
+
+ bridge_ifi = (int) if_nametoindex(bridge_name);
+ if (bridge_ifi <= 0)
+ return log_error_errno(errno, "Failed to resolve interface %s: %m", bridge_name);
+
+ r = sd_netlink_open(&rtnl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to netlink: %m");
+
+ r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate netlink message: %m");
+
+ r = sd_rtnl_message_link_set_flags(m, IFF_UP, IFF_UP);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set IFF_UP flag: %m");
+
+ r = sd_netlink_message_append_string(m, IFLA_IFNAME, veth_name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink interface name field: %m");
+
+ r = sd_netlink_message_append_u32(m, IFLA_MASTER, bridge_ifi);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink master field: %m");
+
+ r = sd_netlink_call(rtnl, m, 0, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add veth interface to bridge: %m");
+
+ return bridge_ifi;
+}
+
+static int parse_interface(struct udev *udev, const char *name) {
+ _cleanup_udev_device_unref_ struct udev_device *d = NULL;
+ char ifi_str[2 + DECIMAL_STR_MAX(int)];
+ int ifi;
+
+ ifi = (int) if_nametoindex(name);
+ if (ifi <= 0)
+ return log_error_errno(errno, "Failed to resolve interface %s: %m", name);
+
+ sprintf(ifi_str, "n%i", ifi);
+ d = udev_device_new_from_device_id(udev, ifi_str);
+ if (!d)
+ return log_error_errno(errno, "Failed to get udev device for interface %s: %m", name);
+
+ if (udev_device_get_is_initialized(d) <= 0) {
+ log_error("Network interface %s is not initialized yet.", name);
+ return -EBUSY;
+ }
+
+ return ifi;
+}
+
+int move_network_interfaces(pid_t pid, char **ifaces) {
+ _cleanup_udev_unref_ struct udev *udev = NULL;
+ _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ char **i;
+ int r;
+
+ if (strv_isempty(ifaces))
+ return 0;
+
+ r = sd_netlink_open(&rtnl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to netlink: %m");
+
+ udev = udev_new();
+ if (!udev) {
+ log_error("Failed to connect to udev.");
+ return -ENOMEM;
+ }
+
+ STRV_FOREACH(i, ifaces) {
+ _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
+ int ifi;
+
+ ifi = parse_interface(udev, *i);
+ if (ifi < 0)
+ return ifi;
+
+ r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, ifi);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate netlink message: %m");
+
+ r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to append namespace PID to netlink message: %m");
+
+ r = sd_netlink_call(rtnl, m, 0, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to move interface %s to namespace: %m", *i);
+ }
+
+ return 0;
+}
+
+int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) {
+ _cleanup_udev_unref_ struct udev *udev = NULL;
+ _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ unsigned idx = 0;
+ char **i;
+ int r;
+
+ if (strv_isempty(ifaces))
+ return 0;
+
+ r = sd_netlink_open(&rtnl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to netlink: %m");
+
+ udev = udev_new();
+ if (!udev) {
+ log_error("Failed to connect to udev.");
+ return -ENOMEM;
+ }
+
+ STRV_FOREACH(i, ifaces) {
+ _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
+ _cleanup_free_ char *n = NULL;
+ struct ether_addr mac;
+ int ifi;
+
+ ifi = parse_interface(udev, *i);
+ if (ifi < 0)
+ return ifi;
+
+ r = generate_mac(machine_name, &mac, MACVLAN_HASH_KEY, idx++);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create MACVLAN MAC address: %m");
+
+ r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate netlink message: %m");
+
+ r = sd_netlink_message_append_u32(m, IFLA_LINK, ifi);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink interface index: %m");
+
+ n = strappend("mv-", *i);
+ if (!n)
+ return log_oom();
+
+ strshorten(n, IFNAMSIZ-1);
+
+ r = sd_netlink_message_append_string(m, IFLA_IFNAME, n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink interface name: %m");
+
+ r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink MAC address: %m");
+
+ r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink namespace field: %m");
+
+ r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open netlink container: %m");
+
+ r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "macvlan");
+ if (r < 0)
+ return log_error_errno(r, "Failed to open netlink container: %m");
+
+ r = sd_netlink_message_append_u32(m, IFLA_MACVLAN_MODE, MACVLAN_MODE_BRIDGE);
+ if (r < 0)
+ return log_error_errno(r, "Failed to append macvlan mode: %m");
+
+ r = sd_netlink_message_close_container(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to close netlink container: %m");
+
+ r = sd_netlink_message_close_container(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to close netlink container: %m");
+
+ r = sd_netlink_call(rtnl, m, 0, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add new macvlan interfaces: %m");
+ }
+
+ return 0;
+}
+
+int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) {
+ _cleanup_udev_unref_ struct udev *udev = NULL;
+ _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ char **i;
+ int r;
+
+ if (strv_isempty(ifaces))
+ return 0;
+
+ r = sd_netlink_open(&rtnl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to netlink: %m");
+
+ udev = udev_new();
+ if (!udev) {
+ log_error("Failed to connect to udev.");
+ return -ENOMEM;
+ }
+
+ STRV_FOREACH(i, ifaces) {
+ _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
+ _cleanup_free_ char *n = NULL;
+ int ifi;
+
+ ifi = parse_interface(udev, *i);
+ if (ifi < 0)
+ return ifi;
+
+ r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate netlink message: %m");
+
+ r = sd_netlink_message_append_u32(m, IFLA_LINK, ifi);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink interface index: %m");
+
+ n = strappend("iv-", *i);
+ if (!n)
+ return log_oom();
+
+ strshorten(n, IFNAMSIZ-1);
+
+ r = sd_netlink_message_append_string(m, IFLA_IFNAME, n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink interface name: %m");
+
+ r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink namespace field: %m");
+
+ r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open netlink container: %m");
+
+ r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "ipvlan");
+ if (r < 0)
+ return log_error_errno(r, "Failed to open netlink container: %m");
+
+ r = sd_netlink_message_append_u16(m, IFLA_IPVLAN_MODE, IPVLAN_MODE_L2);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add ipvlan mode: %m");
+
+ r = sd_netlink_message_close_container(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to close netlink container: %m");
+
+ r = sd_netlink_message_close_container(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to close netlink container: %m");
+
+ r = sd_netlink_call(rtnl, m, 0, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add new ipvlan interfaces: %m");
+ }
+
+ return 0;
+}
diff --git a/src/nspawn/nspawn-network.h b/src/nspawn/nspawn-network.h
new file mode 100644
index 0000000000..311e6d06cb
--- /dev/null
+++ b/src/nspawn/nspawn-network.h
@@ -0,0 +1,36 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <net/if.h>
+
+#include <sys/types.h>
+#include <stdbool.h>
+
+int setup_veth(const char *machine_name, pid_t pid, char iface_name[IFNAMSIZ], bool bridge);
+
+int setup_bridge(const char *veth_name, const char *bridge_name);
+
+int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces);
+int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces);
+
+int move_network_interfaces(pid_t pid, char **ifaces);
diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c
new file mode 100644
index 0000000000..b2776a61c2
--- /dev/null
+++ b/src/nspawn/nspawn-register.c
@@ -0,0 +1,244 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-bus.h"
+
+#include "util.h"
+#include "strv.h"
+#include "bus-util.h"
+#include "bus-error.h"
+
+#include "nspawn-register.h"
+
+int register_machine(
+ const char *machine_name,
+ pid_t pid,
+ const char *directory,
+ sd_id128_t uuid,
+ int local_ifindex,
+ const char *slice,
+ CustomMount *mounts,
+ unsigned n_mounts,
+ int kill_signal,
+ char **properties,
+ bool keep_unit) {
+
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ int r;
+
+ r = sd_bus_default_system(&bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open system bus: %m");
+
+ if (keep_unit) {
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "RegisterMachineWithNetwork",
+ &error,
+ NULL,
+ "sayssusai",
+ machine_name,
+ SD_BUS_MESSAGE_APPEND_ID128(uuid),
+ "nspawn",
+ "container",
+ (uint32_t) pid,
+ strempty(directory),
+ local_ifindex > 0 ? 1 : 0, local_ifindex);
+ } else {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ char **i;
+ unsigned j;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "CreateMachineWithNetwork");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(
+ m,
+ "sayssusai",
+ machine_name,
+ SD_BUS_MESSAGE_APPEND_ID128(uuid),
+ "nspawn",
+ "container",
+ (uint32_t) pid,
+ strempty(directory),
+ local_ifindex > 0 ? 1 : 0, local_ifindex);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(m, 'a', "(sv)");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ if (!isempty(slice)) {
+ r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ r = sd_bus_message_append(m, "(sv)", "DevicePolicy", "s", "strict");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ /* If you make changes here, also make sure to update
+ * systemd-nspawn@.service, to keep the device
+ * policies in sync regardless if we are run with or
+ * without the --keep-unit switch. */
+ r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 9,
+ /* Allow the container to
+ * access and create the API
+ * device nodes, so that
+ * PrivateDevices= in the
+ * container can work
+ * fine */
+ "/dev/null", "rwm",
+ "/dev/zero", "rwm",
+ "/dev/full", "rwm",
+ "/dev/random", "rwm",
+ "/dev/urandom", "rwm",
+ "/dev/tty", "rwm",
+ "/dev/net/tun", "rwm",
+ /* Allow the container
+ * access to ptys. However,
+ * do not permit the
+ * container to ever create
+ * these device nodes. */
+ "/dev/pts/ptmx", "rw",
+ "char-pts", "rw");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ for (j = 0; j < n_mounts; j++) {
+ CustomMount *cm = mounts + j;
+
+ if (cm->type != CUSTOM_MOUNT_BIND)
+ continue;
+
+ r = is_device_node(cm->source);
+ if (r < 0)
+ return log_error_errno(r, "Failed to stat %s: %m", cm->source);
+
+ if (r) {
+ r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 1,
+ cm->source, cm->read_only ? "r" : "rw");
+ if (r < 0)
+ return log_error_errno(r, "Failed to append message arguments: %m");
+ }
+ }
+
+ if (kill_signal != 0) {
+ r = sd_bus_message_append(m, "(sv)", "KillSignal", "i", kill_signal);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "(sv)", "KillMode", "s", "mixed");
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ STRV_FOREACH(i, properties) {
+ r = sd_bus_message_open_container(m, 'r', "sv");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = bus_append_unit_property_assignment(m, *i);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, 0, &error, NULL);
+ }
+
+ if (r < 0) {
+ log_error("Failed to register machine: %s", bus_error_message(&error, r));
+ return r;
+ }
+
+ return 0;
+}
+
+int terminate_machine(pid_t pid) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+ const char *path;
+ int r;
+
+ r = sd_bus_default_system(&bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open system bus: %m");
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "GetMachineByPID",
+ &error,
+ &reply,
+ "u",
+ (uint32_t) pid);
+ if (r < 0) {
+ /* Note that the machine might already have been
+ * cleaned up automatically, hence don't consider it a
+ * failure if we cannot get the machine object. */
+ log_debug("Failed to get machine: %s", bus_error_message(&error, r));
+ return 0;
+ }
+
+ r = sd_bus_message_read(reply, "o", &path);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.machine1",
+ path,
+ "org.freedesktop.machine1.Machine",
+ "Terminate",
+ &error,
+ NULL,
+ NULL);
+ if (r < 0) {
+ log_debug("Failed to terminate machine: %s", bus_error_message(&error, r));
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/src/nspawn/nspawn-register.h b/src/nspawn/nspawn-register.h
new file mode 100644
index 0000000000..b27841ff59
--- /dev/null
+++ b/src/nspawn/nspawn-register.h
@@ -0,0 +1,31 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#include "sd-id128.h"
+
+#include "nspawn-mount.h"
+
+int register_machine(const char *machine_name, pid_t pid, const char *directory, sd_id128_t uuid, int local_ifindex, const char *slice, CustomMount *mounts, unsigned n_mounts, int kill_signal, char **properties, bool keep_unit);
+int terminate_machine(pid_t pid);
diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c
new file mode 100644
index 0000000000..419f5d1c40
--- /dev/null
+++ b/src/nspawn/nspawn-settings.c
@@ -0,0 +1,262 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "conf-parser.h"
+#include "strv.h"
+#include "cap-list.h"
+
+#include "nspawn-settings.h"
+
+int settings_load(FILE *f, const char *path, Settings **ret) {
+ _cleanup_(settings_freep) Settings *s = NULL;
+ int r;
+
+ assert(path);
+ assert(ret);
+
+ s = new0(Settings, 1);
+ if (!s)
+ return -ENOMEM;
+
+ s->boot = -1;
+ s->personality = PERSONALITY_INVALID;
+
+ s->read_only = -1;
+ s->volatile_mode = _VOLATILE_MODE_INVALID;
+
+ s->private_network = -1;
+ s->network_veth = -1;
+
+ r = config_parse(NULL, path, f,
+ "Exec\0"
+ "Network\0"
+ "Files\0",
+ config_item_perf_lookup, nspawn_gperf_lookup,
+ false,
+ false,
+ true,
+ s);
+ if (r < 0)
+ return r;
+
+ *ret = s;
+ s = NULL;
+
+ return 0;
+}
+
+Settings* settings_free(Settings *s) {
+
+ if (!s)
+ return NULL;
+
+ strv_free(s->parameters);
+ strv_free(s->environment);
+ free(s->user);
+
+ strv_free(s->network_interfaces);
+ strv_free(s->network_macvlan);
+ strv_free(s->network_ipvlan);
+ free(s->network_bridge);
+ expose_port_free_all(s->expose_ports);
+
+ custom_mount_free_all(s->custom_mounts, s->n_custom_mounts);
+ free(s);
+
+ return NULL;
+}
+
+DEFINE_CONFIG_PARSE_ENUM(config_parse_volatile_mode, volatile_mode, VolatileMode, "Failed to parse volatile mode");
+
+int config_parse_expose_port(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Settings *s = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ r = expose_port_parse(&s->expose_ports, rvalue);
+ if (r == -EEXIST) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Duplicate port specification, ignoring: %s", rvalue);
+ return 0;
+ }
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse host port %s: %m", rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
+int config_parse_capability(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ uint64_t u = 0, *result = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ int cap;
+
+ r = extract_first_word(&rvalue, &word, NULL, 0);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract capability string, ignoring: %s", rvalue);
+ return 0;
+ }
+ if (r == 0)
+ break;
+
+ cap = capability_from_name(word);
+ if (cap < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, cap, "Failed to parse capability, ignoring: %s", word);
+ continue;
+ }
+
+ u |= 1 << ((uint64_t) cap);
+ }
+
+ if (u == 0)
+ return 0;
+
+ *result |= u;
+ return 0;
+}
+
+int config_parse_id128(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ sd_id128_t t, *result = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ r = sd_id128_from_string(rvalue, &t);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ *result = t;
+ return 0;
+}
+
+int config_parse_bind(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Settings *settings = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ r = bind_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid bind mount specification %s: %m", rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
+int config_parse_tmpfs(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Settings *settings = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ r = tmpfs_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid temporary file system specification %s: %m", rvalue);
+ return 0;
+ }
+
+ if (settings->network_bridge)
+ settings->network_veth = true;
+
+ if (settings->network_interfaces ||
+ settings->network_macvlan ||
+ settings->network_ipvlan ||
+ settings->network_bridge ||
+ settings->network_veth)
+ settings->private_network = true;
+
+ return 0;
+}
diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h
new file mode 100644
index 0000000000..4cec40c1b7
--- /dev/null
+++ b/src/nspawn/nspawn-settings.h
@@ -0,0 +1,87 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+
+#include "macro.h"
+
+#include "nspawn-mount.h"
+#include "nspawn-expose-ports.h"
+
+typedef enum SettingsMask {
+ SETTING_BOOT = 1 << 0,
+ SETTING_ENVIRONMENT = 1 << 1,
+ SETTING_USER = 1 << 2,
+ SETTING_CAPABILITY = 1 << 3,
+ SETTING_KILL_SIGNAL = 1 << 4,
+ SETTING_PERSONALITY = 1 << 5,
+ SETTING_MACHINE_ID = 1 << 6,
+ SETTING_NETWORK = 1 << 7,
+ SETTING_EXPOSE_PORTS = 1 << 8,
+ SETTING_READ_ONLY = 1 << 9,
+ SETTING_VOLATILE_MODE = 1 << 10,
+ SETTING_CUSTOM_MOUNTS = 1 << 11,
+ _SETTINGS_MASK_ALL = (1 << 12) -1
+} SettingsMask;
+
+typedef struct Settings {
+ /* [Run] */
+ int boot;
+ char **parameters;
+ char **environment;
+ char *user;
+ uint64_t capability;
+ uint64_t drop_capability;
+ int kill_signal;
+ unsigned long personality;
+ sd_id128_t machine_id;
+
+ /* [Image] */
+ int read_only;
+ VolatileMode volatile_mode;
+ CustomMount *custom_mounts;
+ unsigned n_custom_mounts;
+
+ /* [Network] */
+ int private_network;
+ int network_veth;
+ char *network_bridge;
+ char **network_interfaces;
+ char **network_macvlan;
+ char **network_ipvlan;
+ ExposePort *expose_ports;
+} Settings;
+
+int settings_load(FILE *f, const char *path, Settings **ret);
+Settings* settings_free(Settings *s);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Settings*, settings_free);
+
+const struct ConfigPerfItem* nspawn_gperf_lookup(const char *key, unsigned length);
+
+int config_parse_capability(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_id128(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_expose_port(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_volatile_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_bind(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_tmpfs(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/nspawn/nspawn-setuid.c b/src/nspawn/nspawn-setuid.c
new file mode 100644
index 0000000000..eda7f62900
--- /dev/null
+++ b/src/nspawn/nspawn-setuid.c
@@ -0,0 +1,272 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <grp.h>
+
+#include "util.h"
+#include "signal-util.h"
+#include "mkdir.h"
+#include "process-util.h"
+
+#include "nspawn-setuid.h"
+
+static int spawn_getent(const char *database, const char *key, pid_t *rpid) {
+ int pipe_fds[2];
+ pid_t pid;
+
+ assert(database);
+ assert(key);
+ assert(rpid);
+
+ if (pipe2(pipe_fds, O_CLOEXEC) < 0)
+ return log_error_errno(errno, "Failed to allocate pipe: %m");
+
+ pid = fork();
+ if (pid < 0)
+ return log_error_errno(errno, "Failed to fork getent child: %m");
+ else if (pid == 0) {
+ int nullfd;
+ char *empty_env = NULL;
+
+ if (dup3(pipe_fds[1], STDOUT_FILENO, 0) < 0)
+ _exit(EXIT_FAILURE);
+
+ if (pipe_fds[0] > 2)
+ safe_close(pipe_fds[0]);
+ if (pipe_fds[1] > 2)
+ safe_close(pipe_fds[1]);
+
+ nullfd = open("/dev/null", O_RDWR);
+ if (nullfd < 0)
+ _exit(EXIT_FAILURE);
+
+ if (dup3(nullfd, STDIN_FILENO, 0) < 0)
+ _exit(EXIT_FAILURE);
+
+ if (dup3(nullfd, STDERR_FILENO, 0) < 0)
+ _exit(EXIT_FAILURE);
+
+ if (nullfd > 2)
+ safe_close(nullfd);
+
+ (void) reset_all_signal_handlers();
+ (void) reset_signal_mask();
+ close_all_fds(NULL, 0);
+
+ execle("/usr/bin/getent", "getent", database, key, NULL, &empty_env);
+ execle("/bin/getent", "getent", database, key, NULL, &empty_env);
+ _exit(EXIT_FAILURE);
+ }
+
+ pipe_fds[1] = safe_close(pipe_fds[1]);
+
+ *rpid = pid;
+
+ return pipe_fds[0];
+}
+
+int change_uid_gid(const char *user, char **_home) {
+ char line[LINE_MAX], *x, *u, *g, *h;
+ const char *word, *state;
+ _cleanup_free_ uid_t *uids = NULL;
+ _cleanup_free_ char *home = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_close_ int fd = -1;
+ unsigned n_uids = 0;
+ size_t sz = 0, l;
+ uid_t uid;
+ gid_t gid;
+ pid_t pid;
+ int r;
+
+ assert(_home);
+
+ if (!user || streq(user, "root") || streq(user, "0")) {
+ /* Reset everything fully to 0, just in case */
+
+ r = reset_uid_gid();
+ if (r < 0)
+ return log_error_errno(r, "Failed to become root: %m");
+
+ *_home = NULL;
+ return 0;
+ }
+
+ /* First, get user credentials */
+ fd = spawn_getent("passwd", user, &pid);
+ if (fd < 0)
+ return fd;
+
+ f = fdopen(fd, "r");
+ if (!f)
+ return log_oom();
+ fd = -1;
+
+ if (!fgets(line, sizeof(line), f)) {
+
+ if (!ferror(f)) {
+ log_error("Failed to resolve user %s.", user);
+ return -ESRCH;
+ }
+
+ log_error_errno(errno, "Failed to read from getent: %m");
+ return -errno;
+ }
+
+ truncate_nl(line);
+
+ wait_for_terminate_and_warn("getent passwd", pid, true);
+
+ x = strchr(line, ':');
+ if (!x) {
+ log_error("/etc/passwd entry has invalid user field.");
+ return -EIO;
+ }
+
+ u = strchr(x+1, ':');
+ if (!u) {
+ log_error("/etc/passwd entry has invalid password field.");
+ return -EIO;
+ }
+
+ u++;
+ g = strchr(u, ':');
+ if (!g) {
+ log_error("/etc/passwd entry has invalid UID field.");
+ return -EIO;
+ }
+
+ *g = 0;
+ g++;
+ x = strchr(g, ':');
+ if (!x) {
+ log_error("/etc/passwd entry has invalid GID field.");
+ return -EIO;
+ }
+
+ *x = 0;
+ h = strchr(x+1, ':');
+ if (!h) {
+ log_error("/etc/passwd entry has invalid GECOS field.");
+ return -EIO;
+ }
+
+ h++;
+ x = strchr(h, ':');
+ if (!x) {
+ log_error("/etc/passwd entry has invalid home directory field.");
+ return -EIO;
+ }
+
+ *x = 0;
+
+ r = parse_uid(u, &uid);
+ if (r < 0) {
+ log_error("Failed to parse UID of user.");
+ return -EIO;
+ }
+
+ r = parse_gid(g, &gid);
+ if (r < 0) {
+ log_error("Failed to parse GID of user.");
+ return -EIO;
+ }
+
+ home = strdup(h);
+ if (!home)
+ return log_oom();
+
+ /* Second, get group memberships */
+ fd = spawn_getent("initgroups", user, &pid);
+ if (fd < 0)
+ return fd;
+
+ fclose(f);
+ f = fdopen(fd, "r");
+ if (!f)
+ return log_oom();
+ fd = -1;
+
+ if (!fgets(line, sizeof(line), f)) {
+ if (!ferror(f)) {
+ log_error("Failed to resolve user %s.", user);
+ return -ESRCH;
+ }
+
+ log_error_errno(errno, "Failed to read from getent: %m");
+ return -errno;
+ }
+
+ truncate_nl(line);
+
+ wait_for_terminate_and_warn("getent initgroups", pid, true);
+
+ /* Skip over the username and subsequent separator whitespace */
+ x = line;
+ x += strcspn(x, WHITESPACE);
+ x += strspn(x, WHITESPACE);
+
+ FOREACH_WORD(word, l, x, state) {
+ char c[l+1];
+
+ memcpy(c, word, l);
+ c[l] = 0;
+
+ if (!GREEDY_REALLOC(uids, sz, n_uids+1))
+ return log_oom();
+
+ r = parse_uid(c, &uids[n_uids++]);
+ if (r < 0) {
+ log_error("Failed to parse group data from getent.");
+ return -EIO;
+ }
+ }
+
+ r = mkdir_parents(home, 0775);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make home root directory: %m");
+
+ r = mkdir_safe(home, 0755, uid, gid);
+ if (r < 0 && r != -EEXIST)
+ return log_error_errno(r, "Failed to make home directory: %m");
+
+ (void) fchown(STDIN_FILENO, uid, gid);
+ (void) fchown(STDOUT_FILENO, uid, gid);
+ (void) fchown(STDERR_FILENO, uid, gid);
+
+ if (setgroups(n_uids, uids) < 0)
+ return log_error_errno(errno, "Failed to set auxiliary groups: %m");
+
+ if (setresgid(gid, gid, gid) < 0)
+ return log_error_errno(errno, "setregid() failed: %m");
+
+ if (setresuid(uid, uid, uid) < 0)
+ return log_error_errno(errno, "setreuid() failed: %m");
+
+ if (_home) {
+ *_home = home;
+ home = NULL;
+ }
+
+ return 0;
+}
diff --git a/src/nspawn/nspawn-setuid.h b/src/nspawn/nspawn-setuid.h
new file mode 100644
index 0000000000..33be44a946
--- /dev/null
+++ b/src/nspawn/nspawn-setuid.h
@@ -0,0 +1,24 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+int change_uid_gid(const char *user, char **ret);
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 837947ee28..33943a4b2f 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -19,95 +19,78 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <signal.h>
-#include <sched.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/mount.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
+#ifdef HAVE_BLKID
+#include <blkid/blkid.h>
+#endif
#include <errno.h>
-#include <sys/prctl.h>
#include <getopt.h>
-#include <grp.h>
-#include <linux/fs.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <net/if.h>
-#include <linux/veth.h>
-#include <sys/personality.h>
#include <linux/loop.h>
-#include <sys/file.h>
-
-#ifdef HAVE_SELINUX
-#include <selinux/selinux.h>
-#endif
-
+#include <sched.h>
#ifdef HAVE_SECCOMP
#include <seccomp.h>
#endif
-
-#ifdef HAVE_BLKID
-#include <blkid/blkid.h>
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
#endif
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/mount.h>
+#include <sys/personality.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <unistd.h>
#include "sd-daemon.h"
-#include "sd-bus.h"
#include "sd-id128.h"
-#include "sd-netlink.h"
-#include "random-util.h"
-#include "log.h"
-#include "util.h"
-#include "mkdir.h"
-#include "rm-rf.h"
-#include "macro.h"
-#include "missing.h"
+
+#include "barrier.h"
+#include "base-filesystem.h"
+#include "blkid-util.h"
+#include "btrfs-util.h"
+#include "build.h"
+#include "cap-list.h"
+#include "capability.h"
#include "cgroup-util.h"
-#include "strv.h"
-#include "path-util.h"
-#include "loopback-setup.h"
+#include "copy.h"
#include "dev-setup.h"
+#include "env-util.h"
+#include "event-util.h"
#include "fdset.h"
-#include "build.h"
#include "fileio.h"
-#include "bus-util.h"
-#include "bus-error.h"
-#include "ptyfwd.h"
-#include "env-util.h"
-#include "netlink-util.h"
-#include "udev-util.h"
-#include "blkid-util.h"
+#include "formats-util.h"
#include "gpt.h"
-#include "siphash24.h"
-#include "copy.h"
-#include "base-filesystem.h"
-#include "barrier.h"
-#include "event-util.h"
-#include "capability.h"
-#include "cap-list.h"
-#include "btrfs-util.h"
+#include "hostname-util.h"
+#include "log.h"
+#include "loopback-setup.h"
#include "machine-image.h"
-#include "list.h"
-#include "in-addr-util.h"
-#include "firewall-util.h"
-#include "local-addresses.h"
-#include "formats-util.h"
+#include "macro.h"
+#include "missing.h"
+#include "mkdir.h"
+#include "netlink-util.h"
+#include "path-util.h"
#include "process-util.h"
-#include "terminal-util.h"
-#include "hostname-util.h"
-#include "signal-util.h"
-
+#include "ptyfwd.h"
+#include "random-util.h"
+#include "rm-rf.h"
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
#endif
+#include "signal-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "udev-util.h"
+#include "util.h"
-typedef struct ExposePort {
- int protocol;
- uint16_t host_port;
- uint16_t container_port;
- LIST_FIELDS(struct ExposePort, ports);
-} ExposePort;
+#include "nspawn-settings.h"
+#include "nspawn-mount.h"
+#include "nspawn-network.h"
+#include "nspawn-expose-ports.h"
+#include "nspawn-cgroup.h"
+#include "nspawn-register.h"
+#include "nspawn-setuid.h"
typedef enum ContainerStatus {
CONTAINER_TERMINATED,
@@ -121,28 +104,6 @@ typedef enum LinkJournal {
LINK_GUEST
} LinkJournal;
-typedef enum Volatile {
- VOLATILE_NO,
- VOLATILE_YES,
- VOLATILE_STATE,
-} Volatile;
-
-typedef enum CustomMountType {
- CUSTOM_MOUNT_BIND,
- CUSTOM_MOUNT_TMPFS,
- CUSTOM_MOUNT_OVERLAY,
-} CustomMountType;
-
-typedef struct CustomMount {
- CustomMountType type;
- bool read_only;
- char *source; /* for overlayfs this is the upper directory */
- char *destination;
- char *options;
- char *work_dir;
- char **lower;
-} CustomMount;
-
static char *arg_directory = NULL;
static char *arg_template = NULL;
static char *arg_user = NULL;
@@ -195,15 +156,19 @@ static char **arg_network_interfaces = NULL;
static char **arg_network_macvlan = NULL;
static char **arg_network_ipvlan = NULL;
static bool arg_network_veth = false;
-static const char *arg_network_bridge = NULL;
+static char *arg_network_bridge = NULL;
static unsigned long arg_personality = PERSONALITY_INVALID;
static char *arg_image = NULL;
-static Volatile arg_volatile = VOLATILE_NO;
+static VolatileMode arg_volatile_mode = VOLATILE_NO;
static ExposePort *arg_expose_ports = NULL;
static char **arg_property = NULL;
static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U;
static bool arg_userns = false;
static int arg_kill_signal = 0;
+static bool arg_unified_cgroup_hierarchy = false;
+static SettingsMask arg_settings_mask = 0;
+static int arg_settings_trusted = -1;
+static char **arg_parameters = NULL;
static void help(void) {
printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
@@ -257,9 +222,11 @@ static void help(void) {
" try-guest, try-host\n"
" -j Equivalent to --link-journal=try-guest\n"
" --read-only Mount the root directory read-only\n"
- " --bind=PATH[:PATH] Bind mount a file or directory from the host into\n"
+ " --bind=PATH[:PATH[:OPTIONS]]\n"
+ " Bind mount a file or directory from the host into\n"
" the container\n"
- " --bind-ro=PATH[:PATH] Similar, but creates a read-only bind mount\n"
+ " --bind-ro=PATH[:PATH[:OPTIONS]\n"
+ " Similar, but creates a read-only bind mount\n"
" --tmpfs=PATH:[OPTIONS] Mount an empty tmpfs to the specified directory\n"
" --overlay=PATH[:PATH...]:PATH\n"
" Create an overlay mount from the host to \n"
@@ -272,62 +239,10 @@ static void help(void) {
" --keep-unit Do not register a scope for the machine, reuse\n"
" the service unit nspawn is running in\n"
" --volatile[=MODE] Run the system in volatile mode\n"
+ " --settings=BOOLEAN Load additional settings from .nspawn file\n"
, program_invocation_short_name);
}
-static CustomMount* custom_mount_add(CustomMountType t) {
- CustomMount *c, *ret;
-
- c = realloc(arg_custom_mounts, (arg_n_custom_mounts + 1) * sizeof(CustomMount));
- if (!c)
- return NULL;
-
- arg_custom_mounts = c;
- ret = arg_custom_mounts + arg_n_custom_mounts;
- arg_n_custom_mounts++;
-
- *ret = (CustomMount) { .type = t };
-
- return ret;
-}
-
-static void custom_mount_free_all(void) {
- unsigned i;
-
- for (i = 0; i < arg_n_custom_mounts; i++) {
- CustomMount *m = &arg_custom_mounts[i];
-
- free(m->source);
- free(m->destination);
- free(m->options);
-
- if (m->work_dir) {
- (void) rm_rf(m->work_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
- free(m->work_dir);
- }
-
- strv_free(m->lower);
- }
-
- arg_custom_mounts = mfree(arg_custom_mounts);
- arg_n_custom_mounts = 0;
-}
-
-static int custom_mount_compare(const void *a, const void *b) {
- const CustomMount *x = a, *y = b;
- int r;
-
- r = path_compare(x->destination, y->destination);
- if (r != 0)
- return r;
-
- if (x->type < y->type)
- return -1;
- if (x->type > y->type)
- return 1;
-
- return 0;
-}
static int custom_mounts_prepare(void) {
unsigned i;
@@ -383,6 +298,30 @@ static int set_sanitized_path(char **b, const char *path) {
return 0;
}
+static int detect_unified_cgroup_hierarchy(void) {
+ const char *e;
+ int r;
+
+ /* Allow the user to control whether the unified hierarchy is used */
+ e = getenv("UNIFIED_CGROUP_HIERARCHY");
+ if (e) {
+ r = parse_boolean(e);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse $UNIFIED_CGROUP_HIERARCHY.");
+
+ arg_unified_cgroup_hierarchy = r;
+ return 0;
+ }
+
+ /* Otherwise inherit the default from the host system */
+ r = cg_unified();
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m");
+
+ arg_unified_cgroup_hierarchy = r;
+ return 0;
+}
+
static int parse_argv(int argc, char *argv[]) {
enum {
@@ -412,6 +351,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_PROPERTY,
ARG_PRIVATE_USERS,
ARG_KILL_SIGNAL,
+ ARG_SETTINGS,
};
static const struct option options[] = {
@@ -454,11 +394,13 @@ static int parse_argv(int argc, char *argv[]) {
{ "property", required_argument, NULL, ARG_PROPERTY },
{ "private-users", optional_argument, NULL, ARG_PRIVATE_USERS },
{ "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL },
+ { "settings", required_argument, NULL, ARG_SETTINGS },
{}
};
int c, r;
uint64_t plus = 0, minus = 0;
+ bool mask_all_settings = false, mask_no_settings = false;
assert(argc >= 0);
assert(argv);
@@ -506,16 +448,20 @@ static int parse_argv(int argc, char *argv[]) {
if (r < 0)
return log_oom();
+ arg_settings_mask |= SETTING_USER;
break;
case ARG_NETWORK_BRIDGE:
- arg_network_bridge = optarg;
+ r = free_and_strdup(&arg_network_bridge, optarg);
+ if (r < 0)
+ return log_oom();
/* fall through */
case 'n':
arg_network_veth = true;
arg_private_network = true;
+ arg_settings_mask |= SETTING_NETWORK;
break;
case ARG_NETWORK_INTERFACE:
@@ -523,6 +469,7 @@ static int parse_argv(int argc, char *argv[]) {
return log_oom();
arg_private_network = true;
+ arg_settings_mask |= SETTING_NETWORK;
break;
case ARG_NETWORK_MACVLAN:
@@ -530,6 +477,7 @@ static int parse_argv(int argc, char *argv[]) {
return log_oom();
arg_private_network = true;
+ arg_settings_mask |= SETTING_NETWORK;
break;
case ARG_NETWORK_IPVLAN:
@@ -540,10 +488,12 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_PRIVATE_NETWORK:
arg_private_network = true;
+ arg_settings_mask |= SETTING_NETWORK;
break;
case 'b':
arg_boot = true;
+ arg_settings_mask |= SETTING_BOOT;
break;
case ARG_UUID:
@@ -552,6 +502,8 @@ static int parse_argv(int argc, char *argv[]) {
log_error("Invalid UUID: %s", optarg);
return r;
}
+
+ arg_settings_mask |= SETTING_MACHINE_ID;
break;
case 'S':
@@ -584,6 +536,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_READ_ONLY:
arg_read_only = true;
+ arg_settings_mask |= SETTING_READ_ONLY;
break;
case ARG_CAPABILITY:
@@ -619,6 +572,7 @@ static int parse_argv(int argc, char *argv[]) {
}
}
+ arg_settings_mask |= SETTING_CAPABILITY;
break;
}
@@ -654,81 +608,21 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_BIND:
- case ARG_BIND_RO: {
- const char *current = optarg;
- _cleanup_free_ char *source = NULL, *destination = NULL;
- CustomMount *m;
-
- r = extract_many_words(&current, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination, NULL);
- switch (r) {
- case 1:
- destination = strdup(source);
- case 2:
- break;
- case -ENOMEM:
- return log_oom();
- default:
- log_error("Invalid bind mount specification: %s", optarg);
- return -EINVAL;
- }
-
- if (!source || !destination)
- return log_oom();
-
- if (!path_is_absolute(source) || !path_is_absolute(destination)) {
- log_error("Invalid bind mount specification: %s", optarg);
- return -EINVAL;
- }
-
- m = custom_mount_add(CUSTOM_MOUNT_BIND);
- if (!m)
- return log_oom();
-
- m->source = source;
- m->destination = destination;
- m->read_only = c == ARG_BIND_RO;
-
- source = destination = NULL;
+ case ARG_BIND_RO:
+ r = bind_mount_parse(&arg_custom_mounts, &arg_n_custom_mounts, optarg, c == ARG_BIND_RO);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --bind(-ro)= argument %s: %m", optarg);
+ arg_settings_mask |= SETTING_CUSTOM_MOUNTS;
break;
- }
-
- case ARG_TMPFS: {
- const char *current = optarg;
- _cleanup_free_ char *path = NULL, *opts = NULL;
- CustomMount *m;
- r = extract_first_word(&current, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
- if (r == -ENOMEM)
- return log_oom();
- else if (r < 0) {
- log_error("Invalid tmpfs specification: %s", optarg);
- return r;
- }
- if (r)
- opts = strdup(current);
- else
- opts = strdup("mode=0755");
-
- if (!path || !opts)
- return log_oom();
-
- if (!path_is_absolute(path)) {
- log_error("Invalid tmpfs specification: %s", optarg);
- return -EINVAL;
- }
-
- m = custom_mount_add(CUSTOM_MOUNT_TMPFS);
- if (!m)
- return log_oom();
-
- m->destination = path;
- m->options = opts;
-
- path = opts = NULL;
+ case ARG_TMPFS:
+ r = tmpfs_mount_parse(&arg_custom_mounts, &arg_n_custom_mounts, optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --tmpfs= argument %s: %m", optarg);
+ arg_settings_mask |= SETTING_CUSTOM_MOUNTS;
break;
- }
case ARG_OVERLAY:
case ARG_OVERLAY_RO: {
@@ -779,7 +673,7 @@ static int parse_argv(int argc, char *argv[]) {
lower[n - 2] = NULL;
}
- m = custom_mount_add(CUSTOM_MOUNT_OVERLAY);
+ m = custom_mount_add(&arg_custom_mounts, &arg_n_custom_mounts, CUSTOM_MOUNT_OVERLAY);
if (!m)
return log_oom();
@@ -791,6 +685,7 @@ static int parse_argv(int argc, char *argv[]) {
upper = destination = NULL;
lower = NULL;
+ arg_settings_mask |= SETTING_CUSTOM_MOUNTS;
break;
}
@@ -808,6 +703,8 @@ static int parse_argv(int argc, char *argv[]) {
strv_free(arg_setenv);
arg_setenv = n;
+
+ arg_settings_mask |= SETTING_ENVIRONMENT;
break;
}
@@ -841,85 +738,36 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
+ arg_settings_mask |= SETTING_PERSONALITY;
break;
case ARG_VOLATILE:
if (!optarg)
- arg_volatile = VOLATILE_YES;
+ arg_volatile_mode = VOLATILE_YES;
else {
- r = parse_boolean(optarg);
- if (r < 0) {
- if (streq(optarg, "state"))
- arg_volatile = VOLATILE_STATE;
- else {
- log_error("Failed to parse --volatile= argument: %s", optarg);
- return r;
- }
- } else
- arg_volatile = r ? VOLATILE_YES : VOLATILE_NO;
- }
-
- break;
-
- case 'p': {
- const char *split, *e;
- uint16_t container_port, host_port;
- int protocol;
- ExposePort *p;
-
- if ((e = startswith(optarg, "tcp:")))
- protocol = IPPROTO_TCP;
- else if ((e = startswith(optarg, "udp:")))
- protocol = IPPROTO_UDP;
- else {
- e = optarg;
- protocol = IPPROTO_TCP;
- }
-
- split = strchr(e, ':');
- if (split) {
- char v[split - e + 1];
-
- memcpy(v, e, split - e);
- v[split - e] = 0;
-
- r = safe_atou16(v, &host_port);
- if (r < 0 || host_port <= 0) {
- log_error("Failed to parse host port: %s", optarg);
- return -EINVAL;
- }
-
- r = safe_atou16(split + 1, &container_port);
- } else {
- r = safe_atou16(e, &container_port);
- host_port = container_port;
- }
-
- if (r < 0 || container_port <= 0) {
- log_error("Failed to parse host port: %s", optarg);
- return -EINVAL;
- }
+ VolatileMode m;
- LIST_FOREACH(ports, p, arg_expose_ports) {
- if (p->protocol == protocol && p->host_port == host_port) {
- log_error("Duplicate port specification: %s", optarg);
+ m = volatile_mode_from_string(optarg);
+ if (m < 0) {
+ log_error("Failed to parse --volatile= argument: %s", optarg);
return -EINVAL;
- }
+ } else
+ arg_volatile_mode = m;
}
- p = new(ExposePort, 1);
- if (!p)
- return log_oom();
-
- p->protocol = protocol;
- p->host_port = host_port;
- p->container_port = container_port;
+ arg_settings_mask |= SETTING_VOLATILE_MODE;
+ break;
- LIST_PREPEND(ports, arg_expose_ports, p);
+ case 'p':
+ r = expose_port_parse(&arg_expose_ports, optarg);
+ if (r == -EEXIST)
+ return log_error_errno(r, "Duplicate port specification: %s", optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse host port %s: %m", optarg);
+ arg_settings_mask |= SETTING_EXPOSE_PORTS;
break;
- }
case ARG_PROPERTY:
if (strv_extend(&arg_property, optarg) < 0)
@@ -963,6 +811,42 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
+ arg_settings_mask |= SETTING_KILL_SIGNAL;
+ break;
+
+ case ARG_SETTINGS:
+
+ /* no → do not read files
+ * yes → read files, do not override cmdline, trust only subset
+ * override → read files, override cmdline, trust only subset
+ * trusted → read files, do not override cmdline, trust all
+ */
+
+ r = parse_boolean(optarg);
+ if (r < 0) {
+ if (streq(optarg, "trusted")) {
+ mask_all_settings = false;
+ mask_no_settings = false;
+ arg_settings_trusted = true;
+
+ } else if (streq(optarg, "override")) {
+ mask_all_settings = false;
+ mask_no_settings = true;
+ arg_settings_trusted = -1;
+ } else
+ return log_error_errno(r, "Failed to parse --settings= argument: %s", optarg);
+ } else if (r > 0) {
+ /* yes */
+ mask_all_settings = false;
+ mask_no_settings = false;
+ arg_settings_trusted = -1;
+ } else {
+ /* no */
+ mask_all_settings = true;
+ mask_no_settings = false;
+ arg_settings_trusted = false;
+ }
+
break;
case '?':
@@ -1015,449 +899,48 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
- if (arg_volatile != VOLATILE_NO && arg_read_only) {
- log_error("Cannot combine --read-only with --volatile. Note that --volatile already implies a read-only base hierarchy.");
- return -EINVAL;
- }
-
- if (arg_expose_ports && !arg_private_network) {
- log_error("Cannot use --port= without private networking.");
- return -EINVAL;
- }
-
if (arg_userns && access("/proc/self/uid_map", F_OK) < 0)
return log_error_errno(EOPNOTSUPP, "--private-users= is not supported, kernel compiled without user namespace support.");
- arg_retain = (arg_retain | plus | (arg_private_network ? 1ULL << CAP_NET_ADMIN : 0)) & ~minus;
-
- if (arg_boot && arg_kill_signal <= 0)
- arg_kill_signal = SIGRTMIN+3;
-
- return 1;
-}
-
-static int tmpfs_patch_options(const char *options, char **ret) {
- char *buf = NULL;
-
- if (arg_userns && arg_uid_shift != 0) {
- assert(arg_uid_shift != UID_INVALID);
-
- if (options)
- (void) asprintf(&buf, "%s,uid=" UID_FMT ",gid=" UID_FMT, options, arg_uid_shift, arg_uid_shift);
- else
- (void) asprintf(&buf, "uid=" UID_FMT ",gid=" UID_FMT, arg_uid_shift, arg_uid_shift);
- if (!buf)
- return -ENOMEM;
-
- options = buf;
- }
-
-#ifdef HAVE_SELINUX
- if (arg_selinux_apifs_context) {
- char *t;
-
- if (options)
- t = strjoin(options, ",context=\"", arg_selinux_apifs_context, "\"", NULL);
- else
- t = strjoin("context=\"", arg_selinux_apifs_context, "\"", NULL);
- if (!t) {
- free(buf);
- return -ENOMEM;
- }
-
- free(buf);
- buf = t;
- }
-#endif
-
- *ret = buf;
- return !!buf;
-}
-
-static int mount_all(const char *dest, bool userns) {
-
- typedef struct MountPoint {
- const char *what;
- const char *where;
- const char *type;
- const char *options;
- unsigned long flags;
- bool fatal;
- bool userns;
- } MountPoint;
-
- static const MountPoint mount_table[] = {
- { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true, true },
- { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, true, true }, /* Bind mount first */
- { NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, true, true }, /* Then, make it r/o */
- { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, true, false },
- { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, true, false },
- { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true, false },
- { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false },
- { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false },
- { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME, true, false },
-#ifdef HAVE_SELINUX
- { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, false, false }, /* Bind mount first */
- { NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, false, false }, /* Then, make it r/o */
-#endif
- };
-
- unsigned k;
- int r;
-
- for (k = 0; k < ELEMENTSOF(mount_table); k++) {
- _cleanup_free_ char *where = NULL, *options = NULL;
- const char *o;
-
- if (userns != mount_table[k].userns)
- continue;
-
- where = prefix_root(dest, mount_table[k].where);
- if (!where)
+ if (argc > optind) {
+ arg_parameters = strv_copy(argv + optind);
+ if (!arg_parameters)
return log_oom();
- r = path_is_mount_point(where, AT_SYMLINK_FOLLOW);
- if (r < 0 && r != -ENOENT)
- return log_error_errno(r, "Failed to detect whether %s is a mount point: %m", where);
-
- /* Skip this entry if it is not a remount. */
- if (mount_table[k].what && r > 0)
- continue;
-
- r = mkdir_p(where, 0755);
- if (r < 0) {
- if (mount_table[k].fatal)
- return log_error_errno(r, "Failed to create directory %s: %m", where);
-
- log_warning_errno(r, "Failed to create directory %s: %m", where);
- continue;
- }
-
- o = mount_table[k].options;
- if (streq_ptr(mount_table[k].type, "tmpfs")) {
- r = tmpfs_patch_options(o, &options);
- if (r < 0)
- return log_oom();
- if (r > 0)
- o = options;
- }
-
- if (mount(mount_table[k].what,
- where,
- mount_table[k].type,
- mount_table[k].flags,
- o) < 0) {
-
- if (mount_table[k].fatal)
- return log_error_errno(errno, "mount(%s) failed: %m", where);
-
- log_warning_errno(errno, "mount(%s) failed, ignoring: %m", where);
- }
+ arg_settings_mask |= SETTING_BOOT;
}
- return 0;
-}
+ /* Load all settings from .nspawn files */
+ if (mask_no_settings)
+ arg_settings_mask = 0;
-static int mount_bind(const char *dest, CustomMount *m) {
- struct stat source_st, dest_st;
- const char *where;
- int r;
-
- assert(m);
-
- if (stat(m->source, &source_st) < 0)
- return log_error_errno(errno, "Failed to stat %s: %m", m->source);
-
- where = prefix_roota(dest, m->destination);
-
- if (stat(where, &dest_st) >= 0) {
- if (S_ISDIR(source_st.st_mode) && !S_ISDIR(dest_st.st_mode)) {
- log_error("Cannot bind mount directory %s on file %s.", m->source, where);
- return -EINVAL;
- }
-
- if (!S_ISDIR(source_st.st_mode) && S_ISDIR(dest_st.st_mode)) {
- log_error("Cannot bind mount file %s on directory %s.", m->source, where);
- return -EINVAL;
- }
-
- } else if (errno == ENOENT) {
- r = mkdir_parents_label(where, 0755);
- if (r < 0)
- return log_error_errno(r, "Failed to make parents of %s: %m", where);
- } else {
- log_error_errno(errno, "Failed to stat %s: %m", where);
- return -errno;
- }
-
- /* Create the mount point. Any non-directory file can be
- * mounted on any non-directory file (regular, fifo, socket,
- * char, block).
- */
- if (S_ISDIR(source_st.st_mode))
- r = mkdir_label(where, 0755);
- else
- r = touch(where);
- if (r < 0 && r != -EEXIST)
- return log_error_errno(r, "Failed to create mount point %s: %m", where);
-
- if (mount(m->source, where, NULL, MS_BIND, NULL) < 0)
- return log_error_errno(errno, "mount(%s) failed: %m", where);
-
- if (m->read_only) {
- r = bind_remount_recursive(where, true);
- if (r < 0)
- return log_error_errno(r, "Read-only bind mount failed: %m");
- }
-
- return 0;
-}
+ /* Don't load any settings from .nspawn files */
+ if (mask_all_settings)
+ arg_settings_mask = _SETTINGS_MASK_ALL;
-static int mount_tmpfs(const char *dest, CustomMount *m) {
- const char *where, *options;
- _cleanup_free_ char *buf = NULL;
- int r;
-
- assert(dest);
- assert(m);
-
- where = prefix_roota(dest, m->destination);
-
- r = mkdir_p_label(where, 0755);
- if (r < 0 && r != -EEXIST)
- return log_error_errno(r, "Creating mount point for tmpfs %s failed: %m", where);
+ arg_retain = (arg_retain | plus | (arg_private_network ? 1ULL << CAP_NET_ADMIN : 0)) & ~minus;
- r = tmpfs_patch_options(m->options, &buf);
+ r = detect_unified_cgroup_hierarchy();
if (r < 0)
- return log_oom();
- options = r > 0 ? buf : m->options;
-
- if (mount("tmpfs", where, "tmpfs", MS_NODEV|MS_STRICTATIME, options) < 0)
- return log_error_errno(errno, "tmpfs mount to %s failed: %m", where);
-
- return 0;
-}
-
-static char *joined_and_escaped_lower_dirs(char * const *lower) {
- _cleanup_strv_free_ char **sv = NULL;
-
- sv = strv_copy(lower);
- if (!sv)
- return NULL;
-
- strv_reverse(sv);
-
- if (!strv_shell_escape(sv, ",:"))
- return NULL;
-
- return strv_join(sv, ":");
-}
-
-static int mount_overlay(const char *dest, CustomMount *m) {
- _cleanup_free_ char *lower = NULL;
- const char *where, *options;
- int r;
-
- assert(dest);
- assert(m);
-
- where = prefix_roota(dest, m->destination);
-
- r = mkdir_label(where, 0755);
- if (r < 0 && r != -EEXIST)
- return log_error_errno(r, "Creating mount point for overlay %s failed: %m", where);
-
- (void) mkdir_p_label(m->source, 0755);
-
- lower = joined_and_escaped_lower_dirs(m->lower);
- if (!lower)
- return log_oom();
-
- if (m->read_only) {
- _cleanup_free_ char *escaped_source = NULL;
-
- escaped_source = shell_escape(m->source, ",:");
- if (!escaped_source)
- return log_oom();
-
- options = strjoina("lowerdir=", escaped_source, ":", lower);
- } else {
- _cleanup_free_ char *escaped_source = NULL, *escaped_work_dir = NULL;
-
- assert(m->work_dir);
- (void) mkdir_label(m->work_dir, 0700);
-
- escaped_source = shell_escape(m->source, ",:");
- if (!escaped_source)
- return log_oom();
- escaped_work_dir = shell_escape(m->work_dir, ",:");
- if (!escaped_work_dir)
- return log_oom();
-
- options = strjoina("lowerdir=", lower, ",upperdir=", escaped_source, ",workdir=", escaped_work_dir);
- }
-
- if (mount("overlay", where, "overlay", m->read_only ? MS_RDONLY : 0, options) < 0)
- return log_error_errno(errno, "overlay mount to %s failed: %m", where);
-
- return 0;
-}
-
-static int mount_custom(const char *dest) {
- unsigned i;
- int r;
-
- assert(dest);
-
- for (i = 0; i < arg_n_custom_mounts; i++) {
- CustomMount *m = &arg_custom_mounts[i];
-
- switch (m->type) {
-
- case CUSTOM_MOUNT_BIND:
- r = mount_bind(dest, m);
- break;
-
- case CUSTOM_MOUNT_TMPFS:
- r = mount_tmpfs(dest, m);
- break;
-
- case CUSTOM_MOUNT_OVERLAY:
- r = mount_overlay(dest, m);
- break;
-
- default:
- assert_not_reached("Unknown custom mount type");
- }
-
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int mount_cgroup_hierarchy(const char *dest, const char *controller, const char *hierarchy, bool read_only) {
- char *to;
- int r;
-
- to = strjoina(dest, "/sys/fs/cgroup/", hierarchy);
-
- r = path_is_mount_point(to, 0);
- if (r < 0 && r != -ENOENT)
- return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to);
- if (r > 0)
- return 0;
-
- mkdir_p(to, 0755);
-
- /* The superblock mount options of the mount point need to be
- * identical to the hosts', and hence writable... */
- if (mount("cgroup", to, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, controller) < 0)
- return log_error_errno(errno, "Failed to mount to %s: %m", to);
+ return r;
- /* ... hence let's only make the bind mount read-only, not the
- * superblock. */
- if (read_only) {
- if (mount(NULL, to, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL) < 0)
- return log_error_errno(errno, "Failed to remount %s read-only: %m", to);
- }
return 1;
}
-static int mount_cgroup(const char *dest) {
- _cleanup_set_free_free_ Set *controllers = NULL;
- const char *cgroup_root;
- int r;
-
- controllers = set_new(&string_hash_ops);
- if (!controllers)
- return log_oom();
-
- r = cg_kernel_controllers(controllers);
- if (r < 0)
- return log_error_errno(r, "Failed to determine cgroup controllers: %m");
-
- for (;;) {
- _cleanup_free_ char *controller = NULL, *origin = NULL, *combined = NULL;
-
- controller = set_steal_first(controllers);
- if (!controller)
- break;
-
- origin = prefix_root("/sys/fs/cgroup/", controller);
- if (!origin)
- return log_oom();
-
- r = readlink_malloc(origin, &combined);
- if (r == -EINVAL) {
- /* Not a symbolic link, but directly a single cgroup hierarchy */
-
- r = mount_cgroup_hierarchy(dest, controller, controller, true);
- if (r < 0)
- return r;
-
- } else if (r < 0)
- return log_error_errno(r, "Failed to read link %s: %m", origin);
- else {
- _cleanup_free_ char *target = NULL;
-
- target = prefix_root(dest, origin);
- if (!target)
- return log_oom();
+static int verify_arguments(void) {
- /* A symbolic link, a combination of controllers in one hierarchy */
-
- if (!filename_is_valid(combined)) {
- log_warning("Ignoring invalid combined hierarchy %s.", combined);
- continue;
- }
-
- r = mount_cgroup_hierarchy(dest, combined, combined, true);
- if (r < 0)
- return r;
-
- r = symlink_idempotent(combined, target);
- if (r == -EINVAL) {
- log_error("Invalid existing symlink for combined hierarchy");
- return r;
- }
- if (r < 0)
- return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
- }
+ if (arg_volatile_mode != VOLATILE_NO && arg_read_only) {
+ log_error("Cannot combine --read-only with --volatile. Note that --volatile already implies a read-only base hierarchy.");
+ return -EINVAL;
}
- r = mount_cgroup_hierarchy(dest, "name=systemd,xattr", "systemd", false);
- if (r < 0)
- return r;
-
- cgroup_root = prefix_roota(dest, "/sys/fs/cgroup");
- if (mount(NULL, cgroup_root, NULL, MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755") < 0)
- return log_error_errno(errno, "Failed to remount %s read-only: %m", cgroup_root);
-
- return 0;
-}
-
-static int mount_systemd_cgroup_writable(const char *dest) {
- _cleanup_free_ char *own_cgroup_path = NULL;
- const char *systemd_root, *systemd_own;
- int r;
-
- assert(dest);
-
- r = cg_pid_get_path(NULL, 0, &own_cgroup_path);
- if (r < 0)
- return log_error_errno(r, "Failed to determine our own cgroup path: %m");
-
- /* Make our own cgroup a (writable) bind mount */
- systemd_own = strjoina(dest, "/sys/fs/cgroup/systemd", own_cgroup_path);
- if (mount(systemd_own, systemd_own, NULL, MS_BIND, NULL) < 0)
- return log_error_errno(errno, "Failed to turn %s into a bind mount: %m", own_cgroup_path);
+ if (arg_expose_ports && !arg_private_network) {
+ log_error("Cannot use --port= without private networking.");
+ return -EINVAL;
+ }
- /* And then remount the systemd cgroup root read-only */
- systemd_root = prefix_roota(dest, "/sys/fs/cgroup/systemd");
- if (mount(NULL, systemd_root, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL) < 0)
- return log_error_errno(errno, "Failed to mount cgroup root read-only: %m");
+ if (arg_boot && arg_kill_signal <= 0)
+ arg_kill_signal = SIGRTMIN+3;
return 0;
}
@@ -1599,114 +1082,6 @@ static int setup_resolv_conf(const char *dest) {
return 0;
}
-static int setup_volatile_state(const char *directory) {
- _cleanup_free_ char *buf = NULL;
- const char *p, *options;
- int r;
-
- assert(directory);
-
- if (arg_volatile != VOLATILE_STATE)
- return 0;
-
- /* --volatile=state means we simply overmount /var
- with a tmpfs, and the rest read-only. */
-
- r = bind_remount_recursive(directory, true);
- if (r < 0)
- return log_error_errno(r, "Failed to remount %s read-only: %m", directory);
-
- p = prefix_roota(directory, "/var");
- r = mkdir(p, 0755);
- if (r < 0 && errno != EEXIST)
- return log_error_errno(errno, "Failed to create %s: %m", directory);
-
- options = "mode=755";
- r = tmpfs_patch_options(options, &buf);
- if (r < 0)
- return log_oom();
- if (r > 0)
- options = buf;
-
- if (mount("tmpfs", p, "tmpfs", MS_STRICTATIME, options) < 0)
- return log_error_errno(errno, "Failed to mount tmpfs to /var: %m");
-
- return 0;
-}
-
-static int setup_volatile(const char *directory) {
- bool tmpfs_mounted = false, bind_mounted = false;
- char template[] = "/tmp/nspawn-volatile-XXXXXX";
- _cleanup_free_ char *buf = NULL;
- const char *f, *t, *options;
- int r;
-
- assert(directory);
-
- if (arg_volatile != VOLATILE_YES)
- return 0;
-
- /* --volatile=yes means we mount a tmpfs to the root dir, and
- the original /usr to use inside it, and that read-only. */
-
- if (!mkdtemp(template))
- return log_error_errno(errno, "Failed to create temporary directory: %m");
-
- options = "mode=755";
- r = tmpfs_patch_options(options, &buf);
- if (r < 0)
- return log_oom();
- if (r > 0)
- options = buf;
-
- if (mount("tmpfs", template, "tmpfs", MS_STRICTATIME, options) < 0) {
- r = log_error_errno(errno, "Failed to mount tmpfs for root directory: %m");
- goto fail;
- }
-
- tmpfs_mounted = true;
-
- f = prefix_roota(directory, "/usr");
- t = prefix_roota(template, "/usr");
-
- r = mkdir(t, 0755);
- if (r < 0 && errno != EEXIST) {
- r = log_error_errno(errno, "Failed to create %s: %m", t);
- goto fail;
- }
-
- if (mount(f, t, NULL, MS_BIND|MS_REC, NULL) < 0) {
- r = log_error_errno(errno, "Failed to create /usr bind mount: %m");
- goto fail;
- }
-
- bind_mounted = true;
-
- r = bind_remount_recursive(t, true);
- if (r < 0) {
- log_error_errno(r, "Failed to remount %s read-only: %m", t);
- goto fail;
- }
-
- if (mount(template, directory, NULL, MS_MOVE, NULL) < 0) {
- r = log_error_errno(errno, "Failed to move root mount: %m");
- goto fail;
- }
-
- (void) rmdir(template);
-
- return 0;
-
-fail:
- if (bind_mounted)
- (void) umount(t);
-
- if (tmpfs_mounted)
- (void) umount(template);
- (void) rmdir(template);
- return r;
-}
-
static char* id128_format_as_uuid(sd_id128_t id, char s[37]) {
assert(s);
@@ -1945,132 +1320,6 @@ static int setup_kmsg(const char *dest, int kmsg_socket) {
return 0;
}
-static int send_rtnl(int send_fd) {
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int))];
- } control = {};
- struct msghdr mh = {
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- };
- struct cmsghdr *cmsg;
- _cleanup_close_ int fd = -1;
- ssize_t k;
-
- assert(send_fd >= 0);
-
- if (!arg_expose_ports)
- return 0;
-
- fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
- if (fd < 0)
- return log_error_errno(errno, "Failed to allocate container netlink: %m");
-
- cmsg = CMSG_FIRSTHDR(&mh);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
-
- mh.msg_controllen = cmsg->cmsg_len;
-
- /* Store away the fd in the socket, so that it stays open as
- * long as we run the child */
- k = sendmsg(send_fd, &mh, MSG_NOSIGNAL);
- if (k < 0)
- return log_error_errno(errno, "Failed to send netlink fd: %m");
-
- return 0;
-}
-
-static int flush_ports(union in_addr_union *exposed) {
- ExposePort *p;
- int r, af = AF_INET;
-
- assert(exposed);
-
- if (!arg_expose_ports)
- return 0;
-
- if (in_addr_is_null(af, exposed))
- return 0;
-
- log_debug("Lost IP address.");
-
- LIST_FOREACH(ports, p, arg_expose_ports) {
- r = fw_add_local_dnat(false,
- af,
- p->protocol,
- NULL,
- NULL, 0,
- NULL, 0,
- p->host_port,
- exposed,
- p->container_port,
- NULL);
- if (r < 0)
- log_warning_errno(r, "Failed to modify firewall: %m");
- }
-
- *exposed = IN_ADDR_NULL;
- return 0;
-}
-
-static int expose_ports(sd_netlink *rtnl, union in_addr_union *exposed) {
- _cleanup_free_ struct local_address *addresses = NULL;
- _cleanup_free_ char *pretty = NULL;
- union in_addr_union new_exposed;
- ExposePort *p;
- bool add;
- int af = AF_INET, r;
-
- assert(exposed);
-
- /* Invoked each time an address is added or removed inside the
- * container */
-
- if (!arg_expose_ports)
- return 0;
-
- r = local_addresses(rtnl, 0, af, &addresses);
- if (r < 0)
- return log_error_errno(r, "Failed to enumerate local addresses: %m");
-
- add = r > 0 &&
- addresses[0].family == af &&
- addresses[0].scope < RT_SCOPE_LINK;
-
- if (!add)
- return flush_ports(exposed);
-
- new_exposed = addresses[0].address;
- if (in_addr_equal(af, exposed, &new_exposed))
- return 0;
-
- in_addr_to_string(af, &new_exposed, &pretty);
- log_debug("New container IP is %s.", strna(pretty));
-
- LIST_FOREACH(ports, p, arg_expose_ports) {
-
- r = fw_add_local_dnat(true,
- af,
- p->protocol,
- NULL,
- NULL, 0,
- NULL, 0,
- p->host_port,
- &new_exposed,
- p->container_port,
- in_addr_is_null(af, exposed) ? NULL : exposed);
- if (r < 0)
- log_warning_errno(r, "Failed to modify firewall: %m");
- }
-
- *exposed = new_exposed;
- return 0;
-}
-
static int on_address_change(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
union in_addr_union *exposed = userdata;
@@ -2078,62 +1327,7 @@ static int on_address_change(sd_netlink *rtnl, sd_netlink_message *m, void *user
assert(m);
assert(exposed);
- expose_ports(rtnl, exposed);
- return 0;
-}
-
-static int watch_rtnl(sd_event *event, int recv_fd, union in_addr_union *exposed, sd_netlink **ret) {
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int))];
- } control = {};
- struct msghdr mh = {
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- };
- struct cmsghdr *cmsg;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
- int fd, r;
- ssize_t k;
-
- assert(event);
- assert(recv_fd >= 0);
- assert(ret);
-
- if (!arg_expose_ports)
- return 0;
-
- k = recvmsg(recv_fd, &mh, MSG_NOSIGNAL);
- if (k < 0)
- return log_error_errno(errno, "Failed to recv netlink fd: %m");
-
- cmsg = CMSG_FIRSTHDR(&mh);
- assert(cmsg->cmsg_level == SOL_SOCKET);
- assert(cmsg->cmsg_type == SCM_RIGHTS);
- assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
- memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
-
- r = sd_netlink_open_fd(&rtnl, fd);
- if (r < 0) {
- safe_close(fd);
- return log_error_errno(r, "Failed to create rtnl object: %m");
- }
-
- r = sd_netlink_add_match(rtnl, RTM_NEWADDR, on_address_change, exposed);
- if (r < 0)
- return log_error_errno(r, "Failed to subscribe to RTM_NEWADDR messages: %m");
-
- r = sd_netlink_add_match(rtnl, RTM_DELADDR, on_address_change, exposed);
- if (r < 0)
- return log_error_errno(r, "Failed to subscribe to RTM_DELADDR messages: %m");
-
- r = sd_netlink_attach_event(rtnl, event, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to add to even loop: %m");
-
- *ret = rtnl;
- rtnl = NULL;
-
+ expose_port_execute(rtnl, arg_expose_ports, exposed);
return 0;
}
@@ -2310,220 +1504,6 @@ static int drop_capabilities(void) {
return capability_bounding_set_drop(~arg_retain, false);
}
-static int register_machine(pid_t pid, int local_ifindex) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
- int r;
-
- if (!arg_register)
- return 0;
-
- r = sd_bus_default_system(&bus);
- if (r < 0)
- return log_error_errno(r, "Failed to open system bus: %m");
-
- if (arg_keep_unit) {
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.machine1",
- "/org/freedesktop/machine1",
- "org.freedesktop.machine1.Manager",
- "RegisterMachineWithNetwork",
- &error,
- NULL,
- "sayssusai",
- arg_machine,
- SD_BUS_MESSAGE_APPEND_ID128(arg_uuid),
- "nspawn",
- "container",
- (uint32_t) pid,
- strempty(arg_directory),
- local_ifindex > 0 ? 1 : 0, local_ifindex);
- } else {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- char **i;
- unsigned j;
-
- r = sd_bus_message_new_method_call(
- bus,
- &m,
- "org.freedesktop.machine1",
- "/org/freedesktop/machine1",
- "org.freedesktop.machine1.Manager",
- "CreateMachineWithNetwork");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(
- m,
- "sayssusai",
- arg_machine,
- SD_BUS_MESSAGE_APPEND_ID128(arg_uuid),
- "nspawn",
- "container",
- (uint32_t) pid,
- strempty(arg_directory),
- local_ifindex > 0 ? 1 : 0, local_ifindex);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_open_container(m, 'a', "(sv)");
- if (r < 0)
- return bus_log_create_error(r);
-
- if (!isempty(arg_slice)) {
- r = sd_bus_message_append(m, "(sv)", "Slice", "s", arg_slice);
- if (r < 0)
- return bus_log_create_error(r);
- }
-
- r = sd_bus_message_append(m, "(sv)", "DevicePolicy", "s", "strict");
- if (r < 0)
- return bus_log_create_error(r);
-
- /* If you make changes here, also make sure to update
- * systemd-nspawn@.service, to keep the device
- * policies in sync regardless if we are run with or
- * without the --keep-unit switch. */
- r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 9,
- /* Allow the container to
- * access and create the API
- * device nodes, so that
- * PrivateDevices= in the
- * container can work
- * fine */
- "/dev/null", "rwm",
- "/dev/zero", "rwm",
- "/dev/full", "rwm",
- "/dev/random", "rwm",
- "/dev/urandom", "rwm",
- "/dev/tty", "rwm",
- "/dev/net/tun", "rwm",
- /* Allow the container
- * access to ptys. However,
- * do not permit the
- * container to ever create
- * these device nodes. */
- "/dev/pts/ptmx", "rw",
- "char-pts", "rw");
- if (r < 0)
- return bus_log_create_error(r);
-
- for (j = 0; j < arg_n_custom_mounts; j++) {
- CustomMount *cm = &arg_custom_mounts[j];
-
- if (cm->type != CUSTOM_MOUNT_BIND)
- continue;
-
- r = is_device_node(cm->source);
- if (r < 0)
- return log_error_errno(r, "Failed to stat %s: %m", cm->source);
-
- if (r) {
- r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 1,
- cm->source, cm->read_only ? "r" : "rw");
- if (r < 0)
- return log_error_errno(r, "Failed to append message arguments: %m");
- }
- }
-
- if (arg_kill_signal != 0) {
- r = sd_bus_message_append(m, "(sv)", "KillSignal", "i", arg_kill_signal);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(m, "(sv)", "KillMode", "s", "mixed");
- if (r < 0)
- return bus_log_create_error(r);
- }
-
- STRV_FOREACH(i, arg_property) {
- r = sd_bus_message_open_container(m, 'r', "sv");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = bus_append_unit_property_assignment(m, *i);
- if (r < 0)
- return r;
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return bus_log_create_error(r);
- }
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_call(bus, m, 0, &error, NULL);
- }
-
- if (r < 0) {
- log_error("Failed to register machine: %s", bus_error_message(&error, r));
- return r;
- }
-
- return 0;
-}
-
-static int terminate_machine(pid_t pid) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
- const char *path;
- int r;
-
- if (!arg_register)
- return 0;
-
- /* If we are reusing the unit, then just exit, systemd will do
- * the right thing when we exit. */
- if (arg_keep_unit)
- return 0;
-
- r = sd_bus_default_system(&bus);
- if (r < 0)
- return log_error_errno(r, "Failed to open system bus: %m");
-
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.machine1",
- "/org/freedesktop/machine1",
- "org.freedesktop.machine1.Manager",
- "GetMachineByPID",
- &error,
- &reply,
- "u",
- (uint32_t) pid);
- if (r < 0) {
- /* Note that the machine might already have been
- * cleaned up automatically, hence don't consider it a
- * failure if we cannot get the machine object. */
- log_debug("Failed to get machine: %s", bus_error_message(&error, r));
- return 0;
- }
-
- r = sd_bus_message_read(reply, "o", &path);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.machine1",
- path,
- "org.freedesktop.machine1.Machine",
- "Terminate",
- &error,
- NULL,
- NULL);
- if (r < 0) {
- log_debug("Failed to terminate machine: %s", bus_error_message(&error, r));
- return 0;
- }
-
- return 0;
-}
-
static int reset_audit_loginuid(void) {
_cleanup_free_ char *p = NULL;
int r;
@@ -2556,427 +1536,6 @@ static int reset_audit_loginuid(void) {
return 0;
}
-#define HOST_HASH_KEY SD_ID128_MAKE(1a,37,6f,c7,46,ec,45,0b,ad,a3,d5,31,06,60,5d,b1)
-#define CONTAINER_HASH_KEY SD_ID128_MAKE(c3,c4,f9,19,b5,57,b2,1c,e6,cf,14,27,03,9c,ee,a2)
-#define MACVLAN_HASH_KEY SD_ID128_MAKE(00,13,6d,bc,66,83,44,81,bb,0c,f9,51,1f,24,a6,6f)
-
-static int generate_mac(struct ether_addr *mac, sd_id128_t hash_key, uint64_t idx) {
- uint8_t result[8];
- size_t l, sz;
- uint8_t *v, *i;
- int r;
-
- l = strlen(arg_machine);
- sz = sizeof(sd_id128_t) + l;
- if (idx > 0)
- sz += sizeof(idx);
-
- v = alloca(sz);
-
- /* fetch some persistent data unique to the host */
- r = sd_id128_get_machine((sd_id128_t*) v);
- if (r < 0)
- return r;
-
- /* combine with some data unique (on this host) to this
- * container instance */
- i = mempcpy(v + sizeof(sd_id128_t), arg_machine, l);
- if (idx > 0) {
- idx = htole64(idx);
- memcpy(i, &idx, sizeof(idx));
- }
-
- /* Let's hash the host machine ID plus the container name. We
- * use a fixed, but originally randomly created hash key here. */
- siphash24(result, v, sz, hash_key.bytes);
-
- assert_cc(ETH_ALEN <= sizeof(result));
- memcpy(mac->ether_addr_octet, result, ETH_ALEN);
-
- /* see eth_random_addr in the kernel */
- mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
- mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
-
- return 0;
-}
-
-static int setup_veth(pid_t pid, char iface_name[IFNAMSIZ], int *ifi) {
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
- struct ether_addr mac_host, mac_container;
- int r, i;
-
- if (!arg_private_network)
- return 0;
-
- if (!arg_network_veth)
- return 0;
-
- /* Use two different interface name prefixes depending whether
- * we are in bridge mode or not. */
- snprintf(iface_name, IFNAMSIZ - 1, "%s-%s",
- arg_network_bridge ? "vb" : "ve", arg_machine);
-
- r = generate_mac(&mac_container, CONTAINER_HASH_KEY, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to generate predictable MAC address for container side: %m");
-
- r = generate_mac(&mac_host, HOST_HASH_KEY, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to generate predictable MAC address for host side: %m");
-
- r = sd_netlink_open(&rtnl);
- if (r < 0)
- return log_error_errno(r, "Failed to connect to netlink: %m");
-
- r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to allocate netlink message: %m");
-
- r = sd_netlink_message_append_string(m, IFLA_IFNAME, iface_name);
- if (r < 0)
- return log_error_errno(r, "Failed to add netlink interface name: %m");
-
- r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac_host);
- if (r < 0)
- return log_error_errno(r, "Failed to add netlink MAC address: %m");
-
- r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
- if (r < 0)
- return log_error_errno(r, "Failed to open netlink container: %m");
-
- r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "veth");
- if (r < 0)
- return log_error_errno(r, "Failed to open netlink container: %m");
-
- r = sd_netlink_message_open_container(m, VETH_INFO_PEER);
- if (r < 0)
- return log_error_errno(r, "Failed to open netlink container: %m");
-
- r = sd_netlink_message_append_string(m, IFLA_IFNAME, "host0");
- if (r < 0)
- return log_error_errno(r, "Failed to add netlink interface name: %m");
-
- r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac_container);
- if (r < 0)
- return log_error_errno(r, "Failed to add netlink MAC address: %m");
-
- r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid);
- if (r < 0)
- return log_error_errno(r, "Failed to add netlink namespace field: %m");
-
- r = sd_netlink_message_close_container(m);
- if (r < 0)
- return log_error_errno(r, "Failed to close netlink container: %m");
-
- r = sd_netlink_message_close_container(m);
- if (r < 0)
- return log_error_errno(r, "Failed to close netlink container: %m");
-
- r = sd_netlink_message_close_container(m);
- if (r < 0)
- return log_error_errno(r, "Failed to close netlink container: %m");
-
- r = sd_netlink_call(rtnl, m, 0, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to add new veth interfaces (host0, %s): %m", iface_name);
-
- i = (int) if_nametoindex(iface_name);
- if (i <= 0)
- return log_error_errno(errno, "Failed to resolve interface %s: %m", iface_name);
-
- *ifi = i;
-
- return 0;
-}
-
-static int setup_bridge(const char veth_name[], int *ifi) {
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
- int r, bridge;
-
- if (!arg_private_network)
- return 0;
-
- if (!arg_network_veth)
- return 0;
-
- if (!arg_network_bridge)
- return 0;
-
- bridge = (int) if_nametoindex(arg_network_bridge);
- if (bridge <= 0)
- return log_error_errno(errno, "Failed to resolve interface %s: %m", arg_network_bridge);
-
- *ifi = bridge;
-
- r = sd_netlink_open(&rtnl);
- if (r < 0)
- return log_error_errno(r, "Failed to connect to netlink: %m");
-
- r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to allocate netlink message: %m");
-
- r = sd_rtnl_message_link_set_flags(m, IFF_UP, IFF_UP);
- if (r < 0)
- return log_error_errno(r, "Failed to set IFF_UP flag: %m");
-
- r = sd_netlink_message_append_string(m, IFLA_IFNAME, veth_name);
- if (r < 0)
- return log_error_errno(r, "Failed to add netlink interface name field: %m");
-
- r = sd_netlink_message_append_u32(m, IFLA_MASTER, bridge);
- if (r < 0)
- return log_error_errno(r, "Failed to add netlink master field: %m");
-
- r = sd_netlink_call(rtnl, m, 0, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to add veth interface to bridge: %m");
-
- return 0;
-}
-
-static int parse_interface(struct udev *udev, const char *name) {
- _cleanup_udev_device_unref_ struct udev_device *d = NULL;
- char ifi_str[2 + DECIMAL_STR_MAX(int)];
- int ifi;
-
- ifi = (int) if_nametoindex(name);
- if (ifi <= 0)
- return log_error_errno(errno, "Failed to resolve interface %s: %m", name);
-
- sprintf(ifi_str, "n%i", ifi);
- d = udev_device_new_from_device_id(udev, ifi_str);
- if (!d)
- return log_error_errno(errno, "Failed to get udev device for interface %s: %m", name);
-
- if (udev_device_get_is_initialized(d) <= 0) {
- log_error("Network interface %s is not initialized yet.", name);
- return -EBUSY;
- }
-
- return ifi;
-}
-
-static int move_network_interfaces(pid_t pid) {
- _cleanup_udev_unref_ struct udev *udev = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
- char **i;
- int r;
-
- if (!arg_private_network)
- return 0;
-
- if (strv_isempty(arg_network_interfaces))
- return 0;
-
- r = sd_netlink_open(&rtnl);
- if (r < 0)
- return log_error_errno(r, "Failed to connect to netlink: %m");
-
- udev = udev_new();
- if (!udev) {
- log_error("Failed to connect to udev.");
- return -ENOMEM;
- }
-
- STRV_FOREACH(i, arg_network_interfaces) {
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
- int ifi;
-
- ifi = parse_interface(udev, *i);
- if (ifi < 0)
- return ifi;
-
- r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, ifi);
- if (r < 0)
- return log_error_errno(r, "Failed to allocate netlink message: %m");
-
- r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid);
- if (r < 0)
- return log_error_errno(r, "Failed to append namespace PID to netlink message: %m");
-
- r = sd_netlink_call(rtnl, m, 0, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to move interface %s to namespace: %m", *i);
- }
-
- return 0;
-}
-
-static int setup_macvlan(pid_t pid) {
- _cleanup_udev_unref_ struct udev *udev = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
- unsigned idx = 0;
- char **i;
- int r;
-
- if (!arg_private_network)
- return 0;
-
- if (strv_isempty(arg_network_macvlan))
- return 0;
-
- r = sd_netlink_open(&rtnl);
- if (r < 0)
- return log_error_errno(r, "Failed to connect to netlink: %m");
-
- udev = udev_new();
- if (!udev) {
- log_error("Failed to connect to udev.");
- return -ENOMEM;
- }
-
- STRV_FOREACH(i, arg_network_macvlan) {
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
- _cleanup_free_ char *n = NULL;
- struct ether_addr mac;
- int ifi;
-
- ifi = parse_interface(udev, *i);
- if (ifi < 0)
- return ifi;
-
- r = generate_mac(&mac, MACVLAN_HASH_KEY, idx++);
- if (r < 0)
- return log_error_errno(r, "Failed to create MACVLAN MAC address: %m");
-
- r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to allocate netlink message: %m");
-
- r = sd_netlink_message_append_u32(m, IFLA_LINK, ifi);
- if (r < 0)
- return log_error_errno(r, "Failed to add netlink interface index: %m");
-
- n = strappend("mv-", *i);
- if (!n)
- return log_oom();
-
- strshorten(n, IFNAMSIZ-1);
-
- r = sd_netlink_message_append_string(m, IFLA_IFNAME, n);
- if (r < 0)
- return log_error_errno(r, "Failed to add netlink interface name: %m");
-
- r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac);
- if (r < 0)
- return log_error_errno(r, "Failed to add netlink MAC address: %m");
-
- r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid);
- if (r < 0)
- return log_error_errno(r, "Failed to add netlink namespace field: %m");
-
- r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
- if (r < 0)
- return log_error_errno(r, "Failed to open netlink container: %m");
-
- r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "macvlan");
- if (r < 0)
- return log_error_errno(r, "Failed to open netlink container: %m");
-
- r = sd_netlink_message_append_u32(m, IFLA_MACVLAN_MODE, MACVLAN_MODE_BRIDGE);
- if (r < 0)
- return log_error_errno(r, "Failed to append macvlan mode: %m");
-
- r = sd_netlink_message_close_container(m);
- if (r < 0)
- return log_error_errno(r, "Failed to close netlink container: %m");
-
- r = sd_netlink_message_close_container(m);
- if (r < 0)
- return log_error_errno(r, "Failed to close netlink container: %m");
-
- r = sd_netlink_call(rtnl, m, 0, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to add new macvlan interfaces: %m");
- }
-
- return 0;
-}
-
-static int setup_ipvlan(pid_t pid) {
- _cleanup_udev_unref_ struct udev *udev = NULL;
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
- char **i;
- int r;
-
- if (!arg_private_network)
- return 0;
-
- if (strv_isempty(arg_network_ipvlan))
- return 0;
-
- r = sd_netlink_open(&rtnl);
- if (r < 0)
- return log_error_errno(r, "Failed to connect to netlink: %m");
-
- udev = udev_new();
- if (!udev) {
- log_error("Failed to connect to udev.");
- return -ENOMEM;
- }
-
- STRV_FOREACH(i, arg_network_ipvlan) {
- _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
- _cleanup_free_ char *n = NULL;
- int ifi;
-
- ifi = parse_interface(udev, *i);
- if (ifi < 0)
- return ifi;
-
- r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to allocate netlink message: %m");
-
- r = sd_netlink_message_append_u32(m, IFLA_LINK, ifi);
- if (r < 0)
- return log_error_errno(r, "Failed to add netlink interface index: %m");
-
- n = strappend("iv-", *i);
- if (!n)
- return log_oom();
-
- strshorten(n, IFNAMSIZ-1);
-
- r = sd_netlink_message_append_string(m, IFLA_IFNAME, n);
- if (r < 0)
- return log_error_errno(r, "Failed to add netlink interface name: %m");
-
- r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid);
- if (r < 0)
- return log_error_errno(r, "Failed to add netlink namespace field: %m");
-
- r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
- if (r < 0)
- return log_error_errno(r, "Failed to open netlink container: %m");
-
- r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "ipvlan");
- if (r < 0)
- return log_error_errno(r, "Failed to open netlink container: %m");
-
- r = sd_netlink_message_append_u16(m, IFLA_IPVLAN_MODE, IPVLAN_MODE_L2);
- if (r < 0)
- return log_error_errno(r, "Failed to add ipvlan mode: %m");
-
- r = sd_netlink_message_close_container(m);
- if (r < 0)
- return log_error_errno(r, "Failed to close netlink container: %m");
-
- r = sd_netlink_message_close_container(m);
- if (r < 0)
- return log_error_errno(r, "Failed to close netlink container: %m");
-
- r = sd_netlink_call(rtnl, m, 0, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to add new ipvlan interfaces: %m");
- }
-
- return 0;
-}
-
static int setup_seccomp(void) {
#ifdef HAVE_SECCOMP
@@ -3675,247 +2234,6 @@ static void loop_remove(int nr, int *image_fd) {
log_debug_errno(errno, "Failed to remove loop %d: %m", nr);
}
-static int spawn_getent(const char *database, const char *key, pid_t *rpid) {
- int pipe_fds[2];
- pid_t pid;
-
- assert(database);
- assert(key);
- assert(rpid);
-
- if (pipe2(pipe_fds, O_CLOEXEC) < 0)
- return log_error_errno(errno, "Failed to allocate pipe: %m");
-
- pid = fork();
- if (pid < 0)
- return log_error_errno(errno, "Failed to fork getent child: %m");
- else if (pid == 0) {
- int nullfd;
- char *empty_env = NULL;
-
- if (dup3(pipe_fds[1], STDOUT_FILENO, 0) < 0)
- _exit(EXIT_FAILURE);
-
- if (pipe_fds[0] > 2)
- safe_close(pipe_fds[0]);
- if (pipe_fds[1] > 2)
- safe_close(pipe_fds[1]);
-
- nullfd = open("/dev/null", O_RDWR);
- if (nullfd < 0)
- _exit(EXIT_FAILURE);
-
- if (dup3(nullfd, STDIN_FILENO, 0) < 0)
- _exit(EXIT_FAILURE);
-
- if (dup3(nullfd, STDERR_FILENO, 0) < 0)
- _exit(EXIT_FAILURE);
-
- if (nullfd > 2)
- safe_close(nullfd);
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
- close_all_fds(NULL, 0);
-
- execle("/usr/bin/getent", "getent", database, key, NULL, &empty_env);
- execle("/bin/getent", "getent", database, key, NULL, &empty_env);
- _exit(EXIT_FAILURE);
- }
-
- pipe_fds[1] = safe_close(pipe_fds[1]);
-
- *rpid = pid;
-
- return pipe_fds[0];
-}
-
-static int change_uid_gid(char **_home) {
- char line[LINE_MAX], *x, *u, *g, *h;
- const char *word, *state;
- _cleanup_free_ uid_t *uids = NULL;
- _cleanup_free_ char *home = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- _cleanup_close_ int fd = -1;
- unsigned n_uids = 0;
- size_t sz = 0, l;
- uid_t uid;
- gid_t gid;
- pid_t pid;
- int r;
-
- assert(_home);
-
- if (!arg_user || streq(arg_user, "root") || streq(arg_user, "0")) {
- /* Reset everything fully to 0, just in case */
-
- r = reset_uid_gid();
- if (r < 0)
- return log_error_errno(r, "Failed to become root: %m");
-
- *_home = NULL;
- return 0;
- }
-
- /* First, get user credentials */
- fd = spawn_getent("passwd", arg_user, &pid);
- if (fd < 0)
- return fd;
-
- f = fdopen(fd, "r");
- if (!f)
- return log_oom();
- fd = -1;
-
- if (!fgets(line, sizeof(line), f)) {
-
- if (!ferror(f)) {
- log_error("Failed to resolve user %s.", arg_user);
- return -ESRCH;
- }
-
- log_error_errno(errno, "Failed to read from getent: %m");
- return -errno;
- }
-
- truncate_nl(line);
-
- wait_for_terminate_and_warn("getent passwd", pid, true);
-
- x = strchr(line, ':');
- if (!x) {
- log_error("/etc/passwd entry has invalid user field.");
- return -EIO;
- }
-
- u = strchr(x+1, ':');
- if (!u) {
- log_error("/etc/passwd entry has invalid password field.");
- return -EIO;
- }
-
- u++;
- g = strchr(u, ':');
- if (!g) {
- log_error("/etc/passwd entry has invalid UID field.");
- return -EIO;
- }
-
- *g = 0;
- g++;
- x = strchr(g, ':');
- if (!x) {
- log_error("/etc/passwd entry has invalid GID field.");
- return -EIO;
- }
-
- *x = 0;
- h = strchr(x+1, ':');
- if (!h) {
- log_error("/etc/passwd entry has invalid GECOS field.");
- return -EIO;
- }
-
- h++;
- x = strchr(h, ':');
- if (!x) {
- log_error("/etc/passwd entry has invalid home directory field.");
- return -EIO;
- }
-
- *x = 0;
-
- r = parse_uid(u, &uid);
- if (r < 0) {
- log_error("Failed to parse UID of user.");
- return -EIO;
- }
-
- r = parse_gid(g, &gid);
- if (r < 0) {
- log_error("Failed to parse GID of user.");
- return -EIO;
- }
-
- home = strdup(h);
- if (!home)
- return log_oom();
-
- /* Second, get group memberships */
- fd = spawn_getent("initgroups", arg_user, &pid);
- if (fd < 0)
- return fd;
-
- fclose(f);
- f = fdopen(fd, "r");
- if (!f)
- return log_oom();
- fd = -1;
-
- if (!fgets(line, sizeof(line), f)) {
- if (!ferror(f)) {
- log_error("Failed to resolve user %s.", arg_user);
- return -ESRCH;
- }
-
- log_error_errno(errno, "Failed to read from getent: %m");
- return -errno;
- }
-
- truncate_nl(line);
-
- wait_for_terminate_and_warn("getent initgroups", pid, true);
-
- /* Skip over the username and subsequent separator whitespace */
- x = line;
- x += strcspn(x, WHITESPACE);
- x += strspn(x, WHITESPACE);
-
- FOREACH_WORD(word, l, x, state) {
- char c[l+1];
-
- memcpy(c, word, l);
- c[l] = 0;
-
- if (!GREEDY_REALLOC(uids, sz, n_uids+1))
- return log_oom();
-
- r = parse_uid(c, &uids[n_uids++]);
- if (r < 0) {
- log_error("Failed to parse group data from getent.");
- return -EIO;
- }
- }
-
- r = mkdir_parents(home, 0775);
- if (r < 0)
- return log_error_errno(r, "Failed to make home root directory: %m");
-
- r = mkdir_safe(home, 0755, uid, gid);
- if (r < 0 && r != -EEXIST)
- return log_error_errno(r, "Failed to make home directory: %m");
-
- (void) fchown(STDIN_FILENO, uid, gid);
- (void) fchown(STDOUT_FILENO, uid, gid);
- (void) fchown(STDERR_FILENO, uid, gid);
-
- if (setgroups(n_uids, uids) < 0)
- return log_error_errno(errno, "Failed to set auxiliary groups: %m");
-
- if (setresgid(gid, gid, gid) < 0)
- return log_error_errno(errno, "setregid() failed: %m");
-
- if (setresuid(uid, uid, uid) < 0)
- return log_error_errno(errno, "setreuid() failed: %m");
-
- if (_home) {
- *_home = home;
- home = NULL;
- }
-
- return 0;
-}
-
/*
* Return values:
* < 0 : wait_for_terminate() failed to get the state of the
@@ -4117,9 +2435,7 @@ static int inner_child(
bool secondary,
int kmsg_socket,
int rtnl_socket,
- FDSet *fds,
- int argc,
- char *argv[]) {
+ FDSet *fds) {
_cleanup_free_ char *home = NULL;
unsigned n_env = 2;
@@ -4143,6 +2459,8 @@ static int inner_child(
assert(directory);
assert(kmsg_socket >= 0);
+ cg_unified_flush();
+
if (arg_userns) {
/* Tell the parent, that it now can write the UID map. */
(void) barrier_place(barrier); /* #1 */
@@ -4154,7 +2472,7 @@ static int inner_child(
}
}
- r = mount_all(NULL, true);
+ r = mount_all(NULL, true, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context);
if (r < 0)
return r;
@@ -4165,7 +2483,7 @@ static int inner_child(
return -ESRCH;
}
- r = mount_systemd_cgroup_writable("");
+ r = mount_systemd_cgroup_writable("", arg_unified_cgroup_hierarchy);
if (r < 0)
return r;
@@ -4190,10 +2508,12 @@ static int inner_child(
if (arg_private_network)
loopback_setup();
- r = send_rtnl(rtnl_socket);
- if (r < 0)
- return r;
- rtnl_socket = safe_close(rtnl_socket);
+ if (arg_expose_ports) {
+ r = expose_port_send_rtnl(rtnl_socket);
+ if (r < 0)
+ return r;
+ rtnl_socket = safe_close(rtnl_socket);
+ }
if (drop_capabilities() < 0)
return log_error_errno(errno, "drop_capabilities() failed: %m");
@@ -4214,7 +2534,7 @@ static int inner_child(
return log_error_errno(errno, "setexeccon(\"%s\") failed: %m", arg_selinux_context);
#endif
- r = change_uid_gid(&home);
+ r = change_uid_gid(arg_user, &home);
if (r < 0)
return r;
@@ -4273,9 +2593,12 @@ static int inner_child(
/* Automatically search for the init system */
- m = 1 + argc - optind;
+ m = 1 + strv_length(arg_parameters);
a = newa(char*, m + 1);
- memcpy(a + 1, argv + optind, m * sizeof(char*));
+ if (strv_isempty(arg_parameters))
+ a[1] = NULL;
+ else
+ memcpy(a + 1, arg_parameters, m * sizeof(char*));
a[0] = (char*) "/usr/lib/systemd/systemd";
execve(a[0], a, env_use);
@@ -4285,10 +2608,10 @@ static int inner_child(
a[0] = (char*) "/sbin/init";
execve(a[0], a, env_use);
- } else if (argc > optind)
- execvpe(argv[optind], argv + optind, env_use);
+ } else if (!strv_isempty(arg_parameters))
+ execvpe(arg_parameters[0], arg_parameters, env_use);
else {
- chdir(home ? home : "/root");
+ chdir(home ?: "/root");
execle("/bin/bash", "-bash", NULL, env_use);
execle("/bin/sh", "-sh", NULL, env_use);
}
@@ -4310,9 +2633,7 @@ static int outer_child(
int kmsg_socket,
int rtnl_socket,
int uid_shift_socket,
- FDSet *fds,
- int argc,
- char *argv[]) {
+ FDSet *fds) {
pid_t pid;
ssize_t l;
@@ -4324,6 +2645,8 @@ static int outer_child(
assert(pid_socket >= 0);
assert(kmsg_socket >= 0);
+ cg_unified_flush();
+
if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0)
return log_error_errno(errno, "PR_SET_PDEATHSIG failed: %m");
@@ -4382,11 +2705,11 @@ static int outer_child(
if (mount(directory, directory, NULL, MS_BIND|MS_REC, NULL) < 0)
return log_error_errno(errno, "Failed to make bind mount: %m");
- r = setup_volatile(directory);
+ r = setup_volatile(directory, arg_volatile_mode, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_context);
if (r < 0)
return r;
- r = setup_volatile_state(directory);
+ r = setup_volatile_state(directory, arg_volatile_mode, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_context);
if (r < 0)
return r;
@@ -4400,16 +2723,18 @@ static int outer_child(
return log_error_errno(r, "Failed to make tree read-only: %m");
}
- r = mount_all(directory, false);
+ r = mount_all(directory, false, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context);
if (r < 0)
return r;
- if (copy_devnodes(directory) < 0)
+ r = copy_devnodes(directory);
+ if (r < 0)
return r;
dev_setup(directory, arg_uid_shift, arg_uid_shift);
- if (setup_pts(directory) < 0)
+ r = setup_pts(directory);
+ if (r < 0)
return r;
r = setup_propagate(directory);
@@ -4436,11 +2761,11 @@ static int outer_child(
if (r < 0)
return r;
- r = mount_custom(directory);
+ r = mount_custom(directory, arg_custom_mounts, arg_n_custom_mounts, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context);
if (r < 0)
return r;
- r = mount_cgroup(directory);
+ r = mount_cgroups(directory, arg_unified_cgroup_hierarchy, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context);
if (r < 0)
return r;
@@ -4455,7 +2780,6 @@ static int outer_child(
NULL);
if (pid < 0)
return log_error_errno(errno, "Failed to fork inner child: %m");
-
if (pid == 0) {
pid_socket = safe_close(pid_socket);
uid_shift_socket = safe_close(uid_shift_socket);
@@ -4464,7 +2788,7 @@ static int outer_child(
* requested, so that we all are owned by the user if
* user namespaces are turned on. */
- r = inner_child(barrier, directory, secondary, kmsg_socket, rtnl_socket, fds, argc, argv);
+ r = inner_child(barrier, directory, secondary, kmsg_socket, rtnl_socket, fds);
if (r < 0)
_exit(EXIT_FAILURE);
@@ -4505,27 +2829,198 @@ static int setup_uid_map(pid_t pid) {
return 0;
}
-static int chown_cgroup(pid_t pid) {
- _cleanup_free_ char *path = NULL, *fs = NULL;
- _cleanup_close_ int fd = -1;
- const char *fn;
+static int load_settings(void) {
+ _cleanup_(settings_freep) Settings *settings = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *p = NULL;
+ const char *fn, *i;
int r;
- r = cg_pid_get_path(NULL, pid, &path);
- if (r < 0)
- return log_error_errno(r, "Failed to get container cgroup path: %m");
+ /* If all settings are masked, there's no point in looking for
+ * the settings file */
+ if ((arg_settings_mask & _SETTINGS_MASK_ALL) == _SETTINGS_MASK_ALL)
+ return 0;
+
+ fn = strjoina(arg_machine, ".nspawn");
+
+ /* We first look in the admin's directories in /etc and /run */
+ FOREACH_STRING(i, "/etc/systemd/nspawn", "/run/systemd/nspawn") {
+ _cleanup_free_ char *j = NULL;
+
+ j = strjoin(i, "/", fn, NULL);
+ if (!j)
+ return log_oom();
+
+ f = fopen(j, "re");
+ if (f) {
+ p = j;
+ j = NULL;
+
+ /* By default we trust configuration from /etc and /run */
+ if (arg_settings_trusted < 0)
+ arg_settings_trusted = true;
+
+ break;
+ }
+
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to open %s: %m", j);
+ }
+
+ if (!f) {
+ /* After that, let's look for a file next to the
+ * actual image we shall boot. */
+
+ if (arg_image) {
+ p = file_in_same_dir(arg_image, fn);
+ if (!p)
+ return log_oom();
+ } else if (arg_directory) {
+ p = file_in_same_dir(arg_directory, fn);
+ if (!p)
+ return log_oom();
+ }
+
+ if (p) {
+ f = fopen(p, "re");
+ if (!f && errno != ENOENT)
+ return log_error_errno(errno, "Failed to open %s: %m", p);
+
+ /* By default we do not trust configuration from /var/lib/machines */
+ if (arg_settings_trusted < 0)
+ arg_settings_trusted = false;
+ }
+ }
+
+ if (!f)
+ return 0;
+
+ log_debug("Settings are trusted: %s", yes_no(arg_settings_trusted));
- r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &fs);
+ r = settings_load(f, p, &settings);
if (r < 0)
- return log_error_errno(r, "Failed to get file system path for container cgroup: %m");
+ return r;
- fd = open(fs, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
- if (fd < 0)
- return log_error_errno(errno, "Failed to open %s: %m", fs);
+ /* Copy over bits from the settings, unless they have been
+ * explicitly masked by command line switches. */
+
+ if ((arg_settings_mask & SETTING_BOOT) == 0 &&
+ settings->boot >= 0) {
+ arg_boot = settings->boot;
+
+ strv_free(arg_parameters);
+ arg_parameters = settings->parameters;
+ settings->parameters = NULL;
+ }
+
+ if ((arg_settings_mask & SETTING_ENVIRONMENT) == 0 &&
+ settings->environment) {
+ strv_free(arg_setenv);
+ arg_setenv = settings->environment;
+ settings->environment = NULL;
+ }
+
+ if ((arg_settings_mask & SETTING_USER) == 0 &&
+ settings->user) {
+ free(arg_user);
+ arg_user = settings->user;
+ settings->user = NULL;
+ }
+
+ if ((arg_settings_mask & SETTING_CAPABILITY) == 0) {
- FOREACH_STRING(fn, ".", "tasks", "notify_on_release", "cgroup.procs", "cgroup.clone_children")
- if (fchownat(fd, fn, arg_uid_shift, arg_uid_shift, 0) < 0)
- log_warning_errno(errno, "Failed to chown() cgroup file %s, ignoring: %m", fn);
+ if (!arg_settings_trusted && settings->capability != 0)
+ log_warning("Ignoring Capability= setting, file %s is not trusted.", p);
+ else
+ arg_retain |= settings->capability;
+
+ arg_retain &= ~settings->drop_capability;
+ }
+
+ if ((arg_settings_mask & SETTING_KILL_SIGNAL) == 0 &&
+ settings->kill_signal > 0)
+ arg_kill_signal = settings->kill_signal;
+
+ if ((arg_settings_mask & SETTING_PERSONALITY) == 0 &&
+ settings->personality != PERSONALITY_INVALID)
+ arg_personality = settings->personality;
+
+ if ((arg_settings_mask & SETTING_MACHINE_ID) == 0 &&
+ !sd_id128_is_null(settings->machine_id)) {
+
+ if (!arg_settings_trusted)
+ log_warning("Ignoring MachineID= setting, file %s is not trusted.", p);
+ else
+ arg_uuid = settings->machine_id;
+ }
+
+ if ((arg_settings_mask & SETTING_READ_ONLY) == 0 &&
+ settings->read_only >= 0)
+ arg_read_only = settings->read_only;
+
+ if ((arg_settings_mask & SETTING_VOLATILE_MODE) == 0 &&
+ settings->volatile_mode != _VOLATILE_MODE_INVALID)
+ arg_volatile_mode = settings->volatile_mode;
+
+ if ((arg_settings_mask & SETTING_CUSTOM_MOUNTS) == 0 &&
+ settings->n_custom_mounts > 0) {
+
+ if (!arg_settings_trusted)
+ log_warning("Ignoring TemporaryFileSystem=, Bind= and BindReadOnly= settings, file %s is not trusted.", p);
+ else {
+ custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts);
+ arg_custom_mounts = settings->custom_mounts;
+ arg_n_custom_mounts = settings->n_custom_mounts;
+
+ settings->custom_mounts = NULL;
+ settings->n_custom_mounts = 0;
+ }
+ }
+
+ if ((arg_settings_mask & SETTING_NETWORK) == 0 &&
+ (settings->private_network >= 0 ||
+ settings->network_veth >= 0 ||
+ settings->network_bridge ||
+ settings->network_interfaces ||
+ settings->network_macvlan ||
+ settings->network_ipvlan)) {
+
+ if (!arg_settings_trusted)
+ log_warning("Ignoring network settings, file %s is not trusted.", p);
+ else {
+ strv_free(arg_network_interfaces);
+ arg_network_interfaces = settings->network_interfaces;
+ settings->network_interfaces = NULL;
+
+ strv_free(arg_network_macvlan);
+ arg_network_macvlan = settings->network_macvlan;
+ settings->network_macvlan = NULL;
+
+ strv_free(arg_network_ipvlan);
+ arg_network_ipvlan = settings->network_ipvlan;
+ settings->network_ipvlan = NULL;
+
+ free(arg_network_bridge);
+ arg_network_bridge = settings->network_bridge;
+ settings->network_bridge = NULL;
+
+ arg_network_veth = settings->network_veth > 0 || settings->network_bridge;
+
+ arg_private_network = true; /* all these settings imply private networking */
+ }
+ }
+
+ if ((arg_settings_mask & SETTING_EXPOSE_PORTS) == 0 &&
+ settings->expose_ports) {
+
+ if (!arg_settings_trusted)
+ log_warning("Ignoring Port= setting, file %s is not trusted.", p);
+ else {
+ expose_port_free_all(arg_expose_ports);
+ arg_expose_ports = settings->expose_ports;
+ settings->expose_ports = NULL;
+ }
+ }
return 0;
}
@@ -4553,15 +3048,22 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto finish;
- r = determine_names();
- if (r < 0)
- goto finish;
-
if (geteuid() != 0) {
log_error("Need to be root.");
r = -EPERM;
goto finish;
}
+ r = determine_names();
+ if (r < 0)
+ goto finish;
+
+ r = load_settings();
+ if (r < 0)
+ goto finish;
+
+ r = verify_arguments();
+ if (r < 0)
+ goto finish;
n_fd_passed = sd_listen_fds(false);
if (n_fd_passed > 0) {
@@ -4770,23 +3272,23 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0) {
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0) {
r = log_error_errno(errno, "Failed to create kmsg socket pair: %m");
goto finish;
}
- if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, rtnl_socket_pair) < 0) {
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, rtnl_socket_pair) < 0) {
r = log_error_errno(errno, "Failed to create rtnl socket pair: %m");
goto finish;
}
- if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, pid_socket_pair) < 0) {
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pid_socket_pair) < 0) {
r = log_error_errno(errno, "Failed to create pid socket pair: %m");
goto finish;
}
if (arg_userns)
- if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0) {
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0) {
r = log_error_errno(errno, "Failed to create uid shift socket pair: %m");
goto finish;
}
@@ -4842,8 +3344,7 @@ int main(int argc, char *argv[]) {
kmsg_socket_pair[1],
rtnl_socket_pair[1],
uid_shift_socket_pair[1],
- fds,
- argc, argv);
+ fds);
if (r < 0)
_exit(EXIT_FAILURE);
@@ -4858,6 +3359,7 @@ int main(int argc, char *argv[]) {
kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]);
rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]);
pid_socket_pair[1] = safe_close(pid_socket_pair[1]);
+ uid_shift_socket_pair[1] = safe_close(uid_shift_socket_pair[1]);
/* Wait for the outer child. */
r = wait_for_terminate_and_warn("namespace helper", pid, NULL);
@@ -4876,7 +3378,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
if (l != sizeof(pid)) {
- log_error("Short read while reading inner child PID: %m");
+ log_error("Short read while reading inner child PID.");
r = EIO;
goto finish;
}
@@ -4896,7 +3398,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
if (l != sizeof(arg_uid_shift)) {
- log_error("Short read while reading UID shift: %m");
+ log_error("Short read while reading UID shift.");
r = EIO;
goto finish;
}
@@ -4908,31 +3410,64 @@ int main(int argc, char *argv[]) {
(void) barrier_place(&barrier); /* #2 */
}
- r = move_network_interfaces(pid);
- if (r < 0)
- goto finish;
+ if (arg_private_network) {
- r = setup_veth(pid, veth_name, &ifi);
- if (r < 0)
- goto finish;
+ r = move_network_interfaces(pid, arg_network_interfaces);
+ if (r < 0)
+ goto finish;
- r = setup_bridge(veth_name, &ifi);
- if (r < 0)
- goto finish;
+ if (arg_network_veth) {
+ r = setup_veth(arg_machine, pid, veth_name, !!arg_network_bridge);
+ if (r < 0)
+ goto finish;
+ else if (r > 0)
+ ifi = r;
- r = setup_macvlan(pid);
- if (r < 0)
- goto finish;
+ if (arg_network_bridge) {
+ r = setup_bridge(veth_name, arg_network_bridge);
+ if (r < 0)
+ goto finish;
+ if (r > 0)
+ ifi = r;
+ }
+ }
- r = setup_ipvlan(pid);
- if (r < 0)
- goto finish;
+ r = setup_macvlan(arg_machine, pid, arg_network_macvlan);
+ if (r < 0)
+ goto finish;
- r = register_machine(pid, ifi);
+ r = setup_ipvlan(arg_machine, pid, arg_network_ipvlan);
+ if (r < 0)
+ goto finish;
+ }
+
+ if (arg_register) {
+ r = register_machine(
+ arg_machine,
+ pid,
+ arg_directory,
+ arg_uuid,
+ ifi,
+ arg_slice,
+ arg_custom_mounts, arg_n_custom_mounts,
+ arg_kill_signal,
+ arg_property,
+ arg_keep_unit);
+ if (r < 0)
+ goto finish;
+ }
+
+ r = sync_cgroup(pid, arg_unified_cgroup_hierarchy);
if (r < 0)
goto finish;
- r = chown_cgroup(pid);
+ if (arg_keep_unit) {
+ r = create_subcgroup(pid, arg_unified_cgroup_hierarchy);
+ if (r < 0)
+ goto finish;
+ }
+
+ r = chown_cgroup(pid, arg_uid_shift);
if (r < 0)
goto finish;
@@ -4985,11 +3520,11 @@ int main(int argc, char *argv[]) {
sd_event_add_signal(event, NULL, SIGCHLD, NULL, NULL);
if (arg_expose_ports) {
- r = watch_rtnl(event, rtnl_socket_pair[0], &exposed, &rtnl);
+ r = expose_port_watch_rtnl(event, rtnl_socket_pair[0], on_address_change, &exposed, &rtnl);
if (r < 0)
goto finish;
- (void) expose_ports(rtnl, &exposed);
+ (void) expose_port_execute(rtnl, arg_expose_ports, &exposed);
}
rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
@@ -5014,7 +3549,8 @@ int main(int argc, char *argv[]) {
putc('\n', stdout);
/* Kill if it is not dead yet anyway */
- terminate_machine(pid);
+ if (arg_register && !arg_keep_unit)
+ terminate_machine(pid);
/* Normally redundant, but better safe than sorry */
kill(pid, SIGKILL);
@@ -5052,7 +3588,7 @@ int main(int argc, char *argv[]) {
break;
}
- flush_ports(&exposed);
+ expose_port_flush(arg_expose_ports, &exposed);
}
finish:
@@ -5084,24 +3620,21 @@ finish:
(void) rm_rf(p, REMOVE_ROOT);
}
+ expose_port_flush(arg_expose_ports, &exposed);
+
free(arg_directory);
free(arg_template);
free(arg_image);
free(arg_machine);
free(arg_user);
strv_free(arg_setenv);
+ free(arg_network_bridge);
strv_free(arg_network_interfaces);
strv_free(arg_network_macvlan);
strv_free(arg_network_ipvlan);
- custom_mount_free_all();
-
- flush_ports(&exposed);
-
- while (arg_expose_ports) {
- ExposePort *p = arg_expose_ports;
- LIST_REMOVE(ports, arg_expose_ports, p);
- free(p);
- }
+ strv_free(arg_parameters);
+ custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts);
+ expose_port_free_all(arg_expose_ports);
return r < 0 ? EXIT_FAILURE : ret;
}
diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h
index 5628e579ad..fbbabaf232 100644
--- a/src/resolve/resolved-dns-packet.h
+++ b/src/resolve/resolved-dns-packet.h
@@ -78,7 +78,7 @@ struct DnsPacket {
DnsQuestion *question;
DnsAnswer *answer;
- /* Packet reception meta data */
+ /* Packet reception metadata */
int ifindex;
int family, ipproto;
union in_addr_union sender, destination;
diff --git a/src/run/run.c b/src/run/run.c
index 3dd97022de..657c6fcaf1 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -36,7 +36,9 @@
#include "ptyfwd.h"
#include "formats-util.h"
#include "signal-util.h"
+#include "spawn-polkit-agent.h"
+static bool arg_ask_password = true;
static bool arg_scope = false;
static bool arg_remain_after_exit = false;
static bool arg_no_block = false;
@@ -64,6 +66,18 @@ static char *arg_on_calendar = NULL;
static char **arg_timer_property = NULL;
static bool arg_quiet = false;
+static void polkit_agent_open_if_enabled(void) {
+
+ /* Open the polkit agent as a child process if necessary */
+ if (!arg_ask_password)
+ return;
+
+ if (arg_transport != BUS_TRANSPORT_LOCAL)
+ return;
+
+ polkit_agent_open();
+}
+
static void help(void) {
printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n"
"Run the specified command in a transient scope or service or timer\n"
@@ -71,6 +85,7 @@ static void help(void) {
"specified with --unit option then command can be omitted.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
+ " --no-ask-password Do not prompt for password\n"
" --user Run as user unit\n"
" -H --host=[USER@]HOST Operate on remote host\n"
" -M --machine=CONTAINER Operate on local container\n"
@@ -108,6 +123,7 @@ static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
+ ARG_NO_ASK_PASSWORD,
ARG_USER,
ARG_SYSTEM,
ARG_SCOPE,
@@ -160,6 +176,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "on-calendar", required_argument, NULL, ARG_ON_CALENDAR },
{ "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY },
{ "no-block", no_argument, NULL, ARG_NO_BLOCK },
+ { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{},
};
@@ -177,6 +194,10 @@ static int parse_argv(int argc, char *argv[]) {
help();
return 0;
+ case ARG_NO_ASK_PASSWORD:
+ arg_ask_password = false;
+ break;
+
case ARG_VERSION:
puts(PACKAGE_STRING);
puts(SYSTEMD_FEATURES);
@@ -681,6 +702,9 @@ static int start_transient_service(
if (r < 0)
return log_error_errno(r, "Failed to determine tty name: %m");
+ if (unlockpt(master) < 0)
+ return log_error_errno(errno, "Failed to unlock tty: %m");
+
} else if (arg_transport == BUS_TRANSPORT_MACHINE) {
_cleanup_bus_unref_ sd_bus *system_bus = NULL;
const char *s;
@@ -717,9 +741,6 @@ static int start_transient_service(
return log_oom();
} else
assert_not_reached("Can't allocate tty via ssh");
-
- if (unlockpt(master) < 0)
- return log_error_errno(errno, "Failed to unlock tty: %m");
}
if (!arg_no_block) {
@@ -745,6 +766,10 @@ static int start_transient_service(
if (r < 0)
return bus_log_create_error(r);
+ r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+ if (r < 0)
+ return bus_log_create_error(r);
+
/* Name and mode */
r = sd_bus_message_append(m, "ss", service, "fail");
if (r < 0)
@@ -768,6 +793,8 @@ static int start_transient_service(
if (r < 0)
return bus_log_create_error(r);
+ polkit_agent_open_if_enabled();
+
r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0) {
log_error("Failed to start transient service unit: %s", bus_error_message(&error, -r));
@@ -860,6 +887,10 @@ static int start_transient_scope(
if (r < 0)
return bus_log_create_error(r);
+ r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+ if (r < 0)
+ return bus_log_create_error(r);
+
/* Name and Mode */
r = sd_bus_message_append(m, "ss", scope, "fail");
if (r < 0)
@@ -883,6 +914,8 @@ static int start_transient_scope(
if (r < 0)
return bus_log_create_error(r);
+ polkit_agent_open_if_enabled();
+
r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0) {
log_error("Failed to start transient scope unit: %s", bus_error_message(&error, -r));
@@ -1025,6 +1058,10 @@ static int start_transient_timer(
if (r < 0)
return bus_log_create_error(r);
+ r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+ if (r < 0)
+ return bus_log_create_error(r);
+
/* Name and Mode */
r = sd_bus_message_append(m, "ss", timer, "fail");
if (r < 0)
@@ -1077,6 +1114,8 @@ static int start_transient_timer(
if (r < 0)
return bus_log_create_error(r);
+ polkit_agent_open_if_enabled();
+
r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0) {
log_error("Failed to start transient timer unit: %s", bus_error_message(&error, -r));
diff --git a/src/shared/Makefile b/src/shared/Makefile
new file mode 120000
index 0000000000..d0b0e8e008
--- /dev/null
+++ b/src/shared/Makefile
@@ -0,0 +1 @@
+../Makefile \ No newline at end of file
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 1369a61458..d9cc19700e 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -220,6 +220,7 @@ int bus_test_polkit(
sd_bus_message *call,
int capability,
const char *action,
+ const char **details,
uid_t good_user,
bool *_challenge,
sd_bus_error *e) {
@@ -242,29 +243,52 @@ int bus_test_polkit(
return 1;
#ifdef ENABLE_POLKIT
else {
+ _cleanup_bus_message_unref_ sd_bus_message *request = NULL;
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
int authorized = false, challenge = false;
- const char *sender;
+ const char *sender, **k, **v;
sender = sd_bus_message_get_sender(call);
if (!sender)
return -EBADMSG;
- r = sd_bus_call_method(
+ r = sd_bus_message_new_method_call(
call->bus,
+ &request,
"org.freedesktop.PolicyKit1",
"/org/freedesktop/PolicyKit1/Authority",
"org.freedesktop.PolicyKit1.Authority",
- "CheckAuthorization",
- e,
- &reply,
- "(sa{sv})sa{ss}us",
+ "CheckAuthorization");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(
+ request,
+ "(sa{sv})s",
"system-bus-name", 1, "name", "s", sender,
- action,
- 0,
- 0,
- "");
+ action);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(request, 'a', "{ss}");
+ if (r < 0)
+ return r;
+ STRV_FOREACH_PAIR(k, v, details) {
+ r = sd_bus_message_append(request, "{ss}", *k, *v);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(request);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(request, "us", 0, NULL);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_call(call->bus, request, 0, e, &reply);
if (r < 0) {
/* Treat no PK available as access denied */
if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
@@ -354,6 +378,7 @@ int bus_verify_polkit_async(
sd_bus_message *call,
int capability,
const char *action,
+ const char **details,
bool interactive,
uid_t good_user,
Hashmap **registry,
@@ -362,7 +387,7 @@ int bus_verify_polkit_async(
#ifdef ENABLE_POLKIT
_cleanup_bus_message_unref_ sd_bus_message *pk = NULL;
AsyncPolkitQuery *q;
- const char *sender;
+ const char *sender, **k, **v;
sd_bus_message_handler_t callback;
void *userdata;
int c;
@@ -460,12 +485,27 @@ int bus_verify_polkit_async(
r = sd_bus_message_append(
pk,
- "(sa{sv})sa{ss}us",
+ "(sa{sv})s",
"system-bus-name", 1, "name", "s", sender,
- action,
- 0,
- !!interactive,
- NULL);
+ action);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(pk, 'a', "{ss}");
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH_PAIR(k, v, details) {
+ r = sd_bus_message_append(pk, "{ss}", *k, *v);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(pk);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(pk, "us", !!interactive, NULL);
if (r < 0)
return r;
diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h
index 4ae216b7d9..d2b2d701ce 100644
--- a/src/shared/bus-util.h
+++ b/src/shared/bus-util.h
@@ -60,9 +60,9 @@ int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error);
int bus_check_peercred(sd_bus *c);
-int bus_test_polkit(sd_bus_message *call, int capability, const char *action, uid_t good_user, bool *_challenge, sd_bus_error *e);
+int bus_test_polkit(sd_bus_message *call, int capability, const char *action, const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e);
-int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error);
+int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, const char **details, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error);
void bus_verify_polkit_async_registry_free(Hashmap *registry);
int bus_open_system_systemd(sd_bus **_bus);
diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c
index 1a2c4b28cd..31b4f6c684 100644
--- a/src/shared/cgroup-show.c
+++ b/src/shared/cgroup-show.c
@@ -152,7 +152,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns
if (!k)
return -ENOMEM;
- if (!(flags & OUTPUT_SHOW_ALL) && cg_is_empty_recursive(NULL, k, false) > 0)
+ if (!(flags & OUTPUT_SHOW_ALL) && cg_is_empty_recursive(NULL, k) > 0)
continue;
if (!shown_pids) {
@@ -161,8 +161,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns
}
if (last) {
- printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_BRANCH),
- basename(last));
+ printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_BRANCH), cg_unescape(basename(last)));
if (!p1) {
p1 = strappend(prefix, draw_special_char(DRAW_TREE_VERTICAL));
@@ -185,8 +184,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns
show_cgroup_one_by_path(path, prefix, n_columns, !!last, kernel_threads, flags);
if (last) {
- printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT),
- basename(last));
+ printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT), cg_unescape(basename(last)));
if (!p2) {
p2 = strappend(prefix, " ");
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index d99aa1d6e9..36150d8744 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -24,7 +24,7 @@
#include <errno.h>
#include <stdlib.h>
-#include "conf-parser.h"
+#include "sd-messages.h"
#include "conf-files.h"
#include "util.h"
#include "macro.h"
@@ -32,7 +32,8 @@
#include "log.h"
#include "utf8.h"
#include "path-util.h"
-#include "sd-messages.h"
+#include "signal-util.h"
+#include "conf-parser.h"
int config_item_table_lookup(
const void *table,
@@ -450,6 +451,7 @@ int config_parse_many(const char *conf_file,
DEFINE_PARSER(int, int, safe_atoi)
DEFINE_PARSER(long, long, safe_atoli)
+DEFINE_PARSER(uint32, uint32_t, safe_atou32)
DEFINE_PARSER(uint64, uint64_t, safe_atou64)
DEFINE_PARSER(unsigned, unsigned, safe_atou)
DEFINE_PARSER(double, double, safe_atod)
@@ -574,6 +576,39 @@ int config_parse_bool(const char* unit,
return 0;
}
+int config_parse_tristate(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ int k, *t = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ /* A tristate is pretty much a boolean, except that it can
+ * also take the special value -1, indicating "uninitialized",
+ * much like NULL is for a pointer type. */
+
+ k = parse_boolean(rvalue);
+ if (k < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ *t = !!k;
+ return 0;
+}
+
int config_parse_string(
const char *unit,
const char *filename,
@@ -801,3 +836,61 @@ int config_parse_log_level(
*o = (*o & LOG_FACMASK) | x;
return 0;
}
+
+int config_parse_signal(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ int *sig = data, r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(sig);
+
+ r = signal_from_string_try_harder(rvalue);
+ if (r <= 0) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse signal name, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ *sig = r;
+ return 0;
+}
+
+int config_parse_personality(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ unsigned long *personality = data, p;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(personality);
+
+ p = personality_from_string(rvalue);
+ if (p == PERSONALITY_INVALID) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse personality, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ *personality = p;
+ return 0;
+}
diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h
index 6152ee33b9..34e3815782 100644
--- a/src/shared/conf-parser.h
+++ b/src/shared/conf-parser.h
@@ -104,12 +104,14 @@ int config_parse_many(const char *conf_file, /* possibly NULL */
int config_parse_int(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unsigned(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_long(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_uint32(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_uint64(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_double(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_iec_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_si_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_iec_off(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_bool(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_tristate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_string(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_path(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_strv(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
@@ -118,6 +120,8 @@ int config_parse_nsec(const char *unit, const char *filename, unsigned line, con
int config_parse_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_log_facility(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_log_level(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_signal(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_personality(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
#define log_invalid_utf8(unit, level, config_file, config_line, error, rvalue) \
do { \
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index 273dacff1f..70220bdd14 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -33,7 +33,7 @@
static const char image_search_path[] =
"/var/lib/machines\0"
- "/var/lib/container\0"
+ "/var/lib/container\0" /* legacy */
"/usr/local/lib/machines\0"
"/usr/lib/machines\0";
diff --git a/src/shared/pager.c b/src/shared/pager.c
index 13f03e798b..3992f9c837 100644
--- a/src/shared/pager.c
+++ b/src/shared/pager.c
@@ -31,18 +31,16 @@
#include "macro.h"
#include "terminal-util.h"
#include "signal-util.h"
+#include "copy.h"
static pid_t pager_pid = 0;
noreturn static void pager_fallback(void) {
- ssize_t n;
-
- do {
- n = splice(STDIN_FILENO, NULL, STDOUT_FILENO, NULL, 64*1024, 0);
- } while (n > 0);
+ int r;
- if (n < 0) {
- log_error_errno(errno, "Internal pager failed: %m");
+ r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (off_t) -1, false);
+ if (r < 0) {
+ log_error_errno(r, "Internal pager failed: %m");
_exit(EXIT_FAILURE);
}
@@ -84,7 +82,7 @@ int pager_open(bool jump_to_end) {
/* In the child start the pager */
if (pager_pid == 0) {
- const char* less_opts;
+ const char* less_opts, *less_charset;
(void) reset_all_signal_handlers();
(void) reset_signal_mask();
@@ -92,6 +90,7 @@ int pager_open(bool jump_to_end) {
dup2(fd[0], STDIN_FILENO);
safe_close_pair(fd);
+ /* Initialize a good set of less options */
less_opts = getenv("SYSTEMD_LESS");
if (!less_opts)
less_opts = "FRSXMK";
@@ -99,6 +98,15 @@ int pager_open(bool jump_to_end) {
less_opts = strjoina(less_opts, " +G");
setenv("LESS", less_opts, 1);
+ /* Initialize a good charset for less. This is
+ * particularly important if we output UTF-8
+ * characters. */
+ less_charset = getenv("SYSTEMD_LESSCHARSET");
+ if (!less_charset && is_locale_utf8())
+ less_charset = "utf-8";
+ if (less_charset)
+ setenv("LESSCHARSET", less_charset, 1);
+
/* Make sure the pager goes away when the parent dies */
if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
_exit(EXIT_FAILURE);
@@ -131,6 +139,8 @@ int pager_open(bool jump_to_end) {
/* Return in the parent */
if (dup2(fd[1], STDOUT_FILENO) < 0)
return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
+ if (dup2(fd[1], STDERR_FILENO) < 0)
+ return log_error_errno(errno, "Failed to duplicate pager pipe: %m");
safe_close_pair(fd);
return 1;
@@ -143,6 +153,11 @@ void pager_close(void) {
/* Inform pager that we are done */
fclose(stdout);
+ stdout = NULL;
+
+ fclose(stderr);
+ stderr = NULL;
+
kill(pager_pid, SIGCONT);
(void) wait_for_terminate(pager_pid, NULL);
pager_pid = 0;
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 3cb5f61868..96c39aa0a9 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -535,10 +535,8 @@ static int get_unit_list(
return bus_log_create_error(r);
r = sd_bus_call(bus, m, 0, &error, &reply);
- if (r < 0) {
- log_error("Failed to list units: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
if (r < 0)
@@ -605,7 +603,7 @@ static int get_unit_list_recursive(
r = set_put(replies, reply);
if (r < 0) {
sd_bus_message_unref(reply);
- return r;
+ return log_oom();
}
if (arg_recursive) {
@@ -614,7 +612,7 @@ static int get_unit_list_recursive(
r = sd_get_machine_names(&machines);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to get machine names: %m");
STRV_FOREACH(i, machines) {
_cleanup_bus_flush_close_unref_ sd_bus *container = NULL;
@@ -622,7 +620,7 @@ static int get_unit_list_recursive(
r = sd_bus_open_system_machine(&container, *i);
if (r < 0) {
- log_error_errno(r, "Failed to connect to container %s: %m", *i);
+ log_warning_errno(r, "Failed to connect to container %s, ignoring: %m", *i);
continue;
}
@@ -635,7 +633,7 @@ static int get_unit_list_recursive(
r = set_put(replies, reply);
if (r < 0) {
sd_bus_message_unref(reply);
- return r;
+ return log_oom();
}
}
@@ -1474,6 +1472,7 @@ static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, cha
"Requisite\0"
"RequisiteOverridable\0"
"Wants\0"
+ "ConsistsOf\0"
"BindsTo\0",
[DEPENDENCY_REVERSE] = "RequiredBy\0"
"RequiredByOverridable\0"
@@ -1743,7 +1742,7 @@ static int get_machine_list(
_cleanup_free_ char *hn = NULL;
size_t sz = 0;
char **i;
- int c = 0;
+ int c = 0, r;
hn = gethostname_malloc();
if (!hn)
@@ -1761,7 +1760,10 @@ static int get_machine_list(
c++;
}
- sd_get_machine_names(&m);
+ r = sd_get_machine_names(&m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get machine list: %m");
+
STRV_FOREACH(i, m) {
_cleanup_free_ char *class = NULL;
@@ -3557,7 +3559,7 @@ static void print_status_info(
if (i->control_group &&
(i->main_pid > 0 || i->control_pid > 0 ||
- ((arg_transport != BUS_TRANSPORT_LOCAL && arg_transport != BUS_TRANSPORT_MACHINE) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group, false) == 0))) {
+ ((arg_transport != BUS_TRANSPORT_LOCAL && arg_transport != BUS_TRANSPORT_MACHINE) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group) == 0))) {
unsigned c;
printf(" CGroup: %s\n", i->control_group);
@@ -4267,10 +4269,8 @@ static int show_one(
&error,
&reply,
"s", "");
- if (r < 0) {
- log_error("Failed to get properties: %s", bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r));
r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
if (r < 0)
diff --git a/src/systemd/sd-dhcp-lease.h b/src/systemd/sd-dhcp-lease.h
index 5afa50a9d0..ed5bceecdd 100644
--- a/src/systemd/sd-dhcp-lease.h
+++ b/src/systemd/sd-dhcp-lease.h
@@ -34,6 +34,9 @@ sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease);
int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr);
int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime);
+int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1);
+int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2);
+int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr);
int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr);
int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr);
int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr);
@@ -44,13 +47,9 @@ int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu);
int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname);
int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname);
int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path);
-int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routesgn);
-int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const uint8_t **data,
- size_t *data_len);
-int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const uint8_t **client_id,
- size_t *client_id_len);
-
-int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file);
-int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file);
+int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes);
+int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len);
+int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len);
+int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone);
#endif
diff --git a/src/systemd/sd-dhcp-server.h b/src/systemd/sd-dhcp-server.h
index 9af3b6532d..4b0c7a1852 100644
--- a/src/systemd/sd-dhcp-server.h
+++ b/src/systemd/sd-dhcp-server.h
@@ -30,11 +30,11 @@
typedef struct sd_dhcp_server sd_dhcp_server;
+int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex);
+
sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server);
sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server);
-int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex);
-
int sd_dhcp_server_attach_event(sd_dhcp_server *client, sd_event *event, int priority);
int sd_dhcp_server_detach_event(sd_dhcp_server *client);
sd_event *sd_dhcp_server_get_event(sd_dhcp_server *client);
@@ -44,8 +44,15 @@ bool sd_dhcp_server_is_running(sd_dhcp_server *server);
int sd_dhcp_server_start(sd_dhcp_server *server);
int sd_dhcp_server_stop(sd_dhcp_server *server);
-int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen);
-int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server, struct in_addr *start, size_t size);
+int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size);
+
+int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *timezone);
+int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n);
+int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr dns[], unsigned n);
+
+int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t);
+int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t);
int sd_dhcp_server_forcerenew(sd_dhcp_server *server);
+
#endif
diff --git a/src/systemd/sd-login.h b/src/systemd/sd-login.h
index 9260396d5d..59c6eedcda 100644
--- a/src/systemd/sd-login.h
+++ b/src/systemd/sd-login.h
@@ -81,34 +81,42 @@ int sd_pid_get_user_slice(pid_t pid, char **slice);
* container. This will return an error for non-machine processes. */
int sd_pid_get_machine_name(pid_t pid, char **machine);
-/* Similar to sd_pid_get_session(), but retrieves data about peer of
- * connected AF_UNIX socket */
+/* Get the control group from a PID, relative to the root of the
+ * hierarchy. */
+int sd_pid_get_cgroup(pid_t pid, char **cgroup);
+
+/* Similar to sd_pid_get_session(), but retrieves data about the peer
+ * of a connected AF_UNIX socket */
int sd_peer_get_session(int fd, char **session);
-/* Similar to sd_pid_get_owner_uid(), but retrieves data about peer of
- * connected AF_UNIX socket */
+/* Similar to sd_pid_get_owner_uid(), but retrieves data about the peer of
+ * a connected AF_UNIX socket */
int sd_peer_get_owner_uid(int fd, uid_t *uid);
-/* Similar to sd_pid_get_unit(), but retrieves data about peer of
- * connected AF_UNIX socket */
+/* Similar to sd_pid_get_unit(), but retrieves data about the peer of
+ * a connected AF_UNIX socket */
int sd_peer_get_unit(int fd, char **unit);
-/* Similar to sd_pid_get_user_unit(), but retrieves data about peer of
- * connected AF_UNIX socket */
+/* Similar to sd_pid_get_user_unit(), but retrieves data about the peer of
+ * a connected AF_UNIX socket */
int sd_peer_get_user_unit(int fd, char **unit);
-/* Similar to sd_pid_get_slice(), but retrieves data about peer of
- * connected AF_UNIX socket */
+/* Similar to sd_pid_get_slice(), but retrieves data about the peer of
+ * a connected AF_UNIX socket */
int sd_peer_get_slice(int fd, char **slice);
-/* Similar to sd_pid_get_user_slice(), but retrieves data about peer of
- * connected AF_UNIX socket */
+/* Similar to sd_pid_get_user_slice(), but retrieves data about the peer of
+ * a connected AF_UNIX socket */
int sd_peer_get_user_slice(int fd, char **slice);
-/* Similar to sd_pid_get_machine_name(), but retrieves data about peer
- * of connected AF_UNIX socket */
+/* Similar to sd_pid_get_machine_name(), but retrieves data about the
+ * peer of a a connected AF_UNIX socket */
int sd_peer_get_machine_name(int fd, char **machine);
+/* Similar to sd_pid_get_cgroup(), but retrieves data about the peer
+ * of a connected AF_UNIX socket. */
+int sd_peer_get_cgroup(pid_t pid, char **cgroup);
+
/* Get state from UID. Possible states: offline, lingering, online, active, closing */
int sd_uid_get_state(uid_t uid, char **state);
diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h
index 4d96c867d9..4179015fbf 100644
--- a/src/systemd/sd-network.h
+++ b/src/systemd/sd-network.h
@@ -122,6 +122,9 @@ int sd_network_link_get_carrier_bound_to(int ifindex, char ***carriers);
/* Get the CARRIERS that are bound to current link. */
int sd_network_link_get_carrier_bound_by(int ifindex, char ***carriers);
+/* Get the timezone that was learnt on a specific link. */
+int sd_network_link_get_timezone(int ifindex, char **timezone);
+
/* Returns whether or not domains that don't match any link should be resolved
* on this link. 1 for yes, 0 for no and negative value for error */
int sd_network_link_get_wildcard_domain(int ifindex);
diff --git a/src/systemd/sd-resolve.h b/src/systemd/sd-resolve.h
index 36cedcd6b5..80c5852e45 100644
--- a/src/systemd/sd-resolve.h
+++ b/src/systemd/sd-resolve.h
@@ -40,7 +40,6 @@ typedef struct sd_resolve_query sd_resolve_query;
/* A callback on completion */
typedef int (*sd_resolve_getaddrinfo_handler_t)(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata);
typedef int (*sd_resolve_getnameinfo_handler_t)(sd_resolve_query *q, int ret, const char *host, const char *serv, void *userdata);
-typedef int (*sd_resolve_res_handler_t)(sd_resolve_query* q, int ret, unsigned char *answer, void *userdata);
enum {
SD_RESOLVE_GET_HOST = 1ULL,
@@ -99,18 +98,6 @@ int sd_resolve_getaddrinfo(sd_resolve *resolve, sd_resolve_query **q, const char
* if you want to query the hostname (resp. the service name). */
int sd_resolve_getnameinfo(sd_resolve *resolve, sd_resolve_query **q, const struct sockaddr *sa, socklen_t salen, int flags, uint64_t get, sd_resolve_getnameinfo_handler_t callback, void *userdata);
-/* Issue a resolver query on the specified session. The arguments are
- * compatible with those of libc's res_query(3). The function returns a new
- * query object. When the query is completed, you may retrieve the results using
- * sd_resolve_res_done(). */
-int sd_resolve_res_query(sd_resolve *resolve, sd_resolve_query **q, const char *dname, int clazz, int type, sd_resolve_res_handler_t callback, void *userdata);
-
-/* Issue a resolver query on the specified session. The arguments are
- * compatible with those of libc's res_search(3). The function returns a new
- * query object. When the query is completed, you may retrieve the results using
- * sd_resolve_res_done(). */
-int sd_resolve_res_search(sd_resolve *resolve, sd_resolve_query **q, const char *dname, int clazz, int type, sd_resolve_res_handler_t callback, void *userdata);
-
sd_resolve_query *sd_resolve_query_ref(sd_resolve_query* q);
sd_resolve_query *sd_resolve_query_unref(sd_resolve_query* q);
diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c
index 72f874d8a9..de6c421b82 100644
--- a/src/test/test-cgroup-mask.c
+++ b/src/test/test-cgroup-mask.c
@@ -61,36 +61,36 @@ static int test_cgroup_mask(void) {
root = UNIT_DEREF(parent->slice);
/* Verify per-unit cgroups settings. */
- assert_se(unit_get_cgroup_mask(son) == (CGROUP_CPU | CGROUP_CPUACCT));
- assert_se(unit_get_cgroup_mask(daughter) == 0);
- assert_se(unit_get_cgroup_mask(grandchild) == 0);
- assert_se(unit_get_cgroup_mask(parent_deep) == CGROUP_MEMORY);
- assert_se(unit_get_cgroup_mask(parent) == CGROUP_BLKIO);
- assert_se(unit_get_cgroup_mask(root) == 0);
+ assert_se(unit_get_own_mask(son) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT));
+ assert_se(unit_get_own_mask(daughter) == 0);
+ assert_se(unit_get_own_mask(grandchild) == 0);
+ assert_se(unit_get_own_mask(parent_deep) == CGROUP_MASK_MEMORY);
+ assert_se(unit_get_own_mask(parent) == CGROUP_MASK_BLKIO);
+ assert_se(unit_get_own_mask(root) == 0);
/* Verify aggregation of member masks */
assert_se(unit_get_members_mask(son) == 0);
assert_se(unit_get_members_mask(daughter) == 0);
assert_se(unit_get_members_mask(grandchild) == 0);
assert_se(unit_get_members_mask(parent_deep) == 0);
- assert_se(unit_get_members_mask(parent) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY));
- assert_se(unit_get_members_mask(root) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO | CGROUP_MEMORY));
+ assert_se(unit_get_members_mask(parent) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY));
+ assert_se(unit_get_members_mask(root) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY));
/* Verify aggregation of sibling masks. */
- assert_se(unit_get_siblings_mask(son) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY));
- assert_se(unit_get_siblings_mask(daughter) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY));
+ assert_se(unit_get_siblings_mask(son) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY));
+ assert_se(unit_get_siblings_mask(daughter) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY));
assert_se(unit_get_siblings_mask(grandchild) == 0);
- assert_se(unit_get_siblings_mask(parent_deep) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY));
- assert_se(unit_get_siblings_mask(parent) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO | CGROUP_MEMORY));
- assert_se(unit_get_siblings_mask(root) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO | CGROUP_MEMORY));
+ assert_se(unit_get_siblings_mask(parent_deep) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY));
+ assert_se(unit_get_siblings_mask(parent) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY));
+ assert_se(unit_get_siblings_mask(root) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY));
/* Verify aggregation of target masks. */
- assert_se(unit_get_target_mask(son) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY) & m->cgroup_supported));
- assert_se(unit_get_target_mask(daughter) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY) & m->cgroup_supported));
+ assert_se(unit_get_target_mask(son) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY) & m->cgroup_supported));
+ assert_se(unit_get_target_mask(daughter) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY) & m->cgroup_supported));
assert_se(unit_get_target_mask(grandchild) == 0);
- assert_se(unit_get_target_mask(parent_deep) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY) & m->cgroup_supported));
- assert_se(unit_get_target_mask(parent) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO | CGROUP_MEMORY) & m->cgroup_supported));
- assert_se(unit_get_target_mask(root) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO | CGROUP_MEMORY) & m->cgroup_supported));
+ assert_se(unit_get_target_mask(parent_deep) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY) & m->cgroup_supported));
+ assert_se(unit_get_target_mask(parent) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY) & m->cgroup_supported));
+ assert_se(unit_get_target_mask(root) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY) & m->cgroup_supported));
manager_free(m);
diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c
index ecc9d70bf4..4ecf09a29e 100644
--- a/src/test/test-cgroup-util.c
+++ b/src/test/test-cgroup-util.c
@@ -295,6 +295,17 @@ static void test_shift_path(void) {
test_shift_path_one("/foobar/waldo", "/fuckfuck", "/foobar/waldo");
}
+static void test_mask_supported(void) {
+
+ CGroupMask m;
+ CGroupController c;
+
+ assert_se(cg_mask_supported(&m) >= 0);
+
+ for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++)
+ printf("'%s' is supported: %s\n", cgroup_controller_to_string(c), yes_no(m & CGROUP_CONTROLLER_TO_MASK(c)));
+}
+
int main(void) {
test_path_decode_unit();
test_path_get_unit();
@@ -309,6 +320,7 @@ int main(void) {
test_controller_is_valid();
test_slice_to_path();
test_shift_path();
+ TEST_REQ_RUNNING_SYSTEMD(test_mask_supported());
return 0;
}
diff --git a/src/test/test-cgroup.c b/src/test/test-cgroup.c
index 4be69a408d..37b1c3554a 100644
--- a/src/test/test-cgroup.c
+++ b/src/test/test-cgroup.c
@@ -56,26 +56,26 @@ int main(int argc, char*argv[]) {
assert_se(path_equal(path, "/sys/fs/cgroup/systemd/test-b/test-d"));
free(path);
- assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-a", false) > 0);
- assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-b", false) > 0);
- assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", false) > 0);
- assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", false) == 0);
+ assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-a") > 0);
+ assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-b") > 0);
+ assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a") > 0);
+ assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b") == 0);
assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false, false, false, NULL) == 0);
assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false, false, false, NULL) > 0);
assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", SYSTEMD_CGROUP_CONTROLLER, "/test-a", false, false) > 0);
- assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", false) == 0);
- assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", false) > 0);
+ assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0);
+ assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b") > 0);
assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false, false, false, NULL) > 0);
assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false, false, false, NULL) == 0);
cg_trim(SYSTEMD_CGROUP_CONTROLLER, "/", false);
- assert_se(cg_delete(SYSTEMD_CGROUP_CONTROLLER, "/test-b") < 0);
- assert_se(cg_delete(SYSTEMD_CGROUP_CONTROLLER, "/test-a") >= 0);
+ assert_se(cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, "/test-b") < 0);
+ assert_se(cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, "/test-a") >= 0);
assert_se(cg_split_spec("foobar:/", &c, &p) == 0);
assert_se(streq(c, "foobar"));
diff --git a/src/test/test-engine.c b/src/test/test-engine.c
index a7ab21a415..6596069ade 100644
--- a/src/test/test-engine.c
+++ b/src/test/test-engine.c
@@ -38,7 +38,7 @@ int main(int argc, char *argv[]) {
/* prepare the test */
assert_se(set_unit_path(TEST_DIR) >= 0);
r = manager_new(MANAGER_USER, true, &m);
- if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT)) {
+ if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT, -ENOEXEC)) {
printf("Skipping test: manager_new: %s", strerror(-r));
return EXIT_TEST_SKIP;
}
diff --git a/src/test/test-path.c b/src/test/test-path.c
index 5d190378f1..676c9f1793 100644
--- a/src/test/test-path.c
+++ b/src/test/test-path.c
@@ -40,7 +40,7 @@ static int setup_test(Manager **m) {
assert_se(m);
r = manager_new(MANAGER_USER, true, &tmp);
- if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT)) {
+ if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT, -ENOEXEC)) {
printf("Skipping test: manager_new: %s", strerror(-r));
return -EXIT_TEST_SKIP;
}
diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c
index f915539e00..ebc9110c4d 100644
--- a/src/test/test-sched-prio.c
+++ b/src/test/test-sched-prio.c
@@ -35,7 +35,7 @@ int main(int argc, char *argv[]) {
/* prepare the test */
assert_se(set_unit_path(TEST_DIR) >= 0);
r = manager_new(MANAGER_USER, true, &m);
- if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT)) {
+ if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT, -ENOEXEC)) {
printf("Skipping test: manager_new: %s", strerror(-r));
return EXIT_TEST_SKIP;
}
diff --git a/src/test/test-util.c b/src/test/test-util.c
index dff38ab6f6..8ceb71f22a 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -270,6 +270,9 @@ static void test_parse_pid(void) {
r = parse_pid("0xFFFFFFFFFFFFFFFFF", &pid);
assert_se(r == -ERANGE);
assert_se(pid == 65);
+
+ r = parse_pid("junk", &pid);
+ assert_se(r == -EINVAL);
}
static void test_parse_uid(void) {
@@ -282,6 +285,9 @@ static void test_parse_uid(void) {
r = parse_uid("65535", &uid);
assert_se(r == -ENXIO);
+
+ r = parse_uid("asdsdas", &uid);
+ assert_se(r == -EINVAL);
}
static void test_safe_atou16(void) {
diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c
index 21d6ee4c0c..6de9e246f6 100644
--- a/src/timedate/timedated.c
+++ b/src/timedate/timedated.c
@@ -68,32 +68,15 @@ static int context_read_data(Context *c) {
assert(c);
- r = readlink_malloc("/etc/localtime", &t);
- if (r < 0) {
- if (r == -EINVAL)
- log_warning("/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/.");
- else
- log_warning_errno(r, "Failed to get target of /etc/localtime: %m");
- } else {
- const char *e;
-
- e = path_startswith(t, "/usr/share/zoneinfo/");
- if (!e)
- e = path_startswith(t, "../usr/share/zoneinfo/");
-
- if (!e)
- log_warning("/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/.");
- else {
- c->zone = strdup(e);
- if (!c->zone)
- return log_oom();
- }
- }
+ r = get_timezone(&t);
+ if (r == -EINVAL)
+ log_warning_errno(r, "/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/.");
+ else if (r < 0)
+ log_warning_errno(r, "Failed to get target of /etc/localtime: %m");
- if (isempty(c->zone)) {
- free(c->zone);
- c->zone = NULL;
- }
+ free(c->zone);
+ c->zone = t;
+ t = NULL;
c->local_rtc = clock_is_localtime() > 0;
@@ -378,6 +361,7 @@ static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *
m,
CAP_SYS_TIME,
"org.freedesktop.timedate1.set-timezone",
+ NULL,
interactive,
UID_INVALID,
&c->polkit_registry,
@@ -445,6 +429,7 @@ static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error
m,
CAP_SYS_TIME,
"org.freedesktop.timedate1.set-local-rtc",
+ NULL,
interactive,
UID_INVALID,
&c->polkit_registry,
@@ -560,6 +545,7 @@ static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *erro
m,
CAP_SYS_TIME,
"org.freedesktop.timedate1.set-time",
+ NULL,
interactive,
UID_INVALID,
&c->polkit_registry,
@@ -618,6 +604,7 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error
m,
CAP_SYS_TIME,
"org.freedesktop.timedate1.set-ntp",
+ NULL,
interactive,
UID_INVALID,
&c->polkit_registry,
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index 6e7e1271fb..589f1f7822 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -280,8 +280,16 @@ static int names_pci(struct udev_device *dev, struct netnames *names) {
assert(names);
parent = udev_device_get_parent(dev);
+
+ /* there can only ever be one virtio bus per parent device, so we can
+ safely ignore any virtio buses. see
+ <http://lists.linuxfoundation.org/pipermail/virtualization/2015-August/030331.html> */
+ while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent)))
+ parent = udev_device_get_parent(parent);
+
if (!parent)
return -ENOENT;
+
/* check if our direct parent is a PCI device with no other bus in-between */
if (streq_ptr("pci", udev_device_get_subsystem(parent))) {
names->type = NET_PCI;
diff --git a/src/udev/udev-builtin-uaccess.c b/src/udev/udev-builtin-uaccess.c
index 43bab8af63..7bf4e7f24d 100644
--- a/src/udev/udev-builtin-uaccess.c
+++ b/src/udev/udev-builtin-uaccess.c
@@ -45,7 +45,7 @@ static int builtin_uaccess(struct udev_device *dev, int argc, char *argv[], bool
seat = "seat0";
r = sd_seat_get_active(seat, NULL, &uid);
- if (r == -ENOENT) {
+ if (r == -ENXIO || r == -ENODATA) {
/* No active session on this seat */
r = 0;
goto finish;
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index 28ac44fb8e..f354ced2e2 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -1706,7 +1706,7 @@ int main(int argc, char *argv[]) {
by PID1. otherwise we are not guaranteed to have a dedicated cgroup */
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup);
if (r < 0) {
- if (r == -ENOENT)
+ if (r == -ENOENT || r == -ENOEXEC)
log_debug_errno(r, "did not find dedicated cgroup: %m");
else
log_warning_errno(r, "failed to get cgroup: %m");