summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/basic/fileio.c5
-rw-r--r--src/basic/missing.h17
-rw-r--r--src/boot/bootctl.c5
-rw-r--r--src/core/cgroup.c35
-rw-r--r--src/core/cgroup.h1
-rw-r--r--src/core/dbus-cgroup.c5
-rw-r--r--src/core/dbus-manager.c57
-rw-r--r--src/core/dbus-mount.c2
-rw-r--r--src/core/dbus-unit.c155
-rw-r--r--src/core/dbus-unit.h8
-rw-r--r--src/core/dbus.c71
-rw-r--r--src/core/dbus.h5
-rw-r--r--src/core/job.c17
-rw-r--r--src/core/job.h4
-rw-r--r--src/core/load-fragment-gperf.gperf.m43
-rw-r--r--src/core/load-fragment.c27
-rw-r--r--src/core/manager.c27
-rw-r--r--src/core/mount.c14
-rw-r--r--src/core/mount.h3
-rw-r--r--src/core/org.freedesktop.systemd1.conf8
-rw-r--r--src/core/unit.c70
-rw-r--r--src/core/unit.h8
-rw-r--r--src/import/export-raw.c1
-rw-r--r--src/journal/journald-server.c20
-rw-r--r--src/libsystemd/libsystemd.sym10
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.c1
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.h1
-rw-r--r--src/libsystemd/sd-bus/bus-internal.h4
-rw-r--r--src/libsystemd/sd-bus/bus-track.c260
-rw-r--r--src/libsystemd/sd-bus/bus-track.h1
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c144
-rw-r--r--src/libsystemd/sd-bus/test-bus-track.c113
-rw-r--r--src/nspawn/nspawn.c44
-rw-r--r--src/run/run.c231
-rw-r--r--src/shared/bus-util.c78
-rw-r--r--src/shared/ptyfwd.c102
-rw-r--r--src/shared/ptyfwd.h8
-rw-r--r--src/shared/seccomp-util.c1
-rw-r--r--src/systemctl/systemctl.c11
-rw-r--r--src/systemd/sd-bus.h10
-rw-r--r--src/udev/net/ethtool-util.c113
-rw-r--r--src/udev/net/ethtool-util.h11
-rw-r--r--src/udev/net/link-config-gperf.gperf43
-rw-r--r--src/udev/net/link-config.c6
-rw-r--r--src/udev/net/link-config.h1
-rw-r--r--src/udev/udevd.c16
46 files changed, 1454 insertions, 323 deletions
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index d642f3daea..a5920e7d36 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -37,6 +37,7 @@
#include "hexdecoct.h"
#include "log.h"
#include "macro.h"
+#include "missing.h"
#include "parse-util.h"
#include "path-util.h"
#include "random-util.h"
@@ -1280,12 +1281,10 @@ int open_tmpfile_unlinkable(const char *directory, int flags) {
/* Returns an unlinked temporary file that cannot be linked into the file system anymore */
-#ifdef O_TMPFILE
/* Try O_TMPFILE first, if it is supported */
fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
if (fd >= 0)
return fd;
-#endif
/* Fall back to unguessable name + unlinking */
p = strjoina(directory, "/systemd-tmp-XXXXXX");
@@ -1313,7 +1312,6 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
* which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in
* "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
-#ifdef O_TMPFILE
{
_cleanup_free_ char *dn = NULL;
@@ -1329,7 +1327,6 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
log_debug_errno(errno, "Failed to use O_TMPFILE on %s: %m", dn);
}
-#endif
r = tempfn_random(target, NULL, &tmp);
if (r < 0)
diff --git a/src/basic/missing.h b/src/basic/missing.h
index f8e096605e..13ff51cd35 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -537,12 +537,21 @@ struct btrfs_ioctl_quota_ctl_args {
# define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f)
#endif
-#if defined(__i386__) || defined(__x86_64__)
-
-/* The precise definition of __O_TMPFILE is arch specific, so let's
- * just define this on x86 where we know the value. */
+/* The precise definition of __O_TMPFILE is arch specific; use the
+ * values defined by the kernel (note: some are hexa, some are octal,
+ * duplicated as-is from the kernel definitions):
+ * - alpha, parisc, sparc: each has a specific value;
+ * - others: they use the "generic" value.
+ */
#ifndef __O_TMPFILE
+#if defined(__alpha__)
+#define __O_TMPFILE 0100000000
+#elif defined(__parisc__) || defined(__hppa__)
+#define __O_TMPFILE 0400000000
+#elif defined(__sparc__) || defined(__sparc64__)
+#define __O_TMPFILE 0x2000000
+#else
#define __O_TMPFILE 020000000
#endif
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index a7cdf92ed2..ee6d7eb864 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -439,9 +439,12 @@ static int status_variables(void) {
for (j = 0; j < n_order; j++)
if (options[i] == order[j])
- continue;
+ goto next_option;
print_efi_option(options[i], false);
+
+ next_option:
+ continue;
}
return 0;
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 086cd7a789..7873f88785 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -66,6 +66,7 @@ void cgroup_context_init(CGroupContext *c) {
c->memory_high = CGROUP_LIMIT_MAX;
c->memory_max = CGROUP_LIMIT_MAX;
+ c->memory_swap_max = CGROUP_LIMIT_MAX;
c->memory_limit = CGROUP_LIMIT_MAX;
@@ -173,6 +174,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
"%sMemoryLow=%" PRIu64 "\n"
"%sMemoryHigh=%" PRIu64 "\n"
"%sMemoryMax=%" PRIu64 "\n"
+ "%sMemorySwapMax=%" PRIu64 "\n"
"%sMemoryLimit=%" PRIu64 "\n"
"%sTasksMax=%" PRIu64 "\n"
"%sDevicePolicy=%s\n"
@@ -194,6 +196,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
prefix, c->memory_low,
prefix, c->memory_high,
prefix, c->memory_max,
+ prefix, c->memory_swap_max,
prefix, c->memory_limit,
prefix, c->tasks_max,
prefix, cgroup_device_policy_to_string(c->device_policy),
@@ -617,7 +620,7 @@ static unsigned cgroup_apply_blkio_device_limit(Unit *u, const char *dev_path, u
}
static bool cgroup_context_has_unified_memory_config(CGroupContext *c) {
- return c->memory_low > 0 || c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX;
+ return c->memory_low > 0 || c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX || c->memory_swap_max != CGROUP_LIMIT_MAX;
}
static void cgroup_apply_unified_memory_limit(Unit *u, const char *file, uint64_t v) {
@@ -848,10 +851,12 @@ static void cgroup_context_apply(Unit *u, CGroupMask mask, ManagerState state) {
if ((mask & CGROUP_MASK_MEMORY) && !is_root) {
if (cg_all_unified() > 0) {
uint64_t max = c->memory_max;
+ uint64_t swap_max = c->memory_swap_max;
- if (cgroup_context_has_unified_memory_config(c))
+ if (cgroup_context_has_unified_memory_config(c)) {
max = c->memory_max;
- else {
+ swap_max = c->memory_swap_max;
+ } else {
max = c->memory_limit;
if (max != CGROUP_LIMIT_MAX)
@@ -861,6 +866,7 @@ static void cgroup_context_apply(Unit *u, CGroupMask mask, ManagerState state) {
cgroup_apply_unified_memory_limit(u, "memory.low", c->memory_low);
cgroup_apply_unified_memory_limit(u, "memory.high", c->memory_high);
cgroup_apply_unified_memory_limit(u, "memory.max", max);
+ cgroup_apply_unified_memory_limit(u, "memory.swap.max", swap_max);
} else {
char buf[DECIMAL_STR_MAX(uint64_t) + 1];
uint64_t val = c->memory_limit;
@@ -1524,6 +1530,8 @@ void unit_prune_cgroup(Unit *u) {
if (!u->cgroup_path)
return;
+ (void) unit_get_cpu_usage(u, NULL); /* Cache the last CPU usage value before we destroy the cgroup */
+
is_root_slice = unit_has_name(u, SPECIAL_ROOT_SLICE);
r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !is_root_slice);
@@ -2044,7 +2052,21 @@ int unit_get_cpu_usage(Unit *u, nsec_t *ret) {
nsec_t ns;
int r;
+ assert(u);
+
+ /* Retrieve the current CPU usage counter. This will subtract the CPU counter taken when the unit was
+ * started. If the cgroup has been removed already, returns the last cached value. To cache the value, simply
+ * call this function with a NULL return value. */
+
r = unit_get_cpu_usage_raw(u, &ns);
+ if (r == -ENODATA && u->cpu_usage_last != NSEC_INFINITY) {
+ /* If we can't get the CPU usage anymore (because the cgroup was already removed, for example), use our
+ * cached value. */
+
+ if (ret)
+ *ret = u->cpu_usage_last;
+ return 0;
+ }
if (r < 0)
return r;
@@ -2053,7 +2075,10 @@ int unit_get_cpu_usage(Unit *u, nsec_t *ret) {
else
ns = 0;
- *ret = ns;
+ u->cpu_usage_last = ns;
+ if (ret)
+ *ret = ns;
+
return 0;
}
@@ -2063,6 +2088,8 @@ int unit_reset_cpu_usage(Unit *u) {
assert(u);
+ u->cpu_usage_last = NSEC_INFINITY;
+
r = unit_get_cpu_usage_raw(u, &ns);
if (r < 0) {
u->cpu_usage_base = 0;
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 2fe9cc4039..4cd168f63e 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -101,6 +101,7 @@ struct CGroupContext {
uint64_t memory_low;
uint64_t memory_high;
uint64_t memory_max;
+ uint64_t memory_swap_max;
/* For legacy hierarchies */
uint64_t cpu_shares;
diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
index 2ca80d2996..c4067a95bf 100644
--- a/src/core/dbus-cgroup.c
+++ b/src/core/dbus-cgroup.c
@@ -233,6 +233,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
SD_BUS_PROPERTY("MemoryLow", "t", NULL, offsetof(CGroupContext, memory_low), 0),
SD_BUS_PROPERTY("MemoryHigh", "t", NULL, offsetof(CGroupContext, memory_high), 0),
SD_BUS_PROPERTY("MemoryMax", "t", NULL, offsetof(CGroupContext, memory_max), 0),
+ SD_BUS_PROPERTY("MemorySwapMax", "t", NULL, offsetof(CGroupContext, memory_swap_max), 0),
SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0),
SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0),
SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0),
@@ -875,7 +876,7 @@ int bus_cgroup_set_property(
return 1;
- } else if (STR_IN_SET(name, "MemoryLow", "MemoryHigh", "MemoryMax")) {
+ } else if (STR_IN_SET(name, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax")) {
uint64_t v;
r = sd_bus_message_read(message, "t", &v);
@@ -889,6 +890,8 @@ int bus_cgroup_set_property(
c->memory_low = v;
else if (streq(name, "MemoryHigh"))
c->memory_high = v;
+ else if (streq(name, "MemorySwapMax"))
+ c->memory_swap_max = v;
else
c->memory_max = v;
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index ef05a75a8b..ea7ced2fd0 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -643,6 +643,54 @@ static int method_set_unit_properties(sd_bus_message *message, void *userdata, s
return bus_unit_method_set_properties(message, u, error);
}
+static int method_ref_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ const char *name;
+ Unit *u;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = manager_load_unit(m, name, NULL, error, &u);
+ if (r < 0)
+ return r;
+
+ r = bus_unit_check_load_state(u, error);
+ if (r < 0)
+ return r;
+
+ return bus_unit_method_ref(message, u, error);
+}
+
+static int method_unref_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ const char *name;
+ Unit *u;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = manager_load_unit(m, name, NULL, error, &u);
+ if (r < 0)
+ return r;
+
+ r = bus_unit_check_load_state(u, error);
+ if (r < 0)
+ return r;
+
+ return bus_unit_method_unref(message, u, error);
+}
+
static int reply_unit_info(sd_bus_message *reply, Unit *u) {
_cleanup_free_ char *unit_path = NULL, *job_path = NULL;
Unit *following;
@@ -781,6 +829,13 @@ static int transient_unit_from_message(
if (r < 0)
return r;
+ /* If the client asked for it, automatically add a reference to this unit. */
+ if (u->bus_track_add) {
+ r = bus_unit_track_add_sender(u, message);
+ if (r < 0)
+ return log_error_errno(r, "Failed to watch sender: %m");
+ }
+
/* Now load the missing bits of the unit we just created */
unit_add_to_load_queue(u);
manager_dispatch_load_queue(m);
@@ -2211,6 +2266,8 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("KillUnit", "ssi", NULL, method_kill_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResetFailedUnit", "s", NULL, method_reset_failed_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetUnitProperties", "sba(sv)", NULL, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("RefUnit", "s", NULL, method_ref_unit, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("UnrefUnit", "s", NULL, method_unref_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", method_start_transient_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetUnitProcesses", "s", "a(sus)", method_get_unit_processes, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetJob", "u", "o", method_get_job, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c
index 3c6bda4073..76a7a7ce97 100644
--- a/src/core/dbus-mount.c
+++ b/src/core/dbus-mount.c
@@ -116,6 +116,8 @@ const sd_bus_vtable bus_mount_vtable[] = {
SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Mount, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Mount, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SloppyOptions", "b", bus_property_get_bool, offsetof(Mount, sloppy_options), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LazyUnmount", "b", bus_property_get_bool, offsetof(Mount, lazy_unmount), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("ForceUnmount", "b", bus_property_get_bool, offsetof(Mount, force_unmount), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Mount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 89e56a2e51..1b86bdde43 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -418,6 +418,7 @@ static int bus_verify_manage_units_async_full(
const char *verb,
int capability,
const char *polkit_message,
+ bool interactive,
sd_bus_message *call,
sd_bus_error *error) {
@@ -433,7 +434,15 @@ static int bus_verify_manage_units_async_full(
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);
+ return bus_verify_polkit_async(
+ call,
+ capability,
+ "org.freedesktop.systemd1.manage-units",
+ details,
+ interactive,
+ UID_INVALID,
+ &u->manager->polkit_registry,
+ error);
}
int bus_unit_method_start_generic(
@@ -486,6 +495,7 @@ int bus_unit_method_start_generic(
verb,
CAP_SYS_ADMIN,
job_type < _JOB_TYPE_MAX ? polkit_message_for_job[job_type] : NULL,
+ true,
message,
error);
if (r < 0)
@@ -558,6 +568,7 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
"kill",
CAP_KILL,
N_("Authentication is required to kill '$(unit)'."),
+ true,
message,
error);
if (r < 0)
@@ -588,6 +599,7 @@ int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus
"reset-failed",
CAP_SYS_ADMIN,
N_("Authentication is required to reset the \"failed\" state of '$(unit)'."),
+ true,
message,
error);
if (r < 0)
@@ -620,6 +632,7 @@ int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_b
"set-property",
CAP_SYS_ADMIN,
N_("Authentication is required to set properties on '$(unit)'."),
+ true,
message,
error);
if (r < 0)
@@ -634,6 +647,53 @@ int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_b
return sd_bus_reply_method_return(message, NULL);
}
+int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Unit *u = userdata;
+ int r;
+
+ assert(message);
+ assert(u);
+
+ r = mac_selinux_unit_access_check(u, message, "start", error);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_manage_units_async_full(
+ u,
+ "ref",
+ CAP_SYS_ADMIN,
+ NULL,
+ false,
+ message,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+ r = bus_unit_track_add_sender(u, message);
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Unit *u = userdata;
+ int r;
+
+ assert(message);
+ assert(u);
+
+ r = bus_unit_track_remove_sender(u, message);
+ if (r == -EUNATCH)
+ return sd_bus_error_setf(error, BUS_ERROR_NOT_REFERENCED, "Unit has not been referenced yet.");
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_VTABLE_START(0),
@@ -715,6 +775,8 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_METHOD("Kill", "si", NULL, bus_unit_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Ref", NULL, NULL, bus_unit_method_ref, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Unref", NULL, NULL, bus_unit_method_unref, SD_BUS_VTABLE_UNPRIVILEGED),
/* Obsolete properties or obsolete alias names */
SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
@@ -1318,6 +1380,29 @@ static int bus_unit_set_transient_property(
return r;
return 1;
+
+ } else if (streq(name, "AddRef")) {
+
+ int b;
+
+ /* Why is this called "AddRef" rather than just "Ref", or "Reference"? There's already a "Ref()" method
+ * on the Unit interface, and it's probably not a good idea to expose a property and a method on the
+ * same interface (well, strictly speaking AddRef isn't exposed as full property, we just read it for
+ * transient units, but still). And "References" and "ReferencedBy" is already used as unit reference
+ * dependency type, hence let's not confuse things with that.
+ *
+ * Note that we don't acually add the reference to the bus track. We do that only after the setup of
+ * the transient unit is complete, so that setting this property multiple times in the same transient
+ * unit creation call doesn't count as individual references. */
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK)
+ u->bus_track_add = b;
+
+ return 1;
}
return 0;
@@ -1422,3 +1507,71 @@ int bus_unit_check_load_state(Unit *u, sd_bus_error *error) {
return sd_bus_error_set_errnof(error, u->load_error, "Unit %s is not loaded properly: %m.", u->id);
}
+
+static int bus_track_handler(sd_bus_track *t, void *userdata) {
+ Unit *u = userdata;
+
+ assert(t);
+ assert(u);
+
+ u->bus_track = sd_bus_track_unref(u->bus_track); /* make sure we aren't called again */
+
+ unit_add_to_gc_queue(u);
+ return 0;
+}
+
+static int allocate_bus_track(Unit *u) {
+ int r;
+
+ assert(u);
+
+ if (u->bus_track)
+ return 0;
+
+ r = sd_bus_track_new(u->manager->api_bus, &u->bus_track, bus_track_handler, u);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_track_set_recursive(u->bus_track, true);
+ if (r < 0) {
+ u->bus_track = sd_bus_track_unref(u->bus_track);
+ return r;
+ }
+
+ return 0;
+}
+
+int bus_unit_track_add_name(Unit *u, const char *name) {
+ int r;
+
+ assert(u);
+
+ r = allocate_bus_track(u);
+ if (r < 0)
+ return r;
+
+ return sd_bus_track_add_name(u->bus_track, name);
+}
+
+int bus_unit_track_add_sender(Unit *u, sd_bus_message *m) {
+ int r;
+
+ assert(u);
+
+ r = allocate_bus_track(u);
+ if (r < 0)
+ return r;
+
+ return sd_bus_track_add_sender(u->bus_track, m);
+}
+
+int bus_unit_track_remove_sender(Unit *u, sd_bus_message *m) {
+ assert(u);
+
+ /* If we haven't allocated the bus track object yet, then there's definitely no reference taken yet, return an
+ * error */
+ if (!u->bus_track)
+ return -EUNATCH;
+
+ return sd_bus_track_remove_sender(u->bus_track, m);
+}
diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h
index 4db88dbebc..b280de7a1d 100644
--- a/src/core/dbus-unit.h
+++ b/src/core/dbus-unit.h
@@ -33,9 +33,15 @@ int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_
int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error);
int bus_unit_set_properties(Unit *u, sd_bus_message *message, UnitSetPropertiesMode mode, bool commit, sd_bus_error *error);
int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error);
int bus_unit_check_load_state(Unit *u, sd_bus_error *error);
+
+int bus_unit_track_add_name(Unit *u, const char *name);
+int bus_unit_track_add_sender(Unit *u, sd_bus_message *m);
+int bus_unit_track_remove_sender(Unit *u, sd_bus_message *m);
diff --git a/src/core/dbus.c b/src/core/dbus.c
index 3422a02d68..1e41a42aa6 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -1168,60 +1168,57 @@ int bus_foreach_bus(
return ret;
}
-void bus_track_serialize(sd_bus_track *t, FILE *f) {
+void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix) {
const char *n;
assert(f);
+ assert(prefix);
- for (n = sd_bus_track_first(t); n; n = sd_bus_track_next(t))
- fprintf(f, "subscribed=%s\n", n);
-}
-
-int bus_track_deserialize_item(char ***l, const char *line) {
- const char *e;
- int r;
-
- assert(l);
- assert(line);
-
- e = startswith(line, "subscribed=");
- if (!e)
- return 0;
+ for (n = sd_bus_track_first(t); n; n = sd_bus_track_next(t)) {
+ int c, j;
- r = strv_extend(l, e);
- if (r < 0)
- return r;
+ c = sd_bus_track_count_name(t, n);
- return 1;
+ for (j = 0; j < c; j++) {
+ fputs(prefix, f);
+ fputc('=', f);
+ fputs(n, f);
+ fputc('\n', f);
+ }
+ }
}
-int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l) {
+int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l) {
+ char **i;
int r = 0;
assert(m);
assert(t);
- assert(l);
-
- if (!strv_isempty(*l) && m->api_bus) {
- char **i;
- if (!*t) {
- r = sd_bus_track_new(m->api_bus, t, NULL, NULL);
- if (r < 0)
- return r;
- }
+ if (strv_isempty(l))
+ return 0;
- r = 0;
- STRV_FOREACH(i, *l) {
- int k;
+ if (!m->api_bus)
+ return 0;
- k = sd_bus_track_add_name(*t, *i);
- if (k < 0)
- r = k;
- }
+ if (!*t) {
+ r = sd_bus_track_new(m->api_bus, t, NULL, NULL);
+ if (r < 0)
+ return r;
}
- *l = strv_free(*l);
+ r = sd_bus_track_set_recursive(*t, recursive);
+ if (r < 0)
+ return r;
+
+ r = 0;
+ STRV_FOREACH(i, l) {
+ int k;
+
+ k = sd_bus_track_add_name(*t, *i);
+ if (k < 0)
+ r = k;
+ }
return r;
}
diff --git a/src/core/dbus.h b/src/core/dbus.h
index 6baaffbd75..a092ed9d76 100644
--- a/src/core/dbus.h
+++ b/src/core/dbus.h
@@ -28,9 +28,8 @@ void bus_done(Manager *m);
int bus_fdset_add_all(Manager *m, FDSet *fds);
-void bus_track_serialize(sd_bus_track *t, FILE *f);
-int bus_track_deserialize_item(char ***l, const char *line);
-int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l);
+void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix);
+int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l);
int manager_sync_bus_names(Manager *m, sd_bus *bus);
diff --git a/src/core/job.c b/src/core/job.c
index 7557874d4d..7faf2ef686 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -997,7 +997,10 @@ char *job_dbus_path(Job *j) {
return p;
}
-int job_serialize(Job *j, FILE *f, FDSet *fds) {
+int job_serialize(Job *j, FILE *f) {
+ assert(j);
+ assert(f);
+
fprintf(f, "job-id=%u\n", j->id);
fprintf(f, "job-type=%s\n", job_type_to_string(j->type));
fprintf(f, "job-state=%s\n", job_state_to_string(j->state));
@@ -1008,15 +1011,16 @@ int job_serialize(Job *j, FILE *f, FDSet *fds) {
if (j->begin_usec > 0)
fprintf(f, "job-begin="USEC_FMT"\n", j->begin_usec);
- bus_track_serialize(j->clients, f);
+ bus_track_serialize(j->clients, f, "subscribed");
/* End marker */
fputc('\n', f);
return 0;
}
-int job_deserialize(Job *j, FILE *f, FDSet *fds) {
+int job_deserialize(Job *j, FILE *f) {
assert(j);
+ assert(f);
for (;;) {
char line[LINE_MAX], *l, *v;
@@ -1106,7 +1110,7 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) {
} else if (streq(l, "subscribed")) {
if (strv_extend(&j->deserialized_clients, v) < 0)
- return log_oom();
+ log_oom();
}
}
}
@@ -1118,9 +1122,8 @@ int job_coldplug(Job *j) {
/* After deserialization is complete and the bus connection
* set up again, let's start watching our subscribers again */
- r = bus_track_coldplug(j->manager, &j->clients, &j->deserialized_clients);
- if (r < 0)
- return r;
+ (void) bus_track_coldplug(j->manager, &j->clients, false, j->deserialized_clients);
+ j->deserialized_clients = strv_free(j->deserialized_clients);
if (j->state == JOB_WAITING)
job_add_to_run_queue(j);
diff --git a/src/core/job.h b/src/core/job.h
index d359e8bb3e..85368f0d30 100644
--- a/src/core/job.h
+++ b/src/core/job.h
@@ -177,8 +177,8 @@ Job* job_install(Job *j);
int job_install_deserialized(Job *j);
void job_uninstall(Job *j);
void job_dump(Job *j, FILE*f, const char *prefix);
-int job_serialize(Job *j, FILE *f, FDSet *fds);
-int job_deserialize(Job *j, FILE *f, FDSet *fds);
+int job_serialize(Job *j, FILE *f);
+int job_deserialize(Job *j, FILE *f);
int job_coldplug(Job *j);
JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts);
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 05fe0df7e3..2e6c965aec 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -131,6 +131,7 @@ $1.MemoryAccounting, config_parse_bool, 0,
$1.MemoryLow, config_parse_memory_limit, 0, offsetof($1, cgroup_context)
$1.MemoryHigh, config_parse_memory_limit, 0, offsetof($1, cgroup_context)
$1.MemoryMax, config_parse_memory_limit, 0, offsetof($1, cgroup_context)
+$1.MemorySwapMax, config_parse_memory_limit, 0, offsetof($1, cgroup_context)
$1.MemoryLimit, config_parse_memory_limit, 0, offsetof($1, cgroup_context)
$1.DeviceAllow, config_parse_device_allow, 0, offsetof($1, cgroup_context)
$1.DevicePolicy, config_parse_device_policy, 0, offsetof($1, cgroup_context.device_policy)
@@ -355,6 +356,8 @@ Mount.Type, config_parse_string, 0,
Mount.TimeoutSec, config_parse_sec, 0, offsetof(Mount, timeout_usec)
Mount.DirectoryMode, config_parse_mode, 0, offsetof(Mount, directory_mode)
Mount.SloppyOptions, config_parse_bool, 0, offsetof(Mount, sloppy_options)
+Mount.LazyUnmount, config_parse_bool, 0, offsetof(Mount, lazy_unmount)
+Mount.ForceUnmount, config_parse_bool, 0, offsetof(Mount, force_unmount)
EXEC_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
CGROUP_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index d5185cf6a0..8f067b5586 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -1338,10 +1338,13 @@ int config_parse_timer(const char *unit,
void *userdata) {
Timer *t = data;
- usec_t u = 0;
+ usec_t usec = 0;
TimerValue *v;
TimerBase b;
CalendarSpec *c = NULL;
+ Unit *u = userdata;
+ _cleanup_free_ char *k = NULL;
+ int r;
assert(filename);
assert(lvalue);
@@ -1360,14 +1363,20 @@ int config_parse_timer(const char *unit,
return 0;
}
+ r = unit_full_printf(u, rvalue, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
+ return 0;
+ }
+
if (b == TIMER_CALENDAR) {
- if (calendar_spec_from_string(rvalue, &c) < 0) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse calendar specification, ignoring: %s", rvalue);
+ if (calendar_spec_from_string(k, &c) < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse calendar specification, ignoring: %s", k);
return 0;
}
} else {
- if (parse_sec(rvalue, &u) < 0) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", rvalue);
+ if (parse_sec(k, &usec) < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", k);
return 0;
}
}
@@ -1379,7 +1388,7 @@ int config_parse_timer(const char *unit,
}
v->base = b;
- v->value = u;
+ v->value = usec;
v->calendar_spec = c;
LIST_PREPEND(value, t->values, v);
@@ -2981,8 +2990,12 @@ int config_parse_memory_limit(
c->memory_high = bytes;
else if (streq(lvalue, "MemoryMax"))
c->memory_max = bytes;
- else
+ else if (streq(lvalue, "MemorySwapMax"))
+ c->memory_swap_max = bytes;
+ else if (streq(lvalue, "MemoryLimit"))
c->memory_limit = bytes;
+ else
+ return -EINVAL;
return 0;
}
diff --git a/src/core/manager.c b/src/core/manager.c
index 7576d038a2..b58f68fa7a 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -1287,10 +1287,11 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
if (q < 0 && r == 0)
r = q;
- /* We might have deserialized the kdbus control fd, but if we
- * didn't, then let's create the bus now. */
- manager_connect_bus(m, !!serialization);
- bus_track_coldplug(m, &m->subscribed, &m->deserialized_subscribed);
+ /* We might have deserialized the kdbus control fd, but if we didn't, then let's create the bus now. */
+ (void) manager_connect_bus(m, !!serialization);
+
+ (void) bus_track_coldplug(m, &m->subscribed, false, m->deserialized_subscribed);
+ m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
/* Third, fire things up! */
manager_coldplug(m);
@@ -2490,7 +2491,7 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
fprintf(f, "kdbus-fd=%i\n", copy);
}
- bus_track_serialize(m->subscribed, f);
+ bus_track_serialize(m->subscribed, f, "subscribed");
r = dynamic_user_serialize(m, f, fds);
if (r < 0)
@@ -2693,15 +2694,13 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
manager_deserialize_uid_refs_one(m, l + 16);
else if (startswith(l, "destroy-ipc-gid="))
manager_deserialize_gid_refs_one(m, l + 16);
- else {
- int k;
-
- k = bus_track_deserialize_item(&m->deserialized_subscribed, l);
- if (k < 0)
- log_debug_errno(k, "Failed to deserialize bus tracker object: %m");
- else if (k == 0)
- log_debug("Unknown serialization item '%s'", l);
- }
+ else if (startswith(l, "subscribed=")) {
+
+ if (strv_extend(&m->deserialized_subscribed, l+11) < 0)
+ log_oom();
+
+ } else
+ log_debug("Unknown serialization item '%s'", l);
}
for (;;) {
diff --git a/src/core/mount.c b/src/core/mount.c
index f2ac8d171f..04025b83b9 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -677,7 +677,10 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {
"%sOptions: %s\n"
"%sFrom /proc/self/mountinfo: %s\n"
"%sFrom fragment: %s\n"
- "%sDirectoryMode: %04o\n",
+ "%sDirectoryMode: %04o\n"
+ "%sSloppyOptions: %s\n"
+ "%sLazyUnmount: %s\n"
+ "%sForceUnmount: %s\n",
prefix, mount_state_to_string(m->state),
prefix, mount_result_to_string(m->result),
prefix, m->where,
@@ -686,7 +689,10 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {
prefix, p ? strna(p->options) : "n/a",
prefix, yes_no(m->from_proc_self_mountinfo),
prefix, yes_no(m->from_fragment),
- prefix, m->directory_mode);
+ prefix, m->directory_mode,
+ prefix, yes_no(m->sloppy_options),
+ prefix, yes_no(m->lazy_unmount),
+ prefix, yes_no(m->force_unmount));
if (m->control_pid > 0)
fprintf(f,
@@ -846,6 +852,10 @@ static void mount_enter_unmounting(Mount *m) {
m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT;
r = exec_command_set(m->control_command, UMOUNT_PATH, m->where, NULL);
+ if (r >= 0 && m->lazy_unmount)
+ r = exec_command_append(m->control_command, "-l", NULL);
+ if (r >= 0 && m->force_unmount)
+ r = exec_command_append(m->control_command, "-f", NULL);
if (r < 0)
goto fail;
diff --git a/src/core/mount.h b/src/core/mount.h
index ac27b518cc..9f7326ba6a 100644
--- a/src/core/mount.h
+++ b/src/core/mount.h
@@ -71,6 +71,9 @@ struct Mount {
bool sloppy_options;
+ bool lazy_unmount;
+ bool force_unmount;
+
MountResult result;
MountResult reload_result;
diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf
index 14f6aec029..647e5f736c 100644
--- a/src/core/org.freedesktop.systemd1.conf
+++ b/src/core/org.freedesktop.systemd1.conf
@@ -184,6 +184,14 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="RefUnit"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
+ send_member="UnrefUnit"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="EnableUnitFiles"/>
<allow send_destination="org.freedesktop.systemd1"
diff --git a/src/core/unit.c b/src/core/unit.c
index c58c501bc3..de22f657c6 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -102,6 +102,7 @@ Unit *unit_new(Manager *m, size_t size) {
u->job_timeout = USEC_INFINITY;
u->ref_uid = UID_INVALID;
u->ref_gid = GID_INVALID;
+ u->cpu_usage_last = NSEC_INFINITY;
RATELIMIT_INIT(u->start_limit, m->default_start_limit_interval, m->default_start_limit_burst);
RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16);
@@ -113,7 +114,7 @@ bool unit_has_name(Unit *u, const char *name) {
assert(u);
assert(name);
- return !!set_get(u->names, (char*) name);
+ return set_contains(u->names, (char*) name);
}
static void unit_init(Unit *u) {
@@ -329,6 +330,9 @@ bool unit_check_gc(Unit *u) {
if (u->refs)
return true;
+ if (sd_bus_track_count(u->bus_track) > 0)
+ return true;
+
if (UNIT_VTABLE(u)->check_gc)
if (UNIT_VTABLE(u)->check_gc(u))
return true;
@@ -509,6 +513,9 @@ void unit_free(Unit *u) {
sd_bus_slot_unref(u->match_bus_slot);
+ sd_bus_track_unref(u->bus_track);
+ u->deserialized_refs = strv_free(u->deserialized_refs);
+
unit_free_requires_mounts_for(u);
SET_FOREACH(t, u->names, i)
@@ -897,6 +904,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
Unit *following;
_cleanup_set_free_ Set *following_set = NULL;
int r;
+ const char *n;
assert(u);
assert(u->type >= 0);
@@ -1038,6 +1046,8 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
else if (u->load_state == UNIT_ERROR)
fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror(-u->load_error));
+ for (n = sd_bus_track_first(u->bus_track); n; n = sd_bus_track_next(u->bus_track))
+ fprintf(f, "%s\tBus Ref: %s\n", prefix, n);
if (u->job)
job_dump(u->job, f, prefix2);
@@ -2611,7 +2621,10 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
unit_serialize_item(u, f, "assert-result", yes_no(u->assert_result));
unit_serialize_item(u, f, "transient", yes_no(u->transient));
+
unit_serialize_item_format(u, f, "cpu-usage-base", "%" PRIu64, u->cpu_usage_base);
+ if (u->cpu_usage_last != NSEC_INFINITY)
+ unit_serialize_item_format(u, f, "cpu-usage-last", "%" PRIu64, u->cpu_usage_last);
if (u->cgroup_path)
unit_serialize_item(u, f, "cgroup", u->cgroup_path);
@@ -2622,15 +2635,17 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
if (gid_is_valid(u->ref_gid))
unit_serialize_item_format(u, f, "ref-gid", GID_FMT, u->ref_gid);
+ bus_track_serialize(u->bus_track, f, "ref");
+
if (serialize_jobs) {
if (u->job) {
fprintf(f, "job\n");
- job_serialize(u->job, f, fds);
+ job_serialize(u->job, f);
}
if (u->nop_job) {
fprintf(f, "job\n");
- job_serialize(u->nop_job, f, fds);
+ job_serialize(u->nop_job, f);
}
}
@@ -2760,7 +2775,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
if (!j)
return log_oom();
- r = job_deserialize(j, f, fds);
+ r = job_deserialize(j, f);
if (r < 0) {
job_free(j);
return r;
@@ -2832,11 +2847,19 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
continue;
- } else if (streq(l, "cpu-usage-base") || streq(l, "cpuacct-usage-base")) {
+ } else if (STR_IN_SET(l, "cpu-usage-base", "cpuacct-usage-base")) {
r = safe_atou64(v, &u->cpu_usage_base);
if (r < 0)
- log_unit_debug(u, "Failed to parse CPU usage %s, ignoring.", v);
+ log_unit_debug(u, "Failed to parse CPU usage base %s, ignoring.", v);
+
+ continue;
+
+ } else if (streq(l, "cpu-usage-last")) {
+
+ r = safe_atou64(v, &u->cpu_usage_last);
+ if (r < 0)
+ log_unit_debug(u, "Failed to read CPU usage last %s, ignoring.", v);
continue;
@@ -2880,6 +2903,12 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
else
unit_ref_uid_gid(u, UID_INVALID, gid);
+ } else if (streq(l, "ref")) {
+
+ r = strv_extend(&u->deserialized_refs, v);
+ if (r < 0)
+ log_oom();
+
continue;
}
@@ -2955,7 +2984,8 @@ int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency dep
}
int unit_coldplug(Unit *u) {
- int r = 0, q = 0;
+ int r = 0, q;
+ char **i;
assert(u);
@@ -2966,18 +2996,26 @@ int unit_coldplug(Unit *u) {
u->coldplugged = true;
- if (UNIT_VTABLE(u)->coldplug)
- r = UNIT_VTABLE(u)->coldplug(u);
+ STRV_FOREACH(i, u->deserialized_refs) {
+ q = bus_unit_track_add_name(u, *i);
+ if (q < 0 && r >= 0)
+ r = q;
+ }
+ u->deserialized_refs = strv_free(u->deserialized_refs);
- if (u->job)
- q = job_coldplug(u->job);
+ if (UNIT_VTABLE(u)->coldplug) {
+ q = UNIT_VTABLE(u)->coldplug(u);
+ if (q < 0 && r >= 0)
+ r = q;
+ }
- if (r < 0)
- return r;
- if (q < 0)
- return q;
+ if (u->job) {
+ q = job_coldplug(u->job);
+ if (q < 0 && r >= 0)
+ r = q;
+ }
- return 0;
+ return r;
}
static bool fragment_mtime_newer(const char *path, usec_t mtime) {
diff --git a/src/core/unit.h b/src/core/unit.h
index f7a65782f1..3584c16d8c 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -108,6 +108,10 @@ struct Unit {
/* The slot used for watching NameOwnerChanged signals */
sd_bus_slot *match_bus_slot;
+ /* References to this unit from clients */
+ sd_bus_track *bus_track;
+ char **deserialized_refs;
+
/* Job timeout and action to take */
usec_t job_timeout;
FailureAction job_timeout_action;
@@ -190,6 +194,7 @@ struct Unit {
/* Where the cpu.stat or cpuacct.usage was at the time the unit was started */
nsec_t cpu_usage_base;
+ nsec_t cpu_usage_last; /* the most recently read value */
/* Counterparts in the cgroup filesystem */
char *cgroup_path;
@@ -247,6 +252,9 @@ struct Unit {
/* Did we already invoke unit_coldplug() for this unit? */
bool coldplugged:1;
+
+ /* For transient units: whether to add a bus track reference after creating the unit */
+ bool bus_track_add:1;
};
struct UnitStatusMessageFormats {
diff --git a/src/import/export-raw.c b/src/import/export-raw.c
index db06e11b87..6136b677dd 100644
--- a/src/import/export-raw.c
+++ b/src/import/export-raw.c
@@ -34,6 +34,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "import-common.h"
+#include "missing.h"
#include "ratelimit.h"
#include "string-util.h"
#include "util.h"
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 2a043a95b1..3507910919 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -267,14 +267,18 @@ static int open_journal(
return r;
}
+static bool flushed_flag_is_set(void) {
+ return (access("/run/systemd/journal/flushed", F_OK) >= 0);
+}
+
static int system_journal_open(Server *s, bool flush_requested) {
+ bool flushed = false;
const char *fn;
int r = 0;
if (!s->system_journal &&
(s->storage == STORAGE_PERSISTENT || s->storage == STORAGE_AUTO) &&
- (flush_requested
- || access("/run/systemd/journal/flushed", F_OK) >= 0)) {
+ (flush_requested || (flushed = flushed_flag_is_set()))) {
/* If in auto mode: first try to create the machine
* path, but not the prefix.
@@ -299,6 +303,16 @@ static int system_journal_open(Server *s, bool flush_requested) {
r = 0;
}
+
+ /* If the runtime journal is open, and we're post-flush, we're
+ * recovering from a failed system journal rotate (ENOSPC)
+ * for which the runtime journal was reopened.
+ *
+ * Perform an implicit flush to var, leaving the runtime
+ * journal closed, now that the system journal is back.
+ */
+ if (s->runtime_journal && flushed)
+ (void) server_flush_to_var(s);
}
if (!s->runtime_journal &&
@@ -1294,7 +1308,7 @@ static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *
log_info("Received request to flush runtime journal from PID " PID_FMT, si->ssi_pid);
- server_flush_to_var(s);
+ (void) server_flush_to_var(s);
server_sync(s);
server_vacuum(s, false, false);
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index 542254295c..70ea347361 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -500,3 +500,13 @@ LIBSYSTEMD_231 {
global:
sd_event_get_iteration;
} LIBSYSTEMD_230;
+
+LIBSYSTEMD_232 {
+global:
+ sd_bus_track_set_recursive;
+ sd_bus_track_get_recursive;
+ sd_bus_track_count_name;
+ sd_bus_track_count_sender;
+ sd_bus_set_exit_on_disconnect;
+ sd_bus_get_exit_on_disconnect;
+} LIBSYSTEMD_231;
diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c
index 32be3cdc38..a69193aa32 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.c
+++ b/src/libsystemd/sd-bus/bus-common-errors.c
@@ -45,6 +45,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_SHUTTING_DOWN, ECANCELED),
SD_BUS_ERROR_MAP(BUS_ERROR_SCOPE_NOT_RUNNING, EHOSTDOWN),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_DYNAMIC_USER, ESRCH),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NOT_REFERENCED, EUNATCH),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_MACHINE, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_IMAGE, ENOENT),
diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h
index befb6fbfe0..5df21c8926 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.h
+++ b/src/libsystemd/sd-bus/bus-common-errors.h
@@ -41,6 +41,7 @@
#define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown"
#define BUS_ERROR_SCOPE_NOT_RUNNING "org.freedesktop.systemd1.ScopeNotRunning"
#define BUS_ERROR_NO_SUCH_DYNAMIC_USER "org.freedesktop.systemd1.NoSuchDynamicUser"
+#define BUS_ERROR_NOT_REFERENCED "org.freedesktop.systemd1.NotReferenced"
#define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine"
#define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage"
diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h
index 216d9f62bc..2608f5469c 100644
--- a/src/libsystemd/sd-bus/bus-internal.h
+++ b/src/libsystemd/sd-bus/bus-internal.h
@@ -209,6 +209,9 @@ struct sd_bus {
bool is_system:1;
bool is_user:1;
bool allow_interactive_authorization:1;
+ bool exit_on_disconnect:1;
+ bool exited:1;
+ bool exit_triggered:1;
int use_memfd;
@@ -320,6 +323,7 @@ struct sd_bus {
sd_bus_track *track_queue;
LIST_HEAD(sd_bus_slot, slots);
+ LIST_HEAD(sd_bus_track, tracks);
};
#define BUS_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC))
diff --git a/src/libsystemd/sd-bus/bus-track.c b/src/libsystemd/sd-bus/bus-track.c
index 1f436fe560..00e93a215f 100644
--- a/src/libsystemd/sd-bus/bus-track.c
+++ b/src/libsystemd/sd-bus/bus-track.c
@@ -24,16 +24,27 @@
#include "bus-track.h"
#include "bus-util.h"
+struct track_item {
+ unsigned n_ref;
+ char *name;
+ sd_bus_slot *slot;
+};
+
struct sd_bus_track {
unsigned n_ref;
+ unsigned n_adding; /* are we in the process of adding a new name? */
sd_bus *bus;
sd_bus_track_handler_t handler;
void *userdata;
Hashmap *names;
LIST_FIELDS(sd_bus_track, queue);
Iterator iterator;
- bool in_queue;
- bool modified;
+ bool in_list:1; /* In bus->tracks? */
+ bool in_queue:1; /* In bus->track_queue? */
+ bool modified:1;
+ bool recursive:1;
+
+ LIST_FIELDS(sd_bus_track, tracks);
};
#define MATCH_PREFIX \
@@ -56,15 +67,47 @@ struct sd_bus_track {
_x; \
})
+static struct track_item* track_item_free(struct track_item *i) {
+
+ if (!i)
+ return NULL;
+
+ sd_bus_slot_unref(i->slot);
+ free(i->name);
+ free(i);
+
+ return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct track_item*, track_item_free);
+
static void bus_track_add_to_queue(sd_bus_track *track) {
assert(track);
+ /* Adds the bus track object to the queue of objects we should dispatch next, subject to a number of
+ * conditions. */
+
+ /* Already in the queue? */
if (track->in_queue)
return;
+ /* if we are currently in the process of adding a new name, then let's not enqueue this just yet, let's wait
+ * until the addition is complete. */
+ if (track->n_adding > 0)
+ return;
+
+ /* still referenced? */
+ if (hashmap_size(track->names) > 0)
+ return;
+
+ /* Nothing to call? */
if (!track->handler)
return;
+ /* Already closed? */
+ if (!track->in_list)
+ return;
+
LIST_PREPEND(queue, track->bus->track_queue, track);
track->in_queue = true;
}
@@ -79,6 +122,24 @@ static void bus_track_remove_from_queue(sd_bus_track *track) {
track->in_queue = false;
}
+static int bus_track_remove_name_fully(sd_bus_track *track, const char *name) {
+ struct track_item *i;
+
+ assert(track);
+ assert(name);
+
+ i = hashmap_remove(track->names, name);
+ if (!i)
+ return 0;
+
+ track_item_free(i);
+
+ bus_track_add_to_queue(track);
+
+ track->modified = true;
+ return 1;
+}
+
_public_ int sd_bus_track_new(
sd_bus *bus,
sd_bus_track **track,
@@ -102,6 +163,9 @@ _public_ int sd_bus_track_new(
t->userdata = userdata;
t->bus = sd_bus_ref(bus);
+ LIST_PREPEND(tracks, bus->tracks, t);
+ t->in_list = true;
+
bus_track_add_to_queue(t);
*track = t;
@@ -121,7 +185,7 @@ _public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) {
}
_public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
- const char *n;
+ struct track_item *i;
if (!track)
return NULL;
@@ -133,8 +197,11 @@ _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
return NULL;
}
- while ((n = hashmap_first_key(track->names)))
- sd_bus_track_remove_name(track, n);
+ while ((i = hashmap_steal_first(track->names)))
+ track_item_free(i);
+
+ if (track->in_list)
+ LIST_REMOVE(tracks, track->bus->tracks, track);
bus_track_remove_from_queue(track);
hashmap_free(track->names);
@@ -156,49 +223,76 @@ static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus
if (r < 0)
return 0;
- sd_bus_track_remove_name(track, name);
+ bus_track_remove_name_fully(track, name);
return 0;
}
_public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
- _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
- _cleanup_free_ char *n = NULL;
+ _cleanup_(track_item_freep) struct track_item *n = NULL;
+ struct track_item *i;
const char *match;
int r;
assert_return(track, -EINVAL);
assert_return(service_name_is_valid(name), -EINVAL);
+ i = hashmap_get(track->names, name);
+ if (i) {
+ if (track->recursive) {
+ unsigned k = track->n_ref + 1;
+
+ if (k < track->n_ref) /* Check for overflow */
+ return -EOVERFLOW;
+
+ track->n_ref = k;
+ }
+
+ bus_track_remove_from_queue(track);
+ return 0;
+ }
+
r = hashmap_ensure_allocated(&track->names, &string_hash_ops);
if (r < 0)
return r;
- n = strdup(name);
+ n = new0(struct track_item, 1);
if (!n)
return -ENOMEM;
+ n->name = strdup(name);
+ if (!n->name)
+ return -ENOMEM;
/* First, subscribe to this name */
- match = MATCH_FOR_NAME(n);
- r = sd_bus_add_match(track->bus, &slot, match, on_name_owner_changed, track);
- if (r < 0)
+ match = MATCH_FOR_NAME(name);
+
+ bus_track_remove_from_queue(track); /* don't dispatch this while we work in it */
+
+ track->n_adding++; /* make sure we aren't dispatched while we synchronously add this match */
+ r = sd_bus_add_match(track->bus, &n->slot, match, on_name_owner_changed, track);
+ track->n_adding--;
+ if (r < 0) {
+ bus_track_add_to_queue(track);
return r;
+ }
- r = hashmap_put(track->names, n, slot);
- if (r == -EEXIST)
- return 0;
- if (r < 0)
+ r = hashmap_put(track->names, n->name, n);
+ if (r < 0) {
+ bus_track_add_to_queue(track);
return r;
+ }
- /* Second, check if it is currently existing, or maybe
- * doesn't, or maybe disappeared already. */
- r = sd_bus_get_name_creds(track->bus, n, 0, NULL);
+ /* Second, check if it is currently existing, or maybe doesn't, or maybe disappeared already. */
+ track->n_adding++; /* again, make sure this isn't dispatch while we are working in it */
+ r = sd_bus_get_name_creds(track->bus, name, 0, NULL);
+ track->n_adding--;
if (r < 0) {
- hashmap_remove(track->names, n);
+ hashmap_remove(track->names, name);
+ bus_track_add_to_queue(track);
return r;
}
+ n->n_ref = 1;
n = NULL;
- slot = NULL;
bus_track_remove_from_queue(track);
track->modified = true;
@@ -207,37 +301,48 @@ _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
}
_public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
- _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
- _cleanup_free_ char *n = NULL;
+ struct track_item *i;
assert_return(name, -EINVAL);
- if (!track)
+ if (!track) /* Treat a NULL track object as an empty track object */
return 0;
- slot = hashmap_remove2(track->names, (char*) name, (void**) &n);
- if (!slot)
- return 0;
+ if (!track->recursive)
+ return bus_track_remove_name_fully(track, name);
- if (hashmap_isempty(track->names))
- bus_track_add_to_queue(track);
+ i = hashmap_get(track->names, name);
+ if (!i)
+ return -EUNATCH;
+ if (i->n_ref <= 0)
+ return -EUNATCH;
- track->modified = true;
+ i->n_ref--;
+
+ if (i->n_ref <= 0)
+ return bus_track_remove_name_fully(track, name);
return 1;
}
_public_ unsigned sd_bus_track_count(sd_bus_track *track) {
- if (!track)
+
+ if (!track) /* Let's consider a NULL object equivalent to an empty object */
return 0;
+ /* This signature really should have returned an int, so that we can propagate errors. But well, ... Also, note
+ * that this returns the number of names being watched, and multiple references to the same name are not
+ * counted. */
+
return hashmap_size(track->names);
}
_public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
- assert_return(track, NULL);
assert_return(name, NULL);
+ if (!track) /* Let's consider a NULL object equivalent to an empty object */
+ return NULL;
+
return hashmap_get(track->names, (void*) name) ? name : NULL;
}
@@ -273,6 +378,9 @@ _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
assert_return(track, -EINVAL);
assert_return(m, -EINVAL);
+ if (sd_bus_message_get_bus(m) != track->bus)
+ return -EINVAL;
+
sender = sd_bus_message_get_sender(m);
if (!sender)
return -EINVAL;
@@ -283,9 +391,14 @@ _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
_public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
const char *sender;
- assert_return(track, -EINVAL);
assert_return(m, -EINVAL);
+ if (!track) /* Treat a NULL track object as an empty track object */
+ return 0;
+
+ if (sd_bus_message_get_bus(m) != track->bus)
+ return -EINVAL;
+
sender = sd_bus_message_get_sender(m);
if (!sender)
return -EINVAL;
@@ -303,7 +416,6 @@ void bus_track_dispatch(sd_bus_track *track) {
int r;
assert(track);
- assert(track->in_queue);
assert(track->handler);
bus_track_remove_from_queue(track);
@@ -319,6 +431,34 @@ void bus_track_dispatch(sd_bus_track *track) {
sd_bus_track_unref(track);
}
+void bus_track_close(sd_bus_track *track) {
+ struct track_item *i;
+
+ assert(track);
+
+ /* Called whenever our bus connected is closed. If so, and our track object is non-empty, dispatch it
+ * immediately, as we are closing now, but first flush out all names. */
+
+ if (!track->in_list)
+ return; /* We already closed this one, don't close it again. */
+
+ /* Remember that this one is closed now */
+ LIST_REMOVE(tracks, track->bus->tracks, track);
+ track->in_list = false;
+
+ /* If there's no name in this one anyway, we don't have to dispatch */
+ if (hashmap_isempty(track->names))
+ return;
+
+ /* Let's flush out all names */
+ while ((i = hashmap_steal_first(track->names)))
+ track_item_free(i);
+
+ /* Invoke handler */
+ if (track->handler)
+ bus_track_dispatch(track);
+}
+
_public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
assert_return(track, NULL);
@@ -335,3 +475,55 @@ _public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) {
return ret;
}
+
+_public_ int sd_bus_track_set_recursive(sd_bus_track *track, int b) {
+ assert_return(track, -EINVAL);
+
+ if (track->recursive == !!b)
+ return 0;
+
+ if (!hashmap_isempty(track->names))
+ return -EBUSY;
+
+ track->recursive = b;
+ return 0;
+}
+
+_public_ int sd_bus_track_get_recursive(sd_bus_track *track) {
+ assert_return(track, -EINVAL);
+
+ return track->recursive;
+}
+
+_public_ int sd_bus_track_count_sender(sd_bus_track *track, sd_bus_message *m) {
+ const char *sender;
+
+ assert_return(m, -EINVAL);
+
+ if (!track) /* Let's consider a NULL object equivalent to an empty object */
+ return 0;
+
+ if (sd_bus_message_get_bus(m) != track->bus)
+ return -EINVAL;
+
+ sender = sd_bus_message_get_sender(m);
+ if (!sender)
+ return -EINVAL;
+
+ return sd_bus_track_count_name(track, sender);
+}
+
+_public_ int sd_bus_track_count_name(sd_bus_track *track, const char *name) {
+ struct track_item *i;
+
+ assert_return(service_name_is_valid(name), -EINVAL);
+
+ if (!track) /* Let's consider a NULL object equivalent to an empty object */
+ return 0;
+
+ i = hashmap_get(track->names, name);
+ if (!i)
+ return 0;
+
+ return i->n_ref;
+}
diff --git a/src/libsystemd/sd-bus/bus-track.h b/src/libsystemd/sd-bus/bus-track.h
index 7d93a727d6..26bd05f5c7 100644
--- a/src/libsystemd/sd-bus/bus-track.h
+++ b/src/libsystemd/sd-bus/bus-track.h
@@ -20,3 +20,4 @@
***/
void bus_track_dispatch(sd_bus_track *track);
+void bus_track_close(sd_bus_track *track);
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index ed5f94e136..d746348544 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -107,6 +107,7 @@ static void bus_free(sd_bus *b) {
assert(b);
assert(!b->track_queue);
+ assert(!b->tracks);
b->state = BUS_CLOSED;
@@ -2640,62 +2641,101 @@ null_message:
return r;
}
-static int process_closing(sd_bus *bus, sd_bus_message **ret) {
+static int bus_exit_now(sd_bus *bus) {
+ assert(bus);
+
+ /* Exit due to close, if this is requested. If this is bus object is attached to an event source, invokes
+ * sd_event_exit(), otherwise invokes libc exit(). */
+
+ if (bus->exited) /* did we already exit? */
+ return 0;
+ if (!bus->exit_triggered) /* was the exit condition triggered? */
+ return 0;
+ if (!bus->exit_on_disconnect) /* Shall we actually exit on disconnection? */
+ return 0;
+
+ bus->exited = true; /* never exit more than once */
+
+ log_debug("Bus connection disconnected, exiting.");
+
+ if (bus->event)
+ return sd_event_exit(bus->event, EXIT_FAILURE);
+ else
+ exit(EXIT_FAILURE);
+
+ assert_not_reached("exit() didn't exit?");
+}
+
+static int process_closing_reply_callback(sd_bus *bus, struct reply_callback *c) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- struct reply_callback *c;
+ sd_bus_slot *slot;
int r;
assert(bus);
- assert(bus->state == BUS_CLOSING);
+ assert(c);
- c = ordered_hashmap_first(bus->reply_callbacks);
- if (c) {
- _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
- sd_bus_slot *slot;
+ r = bus_message_new_synthetic_error(
+ bus,
+ c->cookie,
+ &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Connection terminated"),
+ &m);
+ if (r < 0)
+ return r;
- /* First, fail all outstanding method calls */
- r = bus_message_new_synthetic_error(
- bus,
- c->cookie,
- &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Connection terminated"),
- &m);
- if (r < 0)
- return r;
+ r = bus_seal_synthetic_message(bus, m);
+ if (r < 0)
+ return r;
- r = bus_seal_synthetic_message(bus, m);
- if (r < 0)
- return r;
+ if (c->timeout != 0) {
+ prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
+ c->timeout = 0;
+ }
- if (c->timeout != 0) {
- prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
- c->timeout = 0;
- }
+ ordered_hashmap_remove(bus->reply_callbacks, &c->cookie);
+ c->cookie = 0;
- ordered_hashmap_remove(bus->reply_callbacks, &c->cookie);
- c->cookie = 0;
+ slot = container_of(c, sd_bus_slot, reply_callback);
- slot = container_of(c, sd_bus_slot, reply_callback);
+ bus->iteration_counter++;
- bus->iteration_counter++;
+ bus->current_message = m;
+ bus->current_slot = sd_bus_slot_ref(slot);
+ bus->current_handler = c->callback;
+ bus->current_userdata = slot->userdata;
+ r = c->callback(m, slot->userdata, &error_buffer);
+ bus->current_userdata = NULL;
+ bus->current_handler = NULL;
+ bus->current_slot = NULL;
+ bus->current_message = NULL;
- bus->current_message = m;
- bus->current_slot = sd_bus_slot_ref(slot);
- bus->current_handler = c->callback;
- bus->current_userdata = slot->userdata;
- r = c->callback(m, slot->userdata, &error_buffer);
- bus->current_userdata = NULL;
- bus->current_handler = NULL;
- bus->current_slot = NULL;
- bus->current_message = NULL;
+ if (slot->floating) {
+ bus_slot_disconnect(slot);
+ sd_bus_slot_unref(slot);
+ }
- if (slot->floating) {
- bus_slot_disconnect(slot);
- sd_bus_slot_unref(slot);
- }
+ sd_bus_slot_unref(slot);
- sd_bus_slot_unref(slot);
+ return bus_maybe_reply_error(m, r, &error_buffer);
+}
- return bus_maybe_reply_error(m, r, &error_buffer);
+static int process_closing(sd_bus *bus, sd_bus_message **ret) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ struct reply_callback *c;
+ int r;
+
+ assert(bus);
+ assert(bus->state == BUS_CLOSING);
+
+ /* First, fail all outstanding method calls */
+ c = ordered_hashmap_first(bus->reply_callbacks);
+ if (c)
+ return process_closing_reply_callback(bus, c);
+
+ /* Then, fake-drop all remaining bus tracking references */
+ if (bus->tracks) {
+ bus_track_close(bus->tracks);
+ return 1;
}
/* Then, synthesize a Disconnected message */
@@ -2727,6 +2767,10 @@ static int process_closing(sd_bus *bus, sd_bus_message **ret) {
if (r != 0)
goto finish;
+ /* Nothing else to do, exit now, if the condition holds */
+ bus->exit_triggered = true;
+ (void) bus_exit_now(bus);
+
if (ret) {
*ret = m;
m = NULL;
@@ -3789,3 +3833,21 @@ _public_ void sd_bus_default_flush_close(void) {
flush_close(default_user_bus);
flush_close(default_system_bus);
}
+
+_public_ int sd_bus_set_exit_on_disconnect(sd_bus *bus, int b) {
+ assert_return(bus, -EINVAL);
+
+ /* Turns on exit-on-disconnect, and triggers it immediately if the bus connection was already
+ * disconnected. Note that this is triggered exclusively on disconnections triggered by the server side, never
+ * from the client side. */
+ bus->exit_on_disconnect = b;
+
+ /* If the exit condition was triggered already, exit immediately. */
+ return bus_exit_now(bus);
+}
+
+_public_ int sd_bus_get_exit_on_disconnect(sd_bus *bus) {
+ assert_return(bus, -EINVAL);
+
+ return bus->exit_on_disconnect;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-track.c b/src/libsystemd/sd-bus/test-bus-track.c
new file mode 100644
index 0000000000..4beb61f05a
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-track.c
@@ -0,0 +1,113 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 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 "macro.h"
+
+static bool track_cb_called_x = false;
+static bool track_cb_called_y = false;
+
+static int track_cb_x(sd_bus_track *t, void *userdata) {
+
+ log_error("TRACK CB X");
+
+ assert_se(!track_cb_called_x);
+ track_cb_called_x = true;
+
+ /* This means b's name disappeared. Let's now disconnect, to make sure the track handling on disconnect works
+ * as it should. */
+
+ assert_se(shutdown(sd_bus_get_fd(sd_bus_track_get_bus(t)), SHUT_RDWR) >= 0);
+ return 1;
+}
+
+static int track_cb_y(sd_bus_track *t, void *userdata) {
+ int r;
+
+ log_error("TRACK CB Y");
+
+ assert_se(!track_cb_called_y);
+ track_cb_called_y = true;
+
+ /* We got disconnected, let's close everything */
+
+ r = sd_event_exit(sd_bus_get_event(sd_bus_track_get_bus(t)), EXIT_SUCCESS);
+ assert_se(r >= 0);
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ _cleanup_(sd_bus_track_unrefp) sd_bus_track *x = NULL, *y = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *a = NULL, *b = NULL;
+ const char *unique;
+ int r;
+
+ r = sd_event_default(&event);
+ assert_se(r >= 0);
+
+ r = sd_bus_open_system(&a);
+ if (IN_SET(r, -ECONNREFUSED, -ENOENT)) {
+ log_info("Failed to connect to bus, skipping tests.");
+ return EXIT_TEST_SKIP;
+ }
+ assert_se(r >= 0);
+
+ r = sd_bus_attach_event(a, event, SD_EVENT_PRIORITY_NORMAL);
+ assert_se(r >= 0);
+
+ r = sd_bus_open_system(&b);
+ assert_se(r >= 0);
+
+ r = sd_bus_attach_event(b, event, SD_EVENT_PRIORITY_NORMAL);
+ assert_se(r >= 0);
+
+ /* Watch b's name from a */
+ r = sd_bus_track_new(a, &x, track_cb_x, NULL);
+ assert_se(r >= 0);
+
+ r = sd_bus_get_unique_name(b, &unique);
+ assert_se(r >= 0);
+
+ r = sd_bus_track_add_name(x, unique);
+ assert_se(r >= 0);
+
+ /* Watch's a's own name from a */
+ r = sd_bus_track_new(a, &y, track_cb_y, NULL);
+ assert_se(r >= 0);
+
+ r = sd_bus_get_unique_name(a, &unique);
+ assert_se(r >= 0);
+
+ r = sd_bus_track_add_name(y, unique);
+ assert_se(r >= 0);
+
+ /* Now make b's name disappear */
+ sd_bus_close(b);
+
+ r = sd_event_loop(event);
+ assert_se(r >= 0);
+
+ assert_se(track_cb_called_x);
+ assert_se(track_cb_called_y);
+
+ return 0;
+}
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 24d243109a..6d0420965a 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -169,7 +169,6 @@ static CustomMount *arg_custom_mounts = NULL;
static unsigned arg_n_custom_mounts = 0;
static char **arg_setenv = NULL;
static bool arg_quiet = false;
-static bool arg_share_system = false;
static bool arg_register = true;
static bool arg_keep_unit = false;
static char **arg_network_interfaces = NULL;
@@ -195,6 +194,7 @@ static char **arg_parameters = NULL;
static const char *arg_container_service_name = "systemd-nspawn";
static bool arg_notify_ready = false;
static bool arg_use_cgns = true;
+static unsigned long arg_clone_ns_flags = CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS;
static void help(void) {
printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
@@ -352,6 +352,17 @@ static int detect_unified_cgroup_hierarchy(void) {
return 0;
}
+static void parse_share_ns_env(const char *name, unsigned long ns_flag) {
+ int r;
+
+ r = getenv_bool(name);
+ if (r == -ENXIO)
+ return;
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse %s from environment, defaulting to false.", name);
+ arg_clone_ns_flags = (arg_clone_ns_flags & ~ns_flag) | (r > 0 ? 0 : ns_flag);
+}
+
static int parse_argv(int argc, char *argv[]) {
enum {
@@ -824,8 +835,8 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_SHARE_SYSTEM:
/* We don't officially support this anymore, except for compat reasons. People should use the
- * $SYSTEMD_NSPAWN_SHARE_SYSTEM environment variable instead. */
- arg_share_system = true;
+ * $SYSTEMD_NSPAWN_SHARE_* environment variables instead. */
+ arg_clone_ns_flags = 0;
break;
case ARG_REGISTER:
@@ -1029,20 +1040,22 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (getenv_bool("SYSTEMD_NSPAWN_SHARE_SYSTEM") > 0)
- arg_share_system = true;
+ parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_IPC", CLONE_NEWIPC);
+ parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_PID", CLONE_NEWPID);
+ parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_UTS", CLONE_NEWUTS);
+ parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_SYSTEM", CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS);
- if (arg_share_system)
+ if (arg_clone_ns_flags != (CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS)) {
arg_register = false;
+ if (arg_start_mode != START_PID1) {
+ log_error("--boot cannot be used without namespacing.");
+ return -EINVAL;
+ }
+ }
if (arg_userns_mode == USER_NAMESPACE_PICK)
arg_userns_chown = true;
- if (arg_start_mode != START_PID1 && arg_share_system) {
- log_error("--boot and SYSTEMD_NSPAWN_SHARE_SYSTEM=1 may not be combined.");
- return -EINVAL;
- }
-
if (arg_keep_unit && cg_pid_get_owner_uid(0, NULL) >= 0) {
log_error("--keep-unit may not be used when invoked from a user session.");
return -EINVAL;
@@ -1310,9 +1323,6 @@ static int setup_boot_id(const char *dest) {
const char *from, *to;
int r;
- if (arg_share_system)
- return 0;
-
/* Generate a new randomized boot ID, so that each boot-up of
* the container gets a new one */
@@ -1530,7 +1540,7 @@ static int on_address_change(sd_netlink *rtnl, sd_netlink_message *m, void *user
static int setup_hostname(void) {
- if (arg_share_system)
+ if ((arg_clone_ns_flags & CLONE_NEWUTS) == 0)
return 0;
if (sethostname_idempotent(arg_machine) < 0)
@@ -1681,7 +1691,7 @@ static int reset_audit_loginuid(void) {
_cleanup_free_ char *p = NULL;
int r;
- if (arg_share_system)
+ if ((arg_clone_ns_flags & CLONE_NEWPID) == 0)
return 0;
r = read_one_line_file("/proc/self/loginuid", &p);
@@ -3088,7 +3098,7 @@ static int outer_child(
return fd;
pid = raw_clone(SIGCHLD|CLONE_NEWNS|
- (arg_share_system ? 0 : CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS) |
+ arg_clone_ns_flags |
(arg_private_network ? CLONE_NEWNET : 0) |
(arg_userns_mode != USER_NAMESPACE_NO ? CLONE_NEWUSER : 0));
if (pid < 0)
diff --git a/src/run/run.c b/src/run/run.c
index e8586ef61a..2dd229868c 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -33,6 +33,7 @@
#include "formats-util.h"
#include "parse-util.h"
#include "path-util.h"
+#include "process-util.h"
#include "ptyfwd.h"
#include "signal-util.h"
#include "spawn-polkit-agent.h"
@@ -45,6 +46,7 @@ static bool arg_ask_password = true;
static bool arg_scope = false;
static bool arg_remain_after_exit = false;
static bool arg_no_block = false;
+static bool arg_wait = false;
static const char *arg_unit = NULL;
static const char *arg_description = NULL;
static const char *arg_slice = NULL;
@@ -97,6 +99,7 @@ static void help(void) {
" --slice=SLICE Run in the specified slice\n"
" --no-block Do not wait until operation finished\n"
" -r --remain-after-exit Leave service around until explicitly stopped\n"
+ " --wait Wait until service stopped again\n"
" --send-sighup Send SIGHUP when terminating\n"
" --service-type=TYPE Service type\n"
" --uid=USER Run as system user\n"
@@ -144,6 +147,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_TIMER_PROPERTY,
ARG_NO_BLOCK,
ARG_NO_ASK_PASSWORD,
+ ARG_WAIT,
};
static const struct option options[] = {
@@ -160,6 +164,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "host", required_argument, NULL, 'H' },
{ "machine", required_argument, NULL, 'M' },
{ "service-type", required_argument, NULL, ARG_SERVICE_TYPE },
+ { "wait", no_argument, NULL, ARG_WAIT },
{ "uid", required_argument, NULL, ARG_EXEC_USER },
{ "gid", required_argument, NULL, ARG_EXEC_GROUP },
{ "nice", required_argument, NULL, ARG_NICE },
@@ -357,6 +362,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_no_block = true;
break;
+ case ARG_WAIT:
+ arg_wait = true;
+ break;
+
case '?':
return -EINVAL;
@@ -404,6 +413,23 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
+ if (arg_wait) {
+ if (arg_no_block) {
+ log_error("--wait may not be combined with --no-block.");
+ return -EINVAL;
+ }
+
+ if (with_timer()) {
+ log_error("--wait may not be combined with timer operations.");
+ return -EINVAL;
+ }
+
+ if (arg_scope) {
+ log_error("--wait may not be combined with --scope.");
+ return -EINVAL;
+ }
+ }
+
return 1;
}
@@ -466,6 +492,12 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv, cons
if (r < 0)
return r;
+ if (arg_wait) {
+ r = sd_bus_message_append(m, "(sv)", "AddRef", "b", 1);
+ if (r < 0)
+ return r;
+ }
+
if (arg_remain_after_exit) {
r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
if (r < 0)
@@ -723,9 +755,97 @@ static int make_unit_name(sd_bus *bus, UnitType t, char **ret) {
return 0;
}
+typedef struct RunContext {
+ sd_bus *bus;
+ sd_event *event;
+ PTYForward *forward;
+ sd_bus_slot *match;
+
+ /* The exit data of the unit */
+ char *active_state;
+ uint64_t inactive_exit_usec;
+ uint64_t inactive_enter_usec;
+ char *result;
+ uint64_t cpu_usage_nsec;
+ uint32_t exit_code;
+ uint32_t exit_status;
+} RunContext;
+
+static void run_context_free(RunContext *c) {
+ assert(c);
+
+ c->forward = pty_forward_free(c->forward);
+ c->match = sd_bus_slot_unref(c->match);
+ c->bus = sd_bus_unref(c->bus);
+ c->event = sd_event_unref(c->event);
+
+ free(c->active_state);
+ free(c->result);
+}
+
+static void run_context_check_done(RunContext *c) {
+ bool done = true;
+
+ assert(c);
+
+ if (c->match)
+ done = done && (c->active_state && STR_IN_SET(c->active_state, "inactive", "failed"));
+
+ if (c->forward)
+ done = done && pty_forward_is_done(c->forward);
+
+ if (done)
+ sd_event_exit(c->event, EXIT_SUCCESS);
+}
+
+static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+
+ static const struct bus_properties_map map[] = {
+ { "ActiveState", "s", NULL, offsetof(RunContext, active_state) },
+ { "InactiveExitTimestampMonotonic", "t", NULL, offsetof(RunContext, inactive_exit_usec) },
+ { "InactiveEnterTimestampMonotonic", "t", NULL, offsetof(RunContext, inactive_enter_usec) },
+ { "Result", "s", NULL, offsetof(RunContext, result) },
+ { "ExecMainCode", "i", NULL, offsetof(RunContext, exit_code) },
+ { "ExecMainStatus", "i", NULL, offsetof(RunContext, exit_status) },
+ { "CPUUsageNSec", "t", NULL, offsetof(RunContext, cpu_usage_nsec) },
+ {}
+ };
+
+ RunContext *c = userdata;
+ int r;
+
+ r = bus_map_all_properties(c->bus,
+ "org.freedesktop.systemd1",
+ sd_bus_message_get_path(m),
+ map,
+ c);
+ if (r < 0) {
+ sd_event_exit(c->event, EXIT_FAILURE);
+ return log_error_errno(r, "Failed to query unit state: %m");
+ }
+
+ run_context_check_done(c);
+ return 0;
+}
+
+static int pty_forward_handler(PTYForward *f, int rcode, void *userdata) {
+ RunContext *c = userdata;
+
+ assert(f);
+
+ if (rcode < 0) {
+ sd_event_exit(c->event, EXIT_FAILURE);
+ return log_error_errno(rcode, "Error on PTY forwarding logic: %m");
+ }
+
+ run_context_check_done(c);
+ return 0;
+}
+
static int start_transient_service(
sd_bus *bus,
- char **argv) {
+ char **argv,
+ int *retval) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -736,6 +856,7 @@ static int start_transient_service(
assert(bus);
assert(argv);
+ assert(retval);
if (arg_pty) {
@@ -859,40 +980,95 @@ static int start_transient_service(
return r;
}
- if (master >= 0) {
- _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
- _cleanup_(sd_event_unrefp) sd_event *event = NULL;
- char last_char = 0;
+ if (!arg_quiet)
+ log_info("Running as unit: %s", service);
+
+ if (arg_wait || master >= 0) {
+ _cleanup_(run_context_free) RunContext c = {};
- r = sd_event_default(&event);
+ c.bus = sd_bus_ref(bus);
+
+ r = sd_event_default(&c.event);
if (r < 0)
return log_error_errno(r, "Failed to get event loop: %m");
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
+ if (master >= 0) {
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
+ (void) sd_event_add_signal(c.event, NULL, SIGINT, NULL, NULL);
+ (void) sd_event_add_signal(c.event, NULL, SIGTERM, NULL, NULL);
+
+ if (!arg_quiet)
+ log_info("Press ^] three times within 1s to disconnect TTY.");
- (void) sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
- (void) sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
+ r = pty_forward_new(c.event, master, PTY_FORWARD_IGNORE_INITIAL_VHANGUP, &c.forward);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create PTY forwarder: %m");
+
+ pty_forward_set_handler(c.forward, pty_forward_handler, &c);
+ }
- if (!arg_quiet)
- log_info("Running as unit: %s\nPress ^] three times within 1s to disconnect TTY.", service);
+ if (arg_wait) {
+ _cleanup_free_ char *path = NULL;
+ const char *mt;
- r = pty_forward_new(event, master, PTY_FORWARD_IGNORE_INITIAL_VHANGUP, &forward);
- if (r < 0)
- return log_error_errno(r, "Failed to create PTY forwarder: %m");
+ path = unit_dbus_path_from_name(service);
+ if (!path)
+ return log_oom();
+
+ mt = strjoina("type='signal',"
+ "sender='org.freedesktop.systemd1',"
+ "path='", path, "',"
+ "interface='org.freedesktop.DBus.Properties',"
+ "member='PropertiesChanged'");
+ r = sd_bus_add_match(bus, &c.match, mt, on_properties_changed, &c);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add properties changed signal.");
- r = sd_event_loop(event);
+ r = sd_bus_attach_event(bus, c.event, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach bus to event loop.");
+ }
+
+ r = sd_event_loop(c.event);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
- pty_forward_get_last_char(forward, &last_char);
+ if (c.forward) {
+ char last_char = 0;
- forward = pty_forward_free(forward);
+ r = pty_forward_get_last_char(c.forward, &last_char);
+ if (r >= 0 && !arg_quiet && last_char != '\n')
+ fputc('\n', stdout);
+ }
- if (!arg_quiet && last_char != '\n')
- fputc('\n', stdout);
+ if (!arg_quiet) {
+ if (!isempty(c.result))
+ log_info("Finished with result: %s", strna(c.result));
- } else if (!arg_quiet)
- log_info("Running as unit: %s", service);
+ if (c.exit_code == CLD_EXITED)
+ log_info("Main processes terminated with: code=%s/status=%i", sigchld_code_to_string(c.exit_code), c.exit_status);
+ else if (c.exit_code > 0)
+ log_info("Main processes terminated with: code=%s/status=%s", sigchld_code_to_string(c.exit_code), signal_to_string(c.exit_status));
+
+ if (c.inactive_enter_usec > 0 && c.inactive_enter_usec != USEC_INFINITY &&
+ c.inactive_exit_usec > 0 && c.inactive_exit_usec != USEC_INFINITY &&
+ c.inactive_enter_usec > c.inactive_exit_usec) {
+ char ts[FORMAT_TIMESPAN_MAX];
+ log_info("Service runtime: %s", format_timespan(ts, sizeof(ts), c.inactive_enter_usec - c.inactive_exit_usec, USEC_PER_MSEC));
+ }
+
+ if (c.cpu_usage_nsec > 0 && c.cpu_usage_nsec != NSEC_INFINITY) {
+ char ts[FORMAT_TIMESPAN_MAX];
+ log_info("CPU time consumed: %s", format_timespan(ts, sizeof(ts), (c.cpu_usage_nsec + NSEC_PER_USEC - 1) / NSEC_PER_USEC, USEC_PER_MSEC));
+ }
+ }
+
+ /* Try to propagate the service's return value */
+ if (c.result && STR_IN_SET(c.result, "success", "exit-code") && c.exit_code == CLD_EXITED)
+ *retval = c.exit_status;
+ else
+ *retval = EXIT_FAILURE;
+ }
return 0;
}
@@ -1197,7 +1373,7 @@ static int start_transient_timer(
int main(int argc, char* argv[]) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_free_ char *description = NULL, *command = NULL;
- int r;
+ int r, retval = EXIT_SUCCESS;
log_parse_environment();
log_open();
@@ -1234,7 +1410,12 @@ int main(int argc, char* argv[]) {
arg_description = description;
}
- r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
+ /* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the limited direct
+ * connection */
+ if (arg_wait)
+ r = bus_connect_transport(arg_transport, arg_host, arg_user, &bus);
+ else
+ r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
if (r < 0) {
log_error_errno(r, "Failed to create bus connection: %m");
goto finish;
@@ -1245,12 +1426,12 @@ int main(int argc, char* argv[]) {
else if (with_timer())
r = start_transient_timer(bus, argv + optind);
else
- r = start_transient_service(bus, argv + optind);
+ r = start_transient_service(bus, argv + optind, &retval);
finish:
strv_free(arg_environment);
strv_free(arg_property);
strv_free(arg_timer_property);
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r < 0 ? EXIT_FAILURE : retval;
}
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 52410999cf..e2a216a5cc 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -1016,19 +1016,19 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_
return r;
switch (type) {
+
case SD_BUS_TYPE_STRING: {
- const char *s;
char **p = userdata;
+ const char *s;
r = sd_bus_message_read_basic(m, type, &s);
if (r < 0)
- break;
+ return r;
if (isempty(s))
- break;
+ s = NULL;
- r = free_and_strdup(p, s);
- break;
+ return free_and_strdup(p, s);
}
case SD_BUS_TYPE_ARRAY: {
@@ -1037,13 +1037,12 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_
r = bus_message_read_strv_extend(m, &l);
if (r < 0)
- break;
+ return r;
strv_free(*p);
*p = l;
l = NULL;
-
- break;
+ return 0;
}
case SD_BUS_TYPE_BOOLEAN: {
@@ -1052,57 +1051,48 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_
r = sd_bus_message_read_basic(m, type, &b);
if (r < 0)
- break;
+ return r;
*p = b;
-
- break;
+ return 0;
}
+ case SD_BUS_TYPE_INT32:
case SD_BUS_TYPE_UINT32: {
- uint32_t u;
- uint32_t *p = userdata;
+ uint32_t u, *p = userdata;
r = sd_bus_message_read_basic(m, type, &u);
if (r < 0)
- break;
+ return r;
*p = u;
-
- break;
+ return 0;
}
+ case SD_BUS_TYPE_INT64:
case SD_BUS_TYPE_UINT64: {
- uint64_t t;
- uint64_t *p = userdata;
+ uint64_t t, *p = userdata;
r = sd_bus_message_read_basic(m, type, &t);
if (r < 0)
- break;
+ return r;
*p = t;
-
- break;
+ return 0;
}
case SD_BUS_TYPE_DOUBLE: {
- double d;
- double *p = userdata;
+ double d, *p = userdata;
r = sd_bus_message_read_basic(m, type, &d);
if (r < 0)
- break;
+ return r;
*p = d;
+ return 0;
+ }}
- break;
- }
-
- default:
- break;
- }
-
- return r;
+ return -EOPNOTSUPP;
}
int bus_message_map_all_properties(
@@ -1240,12 +1230,13 @@ int bus_map_all_properties(
return bus_message_map_all_properties(m, map, userdata);
}
-int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
+int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **ret) {
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
int r;
assert(transport >= 0);
assert(transport < _BUS_TRANSPORT_MAX);
- assert(bus);
+ assert(ret);
assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP);
@@ -1254,25 +1245,34 @@ int bus_connect_transport(BusTransport transport, const char *host, bool user, s
case BUS_TRANSPORT_LOCAL:
if (user)
- r = sd_bus_default_user(bus);
+ r = sd_bus_default_user(&bus);
else
- r = sd_bus_default_system(bus);
+ r = sd_bus_default_system(&bus);
break;
case BUS_TRANSPORT_REMOTE:
- r = sd_bus_open_system_remote(bus, host);
+ r = sd_bus_open_system_remote(&bus, host);
break;
case BUS_TRANSPORT_MACHINE:
- r = sd_bus_open_system_machine(bus, host);
+ r = sd_bus_open_system_machine(&bus, host);
break;
default:
assert_not_reached("Hmm, unknown transport type.");
}
+ if (r < 0)
+ return r;
- return r;
+ r = sd_bus_set_exit_on_disconnect(bus, true);
+ if (r < 0)
+ return r;
+
+ *ret = bus;
+ bus = NULL;
+
+ return 0;
}
int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) {
diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c
index 02c03b98d8..24055e772b 100644
--- a/src/shared/ptyfwd.c
+++ b/src/shared/ptyfwd.c
@@ -68,6 +68,8 @@ struct PTYForward {
bool read_from_master:1;
+ bool done:1;
+
bool last_char_set:1;
char last_char;
@@ -76,10 +78,54 @@ struct PTYForward {
usec_t escape_timestamp;
unsigned escape_counter;
+
+ PTYForwardHandler handler;
+ void *userdata;
};
#define ESCAPE_USEC (1*USEC_PER_SEC)
+static void pty_forward_disconnect(PTYForward *f) {
+
+ if (f) {
+ f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
+ f->stdout_event_source = sd_event_source_unref(f->stdout_event_source);
+
+ f->master_event_source = sd_event_source_unref(f->master_event_source);
+ f->sigwinch_event_source = sd_event_source_unref(f->sigwinch_event_source);
+ f->event = sd_event_unref(f->event);
+
+ if (f->saved_stdout)
+ tcsetattr(STDOUT_FILENO, TCSANOW, &f->saved_stdout_attr);
+ if (f->saved_stdin)
+ tcsetattr(STDIN_FILENO, TCSANOW, &f->saved_stdin_attr);
+
+ f->saved_stdout = f->saved_stdin = false;
+ }
+
+ /* STDIN/STDOUT should not be nonblocking normally, so let's unconditionally reset it */
+ fd_nonblock(STDIN_FILENO, false);
+ fd_nonblock(STDOUT_FILENO, false);
+}
+
+static int pty_forward_done(PTYForward *f, int rcode) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ assert(f);
+
+ if (f->done)
+ return 0;
+
+ e = sd_event_ref(f->event);
+
+ f->done = true;
+ pty_forward_disconnect(f);
+
+ if (f->handler)
+ return f->handler(f, rcode, f->userdata);
+ else
+ return sd_event_exit(e, rcode < 0 ? EXIT_FAILURE : rcode);
+}
+
static bool look_for_escape(PTYForward *f, const char *buffer, size_t n) {
const char *p;
@@ -147,7 +193,7 @@ static int shovel(PTYForward *f) {
f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
} else {
log_error_errno(errno, "read(): %m");
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -errno);
}
} else if (k == 0) {
/* EOF on stdin */
@@ -156,12 +202,10 @@ static int shovel(PTYForward *f) {
f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
} else {
- /* Check if ^] has been
- * pressed three times within
- * one second. If we get this
- * we quite immediately. */
+ /* Check if ^] has been pressed three times within one second. If we get this we quite
+ * immediately. */
if (look_for_escape(f, f->in_buffer + f->in_buffer_full, k))
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -ECANCELED);
f->in_buffer_full += (size_t) k;
}
@@ -181,7 +225,7 @@ static int shovel(PTYForward *f) {
f->master_event_source = sd_event_source_unref(f->master_event_source);
} else {
log_error_errno(errno, "write(): %m");
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -errno);
}
} else {
assert(f->in_buffer_full >= (size_t) k);
@@ -211,7 +255,7 @@ static int shovel(PTYForward *f) {
f->master_event_source = sd_event_source_unref(f->master_event_source);
} else {
log_error_errno(errno, "read(): %m");
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -errno);
}
} else {
f->read_from_master = true;
@@ -232,7 +276,7 @@ static int shovel(PTYForward *f) {
f->stdout_event_source = sd_event_source_unref(f->stdout_event_source);
} else {
log_error_errno(errno, "write(): %m");
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -errno);
}
} else {
@@ -255,7 +299,7 @@ static int shovel(PTYForward *f) {
if ((f->out_buffer_full <= 0 || f->stdout_hangup) &&
(f->in_buffer_full <= 0 || f->master_hangup))
- return sd_event_exit(f->event, EXIT_SUCCESS);
+ return pty_forward_done(f, 0);
}
return 0;
@@ -418,27 +462,8 @@ int pty_forward_new(
}
PTYForward *pty_forward_free(PTYForward *f) {
-
- if (f) {
- sd_event_source_unref(f->stdin_event_source);
- sd_event_source_unref(f->stdout_event_source);
- sd_event_source_unref(f->master_event_source);
- sd_event_source_unref(f->sigwinch_event_source);
- sd_event_unref(f->event);
-
- if (f->saved_stdout)
- tcsetattr(STDOUT_FILENO, TCSANOW, &f->saved_stdout_attr);
- if (f->saved_stdin)
- tcsetattr(STDIN_FILENO, TCSANOW, &f->saved_stdin_attr);
-
- free(f);
- }
-
- /* STDIN/STDOUT should not be nonblocking normally, so let's
- * unconditionally reset it */
- fd_nonblock(STDIN_FILENO, false);
- fd_nonblock(STDOUT_FILENO, false);
-
+ pty_forward_disconnect(f);
+ free(f);
return NULL;
}
@@ -477,8 +502,21 @@ int pty_forward_set_ignore_vhangup(PTYForward *f, bool b) {
return 0;
}
-int pty_forward_get_ignore_vhangup(PTYForward *f) {
+bool pty_forward_get_ignore_vhangup(PTYForward *f) {
assert(f);
return !!(f->flags & PTY_FORWARD_IGNORE_VHANGUP);
}
+
+bool pty_forward_is_done(PTYForward *f) {
+ assert(f);
+
+ return f->done;
+}
+
+void pty_forward_set_handler(PTYForward *f, PTYForwardHandler cb, void *userdata) {
+ assert(f);
+
+ f->handler = cb;
+ f->userdata = userdata;
+}
diff --git a/src/shared/ptyfwd.h b/src/shared/ptyfwd.h
index a046eb4e5e..bd5d5fec0d 100644
--- a/src/shared/ptyfwd.h
+++ b/src/shared/ptyfwd.h
@@ -37,12 +37,18 @@ typedef enum PTYForwardFlags {
PTY_FORWARD_IGNORE_INITIAL_VHANGUP = 4,
} PTYForwardFlags;
+typedef int (*PTYForwardHandler)(PTYForward *f, int rcode, void*userdata);
+
int pty_forward_new(sd_event *event, int master, PTYForwardFlags flags, PTYForward **f);
PTYForward *pty_forward_free(PTYForward *f);
int pty_forward_get_last_char(PTYForward *f, char *ch);
int pty_forward_set_ignore_vhangup(PTYForward *f, bool ignore_vhangup);
-int pty_forward_get_ignore_vhangup(PTYForward *f);
+bool pty_forward_get_ignore_vhangup(PTYForward *f);
+
+bool pty_forward_is_done(PTYForward *f);
+
+void pty_forward_set_handler(PTYForward *f, PTYForwardHandler handler, void *userdata);
DEFINE_TRIVIAL_CLEANUP_FUNC(PTYForward*, pty_forward_free);
diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c
index 4667f508c7..6c489284d1 100644
--- a/src/shared/seccomp-util.c
+++ b/src/shared/seccomp-util.c
@@ -137,6 +137,7 @@ const SystemCallFilterSet syscall_filter_sets[] = {
"execve\0"
"exit\0"
"exit_group\0"
+ "getrlimit\0" /* make sure processes can query stack size and such */
"rt_sigreturn\0"
"sigreturn\0"
}, {
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 4b87bdceb2..682805045d 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -3571,6 +3571,7 @@ typedef struct UnitStatusInfo {
uint64_t memory_low;
uint64_t memory_high;
uint64_t memory_max;
+ uint64_t memory_swap_max;
uint64_t memory_limit;
uint64_t cpu_usage_nsec;
uint64_t tasks_current;
@@ -3883,7 +3884,8 @@ static void print_status_info(
printf(" Memory: %s", format_bytes(buf, sizeof(buf), i->memory_current));
- if (i->memory_low > 0 || i->memory_high != CGROUP_LIMIT_MAX || i->memory_max != CGROUP_LIMIT_MAX ||
+ if (i->memory_low > 0 || i->memory_high != CGROUP_LIMIT_MAX ||
+ i->memory_max != CGROUP_LIMIT_MAX || i->memory_swap_max != CGROUP_LIMIT_MAX ||
i->memory_limit != CGROUP_LIMIT_MAX) {
const char *prefix = "";
@@ -3900,6 +3902,10 @@ static void print_status_info(
printf("%smax: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_max));
prefix = " ";
}
+ if (i->memory_swap_max != CGROUP_LIMIT_MAX) {
+ printf("%sswap max: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_swap_max));
+ prefix = " ";
+ }
if (i->memory_limit != CGROUP_LIMIT_MAX) {
printf("%slimit: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_limit));
prefix = " ";
@@ -4140,6 +4146,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *
i->memory_high = u;
else if (streq(name, "MemoryMax"))
i->memory_max = u;
+ else if (streq(name, "MemorySwapMax"))
+ i->memory_swap_max = u;
else if (streq(name, "MemoryLimit"))
i->memory_limit = u;
else if (streq(name, "TasksCurrent"))
@@ -4655,6 +4663,7 @@ static int show_one(
.memory_current = (uint64_t) -1,
.memory_high = CGROUP_LIMIT_MAX,
.memory_max = CGROUP_LIMIT_MAX,
+ .memory_swap_max = CGROUP_LIMIT_MAX,
.memory_limit = (uint64_t) -1,
.cpu_usage_nsec = (uint64_t) -1,
.tasks_current = (uint64_t) -1,
diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h
index 295989cd69..c47459c9ad 100644
--- a/src/systemd/sd-bus.h
+++ b/src/systemd/sd-bus.h
@@ -147,6 +147,8 @@ int sd_bus_can_send(sd_bus *bus, char type);
int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *creds_mask);
int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b);
int sd_bus_get_allow_interactive_authorization(sd_bus *bus);
+int sd_bus_set_exit_on_disconnect(sd_bus *bus, int b);
+int sd_bus_get_exit_on_disconnect(sd_bus *bus);
int sd_bus_start(sd_bus *ret);
@@ -438,8 +440,14 @@ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m);
int sd_bus_track_add_name(sd_bus_track *track, const char *name);
int sd_bus_track_remove_name(sd_bus_track *track, const char *name);
+int sd_bus_track_set_recursive(sd_bus_track *track, int b);
+int sd_bus_track_get_recursive(sd_bus_track *track);
+
unsigned sd_bus_track_count(sd_bus_track *track);
-const char* sd_bus_track_contains(sd_bus_track *track, const char *names);
+int sd_bus_track_count_sender(sd_bus_track *track, sd_bus_message *m);
+int sd_bus_track_count_name(sd_bus_track *track, const char *name);
+
+const char* sd_bus_track_contains(sd_bus_track *track, const char *name);
const char* sd_bus_track_first(sd_bus_track *track);
const char* sd_bus_track_next(sd_bus_track *track);
diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c
index c00ff79123..19c69a98b1 100644
--- a/src/udev/net/ethtool-util.c
+++ b/src/udev/net/ethtool-util.c
@@ -46,6 +46,14 @@ static const char* const wol_table[_WOL_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan);
DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting");
+static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = {
+ [NET_DEV_FEAT_GSO] = "tx-generic-segmentation",
+ [NET_DEV_FEAT_GRO] = "rx-gro",
+ [NET_DEV_FEAT_LRO] = "rx-lro",
+ [NET_DEV_FEAT_TSO] = "tx-tcp-segmentation",
+ [NET_DEV_FEAT_UFO] = "tx-udp-fragmentation",
+};
+
int ethtool_connect(int *ret) {
int fd;
@@ -206,3 +214,108 @@ int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) {
return 0;
}
+
+static int ethtool_get_stringset(int *fd, struct ifreq *ifr, int stringset_id, struct ethtool_gstrings **gstrings) {
+ _cleanup_free_ struct ethtool_gstrings *strings = NULL;
+ struct ethtool_sset_info info = {
+ .cmd = ETHTOOL_GSSET_INFO,
+ .reserved = 0,
+ .sset_mask = 1ULL << stringset_id,
+ };
+ unsigned len;
+ int r;
+
+ ifr->ifr_data = (void *) &info;
+
+ r = ioctl(*fd, SIOCETHTOOL, ifr);
+ if (r < 0)
+ return -errno;
+
+ if (!info.sset_mask)
+ return -EINVAL;
+
+ len = info.data[0];
+
+ strings = malloc0(sizeof(struct ethtool_gstrings) + len * ETH_GSTRING_LEN);
+ if (!strings)
+ return -ENOMEM;
+
+ strings->cmd = ETHTOOL_GSTRINGS;
+ strings->string_set = stringset_id;
+ strings->len = len;
+
+ ifr->ifr_data = (void *) strings;
+
+ r = ioctl(*fd, SIOCETHTOOL, ifr);
+ if (r < 0)
+ return -errno;
+
+ *gstrings = strings;
+ strings = NULL;
+
+ return 0;
+}
+
+static int find_feature_index(struct ethtool_gstrings *strings, const char *feature) {
+ unsigned i;
+
+ for (i = 0; i < strings->len; i++) {
+ if (streq((char *) &strings->data[i * ETH_GSTRING_LEN], feature))
+ return i;
+ }
+
+ return -1;
+}
+
+int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features) {
+ _cleanup_free_ struct ethtool_gstrings *strings = NULL;
+ struct ethtool_sfeatures *sfeatures;
+ int block, bit, i, r;
+ struct ifreq ifr;
+
+ if (*fd < 0) {
+ r = ethtool_connect(fd);
+ if (r < 0)
+ return log_warning_errno(r, "link_config: could not connect to ethtool: %m");
+ }
+
+ strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
+
+ r = ethtool_get_stringset(fd, &ifr, ETH_SS_FEATURES, &strings);
+ if (r < 0)
+ return log_warning_errno(r, "link_config: could not get ethtool features for %s", ifname);
+
+ sfeatures = alloca0(sizeof(struct ethtool_gstrings) + DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0]));
+ sfeatures->cmd = ETHTOOL_SFEATURES;
+ sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
+
+ for (i = 0; i < _NET_DEV_FEAT_MAX; i++) {
+
+ if (features[i] != -1) {
+
+ r = find_feature_index(strings, netdev_feature_table[i]);
+ if (r < 0) {
+ log_warning_errno(r, "link_config: could not find feature: %s", netdev_feature_table[i]);
+ continue;
+ }
+
+ block = r / 32;
+ bit = r % 32;
+
+ sfeatures->features[block].valid |= 1 << bit;
+
+ if (features[i])
+ sfeatures->features[block].requested |= 1 << bit;
+ else
+ sfeatures->features[block].requested &= ~(1 << bit);
+ }
+ }
+
+ ifr.ifr_data = (void *) sfeatures;
+
+ r = ioctl(*fd, SIOCETHTOOL, &ifr);
+ if (r < 0)
+ return log_warning_errno(r, "link_config: could not set ethtool features for %s", ifname);
+
+ return 0;
+}
diff --git a/src/udev/net/ethtool-util.h b/src/udev/net/ethtool-util.h
index 7716516e76..0744164653 100644
--- a/src/udev/net/ethtool-util.h
+++ b/src/udev/net/ethtool-util.h
@@ -38,11 +38,22 @@ typedef enum WakeOnLan {
_WOL_INVALID = -1
} WakeOnLan;
+typedef enum NetDevFeature {
+ NET_DEV_FEAT_GSO,
+ NET_DEV_FEAT_GRO,
+ NET_DEV_FEAT_LRO,
+ NET_DEV_FEAT_TSO,
+ NET_DEV_FEAT_UFO,
+ _NET_DEV_FEAT_MAX,
+ _NET_DEV_FEAT_INVALID = -1
+} NetDevFeature;
+
int ethtool_connect(int *ret);
int ethtool_get_driver(int *fd, const char *ifname, char **ret);
int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex);
int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol);
+int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features);
const char *duplex_to_string(Duplex d) _const_;
Duplex duplex_from_string(const char *d) _pure_;
diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf
index b25e4b3344..f8b85cbd13 100644
--- a/src/udev/net/link-config-gperf.gperf
+++ b/src/udev/net/link-config-gperf.gperf
@@ -16,22 +16,27 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-Match.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, match_mac)
-Match.OriginalName, config_parse_ifnames, 0, offsetof(link_config, match_name)
-Match.Path, config_parse_strv, 0, offsetof(link_config, match_path)
-Match.Driver, config_parse_strv, 0, offsetof(link_config, match_driver)
-Match.Type, config_parse_strv, 0, offsetof(link_config, match_type)
-Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(link_config, match_host)
-Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(link_config, match_virt)
-Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, match_kernel)
-Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(link_config, match_arch)
-Link.Description, config_parse_string, 0, offsetof(link_config, description)
-Link.MACAddressPolicy, config_parse_mac_policy, 0, offsetof(link_config, mac_policy)
-Link.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, mac)
-Link.NamePolicy, config_parse_name_policy, 0, offsetof(link_config, name_policy)
-Link.Name, config_parse_ifname, 0, offsetof(link_config, name)
-Link.Alias, config_parse_ifalias, 0, offsetof(link_config, alias)
-Link.MTUBytes, config_parse_iec_size, 0, offsetof(link_config, mtu)
-Link.BitsPerSecond, config_parse_si_size, 0, offsetof(link_config, speed)
-Link.Duplex, config_parse_duplex, 0, offsetof(link_config, duplex)
-Link.WakeOnLan, config_parse_wol, 0, offsetof(link_config, wol)
+Match.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, match_mac)
+Match.OriginalName, config_parse_ifnames, 0, offsetof(link_config, match_name)
+Match.Path, config_parse_strv, 0, offsetof(link_config, match_path)
+Match.Driver, config_parse_strv, 0, offsetof(link_config, match_driver)
+Match.Type, config_parse_strv, 0, offsetof(link_config, match_type)
+Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(link_config, match_host)
+Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(link_config, match_virt)
+Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, match_kernel)
+Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(link_config, match_arch)
+Link.Description, config_parse_string, 0, offsetof(link_config, description)
+Link.MACAddressPolicy, config_parse_mac_policy, 0, offsetof(link_config, mac_policy)
+Link.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, mac)
+Link.NamePolicy, config_parse_name_policy, 0, offsetof(link_config, name_policy)
+Link.Name, config_parse_ifname, 0, offsetof(link_config, name)
+Link.Alias, config_parse_ifalias, 0, offsetof(link_config, alias)
+Link.MTUBytes, config_parse_iec_size, 0, offsetof(link_config, mtu)
+Link.BitsPerSecond, config_parse_si_size, 0, offsetof(link_config, speed)
+Link.Duplex, config_parse_duplex, 0, offsetof(link_config, duplex)
+Link.WakeOnLan, config_parse_wol, 0, offsetof(link_config, wol)
+Link.GenericSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GSO])
+Link.TCPSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TSO])
+Link.UDPSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_UFO])
+Link.GenericReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GRO])
+Link.LargeReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_LRO])
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
index c66504102f..eedd94e777 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -168,6 +168,8 @@ static int load_link(link_config_ctx *ctx, const char *filename) {
link->wol = _WOL_INVALID;
link->duplex = _DUP_INVALID;
+ memset(&link->features, -1, _NET_DEV_FEAT_MAX);
+
r = config_parse(NULL, filename, file,
"Match\0Link\0Ethernet\0",
config_item_perf_lookup, link_config_gperf_lookup,
@@ -397,6 +399,10 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
log_warning_errno(r, "Could not set WakeOnLan of %s to %s: %m",
old_name, wol_to_string(config->wol));
+ r = ethtool_set_features(&ctx->ethtool_fd, old_name, config->features);
+ if (r < 0)
+ log_warning_errno(r, "Could not set offload features of %s: %m", old_name);
+
ifindex = udev_device_get_ifindex(device);
if (ifindex <= 0) {
log_warning("Could not find ifindex");
diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h
index 9df5529d05..91cc0357c4 100644
--- a/src/udev/net/link-config.h
+++ b/src/udev/net/link-config.h
@@ -70,6 +70,7 @@ struct link_config {
size_t speed;
Duplex duplex;
WakeOnLan wol;
+ NetDevFeature features[_NET_DEV_FEAT_MAX];
LIST_FIELDS(link_config, links);
};
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index a893a2b3d9..19f1c29198 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -776,9 +776,9 @@ static void manager_reload(Manager *manager) {
manager->rules = udev_rules_unref(manager->rules);
udev_builtin_exit(manager->udev);
- sd_notify(false,
- "READY=1\n"
- "STATUS=Processing...");
+ sd_notifyf(false,
+ "READY=1\n"
+ "STATUS=Processing with %u children at max", arg_children_max);
}
static void event_queue_start(Manager *manager) {
@@ -1000,6 +1000,10 @@ static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userd
if (i >= 0) {
log_debug("udevd message (SET_MAX_CHILDREN) received, children_max=%i", i);
arg_children_max = i;
+
+ (void) sd_notifyf(false,
+ "READY=1\n"
+ "STATUS=Processing with %u children at max", arg_children_max);
}
if (udev_ctrl_get_ping(ctrl_msg) > 0)
@@ -1627,9 +1631,9 @@ static int run(int fd_ctrl, int fd_uevent, const char *cgroup) {
if (r < 0)
log_error_errno(r, "failed to apply permissions on static device nodes: %m");
- (void) sd_notify(false,
- "READY=1\n"
- "STATUS=Processing...");
+ (void) sd_notifyf(false,
+ "READY=1\n"
+ "STATUS=Processing with %u children at max", arg_children_max);
r = sd_event_loop(manager->event);
if (r < 0) {