summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/basic/env-util.c3
-rw-r--r--src/basic/extract-word.c2
-rw-r--r--src/basic/missing.h27
-rw-r--r--src/basic/virt.c2
-rw-r--r--src/core/dbus-execute.c2
-rw-r--r--src/core/dbus-job.c121
-rw-r--r--src/core/dbus-job.h4
-rw-r--r--src/core/dbus-manager.c22
-rw-r--r--src/core/dbus-unit.c25
-rw-r--r--src/core/dbus.c16
-rw-r--r--src/core/device.c2
-rw-r--r--src/core/execute.c3
-rw-r--r--src/core/execute.h1
-rw-r--r--src/core/job.c257
-rw-r--r--src/core/job.h16
-rw-r--r--src/core/load-fragment-gperf.gperf.m42
-rw-r--r--src/core/load-fragment.c1
-rw-r--r--src/core/manager.c41
-rw-r--r--src/core/manager.h6
-rw-r--r--src/core/namespace.c416
-rw-r--r--src/core/org.freedesktop.systemd1.conf16
-rw-r--r--src/core/unit.c18
-rw-r--r--src/core/unit.h3
-rw-r--r--src/fstab-generator/fstab-generator.c21
-rw-r--r--src/libsystemd-network/dhcp-internal.h5
-rw-r--r--src/libsystemd-network/dhcp-network.c10
-rw-r--r--src/libsystemd-network/dhcp-packet.c6
-rw-r--r--src/libsystemd-network/sd-dhcp-client.c25
-rw-r--r--src/libsystemd-network/test-dhcp-client.c2
-rw-r--r--src/libsystemd/sd-bus/busctl-introspect.c86
-rw-r--r--src/libsystemd/sd-network/sd-network.c14
-rw-r--r--src/network/.gitignore1
-rw-r--r--src/network/netdev/.gitignore1
-rw-r--r--src/network/netdev/bond.c (renamed from src/network/networkd-netdev-bond.c)2
-rw-r--r--src/network/netdev/bond.h (renamed from src/network/networkd-netdev-bond.h)2
-rw-r--r--src/network/netdev/bridge.c (renamed from src/network/networkd-netdev-bridge.c)4
-rw-r--r--src/network/netdev/bridge.h (renamed from src/network/networkd-netdev-bridge.h)2
-rw-r--r--src/network/netdev/dummy.c (renamed from src/network/networkd-netdev-dummy.c)2
-rw-r--r--src/network/netdev/dummy.h (renamed from src/network/networkd-netdev-dummy.h)2
-rw-r--r--src/network/netdev/ipvlan.c (renamed from src/network/networkd-netdev-ipvlan.c)2
-rw-r--r--src/network/netdev/ipvlan.h (renamed from src/network/networkd-netdev-ipvlan.h)2
-rw-r--r--src/network/netdev/macvlan.c (renamed from src/network/networkd-netdev-macvlan.c)2
-rw-r--r--src/network/netdev/macvlan.h (renamed from src/network/networkd-netdev-macvlan.h)2
-rw-r--r--src/network/netdev/netdev-gperf.gperf (renamed from src/network/networkd-netdev-gperf.gperf)22
-rw-r--r--src/network/netdev/netdev.c (renamed from src/network/networkd-netdev.c)17
-rw-r--r--src/network/netdev/netdev.h (renamed from src/network/networkd-netdev.h)0
-rw-r--r--src/network/netdev/tunnel.c (renamed from src/network/networkd-netdev-tunnel.c)2
-rw-r--r--src/network/netdev/tunnel.h (renamed from src/network/networkd-netdev-tunnel.h)2
-rw-r--r--src/network/netdev/tuntap.c (renamed from src/network/networkd-netdev-tuntap.c)2
-rw-r--r--src/network/netdev/tuntap.h (renamed from src/network/networkd-netdev-tuntap.h)2
-rw-r--r--src/network/netdev/vcan.c (renamed from src/network/networkd-netdev-vcan.c)2
-rw-r--r--src/network/netdev/vcan.h (renamed from src/network/networkd-netdev-vcan.h)2
-rw-r--r--src/network/netdev/veth.c (renamed from src/network/networkd-netdev-veth.c)2
-rw-r--r--src/network/netdev/veth.h (renamed from src/network/networkd-netdev-veth.h)2
-rw-r--r--src/network/netdev/vlan.c (renamed from src/network/networkd-netdev-vlan.c)2
-rw-r--r--src/network/netdev/vlan.h (renamed from src/network/networkd-netdev-vlan.h)2
-rw-r--r--src/network/netdev/vrf.c (renamed from src/network/networkd-netdev-vrf.c)2
-rw-r--r--src/network/netdev/vrf.h (renamed from src/network/networkd-netdev-vrf.h)2
-rw-r--r--src/network/netdev/vxlan.c (renamed from src/network/networkd-netdev-vxlan.c)2
-rw-r--r--src/network/netdev/vxlan.h (renamed from src/network/networkd-netdev-vxlan.h)2
-rw-r--r--src/network/networkd-address-pool.c2
-rw-r--r--src/network/networkd-address.c2
-rw-r--r--src/network/networkd-brvlan.c4
-rw-r--r--src/network/networkd-conf.c2
-rw-r--r--src/network/networkd-conf.h2
-rw-r--r--src/network/networkd-dhcp4.c10
-rw-r--r--src/network/networkd-dhcp6.c4
-rw-r--r--src/network/networkd-fdb.c3
-rw-r--r--src/network/networkd-gperf.gperf1
-rw-r--r--src/network/networkd-ipv4ll.c5
-rw-r--r--src/network/networkd-link-bus.c2
-rw-r--r--src/network/networkd-link.c6
-rw-r--r--src/network/networkd-lldp-tx.c2
-rw-r--r--src/network/networkd-manager-bus.c2
-rw-r--r--src/network/networkd-manager.c2
-rw-r--r--src/network/networkd-manager.h (renamed from src/network/networkd.h)13
-rw-r--r--src/network/networkd-ndisc.c3
-rw-r--r--src/network/networkd-network-bus.c2
-rw-r--r--src/network/networkd-network-gperf.gperf3
-rw-r--r--src/network/networkd-network.c5
-rw-r--r--src/network/networkd-network.h5
-rw-r--r--src/network/networkd-route.c2
-rw-r--r--src/network/networkd.c2
-rw-r--r--src/network/test-network-tables.c8
-rw-r--r--src/network/test-network.c2
-rw-r--r--src/network/wait-online/link.c (renamed from src/network/networkd-wait-online-link.c)4
-rw-r--r--src/network/wait-online/link.h (renamed from src/network/networkd-wait-online-link.h)5
-rw-r--r--src/network/wait-online/manager.c (renamed from src/network/networkd-wait-online-manager.c)4
-rw-r--r--src/network/wait-online/manager.h (renamed from src/network/networkd-wait-online.h)3
-rw-r--r--src/network/wait-online/wait-online.c (renamed from src/network/networkd-wait-online.c)2
-rw-r--r--src/nspawn/nspawn.c12
-rw-r--r--src/resolve/resolved-link.c61
-rw-r--r--src/resolve/resolved-resolv-conf.c2
-rw-r--r--src/resolve/resolved.c4
-rw-r--r--src/shared/bus-unit-util.c21
-rw-r--r--src/shared/bus-util.c37
-rw-r--r--src/shared/bus-util.h2
-rw-r--r--src/shared/condition.c3
-rw-r--r--src/shared/nsflags.c5
-rw-r--r--src/systemctl/systemctl.c99
-rw-r--r--src/systemd/sd-dhcp-client.h3
-rw-r--r--src/test/test-execute.c13
-rw-r--r--src/test/test-nss.c190
-rw-r--r--src/timesync/timesyncd-manager.c4
-rw-r--r--src/udev/net/ethtool-util.c209
-rw-r--r--src/udev/net/ethtool-util.h18
-rw-r--r--src/udev/net/link-config-gperf.gperf1
-rw-r--r--src/udev/net/link-config.c30
-rw-r--r--src/udev/net/link-config.h1
109 files changed, 1484 insertions, 609 deletions
diff --git a/src/basic/env-util.c b/src/basic/env-util.c
index 7c69ccdaf9..96da38d45e 100644
--- a/src/basic/env-util.c
+++ b/src/basic/env-util.c
@@ -395,7 +395,8 @@ int strv_env_replace(char ***l, char *p) {
for (f = *l; f && *f; f++)
if (env_match(*f, p)) {
- free_and_replace(*f, p);
+ free(*f);
+ *f = p;
strv_env_unset(f + 1, p);
return 0;
}
diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c
index d6c1228463..dbe64a9a58 100644
--- a/src/basic/extract-word.c
+++ b/src/basic/extract-word.c
@@ -48,7 +48,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
/* Bail early if called after last value or with no input */
if (!*p)
- goto finish_force_terminate;
+ goto finish;
c = **p;
if (!separators)
diff --git a/src/basic/missing.h b/src/basic/missing.h
index 4c013be608..a5ae5d9e79 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -1076,6 +1076,33 @@ typedef int32_t key_serial_t;
#define IFA_F_MCAUTOJOIN 0x400
#endif
+#ifndef HAVE_STRUCT_ETHTOOL_LINK_SETTINGS
+
+#define ETHTOOL_GLINKSETTINGS 0x0000004c /* Get ethtool_link_settings */
+#define ETHTOOL_SLINKSETTINGS 0x0000004d /* Set ethtool_link_settings */
+
+struct ethtool_link_settings {
+ __u32 cmd;
+ __u32 speed;
+ __u8 duplex;
+ __u8 port;
+ __u8 phy_address;
+ __u8 autoneg;
+ __u8 mdio_support;
+ __u8 eth_tp_mdix;
+ __u8 eth_tp_mdix_ctrl;
+ __s8 link_mode_masks_nwords;
+ __u32 reserved[8];
+ __u32 link_mode_masks[0];
+ /* layout of link_mode_masks fields:
+ * __u32 map_supported[link_mode_masks_nwords];
+ * __u32 map_advertising[link_mode_masks_nwords];
+ * __u32 map_lp_advertising[link_mode_masks_nwords];
+ */
+};
+
+#endif
+
#endif
#include "missing_syscall.h"
diff --git a/src/basic/virt.c b/src/basic/virt.c
index 69b0f96183..d8d57381ad 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -496,7 +496,7 @@ static int userns_has_mapping(const char *name) {
f = fopen(name, "re");
if (!f) {
log_debug_errno(errno, "Failed to open %s: %m", name);
- return errno == -ENOENT ? false : -errno;
+ return errno == ENOENT ? false : -errno;
}
n = getline(&buf, &n_allocated, f);
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index d7bb0496a0..23c1b44573 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -781,7 +781,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("RuntimeDirectory", "as", NULL, offsetof(ExecContext, runtime_directory), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MemoryDenyWriteExecute", "b", bus_property_get_bool, offsetof(ExecContext, memory_deny_write_execute), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RestrictRealtime", "b", bus_property_get_bool, offsetof(ExecContext, restrict_realtime), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("RestrictNamespace", "t", bus_property_get_ulong, offsetof(ExecContext, restrict_namespaces), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RestrictNamespaces", "t", bus_property_get_ulong, offsetof(ExecContext, restrict_namespaces), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_VTABLE_END
};
diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c
index ccf7453d47..effc45db45 100644
--- a/src/core/dbus-job.c
+++ b/src/core/dbus-job.c
@@ -65,7 +65,7 @@ int bus_job_method_cancel(sd_bus_message *message, void *userdata, sd_bus_error
return r;
/* Access is granted to the job owner */
- if (!sd_bus_track_contains(j->clients, sd_bus_message_get_sender(message))) {
+ if (!sd_bus_track_contains(j->bus_track, sd_bus_message_get_sender(message))) {
/* And for everybody else consult PolicyKit */
r = bus_verify_manage_units_async(j->unit->manager, message, error);
@@ -80,9 +80,61 @@ int bus_job_method_cancel(sd_bus_message *message, void *userdata, sd_bus_error
return sd_bus_reply_method_return(message, NULL);
}
+int bus_job_method_get_waiting_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_free_ Job **list = NULL;
+ Job *j = userdata;
+ int r, i, n;
+
+ if (strstr(sd_bus_message_get_member(message), "After"))
+ n = job_get_after(j, &list);
+ else
+ n = job_get_before(j, &list);
+ if (n < 0)
+ return n;
+
+ r = sd_bus_message_new_method_return(message, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "(usssoo)");
+ if (r < 0)
+ return r;
+
+ for (i = 0; i < n; i ++) {
+ _cleanup_free_ char *unit_path = NULL, *job_path = NULL;
+
+ job_path = job_dbus_path(list[i]);
+ if (!job_path)
+ return -ENOMEM;
+
+ unit_path = unit_dbus_path(list[i]->unit);
+ if (!unit_path)
+ return -ENOMEM;
+
+ r = sd_bus_message_append(reply, "(usssoo)",
+ list[i]->id,
+ list[i]->unit->id,
+ job_type_to_string(list[i]->type),
+ job_state_to_string(list[i]->state),
+ job_path,
+ unit_path);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+}
+
const sd_bus_vtable bus_job_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("Cancel", NULL, NULL, bus_job_method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("GetAfter", NULL, "a(usssoo)", bus_job_method_get_waiting_jobs, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("GetBefore", NULL, "a(usssoo)", bus_job_method_get_waiting_jobs, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Job, id), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Unit", "(so)", property_get_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("JobType", "s", property_get_type, offsetof(Job, type), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -143,7 +195,7 @@ void bus_job_send_change_signal(Job *j) {
j->in_dbus_queue = false;
}
- r = bus_foreach_bus(j->manager, j->clients, j->sent_dbus_new_signal ? send_changed_signal : send_new_signal, j);
+ r = bus_foreach_bus(j->manager, j->bus_track, j->sent_dbus_new_signal ? send_changed_signal : send_new_signal, j);
if (r < 0)
log_debug_errno(r, "Failed to send job change signal for %u: %m", j->id);
@@ -187,7 +239,70 @@ void bus_job_send_removed_signal(Job *j) {
if (!j->sent_dbus_new_signal)
bus_job_send_change_signal(j);
- r = bus_foreach_bus(j->manager, j->clients, send_removed_signal, j);
+ r = bus_foreach_bus(j->manager, j->bus_track, send_removed_signal, j);
if (r < 0)
log_debug_errno(r, "Failed to send job remove signal for %u: %m", j->id);
}
+
+static int bus_job_track_handler(sd_bus_track *t, void *userdata) {
+ Job *j = userdata;
+
+ assert(t);
+ assert(j);
+
+ j->bus_track = sd_bus_track_unref(j->bus_track); /* make sure we aren't called again */
+
+ /* Last client dropped off the bus, maybe we should GC this now? */
+ job_add_to_gc_queue(j);
+ return 0;
+}
+
+static int bus_job_allocate_bus_track(Job *j) {
+
+ assert(j);
+
+ if (j->bus_track)
+ return 0;
+
+ return sd_bus_track_new(j->unit->manager->api_bus, &j->bus_track, bus_job_track_handler, j);
+}
+
+int bus_job_coldplug_bus_track(Job *j) {
+ int r = 0;
+ _cleanup_strv_free_ char **deserialized_clients = NULL;
+
+ assert(j);
+
+ deserialized_clients = j->deserialized_clients;
+ j->deserialized_clients = NULL;
+
+ if (strv_isempty(deserialized_clients))
+ return 0;
+
+ if (!j->manager->api_bus)
+ return 0;
+
+ r = bus_job_allocate_bus_track(j);
+ if (r < 0)
+ return r;
+
+ return bus_track_add_name_many(j->bus_track, deserialized_clients);
+}
+
+int bus_job_track_sender(Job *j, sd_bus_message *m) {
+ int r;
+
+ assert(j);
+ assert(m);
+
+ if (sd_bus_message_get_bus(m) != j->unit->manager->api_bus) {
+ j->ref_by_private_bus = true;
+ return 0;
+ }
+
+ r = bus_job_allocate_bus_track(j);
+ if (r < 0)
+ return r;
+
+ return sd_bus_track_add_sender(j->bus_track, m);
+}
diff --git a/src/core/dbus-job.h b/src/core/dbus-job.h
index 024d06719e..a4366a0720 100644
--- a/src/core/dbus-job.h
+++ b/src/core/dbus-job.h
@@ -26,6 +26,10 @@
extern const sd_bus_vtable bus_job_vtable[];
int bus_job_method_cancel(sd_bus_message *message, void *job, sd_bus_error *error);
+int bus_job_method_get_waiting_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error);
void bus_job_send_change_signal(Job *j);
void bus_job_send_removed_signal(Job *j);
+
+int bus_job_coldplug_bus_track(Job *j);
+int bus_job_track_sender(Job *j, sd_bus_message *m);
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 5a7922a249..9af49dd1bc 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -2285,6 +2285,26 @@ static int method_get_unit_file_links(sd_bus_message *message, void *userdata, s
return sd_bus_send(NULL, reply, NULL);
}
+static int method_get_job_waiting(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ uint32_t id;
+ Job *j;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "u", &id);
+ if (r < 0)
+ return r;
+
+ j = manager_get_job(m, id);
+ if (!j)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id);
+
+ return bus_job_method_get_waiting_jobs(message, j, error);
+}
+
const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_VTABLE_START(0),
@@ -2390,6 +2410,8 @@ const sd_bus_vtable bus_manager_vtable[] = {
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),
+ SD_BUS_METHOD("GetJobAfter", "u", "a(usssoo)", method_get_job_waiting, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("GetJobBefore", "u", "a(usssoo)", method_get_job_waiting, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CancelJob", "u", NULL, method_cancel_job, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ClearJobs", NULL, NULL, method_clear_jobs, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResetFailed", NULL, NULL, method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index b6cb6e1350..2adc1d9288 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -22,6 +22,7 @@
#include "alloc-util.h"
#include "bus-common-errors.h"
#include "cgroup-util.h"
+#include "dbus-job.h"
#include "dbus-unit.h"
#include "dbus.h"
#include "fd-util.h"
@@ -1223,17 +1224,9 @@ int bus_unit_queue_job(
if (r < 0)
return r;
- if (sd_bus_message_get_bus(message) == u->manager->api_bus) {
- if (!j->clients) {
- r = sd_bus_track_new(sd_bus_message_get_bus(message), &j->clients, NULL, NULL);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_track_add_sender(j->clients, message);
- if (r < 0)
- return r;
- }
+ r = bus_job_track_sender(j, message);
+ if (r < 0)
+ return r;
path = job_dbus_path(j);
if (!path)
@@ -1507,7 +1500,7 @@ 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) {
+static int bus_unit_track_handler(sd_bus_track *t, void *userdata) {
Unit *u = userdata;
assert(t);
@@ -1519,7 +1512,7 @@ static int bus_track_handler(sd_bus_track *t, void *userdata) {
return 0;
}
-static int allocate_bus_track(Unit *u) {
+static int bus_unit_allocate_bus_track(Unit *u) {
int r;
assert(u);
@@ -1527,7 +1520,7 @@ static int allocate_bus_track(Unit *u) {
if (u->bus_track)
return 0;
- r = sd_bus_track_new(u->manager->api_bus, &u->bus_track, bus_track_handler, u);
+ r = sd_bus_track_new(u->manager->api_bus, &u->bus_track, bus_unit_track_handler, u);
if (r < 0)
return r;
@@ -1545,7 +1538,7 @@ int bus_unit_track_add_name(Unit *u, const char *name) {
assert(u);
- r = allocate_bus_track(u);
+ r = bus_unit_allocate_bus_track(u);
if (r < 0)
return r;
@@ -1557,7 +1550,7 @@ int bus_unit_track_add_sender(Unit *u, sd_bus_message *m) {
assert(u);
- r = allocate_bus_track(u);
+ r = bus_unit_allocate_bus_track(u);
if (r < 0)
return r;
diff --git a/src/core/dbus.c b/src/core/dbus.c
index 070974fe66..07ab21f199 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -1054,8 +1054,8 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
m->subscribed = sd_bus_track_unref(m->subscribed);
HASHMAP_FOREACH(j, m->jobs, i)
- if (j->clients && sd_bus_track_get_bus(j->clients) == *bus)
- j->clients = sd_bus_track_unref(j->clients);
+ if (j->bus_track && sd_bus_track_get_bus(j->bus_track) == *bus)
+ j->bus_track = sd_bus_track_unref(j->bus_track);
/* Get rid of queued message on this bus */
if (m->queued_message && sd_bus_message_get_bus(m->queued_message) == *bus)
@@ -1185,7 +1185,6 @@ 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) {
- char **i;
int r = 0;
assert(m);
@@ -1207,16 +1206,7 @@ int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l) {
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;
+ return bus_track_add_name_many(*t, l);
}
int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
diff --git a/src/core/device.c b/src/core/device.c
index c572a6737c..074e93ffe2 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -831,6 +831,8 @@ const UnitVTable device_vtable = {
"Device\0"
"Install\0",
+ .gc_jobs = true,
+
.init = device_init,
.done = device_done,
.load = unit_load_fragment_and_dropin_optional,
diff --git a/src/core/execute.c b/src/core/execute.c
index 084eca334c..07ab067c05 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -2276,7 +2276,8 @@ static bool context_has_no_new_privileges(const ExecContext *c) {
if (have_effective_cap(CAP_SYS_ADMIN)) /* if we are privileged, we don't need NNP */
return false;
- return context_has_address_families(c) || /* we need NNP if we have any form of seccomp and are unprivileged */
+ /* We need NNP if we have any form of seccomp and are unprivileged */
+ return context_has_address_families(c) ||
c->memory_deny_write_execute ||
c->restrict_realtime ||
exec_context_restrict_namespaces_set(c) ||
diff --git a/src/core/execute.h b/src/core/execute.h
index cd7cbcc5ab..951c8f4da3 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -216,7 +216,6 @@ struct ExecContext {
bool nice_set:1;
bool ioprio_set:1;
bool cpu_sched_set:1;
- bool no_new_privileges_set:1;
};
static inline bool exec_context_restrict_namespaces_set(const ExecContext *c) {
diff --git a/src/core/job.c b/src/core/job.c
index ac6910a906..2ba4c78096 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -90,9 +90,12 @@ void job_free(Job *j) {
if (j->in_dbus_queue)
LIST_REMOVE(dbus_queue, j->manager->dbus_job_queue, j);
+ if (j->in_gc_queue)
+ LIST_REMOVE(gc_queue, j->manager->gc_job_queue, j);
+
sd_event_source_unref(j->timer_event_source);
- sd_bus_track_unref(j->clients);
+ sd_bus_track_unref(j->bus_track);
strv_free(j->deserialized_clients);
free(j);
@@ -226,6 +229,9 @@ Job* job_install(Job *j) {
log_unit_debug(j->unit,
"Installed new job %s/%s as %u",
j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
+
+ job_add_to_gc_queue(j);
+
return j;
}
@@ -267,7 +273,8 @@ JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool
* this means the 'anchor' job (i.e. the one the user
* explicitly asked for) is the requester. */
- if (!(l = new0(JobDependency, 1)))
+ l = new0(JobDependency, 1);
+ if (!l)
return NULL;
l->subject = subject;
@@ -457,9 +464,7 @@ static bool job_is_runnable(Job *j) {
if (j->type == JOB_NOP)
return true;
- if (j->type == JOB_START ||
- j->type == JOB_VERIFY_ACTIVE ||
- j->type == JOB_RELOAD) {
+ if (IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD)) {
/* Immediate result is that the job is or might be
* started. In this case let's wait for the
@@ -476,8 +481,7 @@ static bool job_is_runnable(Job *j) {
SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i)
if (other->job &&
- (other->job->type == JOB_STOP ||
- other->job->type == JOB_RESTART))
+ IN_SET(other->job->type, JOB_STOP, JOB_RESTART))
return false;
/* This means that for a service a and a service b where b
@@ -641,6 +645,7 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR
[JOB_DEPENDENCY] = "Dependency failed for %s.",
[JOB_ASSERT] = "Assertion failed for %s.",
[JOB_UNSUPPORTED] = "Starting of %s not supported.",
+ [JOB_COLLECTED] = "Unecessary job for %s was removed.",
};
static const char *const generic_finished_stop_job[_JOB_RESULT_MAX] = {
[JOB_DONE] = "Stopped %s.",
@@ -700,6 +705,7 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
[JOB_SKIPPED] = { ANSI_HIGHLIGHT, " INFO " },
[JOB_ASSERT] = { ANSI_HIGHLIGHT_YELLOW, "ASSERT" },
[JOB_UNSUPPORTED] = { ANSI_HIGHLIGHT_YELLOW, "UNSUPP" },
+ [JOB_COLLECTED] = { ANSI_HIGHLIGHT, " INFO " },
};
const char *format;
@@ -751,6 +757,7 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
[JOB_INVALID] = LOG_INFO,
[JOB_ASSERT] = LOG_WARNING,
[JOB_UNSUPPORTED] = LOG_WARNING,
+ [JOB_COLLECTED] = LOG_INFO,
};
assert(u);
@@ -862,6 +869,7 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr
job_set_state(j, JOB_WAITING);
job_add_to_run_queue(j);
+ job_add_to_gc_queue(j);
goto finish;
}
@@ -905,11 +913,15 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr
finish:
/* Try to start the next jobs that can be started */
SET_FOREACH(other, u->dependencies[UNIT_AFTER], i)
- if (other->job)
+ if (other->job) {
job_add_to_run_queue(other->job);
+ job_add_to_gc_queue(other->job);
+ }
SET_FOREACH(other, u->dependencies[UNIT_BEFORE], i)
- if (other->job)
+ if (other->job) {
job_add_to_run_queue(other->job);
+ job_add_to_gc_queue(other->job);
+ }
manager_check_finished(u->manager);
@@ -1012,7 +1024,7 @@ int job_serialize(Job *j, FILE *f) {
if (j->begin_usec > 0)
fprintf(f, "job-begin="USEC_FMT"\n", j->begin_usec);
- bus_track_serialize(j->clients, f, "subscribed");
+ bus_track_serialize(j->bus_track, f, "subscribed");
/* End marker */
fputc('\n', f);
@@ -1123,12 +1135,14 @@ int job_coldplug(Job *j) {
/* After deserialization is complete and the bus connection
* set up again, let's start watching our subscribers again */
- (void) bus_track_coldplug(j->manager, &j->clients, false, j->deserialized_clients);
- j->deserialized_clients = strv_free(j->deserialized_clients);
+ (void) bus_job_coldplug_bus_track(j);
if (j->state == JOB_WAITING)
job_add_to_run_queue(j);
+ /* Maybe due to new dependencies we don't actually need this job anymore? */
+ job_add_to_gc_queue(j);
+
if (j->begin_usec == 0 || j->unit->job_timeout == USEC_INFINITY)
return 0;
@@ -1203,9 +1217,225 @@ int job_get_timeout(Job *j, usec_t *timeout) {
return 1;
}
+bool job_check_gc(Job *j) {
+ Unit *other;
+ Iterator i;
+
+ assert(j);
+
+ /* Checks whether this job should be GC'ed away. We only do this for jobs of units that have no effect on their
+ * own and just track external state. For now the only unit type that qualifies for this are .device units. */
+
+ if (!UNIT_VTABLE(j->unit)->gc_jobs)
+ return true;
+
+ if (sd_bus_track_count(j->bus_track) > 0)
+ return true;
+
+ /* FIXME: So this is a bit ugly: for now we don't properly track references made via private bus connections
+ * (because it's nasty, as sd_bus_track doesn't apply to it). We simply remember that the job was once
+ * referenced by one, and reset this whenever we notice that no private bus connections are around. This means
+ * the GC is a bit too conservative when it comes to jobs created by private bus connections. */
+ if (j->ref_by_private_bus) {
+ if (set_isempty(j->unit->manager->private_buses))
+ j->ref_by_private_bus = false;
+ else
+ return true;
+ }
+
+ if (j->type == JOB_NOP)
+ return true;
+
+ /* If a job is ordered after ours, and is to be started, then it needs to wait for us, regardless if we stop or
+ * start, hence let's not GC in that case. */
+ SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i) {
+ if (!other->job)
+ continue;
+
+ if (other->job->ignore_order)
+ continue;
+
+ if (IN_SET(other->job->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD))
+ return true;
+ }
+
+ /* If we are going down, but something else is orederd After= us, then it needs to wait for us */
+ if (IN_SET(j->type, JOB_STOP, JOB_RESTART)) {
+
+ SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i) {
+ if (!other->job)
+ continue;
+
+ if (other->job->ignore_order)
+ continue;
+
+ return true;
+ }
+ }
+
+ /* The logic above is kinda the inverse of the job_is_runnable() logic. Specifically, if the job "we" is
+ * ordered before the job "other":
+ *
+ * we start + other start → stay
+ * we start + other stop → gc
+ * we stop + other start → stay
+ * we stop + other stop → gc
+ *
+ * "we" are ordered after "other":
+ *
+ * we start + other start → gc
+ * we start + other stop → gc
+ * we stop + other start → stay
+ * we stop + other stop → stay
+ *
+ */
+
+ return false;
+}
+
+void job_add_to_gc_queue(Job *j) {
+ assert(j);
+
+ if (j->in_gc_queue)
+ return;
+
+ if (job_check_gc(j))
+ return;
+
+ LIST_PREPEND(gc_queue, j->unit->manager->gc_job_queue, j);
+ j->in_gc_queue = true;
+}
+
+static int job_compare(const void *a, const void *b) {
+ Job *x = *(Job**) a, *y = *(Job**) b;
+
+ if (x->id < y->id)
+ return -1;
+ if (x->id > y->id)
+ return 1;
+
+ return 0;
+}
+
+static size_t sort_job_list(Job **list, size_t n) {
+ Job *previous = NULL;
+ size_t a, b;
+
+ /* Order by numeric IDs */
+ qsort_safe(list, n, sizeof(Job*), job_compare);
+
+ /* Filter out duplicates */
+ for (a = 0, b = 0; a < n; a++) {
+
+ if (previous == list[a])
+ continue;
+
+ previous = list[b++] = list[a];
+ }
+
+ return b;
+}
+
+int job_get_before(Job *j, Job*** ret) {
+ _cleanup_free_ Job** list = NULL;
+ size_t n = 0, n_allocated = 0;
+ Unit *other = NULL;
+ Iterator i;
+
+ /* Returns a list of all pending jobs that need to finish before this job may be started. */
+
+ assert(j);
+ assert(ret);
+
+ if (j->ignore_order) {
+ *ret = NULL;
+ return 0;
+ }
+
+ if (IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD)) {
+
+ SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i) {
+ if (!other->job)
+ continue;
+
+ if (!GREEDY_REALLOC(list, n_allocated, n+1))
+ return -ENOMEM;
+ list[n++] = other->job;
+ }
+ }
+
+ SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i) {
+ if (!other->job)
+ continue;
+
+ if (!IN_SET(other->job->type, JOB_STOP, JOB_RESTART))
+ continue;
+
+ if (!GREEDY_REALLOC(list, n_allocated, n+1))
+ return -ENOMEM;
+ list[n++] = other->job;
+ }
+
+ n = sort_job_list(list, n);
+
+ *ret = list;
+ list = NULL;
+
+ return (int) n;
+}
+
+int job_get_after(Job *j, Job*** ret) {
+ _cleanup_free_ Job** list = NULL;
+ size_t n = 0, n_allocated = 0;
+ Unit *other = NULL;
+ Iterator i;
+
+ assert(j);
+ assert(ret);
+
+ /* Returns a list of all pending jobs that are waiting for this job to finish. */
+
+ SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i) {
+ if (!other->job)
+ continue;
+
+ if (other->job->ignore_order)
+ continue;
+
+ if (!IN_SET(other->job->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD))
+ continue;
+
+ if (!GREEDY_REALLOC(list, n_allocated, n+1))
+ return -ENOMEM;
+ list[n++] = other->job;
+ }
+
+ if (IN_SET(j->type, JOB_STOP, JOB_RESTART)) {
+
+ SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i) {
+ if (!other->job)
+ continue;
+
+ if (other->job->ignore_order)
+ continue;
+
+ if (!GREEDY_REALLOC(list, n_allocated, n+1))
+ return -ENOMEM;
+ list[n++] = other->job;
+ }
+ }
+
+ n = sort_job_list(list, n);
+
+ *ret = list;
+ list = NULL;
+
+ return (int) n;
+}
+
static const char* const job_state_table[_JOB_STATE_MAX] = {
[JOB_WAITING] = "waiting",
- [JOB_RUNNING] = "running"
+ [JOB_RUNNING] = "running",
};
DEFINE_STRING_TABLE_LOOKUP(job_state, JobState);
@@ -1246,6 +1476,7 @@ static const char* const job_result_table[_JOB_RESULT_MAX] = {
[JOB_INVALID] = "invalid",
[JOB_ASSERT] = "assert",
[JOB_UNSUPPORTED] = "unsupported",
+ [JOB_COLLECTED] = "collected",
};
DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult);
diff --git a/src/core/job.h b/src/core/job.h
index 85368f0d30..bea743f462 100644
--- a/src/core/job.h
+++ b/src/core/job.h
@@ -107,6 +107,7 @@ enum JobResult {
JOB_INVALID, /* JOB_RELOAD of inactive unit */
JOB_ASSERT, /* Couldn't start a unit, because an assert didn't hold */
JOB_UNSUPPORTED, /* Couldn't start a unit, because the unit type is not supported on the system */
+ JOB_COLLECTED, /* Job was garbage collected, since nothing needed it anymore */
_JOB_RESULT_MAX,
_JOB_RESULT_INVALID = -1
};
@@ -122,8 +123,8 @@ struct JobDependency {
LIST_FIELDS(JobDependency, subject);
LIST_FIELDS(JobDependency, object);
- bool matters;
- bool conflicts;
+ bool matters:1;
+ bool conflicts:1;
};
struct Job {
@@ -133,6 +134,7 @@ struct Job {
LIST_FIELDS(Job, transaction);
LIST_FIELDS(Job, run_queue);
LIST_FIELDS(Job, dbus_queue);
+ LIST_FIELDS(Job, gc_queue);
LIST_HEAD(JobDependency, subject_list);
LIST_HEAD(JobDependency, object_list);
@@ -156,7 +158,7 @@ struct Job {
*
* There can be more than one client, because of job merging.
*/
- sd_bus_track *clients;
+ sd_bus_track *bus_track;
char **deserialized_clients;
JobResult result;
@@ -168,6 +170,8 @@ struct Job {
bool sent_dbus_new_signal:1;
bool ignore_order:1;
bool irreversible:1;
+ bool in_gc_queue:1;
+ bool ref_by_private_bus:1;
};
Job* job_new(Unit *unit, JobType type);
@@ -227,6 +231,12 @@ void job_shutdown_magic(Job *j);
int job_get_timeout(Job *j, usec_t *timeout) _pure_;
+bool job_check_gc(Job *j);
+void job_add_to_gc_queue(Job *j);
+
+int job_get_before(Job *j, Job*** ret);
+int job_get_after(Job *j, Job*** ret);
+
const char* job_type_to_string(JobType t) _const_;
JobType job_type_from_string(const char *s) _pure_;
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index cb2f384f47..f4ef5a0140 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -57,7 +57,7 @@ m4_ifdef(`HAVE_SECCOMP',
$1.SystemCallArchitectures, config_parse_syscall_archs, 0, offsetof($1, exec_context.syscall_archs)
$1.SystemCallErrorNumber, config_parse_syscall_errno, 0, offsetof($1, exec_context)
$1.MemoryDenyWriteExecute, config_parse_bool, 0, offsetof($1, exec_context.memory_deny_write_execute)
-$1.RestrictNamespaces, config_parse_restrict_namespaces, 0, offsetof($1, exec_context.restrict_namespaces)
+$1.RestrictNamespaces, config_parse_restrict_namespaces, 0, offsetof($1, exec_context)
$1.RestrictRealtime, config_parse_bool, 0, offsetof($1, exec_context.restrict_realtime)
$1.RestrictAddressFamilies, config_parse_address_families, 0, offsetof($1, exec_context)',
`$1.SystemCallFilter, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 52079980d8..970eed27c1 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -3896,7 +3896,6 @@ int config_parse_no_new_privileges(
}
c->no_new_privileges = k;
- c->no_new_privileges_set = true;
return 0;
}
diff --git a/src/core/manager.c b/src/core/manager.c
index b49e3b593a..1f663d3c1d 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -987,10 +987,9 @@ good:
unit_gc_mark_good(u, gc_marker);
}
-static unsigned manager_dispatch_gc_queue(Manager *m) {
+static unsigned manager_dispatch_gc_unit_queue(Manager *m) {
+ unsigned n = 0, gc_marker;
Unit *u;
- unsigned n = 0;
- unsigned gc_marker;
assert(m);
@@ -1002,12 +1001,12 @@ static unsigned manager_dispatch_gc_queue(Manager *m) {
gc_marker = m->gc_marker;
- while ((u = m->gc_queue)) {
+ while ((u = m->gc_unit_queue)) {
assert(u->in_gc_queue);
unit_gc_sweep(u, gc_marker);
- LIST_REMOVE(gc_queue, m->gc_queue, u);
+ LIST_REMOVE(gc_queue, m->gc_unit_queue, u);
u->in_gc_queue = false;
n++;
@@ -1021,7 +1020,29 @@ static unsigned manager_dispatch_gc_queue(Manager *m) {
}
}
- m->n_in_gc_queue = 0;
+ return n;
+}
+
+static unsigned manager_dispatch_gc_job_queue(Manager *m) {
+ unsigned n = 0;
+ Job *j;
+
+ assert(m);
+
+ while ((j = m->gc_job_queue)) {
+ assert(j->in_gc_queue);
+
+ LIST_REMOVE(gc_queue, m->gc_job_queue, j);
+ j->in_gc_queue = false;
+
+ n++;
+
+ if (job_check_gc(j))
+ continue;
+
+ log_unit_debug(j->unit, "Collecting job.");
+ (void) job_finish_and_invalidate(j, JOB_COLLECTED, false, false);
+ }
return n;
}
@@ -1041,7 +1062,8 @@ static void manager_clear_jobs_and_units(Manager *m) {
assert(!m->dbus_unit_queue);
assert(!m->dbus_job_queue);
assert(!m->cleanup_queue);
- assert(!m->gc_queue);
+ assert(!m->gc_unit_queue);
+ assert(!m->gc_job_queue);
assert(hashmap_isempty(m->jobs));
assert(hashmap_isempty(m->units));
@@ -2234,7 +2256,10 @@ int manager_loop(Manager *m) {
if (manager_dispatch_load_queue(m) > 0)
continue;
- if (manager_dispatch_gc_queue(m) > 0)
+ if (manager_dispatch_gc_job_queue(m) > 0)
+ continue;
+
+ if (manager_dispatch_gc_unit_queue(m) > 0)
continue;
if (manager_dispatch_cleanup_queue(m) > 0)
diff --git a/src/core/manager.h b/src/core/manager.h
index 4f17f1eea5..4a9a37caff 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -104,8 +104,9 @@ struct Manager {
/* Units to remove */
LIST_HEAD(Unit, cleanup_queue);
- /* Units to check when doing GC */
- LIST_HEAD(Unit, gc_queue);
+ /* Units and jobs to check when doing GC */
+ LIST_HEAD(Unit, gc_unit_queue);
+ LIST_HEAD(Job, gc_job_queue);
/* Units that should be realized */
LIST_HEAD(Unit, cgroup_queue);
@@ -229,7 +230,6 @@ struct Manager {
int pin_cgroupfs_fd;
int gc_marker;
- unsigned n_in_gc_queue;
/* Flags */
ManagerExitCode exit_code:5;
diff --git a/src/core/namespace.c b/src/core/namespace.c
index 308e4d768e..e9ad26bfc3 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -58,17 +58,13 @@ typedef enum MountMode {
} MountMode;
typedef struct BindMount {
- char *path;
- MountMode mode;
- bool ignore; /* Ignore if path does not exist */
+ const char *path_const; /* Memory allocated on stack or static */
+ MountMode mode:6;
+ bool ignore:1; /* Ignore if path does not exist? */
+ bool has_prefix:1; /* Already is prefixed by the root dir? */
+ char *path_malloc; /* Use this instead of 'path' if we had to allocate memory */
} BindMount;
-typedef struct TargetMount {
- const char *path;
- MountMode mode;
- bool ignore; /* Ignore if path does not exist */
-} TargetMount;
-
/*
* The following Protect tables are to protect paths and mark some of them
* READONLY, in case a path is covered by an option from another table, then
@@ -78,62 +74,62 @@ typedef struct TargetMount {
*/
/* ProtectKernelTunables= option and the related filesystem APIs */
-static const TargetMount protect_kernel_tunables_table[] = {
- { "/proc/sys", READONLY, false },
- { "/proc/sysrq-trigger", READONLY, true },
- { "/proc/latency_stats", READONLY, true },
- { "/proc/mtrr", READONLY, true },
- { "/proc/apm", READONLY, true },
- { "/proc/acpi", READONLY, true },
- { "/proc/timer_stats", READONLY, true },
- { "/proc/asound", READONLY, true },
- { "/proc/bus", READONLY, true },
- { "/proc/fs", READONLY, true },
- { "/proc/irq", READONLY, true },
- { "/sys", READONLY, false },
- { "/sys/kernel/debug", READONLY, true },
- { "/sys/kernel/tracing", READONLY, true },
- { "/sys/fs/cgroup", READWRITE, false }, /* READONLY is set by ProtectControlGroups= option */
+static const BindMount protect_kernel_tunables_table[] = {
+ { "/proc/sys", READONLY, false },
+ { "/proc/sysrq-trigger", READONLY, true },
+ { "/proc/latency_stats", READONLY, true },
+ { "/proc/mtrr", READONLY, true },
+ { "/proc/apm", READONLY, true }, /* Obsolete API, there's no point in permitting access to this, ever */
+ { "/proc/acpi", READONLY, true },
+ { "/proc/timer_stats", READONLY, true },
+ { "/proc/asound", READONLY, true },
+ { "/proc/bus", READONLY, true },
+ { "/proc/fs", READONLY, true },
+ { "/proc/irq", READONLY, true },
+ { "/sys", READONLY, false },
+ { "/sys/kernel/debug", READONLY, true },
+ { "/sys/kernel/tracing", READONLY, true },
+ { "/sys/fs/cgroup", READWRITE, false }, /* READONLY is set by ProtectControlGroups= option */
};
/* ProtectKernelModules= option */
-static const TargetMount protect_kernel_modules_table[] = {
+static const BindMount protect_kernel_modules_table[] = {
#ifdef HAVE_SPLIT_USR
- { "/lib/modules", INACCESSIBLE, true },
+ { "/lib/modules", INACCESSIBLE, true },
#endif
- { "/usr/lib/modules", INACCESSIBLE, true },
+ { "/usr/lib/modules", INACCESSIBLE, true },
};
/*
* ProtectHome=read-only table, protect $HOME and $XDG_RUNTIME_DIR and rest of
* system should be protected by ProtectSystem=
*/
-static const TargetMount protect_home_read_only_table[] = {
- { "/home", READONLY, true },
- { "/run/user", READONLY, true },
- { "/root", READONLY, true },
+static const BindMount protect_home_read_only_table[] = {
+ { "/home", READONLY, true },
+ { "/run/user", READONLY, true },
+ { "/root", READONLY, true },
};
/* ProtectHome=yes table */
-static const TargetMount protect_home_yes_table[] = {
- { "/home", INACCESSIBLE, true },
- { "/run/user", INACCESSIBLE, true },
- { "/root", INACCESSIBLE, true },
+static const BindMount protect_home_yes_table[] = {
+ { "/home", INACCESSIBLE, true },
+ { "/run/user", INACCESSIBLE, true },
+ { "/root", INACCESSIBLE, true },
};
/* ProtectSystem=yes table */
-static const TargetMount protect_system_yes_table[] = {
- { "/usr", READONLY, false },
- { "/boot", READONLY, true },
- { "/efi", READONLY, true },
+static const BindMount protect_system_yes_table[] = {
+ { "/usr", READONLY, false },
+ { "/boot", READONLY, true },
+ { "/efi", READONLY, true },
};
/* ProtectSystem=full includes ProtectSystem=yes */
-static const TargetMount protect_system_full_table[] = {
- { "/usr", READONLY, false },
- { "/boot", READONLY, true },
- { "/efi", READONLY, true },
- { "/etc", READONLY, false },
+static const BindMount protect_system_full_table[] = {
+ { "/usr", READONLY, false },
+ { "/boot", READONLY, true },
+ { "/efi", READONLY, true },
+ { "/etc", READONLY, false },
};
/*
@@ -144,7 +140,7 @@ static const TargetMount protect_system_full_table[] = {
* (And of course /home and friends are also left writable, as ProtectHome=
* shall manage those, orthogonally).
*/
-static const TargetMount protect_system_strict_table[] = {
+static const BindMount protect_system_strict_table[] = {
{ "/", READONLY, false },
{ "/proc", READWRITE, false }, /* ProtectKernelTunables= */
{ "/sys", READWRITE, false }, /* ProtectKernelTunables= */
@@ -154,154 +150,107 @@ static const TargetMount protect_system_strict_table[] = {
{ "/root", READWRITE, true }, /* ProtectHome= */
};
-static void set_bind_mount(BindMount *p, char *path, MountMode mode, bool ignore) {
- p->path = path;
- p->mode = mode;
- p->ignore = ignore;
-}
-
-static int append_one_mount(BindMount **p, const char *root_directory,
- const char *path, MountMode mode, bool ignore) {
- char *lpath;
+static const char *bind_mount_path(const BindMount *p) {
assert(p);
- lpath = prefix_root(root_directory, path);
- if (!lpath)
- return -ENOMEM;
+ /* Returns the path of this bind mount. If the malloc()-allocated ->path_buffer field is set we return that,
+ * otherwise the stack/static ->path field is returned. */
- set_bind_mount((*p)++, lpath, mode, ignore);
- return 0;
+ return p->path_malloc ?: p->path_const;
}
-static int append_mounts(BindMount **p, char **strv, MountMode mode) {
+static int append_access_mounts(BindMount **p, char **strv, MountMode mode) {
char **i;
assert(p);
+ /* Adds a list of user-supplied READWRITE/READONLY/INACCESSIBLE entries */
+
STRV_FOREACH(i, strv) {
- bool ignore = false;
- char *path;
+ bool ignore = false, needs_prefix = false;
+ const char *e = *i;
- if (IN_SET(mode, INACCESSIBLE, READONLY, READWRITE) && startswith(*i, "-")) {
- (*i)++;
+ /* Look for any prefixes */
+ if (startswith(e, "-")) {
+ e++;
ignore = true;
}
+ if (startswith(e, "+")) {
+ e++;
+ needs_prefix = true;
+ }
- if (!path_is_absolute(*i))
+ if (!path_is_absolute(e))
return -EINVAL;
- path = strdup(*i);
- if (!path)
- return -ENOMEM;
-
- set_bind_mount((*p)++, path, mode, ignore);
+ *((*p)++) = (BindMount) {
+ .path_const = e,
+ .mode = mode,
+ .ignore = ignore,
+ .has_prefix = !needs_prefix,
+ };
}
return 0;
}
-static int append_target_mounts(BindMount **p, const char *root_directory,
- const TargetMount *mounts, const size_t size, bool ignore_protect) {
+static int append_static_mounts(BindMount **p, const BindMount *mounts, unsigned n, bool ignore_protect) {
unsigned i;
assert(p);
assert(mounts);
- for (i = 0; i < size; i++) {
- bool ignore;
- /*
- * Here we assume that the ignore field is set during
- * declaration we do not support "-" at the beginning.
- */
- const TargetMount *m = &mounts[i];
- char *path;
-
- path = prefix_root(root_directory, m->path);
- if (!path)
- return -ENOMEM;
-
- if (!path_is_absolute(path))
- return -EINVAL;
-
- /*
- * Ignore paths if they are not present. First we use our
- * static tables otherwise fallback to Unit context.
- */
- ignore = m->ignore || ignore_protect;
+ /* Adds a list of static pre-defined entries */
- set_bind_mount((*p)++, path, m->mode, ignore);
- }
+ for (i = 0; i < n; i++)
+ *((*p)++) = (BindMount) {
+ .path_const = bind_mount_path(mounts+i),
+ .mode = mounts[i].mode,
+ .ignore = mounts[i].ignore || ignore_protect,
+ };
return 0;
}
-static int append_protect_kernel_tunables(BindMount **p, const char *root_directory, bool ignore_protect) {
+static int append_protect_home(BindMount **p, ProtectHome protect_home, bool ignore_protect) {
assert(p);
- return append_target_mounts(p, root_directory, protect_kernel_tunables_table,
- ELEMENTSOF(protect_kernel_tunables_table), ignore_protect);
-}
-
-static int append_protect_kernel_modules(BindMount **p, const char *root_directory, bool ignore_protect) {
- assert(p);
-
- return append_target_mounts(p, root_directory, protect_kernel_modules_table,
- ELEMENTSOF(protect_kernel_modules_table), ignore_protect);
-}
-
-static int append_protect_home(BindMount **p, const char *root_directory, ProtectHome protect_home, bool ignore_protect) {
- int r = 0;
-
- assert(p);
+ switch (protect_home) {
- if (protect_home == PROTECT_HOME_NO)
+ case PROTECT_HOME_NO:
return 0;
- switch (protect_home) {
case PROTECT_HOME_READ_ONLY:
- r = append_target_mounts(p, root_directory, protect_home_read_only_table,
- ELEMENTSOF(protect_home_read_only_table),
- ignore_protect);
- break;
+ return append_static_mounts(p, protect_home_read_only_table, ELEMENTSOF(protect_home_read_only_table), ignore_protect);
+
case PROTECT_HOME_YES:
- r = append_target_mounts(p, root_directory, protect_home_yes_table,
- ELEMENTSOF(protect_home_yes_table), ignore_protect);
- break;
+ return append_static_mounts(p, protect_home_yes_table, ELEMENTSOF(protect_home_yes_table), ignore_protect);
+
default:
- r = -EINVAL;
- break;
+ assert_not_reached("Unexpected ProtectHome= value");
}
-
- return r;
}
-static int append_protect_system(BindMount **p, const char *root_directory, ProtectSystem protect_system, bool ignore_protect) {
- int r = 0;
-
+static int append_protect_system(BindMount **p, ProtectSystem protect_system, bool ignore_protect) {
assert(p);
- if (protect_system == PROTECT_SYSTEM_NO)
+ switch (protect_system) {
+
+ case PROTECT_SYSTEM_NO:
return 0;
- switch (protect_system) {
case PROTECT_SYSTEM_STRICT:
- r = append_target_mounts(p, root_directory, protect_system_strict_table,
- ELEMENTSOF(protect_system_strict_table), ignore_protect);
- break;
+ return append_static_mounts(p, protect_system_strict_table, ELEMENTSOF(protect_system_strict_table), ignore_protect);
+
case PROTECT_SYSTEM_YES:
- r = append_target_mounts(p, root_directory, protect_system_yes_table,
- ELEMENTSOF(protect_system_yes_table), ignore_protect);
- break;
+ return append_static_mounts(p, protect_system_yes_table, ELEMENTSOF(protect_system_yes_table), ignore_protect);
+
case PROTECT_SYSTEM_FULL:
- r = append_target_mounts(p, root_directory, protect_system_full_table,
- ELEMENTSOF(protect_system_full_table), ignore_protect);
- break;
+ return append_static_mounts(p, protect_system_full_table, ELEMENTSOF(protect_system_full_table), ignore_protect);
+
default:
- r = -EINVAL;
- break;
+ assert_not_reached("Unexpected ProtectSystem= value");
}
-
- return r;
}
static int mount_path_compare(const void *a, const void *b) {
@@ -309,7 +258,7 @@ static int mount_path_compare(const void *a, const void *b) {
int d;
/* If the paths are not equal, then order prefixes first */
- d = path_compare(p->path, q->path);
+ d = path_compare(bind_mount_path(p), bind_mount_path(q));
if (d != 0)
return d;
@@ -323,6 +272,34 @@ static int mount_path_compare(const void *a, const void *b) {
return 0;
}
+static int prefix_where_needed(BindMount *m, unsigned n, const char *root_directory) {
+ unsigned i;
+
+ /* Prefixes all paths in the bind mount table with the root directory if it is specified and the entry needs
+ * that. */
+
+ if (!root_directory)
+ return 0;
+
+ for (i = 0; i < n; i++) {
+ char *s;
+
+ if (m[i].has_prefix)
+ continue;
+
+ s = prefix_root(root_directory, bind_mount_path(m+i));
+ if (!s)
+ return -ENOMEM;
+
+ free(m[i].path_malloc);
+ m[i].path_malloc = s;
+
+ m[i].has_prefix = true;
+ }
+
+ return 0;
+}
+
static void drop_duplicates(BindMount *m, unsigned *n) {
BindMount *f, *t, *previous;
@@ -331,13 +308,13 @@ static void drop_duplicates(BindMount *m, unsigned *n) {
/* Drops duplicate entries. Expects that the array is properly ordered already. */
- for (f = m, t = m, previous = NULL; f < m+*n; f++) {
+ for (f = m, t = m, previous = NULL; f < m + *n; f++) {
/* The first one wins (which is the one with the more restrictive mode), see mount_path_compare()
* above. */
- if (previous && path_equal(f->path, previous->path)) {
- log_debug("%s is duplicate.", f->path);
- f->path = mfree(f->path);
+ if (previous && path_equal(bind_mount_path(f), bind_mount_path(previous))) {
+ log_debug("%s is duplicate.", bind_mount_path(f));
+ f->path_malloc = mfree(f->path_malloc);
continue;
}
@@ -359,17 +336,17 @@ static void drop_inaccessible(BindMount *m, unsigned *n) {
/* Drops all entries obstructed by another entry further up the tree. Expects that the array is properly
* ordered already. */
- for (f = m, t = m; f < m+*n; f++) {
+ for (f = m, t = m; f < m + *n; f++) {
/* If we found a path set for INACCESSIBLE earlier, and this entry has it as prefix we should drop
* it, as inaccessible paths really should drop the entire subtree. */
- if (clear && path_startswith(f->path, clear)) {
- log_debug("%s is masked by %s.", f->path, clear);
- f->path = mfree(f->path);
+ if (clear && path_startswith(bind_mount_path(f), clear)) {
+ log_debug("%s is masked by %s.", bind_mount_path(f), clear);
+ f->path_malloc = mfree(f->path_malloc);
continue;
}
- clear = f->mode == INACCESSIBLE ? f->path : NULL;
+ clear = f->mode == INACCESSIBLE ? bind_mount_path(f) : NULL;
*t = *f;
t++;
@@ -387,7 +364,7 @@ static void drop_nop(BindMount *m, unsigned *n) {
/* Drops all entries which have an immediate parent that has the same type, as they are redundant. Assumes the
* list is ordered by prefixes. */
- for (f = m, t = m; f < m+*n; f++) {
+ for (f = m, t = m; f < m + *n; f++) {
/* Only suppress such subtrees for READONLY and READWRITE entries */
if (IN_SET(f->mode, READONLY, READWRITE)) {
@@ -396,7 +373,7 @@ static void drop_nop(BindMount *m, unsigned *n) {
/* Now let's find the first parent of the entry we are looking at. */
for (p = t-1; p >= m; p--) {
- if (path_startswith(f->path, p->path)) {
+ if (path_startswith(bind_mount_path(f), bind_mount_path(p))) {
found = true;
break;
}
@@ -404,8 +381,8 @@ static void drop_nop(BindMount *m, unsigned *n) {
/* We found it, let's see if it's the same mode, if so, we can drop this entry */
if (found && p->mode == f->mode) {
- log_debug("%s is redundant by %s", f->path, p->path);
- f->path = mfree(f->path);
+ log_debug("%s is redundant by %s", bind_mount_path(f), bind_mount_path(p));
+ f->path_malloc = mfree(f->path_malloc);
continue;
}
}
@@ -423,16 +400,17 @@ static void drop_outside_root(const char *root_directory, BindMount *m, unsigned
assert(m);
assert(n);
+ /* Nothing to do */
if (!root_directory)
return;
/* Drops all mounts that are outside of the root directory. */
- for (f = m, t = m; f < m+*n; f++) {
+ for (f = m, t = m; f < m + *n; f++) {
- if (!path_startswith(f->path, root_directory)) {
- log_debug("%s is outside of root directory.", f->path);
- f->path = mfree(f->path);
+ if (!path_startswith(bind_mount_path(f), root_directory)) {
+ log_debug("%s is outside of root directory.", bind_mount_path(f));
+ f->path_malloc = mfree(f->path_malloc);
continue;
}
@@ -548,11 +526,11 @@ static int mount_dev(BindMount *m) {
* missing when the service is started with RootDirectory. This is
* consistent with mount units creating the mount points when missing.
*/
- (void) mkdir_p_label(m->path, 0755);
+ (void) mkdir_p_label(bind_mount_path(m), 0755);
/* Unmount everything in old /dev */
- umount_recursive(m->path, 0);
- if (mount(dev, m->path, NULL, MS_MOVE, NULL) < 0) {
+ umount_recursive(bind_mount_path(m), 0);
+ if (mount(dev, bind_mount_path(m), NULL, MS_MOVE, NULL) < 0) {
r = -errno;
goto fail;
}
@@ -592,7 +570,7 @@ static int apply_mount(
assert(m);
- log_debug("Applying namespace mount on %s", m->path);
+ log_debug("Applying namespace mount on %s", bind_mount_path(m));
switch (m->mode) {
@@ -602,10 +580,10 @@ static int apply_mount(
/* First, get rid of everything that is below if there
* is anything... Then, overmount it with an
* inaccessible path. */
- (void) umount_recursive(m->path, 0);
+ (void) umount_recursive(bind_mount_path(m), 0);
- if (lstat(m->path, &target) < 0)
- return log_debug_errno(errno, "Failed to lstat() %s to determine what to mount over it: %m", m->path);
+ if (lstat(bind_mount_path(m), &target) < 0)
+ return log_debug_errno(errno, "Failed to lstat() %s to determine what to mount over it: %m", bind_mount_path(m));
what = mode_to_inaccessible_node(target.st_mode);
if (!what) {
@@ -618,13 +596,13 @@ static int apply_mount(
case READONLY:
case READWRITE:
- r = path_is_mount_point(m->path, 0);
+ r = path_is_mount_point(bind_mount_path(m), 0);
if (r < 0)
- return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", m->path);
+ return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", bind_mount_path(m));
if (r > 0) /* Nothing to do here, it is already a mount. We just later toggle the MS_RDONLY bit for the mount point if needed. */
return 0;
/* This isn't a mount point yet, let's make it one. */
- what = m->path;
+ what = bind_mount_path(m);
break;
case PRIVATE_TMP:
@@ -644,10 +622,10 @@ static int apply_mount(
assert(what);
- if (mount(what, m->path, NULL, MS_BIND|MS_REC, NULL) < 0)
- return log_debug_errno(errno, "Failed to mount %s to %s: %m", what, m->path);
+ if (mount(what, bind_mount_path(m), NULL, MS_BIND|MS_REC, NULL) < 0)
+ return log_debug_errno(errno, "Failed to mount %s to %s: %m", what, bind_mount_path(m));
- log_debug("Successfully mounted %s to %s", what, m->path);
+ log_debug("Successfully mounted %s to %s", what, bind_mount_path(m));
return 0;
}
@@ -657,9 +635,9 @@ static int make_read_only(BindMount *m, char **blacklist) {
assert(m);
if (IN_SET(m->mode, INACCESSIBLE, READONLY))
- r = bind_remount_recursive(m->path, true, blacklist);
+ r = bind_remount_recursive(bind_mount_path(m), true, blacklist);
else if (m->mode == PRIVATE_DEV) { /* Can be readonly but the submounts can't*/
- if (mount(NULL, m->path, NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL) < 0)
+ if (mount(NULL, bind_mount_path(m), NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL) < 0)
r = -errno;
} else
return 0;
@@ -671,9 +649,10 @@ static int make_read_only(BindMount *m, char **blacklist) {
return r;
}
+/* Chase symlinks and remove failed paths from mounts */
static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned *n) {
BindMount *f, *t;
- int r;
+ int r = 0;
assert(m);
assert(n);
@@ -684,21 +663,26 @@ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned
for (f = m, t = m; f < m + *n; f++) {
_cleanup_free_ char *chased = NULL;
+ int k;
+
+ k = chase_symlinks(bind_mount_path(f), root_directory, &chased);
+ if (k < 0) {
+ /* Get only real errors */
+ if (r >= 0 && (k != -ENOENT || !f->ignore))
+ r = k;
- r = chase_symlinks(f->path, root_directory, &chased);
- if (r == -ENOENT && f->ignore) {
- /* Doesn't exist? Then remove it! */
- f->path = mfree(f->path);
+ /* Doesn't exist or failed? Then remove it and continue! */
+ log_debug_errno(k, "Failed to chase symlinks for %s: %m", bind_mount_path(f));
+ f->path_malloc = mfree(f->path_malloc);
continue;
}
- if (r < 0)
- return log_debug_errno(r, "Failed to chase symlinks for %s: %m", f->path);
- if (!path_equal(f->path, chased)) {
- log_debug("Chased %s → %s", f->path, chased);
- r = free_and_replace(f->path, chased);
- if (r < 0)
- return r;
+ if (!path_equal(bind_mount_path(f), chased)) {
+ log_debug("Chased %s → %s", bind_mount_path(f), chased);
+
+ free(f->path_malloc);
+ f->path_malloc = chased;
+ chased = NULL;
}
*t = *f;
@@ -706,7 +690,7 @@ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned
}
*n = t - m;
- return 0;
+ return r;
}
static unsigned namespace_calculate_mounts(
@@ -778,67 +762,73 @@ int setup_namespace(
if (n_mounts > 0) {
m = mounts = (BindMount *) alloca0(n_mounts * sizeof(BindMount));
- r = append_mounts(&m, read_write_paths, READWRITE);
+ r = append_access_mounts(&m, read_write_paths, READWRITE);
if (r < 0)
goto finish;
- r = append_mounts(&m, read_only_paths, READONLY);
+ r = append_access_mounts(&m, read_only_paths, READONLY);
if (r < 0)
goto finish;
- r = append_mounts(&m, inaccessible_paths, INACCESSIBLE);
+ r = append_access_mounts(&m, inaccessible_paths, INACCESSIBLE);
if (r < 0)
goto finish;
if (tmp_dir) {
- r = append_one_mount(&m, root_directory, "/tmp", PRIVATE_TMP, false);
- if (r < 0)
- goto finish;
+ *(m++) = (BindMount) {
+ .path_const = "/tmp",
+ .mode = PRIVATE_TMP,
+ };
}
if (var_tmp_dir) {
- r = append_one_mount(&m, root_directory, "/var/tmp", PRIVATE_VAR_TMP, false);
- if (r < 0)
- goto finish;
+ *(m++) = (BindMount) {
+ .path_const = "/var/tmp",
+ .mode = PRIVATE_VAR_TMP,
+ };
}
if (ns_info->private_dev) {
- r = append_one_mount(&m, root_directory, "/dev", PRIVATE_DEV, false);
- if (r < 0)
- goto finish;
+ *(m++) = (BindMount) {
+ .path_const = "/dev",
+ .mode = PRIVATE_DEV,
+ };
}
if (ns_info->protect_kernel_tunables) {
- r = append_protect_kernel_tunables(&m, root_directory,
- ns_info->ignore_protect_paths);
+ r = append_static_mounts(&m, protect_kernel_tunables_table, ELEMENTSOF(protect_kernel_tunables_table), ns_info->ignore_protect_paths);
if (r < 0)
goto finish;
}
if (ns_info->protect_kernel_modules) {
- r = append_protect_kernel_modules(&m, root_directory,
- ns_info->ignore_protect_paths);
+ r = append_static_mounts(&m, protect_kernel_modules_table, ELEMENTSOF(protect_kernel_modules_table), ns_info->ignore_protect_paths);
if (r < 0)
goto finish;
}
if (ns_info->protect_control_groups) {
- r = append_one_mount(&m, root_directory, "/sys/fs/cgroup", READONLY, false);
- if (r < 0)
- goto finish;
+ *(m++) = (BindMount) {
+ .path_const = "/sys/fs/cgroup",
+ .mode = READONLY,
+ };
}
- r = append_protect_home(&m, root_directory, protect_home,
- ns_info->ignore_protect_paths);
+ r = append_protect_home(&m, protect_home, ns_info->ignore_protect_paths);
if (r < 0)
goto finish;
- r = append_protect_system(&m, root_directory, protect_system, false);
+ r = append_protect_system(&m, protect_system, false);
if (r < 0)
goto finish;
assert(mounts + n_mounts == m);
+ /* Prepend the root directory where that's necessary */
+ r = prefix_where_needed(mounts, n_mounts, root_directory);
+ if (r < 0)
+ goto finish;
+
/* Resolve symlinks manually first, as mount() will always follow them relative to the host's
* root. Moreover we want to suppress duplicates based on the resolved paths. This of course is a bit
* racy. */
@@ -895,7 +885,7 @@ int setup_namespace(
/* Create a blacklist we can pass to bind_mount_recursive() */
blacklist = newa(char*, n_mounts+1);
for (j = 0; j < n_mounts; j++)
- blacklist[j] = (char*) mounts[j].path;
+ blacklist[j] = (char*) bind_mount_path(mounts+j);
blacklist[j] = NULL;
/* Second round, flip the ro bits if necessary. */
@@ -925,7 +915,7 @@ int setup_namespace(
finish:
for (m = mounts; m < mounts + n_mounts; m++)
- free(m->path);
+ free(m->path_malloc);
return r;
}
diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf
index a61677e645..e824a2233c 100644
--- a/src/core/org.freedesktop.systemd1.conf
+++ b/src/core/org.freedesktop.systemd1.conf
@@ -66,6 +66,14 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="GetJobAfter"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
+ send_member="GetJobBefore"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="ListUnits"/>
<allow send_destination="org.freedesktop.systemd1"
@@ -250,6 +258,14 @@
send_interface="org.freedesktop.systemd1.Job"
send_member="Cancel"/>
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Job"
+ send_member="GetAfter"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Job"
+ send_member="GetBefore"/>
+
<allow receive_sender="org.freedesktop.systemd1"/>
</policy>
diff --git a/src/core/unit.c b/src/core/unit.c
index 7b78ba9a9e..cba6342eca 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -389,10 +389,8 @@ void unit_add_to_gc_queue(Unit *u) {
if (unit_check_gc(u))
return;
- LIST_PREPEND(gc_queue, u->manager->gc_queue, u);
+ LIST_PREPEND(gc_queue, u->manager->gc_unit_queue, u);
u->in_gc_queue = true;
-
- u->manager->n_in_gc_queue++;
}
void unit_add_to_dbus_queue(Unit *u) {
@@ -570,10 +568,8 @@ void unit_free(Unit *u) {
if (u->in_cleanup_queue)
LIST_REMOVE(cleanup_queue, u->manager->cleanup_queue, u);
- if (u->in_gc_queue) {
- LIST_REMOVE(gc_queue, u->manager->gc_queue, u);
- u->manager->n_in_gc_queue--;
- }
+ if (u->in_gc_queue)
+ LIST_REMOVE(gc_queue, u->manager->gc_unit_queue, u);
if (u->in_cgroup_queue)
LIST_REMOVE(cgroup_queue, u->manager->cgroup_queue, u);
@@ -3440,14 +3436,6 @@ int unit_patch_contexts(Unit *u) {
ec->working_directory_missing_ok = true;
}
- if (MANAGER_IS_USER(u->manager) &&
- (ec->syscall_whitelist ||
- !set_isempty(ec->syscall_filter) ||
- !set_isempty(ec->syscall_archs) ||
- ec->address_families_whitelist ||
- !set_isempty(ec->address_families)))
- ec->no_new_privileges = true;
-
if (ec->private_devices)
ec->capability_bounding_set &= ~((UINT64_C(1) << CAP_MKNOD) | (UINT64_C(1) << CAP_SYS_RAWIO));
diff --git a/src/core/unit.h b/src/core/unit.h
index 1ef92f3263..8052c234fd 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -441,6 +441,9 @@ struct UnitVTable {
/* True if transient units of this type are OK */
bool can_transient:1;
+
+ /* True if queued jobs of this type should be GC'ed if no other job needs them anymore */
+ bool gc_jobs:1;
};
extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX];
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index 46507de937..f6a912ae06 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -141,13 +141,14 @@ static bool mount_in_initrd(struct mntent *me) {
streq(me->mnt_dir, "/usr");
}
-static int write_idle_timeout(FILE *f, const char *where, const char *opts) {
+static int write_timeout(FILE *f, const char *where, const char *opts,
+ const char *filter, const char *variable) {
_cleanup_free_ char *timeout = NULL;
char timespan[FORMAT_TIMESPAN_MAX];
usec_t u;
int r;
- r = fstab_filter_options(opts, "x-systemd.idle-timeout\0", NULL, &timeout, NULL);
+ r = fstab_filter_options(opts, filter, NULL, &timeout, NULL);
if (r < 0)
return log_warning_errno(r, "Failed to parse options: %m");
if (r == 0)
@@ -159,11 +160,21 @@ static int write_idle_timeout(FILE *f, const char *where, const char *opts) {
return 0;
}
- fprintf(f, "TimeoutIdleSec=%s\n", format_timespan(timespan, sizeof(timespan), u, 0));
+ fprintf(f, "%s=%s\n", variable, format_timespan(timespan, sizeof(timespan), u, 0));
return 0;
}
+static int write_idle_timeout(FILE *f, const char *where, const char *opts) {
+ return write_timeout(f, where, opts,
+ "x-systemd.idle-timeout\0", "TimeoutIdleSec");
+}
+
+static int write_mount_timeout(FILE *f, const char *where, const char *opts) {
+ return write_timeout(f, where, opts,
+ "x-systemd.mount-timeout\0", "TimeoutSec");
+}
+
static int write_requires_after(FILE *f, const char *opts) {
_cleanup_strv_free_ char **names = NULL, **units = NULL;
_cleanup_free_ char *res = NULL;
@@ -327,6 +338,10 @@ static int add_mount(
if (r < 0)
return r;
+ r = write_mount_timeout(f, where, opts);
+ if (r < 0)
+ return r;
+
if (!isempty(filtered) && !streq(filtered, "defaults"))
fprintf(f, "Options=%s\n", filtered);
diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h
index 99f690897d..5aa8aca426 100644
--- a/src/libsystemd-network/dhcp-internal.h
+++ b/src/libsystemd-network/dhcp-internal.h
@@ -32,7 +32,8 @@
int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link,
uint32_t xid, const uint8_t *mac_addr,
- size_t mac_addr_len, uint16_t arp_type);
+ size_t mac_addr_len, uint16_t arp_type,
+ uint16_t port);
int dhcp_network_bind_udp_socket(be32_t address, uint16_t port);
int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
const void *packet, size_t len);
@@ -57,7 +58,7 @@ void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
uint16_t source, be32_t destination_addr,
uint16_t destination, uint16_t len);
-int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum);
+int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, uint16_t port);
/* If we are invoking callbacks of a dhcp-client, ensure unreffing the
* client from the callback doesn't destroy the object we are working
diff --git a/src/libsystemd-network/dhcp-network.c b/src/libsystemd-network/dhcp-network.c
index a9f5a0a5de..3c85bb0b54 100644
--- a/src/libsystemd-network/dhcp-network.c
+++ b/src/libsystemd-network/dhcp-network.c
@@ -36,7 +36,8 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
size_t mac_addr_len,
const uint8_t *bcast_addr,
const struct ether_addr *eth_mac,
- uint16_t arp_type, uint8_t dhcp_hlen) {
+ uint16_t arp_type, uint8_t dhcp_hlen,
+ uint16_t port) {
struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(DHCPPacket), 1, 0), /* packet >= DHCPPacket ? */
@@ -53,7 +54,7 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, udp.dest)), /* A <- UDP destination port */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_PORT_CLIENT, 1, 0), /* UDP destination port == DHCP client port ? */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, port, 1, 0), /* UDP destination port == DHCP client port ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.op)), /* A <- DHCP op */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTREPLY, 1, 0), /* op == BOOTREPLY ? */
@@ -125,7 +126,8 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link,
uint32_t xid, const uint8_t *mac_addr,
- size_t mac_addr_len, uint16_t arp_type) {
+ size_t mac_addr_len, uint16_t arp_type,
+ uint16_t port) {
static const uint8_t eth_bcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
/* Default broadcast address for IPoIB */
static const uint8_t ib_bcast[] = {
@@ -151,7 +153,7 @@ int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link,
return -EINVAL;
return _bind_raw_socket(ifindex, link, xid, mac_addr, mac_addr_len,
- bcast_addr, &eth_mac, arp_type, dhcp_hlen);
+ bcast_addr, &eth_mac, arp_type, dhcp_hlen, port);
}
int dhcp_network_bind_udp_socket(be32_t address, uint16_t port) {
diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c
index 8be774061d..40442b3636 100644
--- a/src/libsystemd-network/dhcp-packet.c
+++ b/src/libsystemd-network/dhcp-packet.c
@@ -114,7 +114,7 @@ void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
packet->ip.check = dhcp_packet_checksum((uint8_t*)&packet->ip, DHCP_IP_SIZE);
}
-int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) {
+int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, uint16_t port) {
size_t hdrlen;
assert(packet);
@@ -160,10 +160,10 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) {
return -EINVAL;
}
- if (be16toh(packet->udp.dest) != DHCP_PORT_CLIENT) {
+ if (be16toh(packet->udp.dest) != port) {
log_debug("ignoring packet: to port %u, which "
"is not the DHCP client port (%u)",
- be16toh(packet->udp.dest), DHCP_PORT_CLIENT);
+ be16toh(packet->udp.dest), port);
return -EINVAL;
}
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index 5ccb23922c..6475da2c2a 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -55,6 +55,7 @@ struct sd_dhcp_client {
sd_event_source *timeout_resend;
int ifindex;
int fd;
+ uint16_t port;
union sockaddr_union link;
sd_event_source *receive_message;
bool request_broadcast;
@@ -426,6 +427,17 @@ int sd_dhcp_client_set_vendor_class_identifier(
return 0;
}
+int sd_dhcp_client_set_client_port(
+ sd_dhcp_client *client,
+ uint16_t port) {
+
+ assert_return(client, -EINVAL);
+
+ client->port = port;
+
+ return 0;
+}
+
int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
assert_return(client, -EINVAL);
assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE);
@@ -668,7 +680,7 @@ static int dhcp_client_send_raw(
DHCPPacket *packet,
size_t len) {
- dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
+ dhcp_packet_append_ip_headers(packet, INADDR_ANY, client->port,
INADDR_BROADCAST, DHCP_PORT_SERVER, len);
return dhcp_network_send_raw_socket(client->fd, &client->link,
@@ -1120,7 +1132,7 @@ static int client_start_delayed(sd_dhcp_client *client) {
r = dhcp_network_bind_raw_socket(client->ifindex, &client->link,
client->xid, client->mac_addr,
- client->mac_addr_len, client->arp_type);
+ client->mac_addr_len, client->arp_type, client->port);
if (r < 0) {
client_stop(client, r);
return r;
@@ -1170,7 +1182,8 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
r = dhcp_network_bind_raw_socket(client->ifindex, &client->link,
client->xid, client->mac_addr,
- client->mac_addr_len, client->arp_type);
+ client->mac_addr_len, client->arp_type,
+ client->port);
if (r < 0) {
client_stop(client, r);
return 0;
@@ -1555,8 +1568,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
goto error;
}
- r = dhcp_network_bind_udp_socket(client->lease->address,
- DHCP_PORT_CLIENT);
+ r = dhcp_network_bind_udp_socket(client->lease->address, client->port);
if (r < 0) {
log_dhcp_client(client, "could not bind UDP socket");
goto error;
@@ -1766,7 +1778,7 @@ static int client_receive_message_raw(
}
}
- r = dhcp_packet_verify_headers(packet, len, checksum);
+ r = dhcp_packet_verify_headers(packet, len, checksum, client->port);
if (r < 0)
return 0;
@@ -1891,6 +1903,7 @@ int sd_dhcp_client_new(sd_dhcp_client **ret) {
client->fd = -1;
client->attempt = 1;
client->mtu = DHCP_DEFAULT_MIN_SIZE;
+ client->port = DHCP_PORT_CLIENT;
client->req_opts_size = ELEMENTSOF(default_req_opts);
client->req_opts = memdup(default_req_opts, client->req_opts_size);
diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c
index 2a101cb1fe..c10ca74b86 100644
--- a/src/libsystemd-network/test-dhcp-client.c
+++ b/src/libsystemd-network/test-dhcp-client.c
@@ -195,7 +195,7 @@ int dhcp_network_bind_raw_socket(
union sockaddr_union *link,
uint32_t id,
const uint8_t *addr, size_t addr_len,
- uint16_t arp_type) {
+ uint16_t arp_type, uint16_t port) {
if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0)
return -errno;
diff --git a/src/libsystemd/sd-bus/busctl-introspect.c b/src/libsystemd/sd-bus/busctl-introspect.c
index 09cbd9ab44..a05794941f 100644
--- a/src/libsystemd/sd-bus/busctl-introspect.c
+++ b/src/libsystemd/sd-bus/busctl-introspect.c
@@ -143,9 +143,7 @@ static int parse_xml_annotation(Context *context, uint64_t *flags) {
case STATE_NAME:
if (t == XML_ATTRIBUTE_VALUE) {
- free(field);
- field = name;
- name = NULL;
+ free_and_replace(field, name);
state = STATE_ANNOTATION;
} else {
@@ -158,9 +156,7 @@ static int parse_xml_annotation(Context *context, uint64_t *flags) {
case STATE_VALUE:
if (t == XML_ATTRIBUTE_VALUE) {
- free(value);
- value = name;
- name = NULL;
+ free_and_replace(value, name);
state = STATE_ANNOTATION;
} else {
@@ -194,6 +190,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
STATE_SIGNAL_ARG,
STATE_SIGNAL_ARG_NAME,
STATE_SIGNAL_ARG_TYPE,
+ STATE_SIGNAL_ARG_DIRECTION,
STATE_PROPERTY,
STATE_PROPERTY_NAME,
STATE_PROPERTY_TYPE,
@@ -350,11 +347,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
case STATE_INTERFACE_NAME:
if (t == XML_ATTRIBUTE_VALUE) {
- if (n_depth == 0) {
- free(context->interface_name);
- context->interface_name = name;
- name = NULL;
- }
+ if (n_depth == 0)
+ free_and_replace(context->interface_name, name);
state = STATE_INTERFACE;
} else {
@@ -409,12 +403,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
case STATE_METHOD_NAME:
if (t == XML_ATTRIBUTE_VALUE) {
-
- if (n_depth == 0) {
- free(context->member_name);
- context->member_name = name;
- name = NULL;
- }
+ if (n_depth == 0)
+ free_and_replace(context->member_name, name);
state = STATE_METHOD;
} else {
@@ -432,7 +422,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
else if (streq_ptr(name, "type"))
state = STATE_METHOD_ARG_TYPE;
else if (streq_ptr(name, "direction"))
- state = STATE_METHOD_ARG_DIRECTION;
+ state = STATE_METHOD_ARG_DIRECTION;
else {
log_error("Unexpected method <arg> attribute %s.", name);
return -EBADMSG;
@@ -458,7 +448,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
} else if (streq(argument_direction, "out")) {
if (!strextend(&context->member_result, argument_type, NULL))
return log_oom();
- }
+ } else
+ log_error("Unexpected method <arg> direction value '%s'.", argument_direction);
}
argument_type = mfree(argument_type);
@@ -487,9 +478,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
case STATE_METHOD_ARG_TYPE:
if (t == XML_ATTRIBUTE_VALUE) {
- free(argument_type);
- argument_type = name;
- name = NULL;
+ free_and_replace(argument_type, name);
state = STATE_METHOD_ARG;
} else {
@@ -502,9 +491,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
case STATE_METHOD_ARG_DIRECTION:
if (t == XML_ATTRIBUTE_VALUE) {
- free(argument_direction);
- argument_direction = name;
- name = NULL;
+ free_and_replace(argument_direction, name);
state = STATE_METHOD_ARG;
} else {
@@ -559,12 +546,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
case STATE_SIGNAL_NAME:
if (t == XML_ATTRIBUTE_VALUE) {
-
- if (n_depth == 0) {
- free(context->member_name);
- context->member_name = name;
- name = NULL;
- }
+ if (n_depth == 0)
+ free_and_replace(context->member_name, name);
state = STATE_SIGNAL;
} else {
@@ -582,6 +565,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
state = STATE_SIGNAL_ARG_NAME;
else if (streq_ptr(name, "type"))
state = STATE_SIGNAL_ARG_TYPE;
+ else if (streq_ptr(name, "direction"))
+ state = STATE_SIGNAL_ARG_DIRECTION;
else {
log_error("Unexpected signal <arg> attribute %s.", name);
return -EBADMSG;
@@ -599,8 +584,11 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
(t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) {
if (argument_type) {
- if (!strextend(&context->member_signature, argument_type, NULL))
- return log_oom();
+ if (!argument_direction || streq(argument_direction, "out")) {
+ if (!strextend(&context->member_signature, argument_type, NULL))
+ return log_oom();
+ } else
+ log_error("Unexpected signal <arg> direction value '%s'.", argument_direction);
argument_type = mfree(argument_type);
}
@@ -627,9 +615,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
case STATE_SIGNAL_ARG_TYPE:
if (t == XML_ATTRIBUTE_VALUE) {
- free(argument_type);
- argument_type = name;
- name = NULL;
+ free_and_replace(argument_type, name);
state = STATE_SIGNAL_ARG;
} else {
@@ -639,6 +625,19 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
break;
+ case STATE_SIGNAL_ARG_DIRECTION:
+
+ if (t == XML_ATTRIBUTE_VALUE) {
+ free_and_replace(argument_direction, name);
+
+ state = STATE_SIGNAL_ARG;
+ } else {
+ log_error("Unexpected token in signal <arg>. (4)");
+ return -EINVAL;
+ }
+
+ break;
+
case STATE_PROPERTY:
if (t == XML_ATTRIBUTE_NAME) {
@@ -688,12 +687,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
case STATE_PROPERTY_NAME:
if (t == XML_ATTRIBUTE_VALUE) {
+ if (n_depth == 0)
+ free_and_replace(context->member_name, name);
- if (n_depth == 0) {
- free(context->member_name);
- context->member_name = name;
- name = NULL;
- }
state = STATE_PROPERTY;
} else {
log_error("Unexpected token in <property>. (2)");
@@ -705,12 +701,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
case STATE_PROPERTY_TYPE:
if (t == XML_ATTRIBUTE_VALUE) {
-
- if (n_depth == 0) {
- free(context->member_signature);
- context->member_signature = name;
- name = NULL;
- }
+ if (n_depth == 0)
+ free_and_replace(context->member_signature, name);
state = STATE_PROPERTY;
} else {
diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c
index f8e18f23fd..0d8d99c56d 100644
--- a/src/libsystemd/sd-network/sd-network.c
+++ b/src/libsystemd/sd-network/sd-network.c
@@ -224,13 +224,8 @@ static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) {
return -ENODATA;
if (r < 0)
return r;
- if (isempty(s)) {
- *ret = NULL;
- return 0;
- }
- x = s;
- for (;;) {
+ for (x = s;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&x, &word, NULL, 0);
@@ -243,15 +238,14 @@ static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) {
if (r < 0)
return r;
- if (!GREEDY_REALLOC(ifis, allocated, c + 1))
+ if (!GREEDY_REALLOC(ifis, allocated, c + 2))
return -ENOMEM;
ifis[c++] = ifindex;
}
- if (!GREEDY_REALLOC(ifis, allocated, c + 1))
- return -ENOMEM;
- ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice*/
+ if (ifis)
+ ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice*/
*ret = ifis;
ifis = NULL;
diff --git a/src/network/.gitignore b/src/network/.gitignore
index aca55206b7..230671763d 100644
--- a/src/network/.gitignore
+++ b/src/network/.gitignore
@@ -1,3 +1,2 @@
/networkd-network-gperf.c
-/networkd-netdev-gperf.c
/networkd-gperf.c
diff --git a/src/network/netdev/.gitignore b/src/network/netdev/.gitignore
new file mode 100644
index 0000000000..0f1a65d2e6
--- /dev/null
+++ b/src/network/netdev/.gitignore
@@ -0,0 +1 @@
+/netdev-gperf.c
diff --git a/src/network/networkd-netdev-bond.c b/src/network/netdev/bond.c
index 46d1669337..19b0e8da40 100644
--- a/src/network/networkd-netdev-bond.c
+++ b/src/network/netdev/bond.c
@@ -27,7 +27,7 @@
#include "conf-parser.h"
#include "extract-word.h"
#include "missing.h"
-#include "networkd-netdev-bond.h"
+#include "netdev/bond.h"
#include "string-table.h"
#include "string-util.h"
diff --git a/src/network/networkd-netdev-bond.h b/src/network/netdev/bond.h
index b941edb344..fb88b538ed 100644
--- a/src/network/networkd-netdev-bond.h
+++ b/src/network/netdev/bond.h
@@ -22,7 +22,7 @@
#include "in-addr-util.h"
#include "list.h"
-#include "networkd-netdev.h"
+#include "netdev/netdev.h"
/*
* Maximum number of targets supported by the kernel for a single
diff --git a/src/network/networkd-netdev-bridge.c b/src/network/netdev/bridge.c
index 002ad94210..08e31b974f 100644
--- a/src/network/networkd-netdev-bridge.c
+++ b/src/network/netdev/bridge.c
@@ -22,8 +22,8 @@
#include "missing.h"
#include "netlink-util.h"
-#include "networkd.h"
-#include "networkd-netdev-bridge.h"
+#include "netdev/bridge.h"
+#include "networkd-manager.h"
/* callback for brige netdev's parameter set */
static int netdev_bridge_set_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
diff --git a/src/network/networkd-netdev-bridge.h b/src/network/netdev/bridge.h
index 53f72f1ea5..093c60d5b5 100644
--- a/src/network/networkd-netdev-bridge.h
+++ b/src/network/netdev/bridge.h
@@ -19,7 +19,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "networkd-netdev.h"
+#include "netdev/netdev.h"
typedef struct Bridge {
NetDev meta;
diff --git a/src/network/networkd-netdev-dummy.c b/src/network/netdev/dummy.c
index 6617a86c20..5e6e162931 100644
--- a/src/network/networkd-netdev-dummy.c
+++ b/src/network/netdev/dummy.c
@@ -19,7 +19,7 @@
***/
-#include "networkd-netdev-dummy.h"
+#include "netdev/dummy.h"
const NetDevVTable dummy_vtable = {
.object_size = sizeof(Dummy),
diff --git a/src/network/networkd-netdev-dummy.h b/src/network/netdev/dummy.h
index efe302267e..a908400459 100644
--- a/src/network/networkd-netdev-dummy.h
+++ b/src/network/netdev/dummy.h
@@ -19,7 +19,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "networkd-netdev.h"
+#include "netdev/netdev.h"
typedef struct Dummy {
NetDev meta;
diff --git a/src/network/networkd-netdev-ipvlan.c b/src/network/netdev/ipvlan.c
index af4177e43a..3b5c30fed8 100644
--- a/src/network/networkd-netdev-ipvlan.c
+++ b/src/network/netdev/ipvlan.c
@@ -20,7 +20,7 @@
#include <net/if.h>
#include "conf-parser.h"
-#include "networkd-netdev-ipvlan.h"
+#include "netdev/ipvlan.h"
#include "string-table.h"
static const char* const ipvlan_mode_table[_NETDEV_IPVLAN_MODE_MAX] = {
diff --git a/src/network/networkd-netdev-ipvlan.h b/src/network/netdev/ipvlan.h
index 10d4079844..7d7d0184f1 100644
--- a/src/network/networkd-netdev-ipvlan.h
+++ b/src/network/netdev/ipvlan.h
@@ -20,7 +20,7 @@
***/
#include "missing.h"
-#include "networkd-netdev.h"
+#include "netdev/netdev.h"
typedef enum IPVlanMode {
NETDEV_IPVLAN_MODE_L2 = IPVLAN_MODE_L2,
diff --git a/src/network/networkd-netdev-macvlan.c b/src/network/netdev/macvlan.c
index 48e98aa51b..93f650def5 100644
--- a/src/network/networkd-netdev-macvlan.c
+++ b/src/network/netdev/macvlan.c
@@ -20,7 +20,7 @@
#include <net/if.h>
#include "conf-parser.h"
-#include "networkd-netdev-macvlan.h"
+#include "netdev/macvlan.h"
#include "string-table.h"
static const char* const macvlan_mode_table[_NETDEV_MACVLAN_MODE_MAX] = {
diff --git a/src/network/networkd-netdev-macvlan.h b/src/network/netdev/macvlan.h
index 3663f4f051..118d55658c 100644
--- a/src/network/networkd-netdev-macvlan.h
+++ b/src/network/netdev/macvlan.h
@@ -21,7 +21,7 @@
typedef struct MacVlan MacVlan;
-#include "networkd-netdev.h"
+#include "netdev/netdev.h"
typedef enum MacVlanMode {
NETDEV_MACVLAN_MODE_PRIVATE = MACVLAN_MODE_PRIVATE,
diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf
index 323eaa8032..b3461e39a9 100644
--- a/src/network/networkd-netdev-gperf.gperf
+++ b/src/network/netdev/netdev-gperf.gperf
@@ -2,17 +2,17 @@
#include <stddef.h>
#include "conf-parser.h"
#include "network-internal.h"
-#include "networkd-netdev-bond.h"
-#include "networkd-netdev-bridge.h"
-#include "networkd-netdev-ipvlan.h"
-#include "networkd-netdev-macvlan.h"
-#include "networkd-netdev-tunnel.h"
-#include "networkd-netdev-tuntap.h"
-#include "networkd-netdev-veth.h"
-#include "networkd-netdev-vlan.h"
-#include "networkd-netdev-vxlan.h"
-#include "networkd-netdev-vrf.h"
-#include "networkd-netdev.h"
+#include "netdev/bond.h"
+#include "netdev/bridge.h"
+#include "netdev/ipvlan.h"
+#include "netdev/macvlan.h"
+#include "netdev/tunnel.h"
+#include "netdev/tuntap.h"
+#include "netdev/veth.h"
+#include "netdev/vlan.h"
+#include "netdev/vxlan.h"
+#include "netdev/vrf.h"
+#include "netdev/netdev.h"
#include "vlan-util.h"
%}
struct ConfigPerfItem;
diff --git a/src/network/networkd-netdev.c b/src/network/netdev/netdev.c
index a210ba1242..9b9e83d9db 100644
--- a/src/network/networkd-netdev.c
+++ b/src/network/netdev/netdev.c
@@ -26,13 +26,26 @@
#include "list.h"
#include "netlink-util.h"
#include "network-internal.h"
-#include "networkd-netdev.h"
-#include "networkd.h"
+#include "netdev/netdev.h"
+#include "networkd-manager.h"
#include "siphash24.h"
#include "stat-util.h"
#include "string-table.h"
#include "string-util.h"
+#include "netdev/bridge.h"
+#include "netdev/bond.h"
+#include "netdev/vlan.h"
+#include "netdev/macvlan.h"
+#include "netdev/ipvlan.h"
+#include "netdev/vxlan.h"
+#include "netdev/tunnel.h"
+#include "netdev/tuntap.h"
+#include "netdev/veth.h"
+#include "netdev/dummy.h"
+#include "netdev/vrf.h"
+#include "netdev/vcan.h"
+
const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_BRIDGE] = &bridge_vtable,
[NETDEV_KIND_BOND] = &bond_vtable,
diff --git a/src/network/networkd-netdev.h b/src/network/netdev/netdev.h
index 70ff947b99..70ff947b99 100644
--- a/src/network/networkd-netdev.h
+++ b/src/network/netdev/netdev.h
diff --git a/src/network/networkd-netdev-tunnel.c b/src/network/netdev/tunnel.c
index 9138ee4511..b03e770061 100644
--- a/src/network/networkd-netdev-tunnel.c
+++ b/src/network/netdev/tunnel.c
@@ -28,7 +28,7 @@
#include "conf-parser.h"
#include "missing.h"
#include "networkd-link.h"
-#include "networkd-netdev-tunnel.h"
+#include "netdev/tunnel.h"
#include "parse-util.h"
#include "string-table.h"
#include "string-util.h"
diff --git a/src/network/networkd-netdev-tunnel.h b/src/network/netdev/tunnel.h
index 32a46bd82f..d78c6135ee 100644
--- a/src/network/networkd-netdev-tunnel.h
+++ b/src/network/netdev/tunnel.h
@@ -21,7 +21,7 @@
#include "in-addr-util.h"
-#include "networkd-netdev.h"
+#include "netdev/netdev.h"
typedef enum Ip6TnlMode {
NETDEV_IP6_TNL_MODE_IP6IP6,
diff --git a/src/network/networkd-netdev-tuntap.c b/src/network/netdev/tuntap.c
index 088a4d8d32..3d62808842 100644
--- a/src/network/networkd-netdev-tuntap.c
+++ b/src/network/netdev/tuntap.c
@@ -27,7 +27,7 @@
#include "alloc-util.h"
#include "fd-util.h"
-#include "networkd-netdev-tuntap.h"
+#include "netdev/tuntap.h"
#include "user-util.h"
#define TUN_DEV "/dev/net/tun"
diff --git a/src/network/networkd-netdev-tuntap.h b/src/network/netdev/tuntap.h
index 120f00a353..95d3fcf1e9 100644
--- a/src/network/networkd-netdev-tuntap.h
+++ b/src/network/netdev/tuntap.h
@@ -21,7 +21,7 @@
typedef struct TunTap TunTap;
-#include "networkd-netdev.h"
+#include "netdev/netdev.h"
struct TunTap {
NetDev meta;
diff --git a/src/network/networkd-netdev-vcan.c b/src/network/netdev/vcan.c
index bfce6e1962..7f56702938 100644
--- a/src/network/networkd-netdev-vcan.c
+++ b/src/network/netdev/vcan.c
@@ -17,7 +17,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "networkd-netdev-vcan.h"
+#include "netdev/vcan.h"
const NetDevVTable vcan_vtable = {
.object_size = sizeof(VCan),
diff --git a/src/network/networkd-netdev-vcan.h b/src/network/netdev/vcan.h
index 6ba47fd70e..00838b7675 100644
--- a/src/network/networkd-netdev-vcan.h
+++ b/src/network/netdev/vcan.h
@@ -23,7 +23,7 @@ typedef struct VCan VCan;
#include <linux/can/netlink.h>
-#include "networkd-netdev.h"
+#include "netdev/netdev.h"
struct VCan {
NetDev meta;
diff --git a/src/network/networkd-netdev-veth.c b/src/network/netdev/veth.c
index b122a06c25..350b59bf03 100644
--- a/src/network/networkd-netdev-veth.c
+++ b/src/network/netdev/veth.c
@@ -22,7 +22,7 @@
#include "sd-netlink.h"
-#include "networkd-netdev-veth.h"
+#include "netdev/veth.h"
static int netdev_veth_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
Veth *v;
diff --git a/src/network/networkd-netdev-veth.h b/src/network/netdev/veth.h
index e69bfbc8f0..b00ce476e8 100644
--- a/src/network/networkd-netdev-veth.h
+++ b/src/network/netdev/veth.h
@@ -21,7 +21,7 @@
typedef struct Veth Veth;
-#include "networkd-netdev.h"
+#include "netdev/netdev.h"
struct Veth {
NetDev meta;
diff --git a/src/network/networkd-netdev-vlan.c b/src/network/netdev/vlan.c
index 3cc072388f..28c061fa4f 100644
--- a/src/network/networkd-netdev-vlan.c
+++ b/src/network/netdev/vlan.c
@@ -19,7 +19,7 @@
#include <net/if.h>
-#include "networkd-netdev-vlan.h"
+#include "netdev/vlan.h"
#include "vlan-util.h"
static int netdev_vlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) {
diff --git a/src/network/networkd-netdev-vlan.h b/src/network/netdev/vlan.h
index 2dfe314b6e..fade899997 100644
--- a/src/network/networkd-netdev-vlan.h
+++ b/src/network/netdev/vlan.h
@@ -21,7 +21,7 @@
typedef struct VLan VLan;
-#include "networkd-netdev.h"
+#include "netdev/netdev.h"
struct VLan {
NetDev meta;
diff --git a/src/network/networkd-netdev-vrf.c b/src/network/netdev/vrf.c
index 89bd142e8c..f48b413102 100644
--- a/src/network/networkd-netdev-vrf.c
+++ b/src/network/netdev/vrf.c
@@ -21,7 +21,7 @@
#include "sd-netlink.h"
#include "missing.h"
-#include "networkd-netdev-vrf.h"
+#include "netdev/vrf.h"
static int netdev_vrf_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
Vrf *v;
diff --git a/src/network/networkd-netdev-vrf.h b/src/network/netdev/vrf.h
index 3d92a26a4d..00f54ed96d 100644
--- a/src/network/networkd-netdev-vrf.h
+++ b/src/network/netdev/vrf.h
@@ -21,7 +21,7 @@
typedef struct Vrf Vrf;
-#include "networkd-netdev.h"
+#include "netdev/netdev.h"
struct Vrf {
NetDev meta;
diff --git a/src/network/networkd-netdev-vxlan.c b/src/network/netdev/vxlan.c
index 706e52b698..10c892b044 100644
--- a/src/network/networkd-netdev-vxlan.c
+++ b/src/network/netdev/vxlan.c
@@ -28,7 +28,7 @@
#include "missing.h"
#include "networkd-link.h"
-#include "networkd-netdev-vxlan.h"
+#include "netdev/vxlan.h"
static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
VxLan *v;
diff --git a/src/network/networkd-netdev-vxlan.h b/src/network/netdev/vxlan.h
index 3906820afb..6c3081d5fc 100644
--- a/src/network/networkd-netdev-vxlan.h
+++ b/src/network/netdev/vxlan.h
@@ -22,7 +22,7 @@
typedef struct VxLan VxLan;
#include "in-addr-util.h"
-#include "networkd-netdev.h"
+#include "netdev/netdev.h"
#define VXLAN_VID_MAX (1u << 24) - 1
diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c
index ebc6c9eb9e..a63b925a4a 100644
--- a/src/network/networkd-address-pool.c
+++ b/src/network/networkd-address-pool.c
@@ -19,7 +19,7 @@
#include "alloc-util.h"
#include "networkd-address-pool.h"
-#include "networkd.h"
+#include "networkd-manager.h"
#include "set.h"
#include "string-util.h"
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index ed52d5e42d..2b698d9531 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -24,7 +24,7 @@
#include "firewall-util.h"
#include "netlink-util.h"
#include "networkd-address.h"
-#include "networkd.h"
+#include "networkd-manager.h"
#include "parse-util.h"
#include "set.h"
#include "socket-util.h"
diff --git a/src/network/networkd-brvlan.c b/src/network/networkd-brvlan.c
index 18ecd86858..fa5d3ee7fa 100644
--- a/src/network/networkd-brvlan.c
+++ b/src/network/networkd-brvlan.c
@@ -25,7 +25,9 @@
#include "conf-parser.h"
#include "netlink-util.h"
#include "networkd-brvlan.h"
-#include "networkd.h"
+#include "networkd-link.h"
+#include "networkd-manager.h"
+#include "networkd-network.h"
#include "parse-util.h"
#include "vlan-util.h"
diff --git a/src/network/networkd-conf.c b/src/network/networkd-conf.c
index 49bb8c18f6..aaa27f311d 100644
--- a/src/network/networkd-conf.c
+++ b/src/network/networkd-conf.c
@@ -22,8 +22,10 @@
#include "conf-parser.h"
#include "def.h"
#include "dhcp-identifier.h"
+#include "extract-word.h"
#include "hexdecoct.h"
#include "networkd-conf.h"
+#include "networkd-network.h"
#include "string-table.h"
int manager_parse_config_file(Manager *m) {
diff --git a/src/network/networkd-conf.h b/src/network/networkd-conf.h
index c7bfb42a72..93819626ba 100644
--- a/src/network/networkd-conf.h
+++ b/src/network/networkd-conf.h
@@ -19,7 +19,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "networkd.h"
+typedef struct Manager Manager;
int manager_parse_config_file(Manager *m);
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 76d3d132ea..614bceefab 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -24,7 +24,9 @@
#include "dhcp-lease-internal.h"
#include "hostname-util.h"
#include "network-internal.h"
-#include "networkd.h"
+#include "networkd-link.h"
+#include "networkd-manager.h"
+#include "networkd-network.h"
static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
void *userdata) {
@@ -630,6 +632,12 @@ int dhcp4_configure(Link *link) {
return r;
}
+ if (link->network->dhcp_client_port) {
+ r = sd_dhcp_client_set_client_port(link->dhcp_client, link->network->dhcp_client_port);
+ if (r < 0)
+ return r;
+ }
+
switch (link->network->dhcp_client_identifier) {
case DHCP_CLIENT_ID_DUID: {
/* If configured, apply user specified DUID and/or IAID */
diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c
index 15acf56a5f..6ba2d170e7 100644
--- a/src/network/networkd-dhcp6.c
+++ b/src/network/networkd-dhcp6.c
@@ -23,7 +23,8 @@
#include "sd-dhcp6-client.h"
#include "network-internal.h"
-#include "networkd.h"
+#include "networkd-link.h"
+#include "networkd-manager.h"
static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
@@ -125,7 +126,6 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
assert(link);
assert(link->network);
- assert(link->manager);
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return;
diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c
index ed5a47589e..3d7f4d2b2d 100644
--- a/src/network/networkd-fdb.c
+++ b/src/network/networkd-fdb.c
@@ -22,9 +22,10 @@
#include "alloc-util.h"
#include "conf-parser.h"
+#include "netdev/bridge.h"
#include "netlink-util.h"
#include "networkd-fdb.h"
-#include "networkd.h"
+#include "networkd-manager.h"
#include "util.h"
#include "vlan-util.h"
diff --git a/src/network/networkd-gperf.gperf b/src/network/networkd-gperf.gperf
index 3fdfe74955..eca436d9fd 100644
--- a/src/network/networkd-gperf.gperf
+++ b/src/network/networkd-gperf.gperf
@@ -2,6 +2,7 @@
#include <stddef.h>
#include "conf-parser.h"
#include "networkd-conf.h"
+#include "networkd-manager.h"
%}
struct ConfigPerfItem;
%null_strings
diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c
index 2d81311e81..7ba05dbec3 100644
--- a/src/network/networkd-ipv4ll.c
+++ b/src/network/networkd-ipv4ll.c
@@ -21,7 +21,9 @@
#include <linux/if.h>
#include "network-internal.h"
-#include "networkd.h"
+#include "networkd-address.h"
+#include "networkd-manager.h"
+#include "networkd-link.h"
static int ipv4ll_address_lost(Link *link) {
_cleanup_address_free_ Address *address = NULL;
@@ -171,7 +173,6 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) {
assert(link);
assert(link->network);
- assert(link->manager);
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return;
diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c
index 532557ed6c..c39c648334 100644
--- a/src/network/networkd-link-bus.c
+++ b/src/network/networkd-link-bus.c
@@ -20,7 +20,7 @@
#include "alloc-util.h"
#include "bus-util.h"
#include "networkd-link.h"
-#include "networkd.h"
+#include "networkd-manager.h"
#include "parse-util.h"
#include "strv.h"
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index aefe7335b9..0b634572a9 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -29,8 +29,8 @@
#include "netlink-util.h"
#include "network-internal.h"
#include "networkd-lldp-tx.h"
+#include "networkd-manager.h"
#include "networkd-ndisc.h"
-#include "networkd.h"
#include "set.h"
#include "socket-util.h"
#include "stdio-util.h"
@@ -2632,7 +2632,7 @@ static int link_initialized_and_synced(sd_netlink *rtnl, sd_netlink_message *m,
log_link_debug(link, "Ignoring DHCP server for loopback link");
}
- r = network_apply(link->manager, network, link);
+ r = network_apply(network, link);
if (r < 0)
return r;
}
@@ -2728,7 +2728,7 @@ static int link_load(Link *link) {
goto network_file_fail;
}
- r = network_apply(link->manager, network, link);
+ r = network_apply(network, link);
if (r < 0)
return log_link_error_errno(link, r, "Failed to apply network %s: %m", basename(network_file));
}
diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c
index 3aa768388b..2de63ce746 100644
--- a/src/network/networkd-lldp-tx.c
+++ b/src/network/networkd-lldp-tx.c
@@ -26,7 +26,7 @@
#include "fileio.h"
#include "hostname-util.h"
#include "networkd-lldp-tx.h"
-#include "networkd.h"
+#include "networkd-manager.h"
#include "parse-util.h"
#include "random-util.h"
#include "socket-util.h"
diff --git a/src/network/networkd-manager-bus.c b/src/network/networkd-manager-bus.c
index 0c429b9471..cbb1b93031 100644
--- a/src/network/networkd-manager-bus.c
+++ b/src/network/networkd-manager-bus.c
@@ -19,7 +19,7 @@
#include "alloc-util.h"
#include "bus-util.h"
-#include "networkd.h"
+#include "networkd-manager.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_operational_state, link_operstate, LinkOperationalState);
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index 9174dcc7f4..a1252c9b51 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -33,7 +33,7 @@
#include "libudev-private.h"
#include "local-addresses.h"
#include "netlink-util.h"
-#include "networkd.h"
+#include "networkd-manager.h"
#include "ordered-set.h"
#include "path-util.h"
#include "set.h"
diff --git a/src/network/networkd.h b/src/network/networkd-manager.h
index cb1b73145e..a90d9a933f 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd-manager.h
@@ -32,20 +32,7 @@
#include "networkd-address-pool.h"
#include "networkd-link.h"
-#include "networkd-netdev-bond.h"
-#include "networkd-netdev-bridge.h"
-#include "networkd-netdev-dummy.h"
-#include "networkd-netdev-ipvlan.h"
-#include "networkd-netdev-macvlan.h"
-#include "networkd-netdev-tunnel.h"
-#include "networkd-netdev-tuntap.h"
-#include "networkd-netdev-veth.h"
-#include "networkd-netdev-vlan.h"
-#include "networkd-netdev-vrf.h"
-#include "networkd-netdev-vxlan.h"
-#include "networkd-netdev-vcan.h"
#include "networkd-network.h"
-#include "networkd-util.h"
extern const char* const network_dirs[];
diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c
index 4853791aa5..70283e5347 100644
--- a/src/network/networkd-ndisc.c
+++ b/src/network/networkd-ndisc.c
@@ -18,11 +18,12 @@
***/
#include <netinet/icmp6.h>
+#include <arpa/inet.h>
#include "sd-ndisc.h"
-#include "networkd.h"
#include "networkd-ndisc.h"
+#include "networkd-route.h"
#define NDISC_DNSSL_MAX 64U
#define NDISC_RDNSS_MAX 64U
diff --git a/src/network/networkd-network-bus.c b/src/network/networkd-network-bus.c
index 6e21676d23..3b835b52f9 100644
--- a/src/network/networkd-network-bus.c
+++ b/src/network/networkd-network-bus.c
@@ -18,7 +18,7 @@
***/
#include "alloc-util.h"
-#include "networkd.h"
+#include "networkd-manager.h"
#include "string-util.h"
#include "strv.h"
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index bcf8186c33..efd3176ac3 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -1,8 +1,8 @@
%{
#include <stddef.h>
#include "conf-parser.h"
-#include "networkd.h"
#include "networkd-conf.h"
+#include "networkd-network.h"
#include "network-internal.h"
#include "vlan-util.h"
%}
@@ -100,6 +100,7 @@ DHCP.RouteMetric, config_parse_unsigned,
DHCP.RouteTable, config_parse_dhcp_route_table, 0, offsetof(Network, dhcp_route_table)
DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
DHCP.IAID, config_parse_iaid, 0, offsetof(Network, iaid)
+DHCP.ListenPort, config_parse_uint32, 0, offsetof(Network, dhcp_client_port)
IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns)
IPv6AcceptRA.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains)
IPv6AcceptRA.RouteTable, config_parse_dhcp_route_table, 0, offsetof(Network, ipv6_accept_ra_route_table)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 0dc00e874d..31e899eecd 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -27,8 +27,8 @@
#include "fd-util.h"
#include "hostname-util.h"
#include "network-internal.h"
+#include "networkd-manager.h"
#include "networkd-network.h"
-#include "networkd.h"
#include "parse-util.h"
#include "set.h"
#include "stat-util.h"
@@ -368,10 +368,9 @@ int network_get(Manager *manager, struct udev_device *device,
return -ENOENT;
}
-int network_apply(Manager *manager, Network *network, Link *link) {
+int network_apply(Network *network, Link *link) {
int r;
- assert(manager);
assert(network);
assert(link);
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 42fc82d392..e956a59fe3 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -31,9 +31,9 @@
#include "networkd-brvlan.h"
#include "networkd-fdb.h"
#include "networkd-lldp-tx.h"
-#include "networkd-netdev.h"
#include "networkd-route.h"
#include "networkd-util.h"
+#include "netdev/netdev.h"
#define DHCP_ROUTE_METRIC 1024
#define IPV4LL_ROUTE_METRIC 2048
@@ -124,6 +124,7 @@ struct Network {
bool dhcp_use_timezone;
unsigned dhcp_route_metric;
uint32_t dhcp_route_table;
+ uint32_t dhcp_client_port;
/* DHCP Server Support */
bool dhcp_server;
@@ -212,7 +213,7 @@ int network_load(Manager *manager);
int network_get_by_name(Manager *manager, const char *name, Network **ret);
int network_get(Manager *manager, struct udev_device *device, const char *ifname, const struct ether_addr *mac, Network **ret);
-int network_apply(Manager *manager, Network *network, Link *link);
+int network_apply(Network *network, Link *link);
bool network_has_static_ipv6_addresses(Network *network);
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index 6f60ee5e31..f78e106991 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -21,8 +21,8 @@
#include "conf-parser.h"
#include "in-addr-util.h"
#include "netlink-util.h"
+#include "networkd-manager.h"
#include "networkd-route.h"
-#include "networkd.h"
#include "parse-util.h"
#include "set.h"
#include "string-util.h"
diff --git a/src/network/networkd.c b/src/network/networkd.c
index c8f81a2ca6..2851432eff 100644
--- a/src/network/networkd.c
+++ b/src/network/networkd.c
@@ -20,8 +20,8 @@
#include "sd-daemon.h"
#include "capability-util.h"
-#include "networkd.h"
#include "networkd-conf.h"
+#include "networkd-manager.h"
#include "signal-util.h"
#include "user-util.h"
diff --git a/src/network/test-network-tables.c b/src/network/test-network-tables.c
index adbe09a5e1..eee91d11d2 100644
--- a/src/network/test-network-tables.c
+++ b/src/network/test-network-tables.c
@@ -2,9 +2,11 @@
#include "dhcp6-protocol.h"
#include "ethtool-util.h"
#include "netlink-internal.h"
-#include "networkd-netdev-bond.h"
-#include "networkd-netdev-macvlan.h"
-#include "networkd.h"
+#include "netdev/bond.h"
+#include "netdev/ipvlan.h"
+#include "netdev/macvlan.h"
+#include "networkd-link.h"
+#include "networkd-util.h"
#include "test-tables.h"
int main(int argc, char **argv) {
diff --git a/src/network/test-network.c b/src/network/test-network.c
index 855646173f..93184a7f88 100644
--- a/src/network/test-network.c
+++ b/src/network/test-network.c
@@ -20,7 +20,7 @@
#include "alloc-util.h"
#include "dhcp-lease-internal.h"
#include "network-internal.h"
-#include "networkd.h"
+#include "networkd-manager.h"
static void test_deserialize_in_addr(void) {
_cleanup_free_ struct in_addr *addresses = NULL;
diff --git a/src/network/networkd-wait-online-link.c b/src/network/wait-online/link.c
index e63ba07e90..bd8578cf93 100644
--- a/src/network/networkd-wait-online-link.c
+++ b/src/network/wait-online/link.c
@@ -21,7 +21,9 @@
#include "sd-network.h"
#include "alloc-util.h"
-#include "networkd-wait-online-link.h"
+#include "hashmap.h"
+#include "link.h"
+#include "manager.h"
#include "string-util.h"
int link_new(Manager *m, Link **ret, int ifindex, const char *ifname) {
diff --git a/src/network/networkd-wait-online-link.h b/src/network/wait-online/link.h
index dc35085c55..c846e60c45 100644
--- a/src/network/networkd-wait-online-link.h
+++ b/src/network/wait-online/link.h
@@ -20,9 +20,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-typedef struct Link Link;
+#include "sd-netlink.h"
-#include "networkd-wait-online.h"
+typedef struct Link Link;
+typedef struct Manager Manager;
struct Link {
Manager *manager;
diff --git a/src/network/networkd-wait-online-manager.c b/src/network/wait-online/manager.c
index 725b3310dd..d51b0a59d6 100644
--- a/src/network/networkd-wait-online-manager.c
+++ b/src/network/wait-online/manager.c
@@ -22,10 +22,10 @@
#include <fnmatch.h>
#include "alloc-util.h"
+#include "link.h"
+#include "manager.h"
#include "netlink-util.h"
#include "network-internal.h"
-#include "networkd-wait-online-link.h"
-#include "networkd-wait-online.h"
#include "time-util.h"
#include "util.h"
diff --git a/src/network/networkd-wait-online.h b/src/network/wait-online/manager.h
index f91995c306..052f6b9780 100644
--- a/src/network/networkd-wait-online.h
+++ b/src/network/wait-online/manager.h
@@ -26,8 +26,7 @@
#include "hashmap.h"
typedef struct Manager Manager;
-
-#include "networkd-wait-online-link.h"
+typedef struct Link Link;
struct Manager {
Hashmap *links;
diff --git a/src/network/networkd-wait-online.c b/src/network/wait-online/wait-online.c
index 3220c4b7ef..268cbdb629 100644
--- a/src/network/networkd-wait-online.c
+++ b/src/network/wait-online/wait-online.c
@@ -22,7 +22,7 @@
#include "sd-daemon.h"
-#include "networkd-wait-online.h"
+#include "manager.h"
#include "signal-util.h"
#include "strv.h"
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 9b9ae909c9..50d8aa049c 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -111,6 +111,8 @@
* the init process in the container pid can send messages to nspawn following the sd_notify(3) protocol */
#define NSPAWN_NOTIFY_SOCKET_PATH "/run/systemd/nspawn/notify"
+#define EXIT_FORCE_RESTART 133
+
typedef enum ContainerStatus {
CONTAINER_TERMINATED,
CONTAINER_REBOOTED
@@ -4002,7 +4004,7 @@ static int run(int master,
* because 133 is special-cased in the service file to reboot the container.
* otherwise → The container exited with zero status and a reboot was not requested.
*/
- if (r == 133)
+ if (r == EXIT_FORCE_RESTART)
r = EXIT_FAILURE; /* replace 133 with the general failure code */
*ret = r;
return 0; /* finito */
@@ -4017,8 +4019,8 @@ static int run(int master,
* file uses RestartForceExitStatus=133 so that this results in a full
* nspawn restart. This is necessary since we might have cgroup parameters
* set we want to have flushed out. */
- *ret = 0;
- return 133;
+ *ret = EXIT_FORCE_RESTART;
+ return 0; /* finito */
}
expose_port_flush(arg_expose_ports, exposed);
@@ -4276,8 +4278,8 @@ int main(int argc, char *argv[]) {
finish:
sd_notify(false,
- "STOPPING=1\n"
- "STATUS=Terminating...");
+ r == 0 && ret == EXIT_FORCE_RESTART ? "STOPPING=1\nSTATUS=Restarting..." :
+ "STOPPING=1\nSTATUS=Terminating...");
if (pid > 0)
kill(pid, SIGKILL);
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
index 13e1f91192..e7e5c5f5a7 100644
--- a/src/resolve/resolved-link.c
+++ b/src/resolve/resolved-link.c
@@ -997,6 +997,7 @@ int link_load_user(Link *l) {
*ntas = NULL;
ResolveSupport s;
+ const char *p;
int r;
assert(l);
@@ -1037,48 +1038,40 @@ int link_load_user(Link *l) {
/* If we can't recognize the DNSSEC setting, then set it to invalid, so that the daemon default is used. */
l->dnssec_mode = dnssec_mode_from_string(dnssec);
- if (servers) {
- const char *p = servers;
+ for (p = servers;;) {
+ _cleanup_free_ char *word = NULL;
- for (;;) {
- _cleanup_free_ char *word = NULL;
-
- r = extract_first_word(&p, &word, NULL, 0);
- if (r < 0)
- goto fail;
- if (r == 0)
- break;
+ r = extract_first_word(&p, &word, NULL, 0);
+ if (r < 0)
+ goto fail;
+ if (r == 0)
+ break;
- r = link_update_dns_server_one(l, word);
- if (r < 0) {
- log_debug_errno(r, "Failed to load DNS server '%s', ignoring: %m", word);
- continue;
- }
+ r = link_update_dns_server_one(l, word);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to load DNS server '%s', ignoring: %m", word);
+ continue;
}
}
- if (domains) {
- const char *p = domains;
+ for (p = domains;;) {
+ _cleanup_free_ char *word = NULL;
+ const char *n;
+ bool is_route;
- for (;;) {
- _cleanup_free_ char *word = NULL;
- const char *n;
- bool is_route;
-
- r = extract_first_word(&p, &word, NULL, 0);
- if (r < 0)
- goto fail;
- if (r == 0)
- break;
+ r = extract_first_word(&p, &word, NULL, 0);
+ if (r < 0)
+ goto fail;
+ if (r == 0)
+ break;
- is_route = word[0] == '~';
- n = is_route ? word + 1 : word;
+ is_route = word[0] == '~';
+ n = is_route ? word + 1 : word;
- r = link_update_search_domain_one(l, n, is_route);
- if (r < 0) {
- log_debug_errno(r, "Failed to load search domain '%s', ignoring: %m", word);
- continue;
- }
+ r = link_update_search_domain_one(l, n, is_route);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to load search domain '%s', ignoring: %m", word);
+ continue;
}
}
diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c
index 801014caf5..13f08f8a6c 100644
--- a/src/resolve/resolved-resolv-conf.c
+++ b/src/resolve/resolved-resolv-conf.c
@@ -60,7 +60,7 @@ int manager_read_resolv_conf(Manager *m) {
return 0;
/* Is it symlinked to our own file? */
- if (stat("/run/systemd/resolve/resolv.conf", &own) >= 0 &&
+ if (stat(PRIVATE_RESOLV_CONF, &own) >= 0 &&
st.st_dev == own.st_dev &&
st.st_ino == own.st_ino)
return 0;
diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c
index deb75f9ae5..8d5a5c6b79 100644
--- a/src/resolve/resolved.c
+++ b/src/resolve/resolved.c
@@ -112,6 +112,10 @@ int main(int argc, char *argv[]) {
sd_event_get_exit_code(m->event, &r);
finish:
+ /* systemd-nspawn checks for private resov.conf to decide whether
+ or not to mount it into the container. So just delete it. */
+ (void) unlink(PRIVATE_RESOLV_CONF);
+
sd_notify(false,
"STOPPING=1\n"
"STATUS=Shutting down...");
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index 35e2c8f18e..388b391342 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -62,6 +62,7 @@ int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) {
const char *eq, *field;
+ UnitDependency dep;
int r, rl;
assert(m);
@@ -398,9 +399,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
if (r < 0)
return bus_log_create_error(r);
- p = eq;
-
- for (;;) {
+ for (p = eq;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
@@ -482,9 +481,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
if (r < 0)
return bus_log_create_error(r);
- p = eq;
-
- for (;;) {
+ for (p = eq;;) {
_cleanup_free_ char *word = NULL;
int offset;
@@ -531,9 +528,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
if (r < 0)
return bus_log_create_error(r);
- p = eq;
-
- for (;;) {
+ for (p = eq;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
@@ -578,7 +573,9 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
flags = (~flags) & NAMESPACE_FLAGS_ALL;
r = sd_bus_message_append(m, "v", "t", flags);
- } else {
+ } else if ((dep = unit_dependency_from_string(field)) >= 0)
+ r = sd_bus_message_append(m, "v", "as", 1, eq);
+ else {
log_error("Unknown assignment %s.", assignment);
return -EINVAL;
}
@@ -844,6 +841,8 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const*
log_error("Assertion failed on job for %s.", strna(d->name));
else if (streq(d->result, "unsupported"))
log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
+ else if (streq(d->result, "collected"))
+ log_error("Queued job for %s was garbage collected.", strna(d->name));
else if (!streq(d->result, "done") && !streq(d->result, "skipped")) {
if (d->name) {
int q;
@@ -859,7 +858,7 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const*
}
}
- if (streq(d->result, "canceled"))
+ if (STR_IN_SET(d->result, "canceled", "collected"))
r = -ECANCELED;
else if (streq(d->result, "timeout"))
r = -ETIME;
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 3b8768b9a7..6aebe18fc0 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -43,6 +43,7 @@
#include "escape.h"
#include "fd-util.h"
#include "missing.h"
+#include "nsflags.h"
#include "parse-util.h"
#include "proc-cmdline.h"
#include "rlimit-util.h"
@@ -769,6 +770,23 @@ int bus_print_property(const char *name, sd_bus_message *property, bool value, b
char timespan[FORMAT_TIMESPAN_MAX];
print_property(name, "%s", format_timespan(timespan, sizeof(timespan), u, 0));
+ } else if (streq(name, "RestrictNamespaces")) {
+ _cleanup_free_ char *s = NULL;
+ const char *result = NULL;
+
+ if ((u & NAMESPACE_FLAGS_ALL) == 0)
+ result = "yes";
+ else if ((u & NAMESPACE_FLAGS_ALL) == NAMESPACE_FLAGS_ALL)
+ result = "no";
+ else {
+ r = namespace_flag_to_string_many(u, &s);
+ if (r < 0)
+ return r;
+
+ result = s;
+ }
+
+ print_property(name, "%s", result);
} else
print_property(name, "%"PRIu64, u);
@@ -1565,3 +1583,22 @@ int bus_property_get_rlimit(
return sd_bus_message_append(reply, "t", u);
}
+
+int bus_track_add_name_many(sd_bus_track *t, char **l) {
+ int r = 0;
+ char **i;
+
+ assert(t);
+
+ /* Continues adding after failure, and returns the first failure. */
+
+ STRV_FOREACH(i, l) {
+ int k;
+
+ k = sd_bus_track_add_name(t, *i);
+ if (k < 0 && r >= 0)
+ r = k;
+ }
+
+ return r;
+}
diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h
index 934e0b5b77..af5f133912 100644
--- a/src/shared/bus-util.h
+++ b/src/shared/bus-util.h
@@ -159,3 +159,5 @@ int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id,
int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external);
int bus_property_get_rlimit(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+
+int bus_track_add_name_many(sd_bus_track *t, char **l);
diff --git a/src/shared/condition.c b/src/shared/condition.c
index 8bd6a51a99..525e65aedf 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -111,9 +111,8 @@ static int condition_test_kernel_command_line(Condition *c) {
return r;
equal = !!strchr(c->parameter, '=');
- p = line;
- for (;;) {
+ for (p = line;;) {
_cleanup_free_ char *word = NULL;
bool found;
diff --git a/src/shared/nsflags.c b/src/shared/nsflags.c
index 49afbede73..aeb79b131e 100644
--- a/src/shared/nsflags.c
+++ b/src/shared/nsflags.c
@@ -68,11 +68,6 @@ int namespace_flag_from_string_many(const char *name, unsigned long *ret) {
assert_se(ret);
- if (!name) {
- *ret = 0;
- return 0;
- }
-
for (;;) {
_cleanup_free_ char *word = NULL;
unsigned long f;
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index af5b18c0ed..4fd8d7ba27 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -173,6 +173,8 @@ static OutputMode arg_output = OUTPUT_SHORT;
static bool arg_plain = false;
static bool arg_firmware_setup = false;
static bool arg_now = false;
+static bool arg_jobs_before = false;
+static bool arg_jobs_after = false;
static int daemon_reload(int argc, char *argv[], void* userdata);
static int trivial_method(int argc, char *argv[], void *userdata);
@@ -204,6 +206,9 @@ static int acquire_bus(BusFocus focus, sd_bus **ret) {
if (arg_transport != BUS_TRANSPORT_LOCAL)
focus = BUS_FULL;
+ if (getenv_bool("SYSTEMCTL_FORCE_BUS") > 0)
+ focus = BUS_FULL;
+
if (!busses[focus]) {
bool user;
@@ -2192,12 +2197,49 @@ finish:
return r;
}
+static int output_waiting_jobs(sd_bus *bus, uint32_t id, const char *method, const char *prefix) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ const char *name, *type, *state, *job_path, *unit_path;
+ uint32_t other_id;
+ int r;
+
+ assert(bus);
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ method,
+ &error,
+ &reply,
+ "u", id);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to get waiting jobs for job %" PRIu32, id);
+
+ r = sd_bus_message_enter_container(reply, 'a', "(usssoo)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(reply, "(usssoo)", &other_id, &name, &type, &state, &job_path, &unit_path)) > 0)
+ printf("%s %u (%s/%s)\n", prefix, other_id, name, type);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ return 0;
+}
+
struct job_info {
uint32_t id;
const char *name, *type, *state;
};
-static void output_jobs_list(const struct job_info* jobs, unsigned n, bool skipped) {
+static void output_jobs_list(sd_bus *bus, const struct job_info* jobs, unsigned n, bool skipped) {
unsigned id_len, unit_len, type_len, state_len;
const struct job_info *j;
const char *on, *off;
@@ -2259,6 +2301,11 @@ static void output_jobs_list(const struct job_info* jobs, unsigned n, bool skipp
on, unit_len, e ? e : j->name, off,
type_len, j->type,
on, state_len, j->state, off);
+
+ if (arg_jobs_after)
+ output_waiting_jobs(bus, j->id, "GetJobAfter", "\twaiting for job");
+ if (arg_jobs_before)
+ output_waiting_jobs(bus, j->id, "GetJobBefore", "\tblocking job");
}
if (!arg_no_legend) {
@@ -2327,7 +2374,7 @@ static int list_jobs(int argc, char *argv[], void *userdata) {
pager_open(arg_no_pager, false);
- output_jobs_list(jobs, c, skipped);
+ output_jobs_list(bus, jobs, c, skipped);
return 0;
}
@@ -2432,17 +2479,24 @@ static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **un
assert(unit_path);
STRV_FOREACH(p, lp->search_path) {
- _cleanup_free_ char *path;
+ _cleanup_free_ char *path = NULL, *lpath = NULL;
+ int r;
path = path_join(arg_root, *p, unit_name);
if (!path)
return log_oom();
- if (access(path, F_OK) == 0) {
- *unit_path = path;
- path = NULL;
- return 1;
- }
+ r = chase_symlinks(path, arg_root, &lpath);
+ if (r == -ENOENT)
+ continue;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ return log_error_errno(r, "Failed to access path '%s': %m", path);
+
+ *unit_path = lpath;
+ lpath = NULL;
+ return 1;
}
return 0;
@@ -2509,10 +2563,6 @@ static int unit_find_paths(
if (!names)
return log_oom();
- r = set_put(names, unit_name);
- if (r < 0)
- return log_error_errno(r, "Failed to add unit name: %m");
-
r = unit_file_find_path(lp, unit_name, &path);
if (r < 0)
return r;
@@ -2530,6 +2580,10 @@ static int unit_find_paths(
}
}
+ r = set_put(names, basename(path));
+ if (r < 0)
+ return log_error_errno(r, "Failed to add unit name: %m");
+
if (dropin_paths) {
r = unit_file_find_dropin_paths(lp->search_path, NULL, names, &dropins);
if (r < 0)
@@ -6735,9 +6789,9 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
if (path) {
if (arg_full)
- r = unit_file_create_copy(&lp, *name, path, &new_path, &tmp_path);
+ r = unit_file_create_copy(&lp, basename(path), path, &new_path, &tmp_path);
else
- r = unit_file_create_new(&lp, *name, ".d/override.conf", &new_path, &tmp_path);
+ r = unit_file_create_new(&lp, basename(path), ".d/override.conf", &new_path, &tmp_path);
} else
r = unit_file_create_new(&lp, *name, NULL, &new_path, &tmp_path);
if (r < 0)
@@ -7214,14 +7268,12 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
- p = optarg;
- for (;;) {
+ for (p = optarg;;) {
_cleanup_free_ char *type = NULL;
r = extract_first_word(&p, &type, ",", 0);
if (r < 0)
return log_error_errno(r, "Failed to parse type: %s", optarg);
-
if (r == 0)
break;
@@ -7263,15 +7315,13 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
arg_properties = new0(char*, 1);
if (!arg_properties)
return log_oom();
- } else {
- p = optarg;
- for (;;) {
+ } else
+ for (p = optarg;;) {
_cleanup_free_ char *prop = NULL;
r = extract_first_word(&p, &prop, ",", 0);
if (r < 0)
return log_error_errno(r, "Failed to parse property: %s", optarg);
-
if (r == 0)
break;
@@ -7280,7 +7330,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
prop = NULL;
}
- }
/* If the user asked for a particular
* property, show it to him, even if it is
@@ -7300,10 +7349,12 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
case ARG_AFTER:
arg_dependency = DEPENDENCY_AFTER;
+ arg_jobs_after = true;
break;
case ARG_BEFORE:
arg_dependency = DEPENDENCY_BEFORE;
+ arg_jobs_before = true;
break;
case ARG_SHOW_TYPES:
@@ -7457,14 +7508,12 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
- p = optarg;
- for (;;) {
+ for (p = optarg;;) {
_cleanup_free_ char *s = NULL;
r = extract_first_word(&p, &s, ",", 0);
if (r < 0)
return log_error_errno(r, "Failed to parse signal: %s", optarg);
-
if (r == 0)
break;
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index 9a90c2ed42..ffe7f836de 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -126,6 +126,9 @@ int sd_dhcp_client_get_client_id(
int sd_dhcp_client_set_mtu(
sd_dhcp_client *client,
uint32_t mtu);
+int sd_dhcp_client_set_client_port(
+ sd_dhcp_client *client,
+ uint16_t port);
int sd_dhcp_client_set_hostname(
sd_dhcp_client *client,
const char *hostname);
diff --git a/src/test/test-execute.c b/src/test/test-execute.c
index 6029853e3e..b2ea358b8c 100644
--- a/src/test/test-execute.c
+++ b/src/test/test-execute.c
@@ -219,6 +219,18 @@ static void test_exec_systemcallerrornumber(Manager *m) {
#endif
}
+static void test_exec_restrict_namespaces(Manager *m) {
+#ifdef HAVE_SECCOMP
+ if (!is_seccomp_available())
+ return;
+
+ test(m, "exec-restrict-namespaces-no.service", 0, CLD_EXITED);
+ test(m, "exec-restrict-namespaces-yes.service", 1, CLD_EXITED);
+ test(m, "exec-restrict-namespaces-mnt.service", 0, CLD_EXITED);
+ test(m, "exec-restrict-namespaces-mnt-blacklist.service", 1, CLD_EXITED);
+#endif
+}
+
static void test_exec_systemcall_system_mode_with_user(Manager *m) {
#ifdef HAVE_SECCOMP
if (!is_seccomp_available())
@@ -435,6 +447,7 @@ int main(int argc, char *argv[]) {
test_exec_privatenetwork,
test_exec_systemcallfilter,
test_exec_systemcallerrornumber,
+ test_exec_restrict_namespaces,
test_exec_user,
test_exec_group,
test_exec_supplementary_groups,
diff --git a/src/test/test-nss.c b/src/test/test-nss.c
index c43bda5917..4ccfe75147 100644
--- a/src/test/test-nss.c
+++ b/src/test/test-nss.c
@@ -77,7 +77,8 @@ static void* open_handle(const char* dir, const char* module, int flags) {
path = strjoina("libnss_", module, ".so.2");
handle = dlopen(path, flags);
- assert_se(handle);
+ if (!handle)
+ log_error("Failed to load module %s: %s", module, dlerror());
return handle;
}
@@ -379,75 +380,158 @@ static void test_byaddr(void *handle,
puts("");
}
+static int make_addresses(struct local_address **addresses) {
+ int n;
+ size_t n_alloc;
+ _cleanup_free_ struct local_address *addrs = NULL;
+
+ n = local_addresses(NULL, 0, AF_UNSPEC, &addrs);
+ if (n < 0)
+ log_info_errno(n, "Failed to query local addresses: %m");
+
+ n_alloc = n; /* we _can_ do that */
+ if (!GREEDY_REALLOC(addrs, n_alloc, n + 3))
+ return log_oom();
+
+ addrs[n++] = (struct local_address) { .family = AF_INET,
+ .address.in = { htobe32(0x7F000001) } };
+ addrs[n++] = (struct local_address) { .family = AF_INET,
+ .address.in = { htobe32(0x7F000002) } };
+ addrs[n++] = (struct local_address) { .family = AF_INET6,
+ .address.in6 = in6addr_loopback };
+ return 0;
+}
+
+static int test_one_module(const char* dir,
+ const char *module,
+ char **names,
+ struct local_address *addresses,
+ int n_addresses) {
+ void *handle;
+ char **name;
+ int i;
+
+
+ log_info("======== %s ========", module);
+
+ handle = open_handle(streq(module, "dns") ? NULL : dir,
+ module,
+ RTLD_LAZY|RTLD_NODELETE);
+ if (!handle)
+ return -EINVAL;
+
+ STRV_FOREACH(name, names)
+ test_byname(handle, module, *name);
+
+ for (i = 0; i < n_addresses; i++)
+ test_byaddr(handle, module,
+ &addresses[i].address,
+ FAMILY_ADDRESS_SIZE(addresses[i].family),
+ addresses[i].family);
+
+ log_info(" ");
+ dlclose(handle);
+ return 0;
+}
+
+static int parse_argv(int argc, char **argv,
+ char ***the_modules,
+ char ***the_names,
+ struct local_address **the_addresses, int *n_addresses) {
+
+ int r, n = 0;
+ _cleanup_strv_free_ char **modules = NULL, **names = NULL;
+ _cleanup_free_ struct local_address *addrs = NULL;
+ size_t n_allocated = 0;
+
+ if (argc > 1)
+ modules = strv_new(argv[1], NULL);
+ else
+ modules = strv_new(
#ifdef HAVE_MYHOSTNAME
-# define MODULE1 "myhostname\0"
-#else
-# define MODULE1
+ "myhostname",
#endif
#ifdef HAVE_RESOLVED
-# define MODULE2 "resolve\0"
-#else
-# define MODULE2
+ "resolve",
#endif
#ifdef HAVE_MACHINED
-# define MODULE3 "mymachines\0"
-#else
-# define MODULE3
+ "mymachines",
#endif
-#define MODULE4 "dns\0"
+ "dns",
+ NULL);
+ if (!modules)
+ return -ENOMEM;
+
+ if (argc > 2) {
+ char **name;
+ int family;
+ union in_addr_union address;
+
+ STRV_FOREACH(name, argv + 2) {
+ r = in_addr_from_string_auto(*name, &family, &address);
+ if (r < 0) {
+ /* assume this is a name */
+ r = strv_extend(&names, *name);
+ if (r < 0)
+ return r;
+ } else {
+ if (!GREEDY_REALLOC0(addrs, n_allocated, n + 1))
+ return -ENOMEM;
+
+ addrs[n++] = (struct local_address) { .family = family,
+ .address = address };
+ }
+ }
+ } else {
+ _cleanup_free_ char *hostname;
-int main(int argc, char **argv) {
- _cleanup_free_ char *dir = NULL, *hostname = NULL;
- const char *module;
+ hostname = gethostname_malloc();
+ if (!hostname)
+ return -ENOMEM;
- const uint32_t local_address_ipv4 = htobe32(0x7F000001);
- const uint32_t local_address_ipv4_2 = htobe32(0x7F000002);
+ names = strv_new("localhost", "gateway", "foo_no_such_host", hostname, NULL);
+ if (!names)
+ return -ENOMEM;
+
+ n = make_addresses(&addrs);
+ if (n < 0)
+ return n;
+ }
+
+ *the_modules = modules;
+ *the_names = names;
+ modules = names = NULL;
+ *the_addresses = addrs;
+ *n_addresses = n;
+ addrs = NULL;
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ _cleanup_free_ char *dir = NULL;
+ _cleanup_strv_free_ char **modules = NULL, **names = NULL;
_cleanup_free_ struct local_address *addresses = NULL;
int n_addresses;
+ char **module;
+ int r;
log_set_max_level(LOG_INFO);
log_parse_environment();
- dir = dirname_malloc(argv[0]);
- assert_se(dir);
-
- hostname = gethostname_malloc();
- assert_se(hostname);
-
- n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
- if (n_addresses < 0) {
- log_info_errno(n_addresses, "Failed to query local addresses: %m");
- n_addresses = 0;
+ r = parse_argv(argc, argv, &modules, &names, &addresses, &n_addresses);
+ if (r < 0) {
+ log_error_errno(r, "Failed to parse arguments: %m");
+ return EXIT_FAILURE;
}
- NULSTR_FOREACH(module, MODULE1 MODULE2 MODULE3 MODULE4) {
- void *handle;
- const char *name;
- int i;
-
- log_info("======== %s ========", module);
-
- handle = open_handle(streq(module, "dns") ? NULL : dir,
- module,
- RTLD_LAZY|RTLD_NODELETE);
- NULSTR_FOREACH(name, "localhost\0" "gateway\0" "foo_no_such_host\0")
- test_byname(handle, module, name);
-
- test_byname(handle, module, hostname);
-
- test_byaddr(handle, module, &local_address_ipv4, sizeof local_address_ipv4, AF_INET);
- test_byaddr(handle, module, &local_address_ipv4_2, sizeof local_address_ipv4_2, AF_INET);
- test_byaddr(handle, module, &in6addr_loopback, sizeof in6addr_loopback, AF_INET6);
-
- for (i = 0; i < n_addresses; i++)
- test_byaddr(handle, module,
- &addresses[i].address,
- FAMILY_ADDRESS_SIZE(addresses[i].family),
- addresses[i].family);
-
- dlclose(handle);
+ dir = dirname_malloc(argv[0]);
+ if (!dir)
+ return EXIT_FAILURE;
- log_info(" ");
+ STRV_FOREACH(module, modules) {
+ r = test_one_module(dir, *module, names, addresses, n_addresses);
+ if (r < 0)
+ return EXIT_FAILURE;
}
return EXIT_SUCCESS;
diff --git a/src/timesync/timesyncd-manager.c b/src/timesync/timesyncd-manager.c
index a455652a27..6a6c1577c6 100644
--- a/src/timesync/timesyncd-manager.c
+++ b/src/timesync/timesyncd-manager.c
@@ -330,11 +330,13 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) {
tmx.esterror = 0;
log_debug(" adjust (slew): %+.3f sec", offset);
} else {
- tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_SETOFFSET;
+ tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_SETOFFSET | ADJ_MAXERROR | ADJ_ESTERROR;
/* ADJ_NANO uses nanoseconds in the microseconds field */
tmx.time.tv_sec = (long)offset;
tmx.time.tv_usec = (offset - tmx.time.tv_sec) * NSEC_PER_SEC;
+ tmx.maxerror = 0;
+ tmx.esterror = 0;
/* the kernel expects -0.3s as {-1, 7000.000.000} */
if (tmx.time.tv_usec < 0) {
diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c
index 708a665576..d7edbb396b 100644
--- a/src/udev/net/ethtool-util.c
+++ b/src/udev/net/ethtool-util.c
@@ -29,6 +29,7 @@
#include "string-table.h"
#include "strxcpyx.h"
#include "util.h"
+#include "missing.h"
static const char* const duplex_table[_DUP_MAX] = {
[DUP_FULL] = "full",
@@ -323,3 +324,211 @@ int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features) {
return 0;
}
+
+static int get_glinksettings(int *fd, struct ifreq *ifr, struct ethtool_link_usettings **g) {
+ struct ecmd {
+ struct ethtool_link_settings req;
+ __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
+ } ecmd = {
+ .req.cmd = ETHTOOL_GLINKSETTINGS,
+ };
+ struct ethtool_link_usettings *u;
+ unsigned offset;
+ int r;
+
+ /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS
+ handshake first to agree on the length of the link mode bitmaps. If kernel doesn't
+ agree with user, it returns the bitmap length it is expecting from user as a negative
+ length (and cmd field is 0). When kernel and user agree, kernel returns valid info in
+ all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on
+ https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf
+ */
+
+ ifr->ifr_data = (void *) &ecmd;
+
+ r = ioctl(*fd, SIOCETHTOOL, ifr);
+ if (r < 0)
+ return -errno;
+
+ if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
+ return -ENOTSUP;
+
+ ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords;
+
+ ifr->ifr_data = (void *) &ecmd;
+
+ r = ioctl(*fd, SIOCETHTOOL, ifr);
+ if (r < 0)
+ return -errno;
+
+ if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
+ return -ENOTSUP;
+
+ u = new0(struct ethtool_link_usettings , 1);
+ if (!u)
+ return -ENOMEM;
+
+ memcpy(&u->base, &ecmd.req, sizeof(struct ethtool_link_settings));
+
+ offset = 0;
+ memcpy(u->link_modes.supported, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
+
+ offset += ecmd.req.link_mode_masks_nwords;
+ memcpy(u->link_modes.advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
+
+ offset += ecmd.req.link_mode_masks_nwords;
+ memcpy(u->link_modes.lp_advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
+
+ *g = u;
+
+ return 0;
+}
+
+static int get_gset(int *fd, struct ifreq *ifr, struct ethtool_link_usettings **u) {
+ struct ethtool_link_usettings *e;
+ struct ethtool_cmd ecmd = {
+ .cmd = ETHTOOL_GSET,
+ };
+ int r;
+
+ ifr->ifr_data = (void *) &ecmd;
+
+ r = ioctl(*fd, SIOCETHTOOL, ifr);
+ if (r < 0)
+ return -errno;
+
+ e = new0(struct ethtool_link_usettings, 1);
+ if (!e)
+ return -ENOMEM;
+
+ e->base.cmd = ETHTOOL_GSET;
+
+ e->base.link_mode_masks_nwords = 1;
+ e->base.speed = ethtool_cmd_speed(&ecmd);
+ e->base.duplex = ecmd.duplex;
+ e->base.port = ecmd.port;
+ e->base.phy_address = ecmd.phy_address;
+ e->base.autoneg = ecmd.autoneg;
+ e->base.mdio_support = ecmd.mdio_support;
+
+ e->link_modes.supported[0] = ecmd.supported;
+ e->link_modes.advertising[0] = ecmd.advertising;
+ e->link_modes.lp_advertising[0] = ecmd.lp_advertising;
+
+ *u = e;
+
+ return 0;
+}
+
+static int set_slinksettings(int *fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
+ struct {
+ struct ethtool_link_settings req;
+ __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
+ } ecmd = {
+ .req.cmd = ETHTOOL_SLINKSETTINGS,
+ };
+ unsigned int offset;
+ int r;
+
+ if (u->base.cmd != ETHTOOL_GLINKSETTINGS || u->base.link_mode_masks_nwords <= 0)
+ return -EINVAL;
+
+ offset = 0;
+ memcpy(&ecmd.link_mode_data[offset], u->link_modes.supported, 4 * ecmd.req.link_mode_masks_nwords);
+
+ offset += ecmd.req.link_mode_masks_nwords;
+ memcpy(&ecmd.link_mode_data[offset], u->link_modes.advertising, 4 * ecmd.req.link_mode_masks_nwords);
+
+ offset += ecmd.req.link_mode_masks_nwords;
+ memcpy(&ecmd.link_mode_data[offset], u->link_modes.lp_advertising, 4 * ecmd.req.link_mode_masks_nwords);
+
+ ifr->ifr_data = (void *) &ecmd;
+
+ r = ioctl(*fd, SIOCETHTOOL, ifr);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int set_sset(int *fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
+ struct ethtool_cmd ecmd = {
+ .cmd = ETHTOOL_SSET,
+ };
+ int r;
+
+ if (u->base.cmd != ETHTOOL_GSET || u->base.link_mode_masks_nwords <= 0)
+ return -EINVAL;
+
+ ecmd.supported = u->link_modes.supported[0];
+ ecmd.advertising = u->link_modes.advertising[0];
+ ecmd.lp_advertising = u->link_modes.lp_advertising[0];
+
+ ethtool_cmd_speed_set(&ecmd, u->base.speed);
+
+ ecmd.duplex = u->base.duplex;
+ ecmd.port = u->base.port;
+ ecmd.phy_address = u->base.phy_address;
+ ecmd.autoneg = u->base.autoneg;
+ ecmd.mdio_support = u->base.mdio_support;
+
+ ifr->ifr_data = (void *) &ecmd;
+
+ r = ioctl(*fd, SIOCETHTOOL, ifr);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+/* If autonegotiation is disabled, the speed and duplex represent the fixed link
+ * mode and are writable if the driver supports multiple link modes. If it is
+ * enabled then they are read-only. If the link is up they represent the negotiated
+ * link mode; if the link is down, the speed is 0, %SPEED_UNKNOWN or the highest
+ * enabled speed and @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode.
+ */
+
+int ethtool_set_glinksettings(int *fd, const char *ifname, unsigned int speed, Duplex duplex, int autonegotiation) {
+ _cleanup_free_ struct ethtool_link_usettings *u = NULL;
+ struct ifreq ifr = {};
+ int r;
+
+ if (autonegotiation != 0) {
+ log_info("link_config: autonegotiation is unset or enabled, the speed and duplex are not writable.");
+ return 0;
+ }
+
+ 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 = get_glinksettings(fd, &ifr, &u);
+ if (r < 0) {
+
+ r = get_gset(fd, &ifr, &u);
+ if (r < 0)
+ return log_warning_errno(r, "link_config: Cannot get device settings for %s : %m", ifname);
+ }
+
+ if (speed)
+ u->base.speed = speed;
+
+ if (duplex != _DUP_INVALID)
+ u->base.duplex = duplex;
+
+ u->base.autoneg = autonegotiation;
+
+ if (u->base.cmd == ETHTOOL_GLINKSETTINGS)
+ r = set_slinksettings(fd, &ifr, u);
+ else
+ r = set_sset(fd, &ifr, u);
+
+ if (r < 0)
+ return log_warning_errno(r, "link_config: Cannot set device settings for %s : %m", ifname);
+
+ return r;
+}
diff --git a/src/udev/net/ethtool-util.h b/src/udev/net/ethtool-util.h
index 0744164653..75d6af396b 100644
--- a/src/udev/net/ethtool-util.h
+++ b/src/udev/net/ethtool-util.h
@@ -20,6 +20,9 @@
***/
#include <macro.h>
+#include <linux/ethtool.h>
+
+#include "missing.h"
/* we can't use DUPLEX_ prefix, as it
* clashes with <linux/ethtool.h> */
@@ -48,12 +51,27 @@ typedef enum NetDevFeature {
_NET_DEV_FEAT_INVALID = -1
} NetDevFeature;
+
+#define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32 (SCHAR_MAX)
+
+/* layout of the struct passed from/to userland */
+struct ethtool_link_usettings {
+ struct ethtool_link_settings base;
+
+ struct {
+ uint32_t supported[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
+ uint32_t advertising[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
+ uint32_t lp_advertising[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
+ } link_modes;
+};
+
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);
+int ethtool_set_glinksettings(int *fd, const char *ifname, unsigned int speed, Duplex duplex, int autoneg);
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 f8b85cbd13..78e551df22 100644
--- a/src/udev/net/link-config-gperf.gperf
+++ b/src/udev/net/link-config-gperf.gperf
@@ -34,6 +34,7 @@ Link.Alias, config_parse_ifalias, 0,
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.AutoNegotiation, config_parse_tristate, 0, offsetof(link_config, autonegotiation)
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])
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
index ece9248c2a..1dca375279 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -167,8 +167,9 @@ static int load_link(link_config_ctx *ctx, const char *filename) {
link->mac_policy = _MACPOLICY_INVALID;
link->wol = _WOL_INVALID;
link->duplex = _DUP_INVALID;
+ link->autonegotiation = -1;
- memset(&link->features, -1, _NET_DEV_FEAT_MAX);
+ memset(&link->features, -1, sizeof(link->features));
r = config_parse(NULL, filename, file,
"Match\0Link\0Ethernet\0",
@@ -202,9 +203,9 @@ static bool enable_name_policy(void) {
}
int link_config_load(link_config_ctx *ctx) {
- int r;
_cleanup_strv_free_ char **files;
char **f;
+ int r;
link_configs_free(ctx);
@@ -364,11 +365,12 @@ static int get_mac(struct udev_device *device, bool want_random,
int link_config_apply(link_config_ctx *ctx, link_config *config,
struct udev_device *device, const char **name) {
- const char *old_name;
- const char *new_name = NULL;
+ bool respect_predictable = false;
struct ether_addr generated_mac;
struct ether_addr *mac = NULL;
- bool respect_predictable = false;
+ const char *new_name = NULL;
+ const char *old_name;
+ unsigned speed;
int r, ifindex;
assert(ctx);
@@ -380,11 +382,19 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
if (!old_name)
return -EINVAL;
- r = ethtool_set_speed(&ctx->ethtool_fd, old_name, config->speed / 1024, config->duplex);
- if (r < 0)
- log_warning_errno(r, "Could not set speed or duplex of %s to %zu Mbps (%s): %m",
- old_name, config->speed / 1024,
- duplex_to_string(config->duplex));
+
+ speed = DIV_ROUND_UP(config->speed, 1000000);
+
+ r = ethtool_set_glinksettings(&ctx->ethtool_fd, old_name, speed, config->duplex, config->autonegotiation);
+ if (r < 0) {
+
+ if (r == -EOPNOTSUPP)
+ r = ethtool_set_speed(&ctx->ethtool_fd, old_name, speed, config->duplex);
+
+ if (r < 0)
+ log_warning_errno(r, "Could not set speed or duplex of %s to %u Mbps (%s): %m",
+ old_name, speed, duplex_to_string(config->duplex));
+ }
r = ethtool_set_wol(&ctx->ethtool_fd, old_name, config->wol);
if (r < 0)
diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h
index 91cc0357c4..a99060d943 100644
--- a/src/udev/net/link-config.h
+++ b/src/udev/net/link-config.h
@@ -69,6 +69,7 @@ struct link_config {
size_t mtu;
size_t speed;
Duplex duplex;
+ int autonegotiation;
WakeOnLan wol;
NetDevFeature features[_NET_DEV_FEAT_MAX];