diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/analyze/analyze.c | 2 | ||||
-rw-r--r-- | src/basic/env-util.c | 3 | ||||
-rw-r--r-- | src/basic/extract-word.c | 2 | ||||
-rw-r--r-- | src/basic/missing.h | 27 | ||||
-rw-r--r-- | src/basic/terminal-util.c | 19 | ||||
-rw-r--r-- | src/basic/virt.c | 2 | ||||
-rw-r--r-- | src/core/dbus-execute.c | 2 | ||||
-rw-r--r-- | src/core/dbus-job.c | 121 | ||||
-rw-r--r-- | src/core/dbus-job.h | 4 | ||||
-rw-r--r-- | src/core/dbus-manager.c | 22 | ||||
-rw-r--r-- | src/core/dbus-unit.c | 25 | ||||
-rw-r--r-- | src/core/dbus.c | 16 | ||||
-rw-r--r-- | src/core/device.c | 2 | ||||
-rw-r--r-- | src/core/execute.c | 160 | ||||
-rw-r--r-- | src/core/execute.h | 18 | ||||
-rw-r--r-- | src/core/job.c | 257 | ||||
-rw-r--r-- | src/core/job.h | 16 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.m4 | 2 | ||||
-rw-r--r-- | src/core/load-fragment.c | 1 | ||||
-rw-r--r-- | src/core/main.c | 45 | ||||
-rw-r--r-- | src/core/manager.c | 103 | ||||
-rw-r--r-- | src/core/manager.h | 12 | ||||
-rw-r--r-- | src/core/mount.c | 2 | ||||
-rw-r--r-- | src/core/namespace.c | 416 | ||||
-rw-r--r-- | src/core/org.freedesktop.systemd1.conf | 16 | ||||
-rw-r--r-- | src/core/service.c | 2 | ||||
-rw-r--r-- | src/core/socket.c | 2 | ||||
-rw-r--r-- | src/core/swap.c | 2 | ||||
-rw-r--r-- | src/core/unit.c | 29 | ||||
-rw-r--r-- | src/core/unit.h | 5 | ||||
-rw-r--r-- | src/fstab-generator/fstab-generator.c | 21 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp-internal.h | 5 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp-network.c | 10 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp-packet.c | 6 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp-client.c | 25 | ||||
-rw-r--r-- | src/libsystemd-network/test-dhcp-client.c | 2 | ||||
-rw-r--r-- | src/libsystemd/sd-bus/busctl-introspect.c | 86 | ||||
-rw-r--r-- | src/libsystemd/sd-network/sd-network.c | 14 | ||||
-rw-r--r-- | src/network/.gitignore | 1 | ||||
-rw-r--r-- | src/network/netdev/.gitignore | 1 | ||||
-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.c | 2 | ||||
-rw-r--r-- | src/network/networkd-address.c | 2 | ||||
-rw-r--r-- | src/network/networkd-brvlan.c | 4 | ||||
-rw-r--r-- | src/network/networkd-conf.c | 2 | ||||
-rw-r--r-- | src/network/networkd-conf.h | 2 | ||||
-rw-r--r-- | src/network/networkd-dhcp4.c | 10 | ||||
-rw-r--r-- | src/network/networkd-dhcp6.c | 4 | ||||
-rw-r--r-- | src/network/networkd-fdb.c | 3 | ||||
-rw-r--r-- | src/network/networkd-gperf.gperf | 1 | ||||
-rw-r--r-- | src/network/networkd-ipv4ll.c | 5 | ||||
-rw-r--r-- | src/network/networkd-link-bus.c | 2 | ||||
-rw-r--r-- | src/network/networkd-link.c | 6 | ||||
-rw-r--r-- | src/network/networkd-lldp-tx.c | 2 | ||||
-rw-r--r-- | src/network/networkd-manager-bus.c | 2 | ||||
-rw-r--r-- | src/network/networkd-manager.c | 2 | ||||
-rw-r--r-- | src/network/networkd-manager.h (renamed from src/network/networkd.h) | 13 | ||||
-rw-r--r-- | src/network/networkd-ndisc.c | 3 | ||||
-rw-r--r-- | src/network/networkd-network-bus.c | 2 | ||||
-rw-r--r-- | src/network/networkd-network-gperf.gperf | 3 | ||||
-rw-r--r-- | src/network/networkd-network.c | 5 | ||||
-rw-r--r-- | src/network/networkd-network.h | 5 | ||||
-rw-r--r-- | src/network/networkd-route.c | 2 | ||||
-rw-r--r-- | src/network/networkd.c | 2 | ||||
-rw-r--r-- | src/network/test-network-tables.c | 8 | ||||
-rw-r--r-- | src/network/test-network.c | 2 | ||||
-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.c | 12 | ||||
-rw-r--r-- | src/resolve/resolved-link.c | 61 | ||||
-rw-r--r-- | src/resolve/resolved-resolv-conf.c | 2 | ||||
-rw-r--r-- | src/resolve/resolved.c | 4 | ||||
-rw-r--r-- | src/shared/bus-unit-util.c | 21 | ||||
-rw-r--r-- | src/shared/bus-util.c | 37 | ||||
-rw-r--r-- | src/shared/bus-util.h | 2 | ||||
-rw-r--r-- | src/shared/condition.c | 3 | ||||
-rw-r--r-- | src/shared/nsflags.c | 6 | ||||
-rw-r--r-- | src/systemctl/systemctl.c | 99 | ||||
-rw-r--r-- | src/systemd/sd-dhcp-client.h | 3 | ||||
-rw-r--r-- | src/test/test-execute.c | 13 | ||||
-rw-r--r-- | src/test/test-nss.c | 190 | ||||
-rw-r--r-- | src/timesync/timesyncd-manager.c | 4 | ||||
-rw-r--r-- | src/udev/net/ethtool-util.c | 209 | ||||
-rw-r--r-- | src/udev/net/ethtool-util.h | 18 | ||||
-rw-r--r-- | src/udev/net/link-config-gperf.gperf | 1 | ||||
-rw-r--r-- | src/udev/net/link-config.c | 30 | ||||
-rw-r--r-- | src/udev/net/link-config.h | 1 |
116 files changed, 1742 insertions, 681 deletions
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index f744a84501..51d881c5fb 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -36,7 +36,9 @@ #include "log.h" #include "pager.h" #include "parse-util.h" +#ifdef HAVE_SECCOMP #include "seccomp-util.h" +#endif #include "special.h" #include "strv.h" #include "strxcpyx.h" 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/terminal-util.c b/src/basic/terminal-util.c index eafdea9eb3..9a8ef825c5 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -144,12 +144,14 @@ int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) { return 0; } -int ask_char(char *ret, const char *replies, const char *text, ...) { +#define DEFAULT_ASK_REFRESH_USEC (2*USEC_PER_SEC) + +int ask_char(char *ret, const char *replies, const char *fmt, ...) { int r; assert(ret); assert(replies); - assert(text); + assert(fmt); for (;;) { va_list ap; @@ -159,8 +161,10 @@ int ask_char(char *ret, const char *replies, const char *text, ...) { if (colors_enabled()) fputs(ANSI_HIGHLIGHT, stdout); - va_start(ap, text); - vprintf(text, ap); + putchar('\r'); + + va_start(ap, fmt); + vprintf(fmt, ap); va_end(ap); if (colors_enabled()) @@ -168,9 +172,12 @@ int ask_char(char *ret, const char *replies, const char *text, ...) { fflush(stdout); - r = read_one_char(stdin, &c, USEC_INFINITY, &need_nl); + r = read_one_char(stdin, &c, DEFAULT_ASK_REFRESH_USEC, &need_nl); if (r < 0) { + if (r == -ETIMEDOUT) + continue; + if (r == -EBADMSG) { puts("Bad input, please try again."); continue; @@ -455,7 +462,7 @@ int acquire_terminal( goto fail; } - r = fd_wait_for_event(fd, POLLIN, ts + timeout - n); + r = fd_wait_for_event(notify, POLLIN, ts + timeout - n); if (r < 0) goto fail; 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 f666f7c6ce..07ab067c05 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -624,7 +624,7 @@ static int chown_terminal(int fd, uid_t uid) { return 0; } -static int setup_confirm_stdio(int *_saved_stdin, int *_saved_stdout) { +static int setup_confirm_stdio(const char *vc, int *_saved_stdin, int *_saved_stdout) { _cleanup_close_ int fd = -1, saved_stdin = -1, saved_stdout = -1; int r; @@ -639,12 +639,7 @@ static int setup_confirm_stdio(int *_saved_stdin, int *_saved_stdout) { if (saved_stdout < 0) return -errno; - fd = acquire_terminal( - "/dev/console", - false, - false, - false, - DEFAULT_CONFIRM_USEC); + fd = acquire_terminal(vc, false, false, false, DEFAULT_CONFIRM_USEC); if (fd < 0) return fd; @@ -674,21 +669,27 @@ static int setup_confirm_stdio(int *_saved_stdin, int *_saved_stdout) { return 0; } -_printf_(1, 2) static int write_confirm_message(const char *format, ...) { +static void write_confirm_error_fd(int err, int fd, const Unit *u) { + assert(err < 0); + + if (err == -ETIMEDOUT) + dprintf(fd, "Confirmation question timed out for %s, assuming positive response.\n", u->id); + else { + errno = -err; + dprintf(fd, "Couldn't ask confirmation for %s: %m, assuming positive response.\n", u->id); + } +} + +static void write_confirm_error(int err, const char *vc, const Unit *u) { _cleanup_close_ int fd = -1; - va_list ap; - assert(format); + assert(vc); - fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); + fd = open_terminal(vc, O_WRONLY|O_NOCTTY|O_CLOEXEC); if (fd < 0) - return fd; - - va_start(ap, format); - vdprintf(fd, format, ap); - va_end(ap); + return; - return 0; + write_confirm_error_fd(err, fd, u); } static int restore_confirm_stdio(int *saved_stdin, int *saved_stdout) { @@ -713,22 +714,96 @@ static int restore_confirm_stdio(int *saved_stdin, int *saved_stdout) { return r; } -static int ask_for_confirmation(char *response, char **argv) { +enum { + CONFIRM_PRETEND_FAILURE = -1, + CONFIRM_PRETEND_SUCCESS = 0, + CONFIRM_EXECUTE = 1, +}; + +static int ask_for_confirmation(const char *vc, Unit *u, const char *cmdline) { int saved_stdout = -1, saved_stdin = -1, r; - _cleanup_free_ char *line = NULL; + _cleanup_free_ char *e = NULL; + char c; - r = setup_confirm_stdio(&saved_stdin, &saved_stdout); - if (r < 0) - return r; + /* For any internal errors, assume a positive response. */ + r = setup_confirm_stdio(vc, &saved_stdin, &saved_stdout); + if (r < 0) { + write_confirm_error(r, vc, u); + return CONFIRM_EXECUTE; + } - line = exec_command_line(argv); - if (!line) - return -ENOMEM; + /* confirm_spawn might have been disabled while we were sleeping. */ + if (manager_is_confirm_spawn_disabled(u->manager)) { + r = 1; + goto restore_stdio; + } - r = ask_char(response, "yns", "Execute %s? [Yes, No, Skip] ", line); + e = ellipsize(cmdline, 60, 100); + if (!e) { + log_oom(); + r = CONFIRM_EXECUTE; + goto restore_stdio; + } - restore_confirm_stdio(&saved_stdin, &saved_stdout); + for (;;) { + r = ask_char(&c, "yfshiDjcn", "Execute %s? [y, f, s – h for help] ", e); + if (r < 0) { + write_confirm_error_fd(r, STDOUT_FILENO, u); + r = CONFIRM_EXECUTE; + goto restore_stdio; + } + + switch (c) { + case 'c': + printf("Resuming normal execution.\n"); + manager_disable_confirm_spawn(); + r = 1; + break; + case 'D': + unit_dump(u, stdout, " "); + continue; /* ask again */ + case 'f': + printf("Failing execution.\n"); + r = CONFIRM_PRETEND_FAILURE; + break; + case 'h': + printf(" c - continue, proceed without asking anymore\n" + " D - dump, show the state of the unit\n" + " f - fail, don't execute the command and pretend it failed\n" + " h - help\n" + " i - info, show a short summary of the unit\n" + " j - jobs, show jobs that are in progress\n" + " s - skip, don't execute the command and pretend it succeeded\n" + " y - yes, execute the command\n"); + continue; /* ask again */ + case 'i': + printf(" Description: %s\n" + " Unit: %s\n" + " Command: %s\n", + u->id, u->description, cmdline); + continue; /* ask again */ + case 'j': + manager_dump_jobs(u->manager, stdout, " "); + continue; /* ask again */ + case 'n': + /* 'n' was removed in favor of 'f'. */ + printf("Didn't understand 'n', did you mean 'f'?\n"); + continue; /* ask again */ + case 's': + printf("Skipping execution.\n"); + r = CONFIRM_PRETEND_SUCCESS; + break; + case 'y': + r = CONFIRM_EXECUTE; + break; + default: + assert_not_reached("Unhandled choice"); + } + break; + } +restore_stdio: + restore_confirm_stdio(&saved_stdin, &saved_stdout); return r; } @@ -2201,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) || @@ -2314,22 +2390,24 @@ static int exec_child( exec_context_tty_reset(context, params); - if (params->flags & EXEC_CONFIRM_SPAWN) { - char response; + if (unit_shall_confirm_spawn(unit)) { + const char *vc = params->confirm_spawn; + _cleanup_free_ char *cmdline = NULL; - r = ask_for_confirmation(&response, argv); - if (r == -ETIMEDOUT) - write_confirm_message("Confirmation question timed out, assuming positive response.\n"); - else if (r < 0) - write_confirm_message("Couldn't ask confirmation question, assuming positive response: %s\n", strerror(-r)); - else if (response == 's') { - write_confirm_message("Skipping execution.\n"); + cmdline = exec_command_line(argv); + if (!cmdline) { + *exit_status = EXIT_CONFIRM; + return -ENOMEM; + } + + r = ask_for_confirmation(vc, unit, cmdline); + if (r != CONFIRM_EXECUTE) { + if (r == CONFIRM_PRETEND_SUCCESS) { + *exit_status = EXIT_SUCCESS; + return 0; + } *exit_status = EXIT_CONFIRM; return -ECANCELED; - } else if (response == 'n') { - write_confirm_message("Failing execution.\n"); - *exit_status = 0; - return 0; } } diff --git a/src/core/execute.h b/src/core/execute.h index 56f880cffe..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) { @@ -226,16 +225,15 @@ static inline bool exec_context_restrict_namespaces_set(const ExecContext *c) { } typedef enum ExecFlags { - EXEC_CONFIRM_SPAWN = 1U << 0, - EXEC_APPLY_PERMISSIONS = 1U << 1, - EXEC_APPLY_CHROOT = 1U << 2, - EXEC_APPLY_TTY_STDIN = 1U << 3, + EXEC_APPLY_PERMISSIONS = 1U << 0, + EXEC_APPLY_CHROOT = 1U << 1, + EXEC_APPLY_TTY_STDIN = 1U << 2, /* The following are not used by execute.c, but by consumers internally */ - EXEC_PASS_FDS = 1U << 4, - EXEC_IS_CONTROL = 1U << 5, - EXEC_SETENV_RESULT = 1U << 6, - EXEC_SET_WATCHDOG = 1U << 7, + EXEC_PASS_FDS = 1U << 3, + EXEC_IS_CONTROL = 1U << 4, + EXEC_SETENV_RESULT = 1U << 5, + EXEC_SET_WATCHDOG = 1U << 6, } ExecFlags; struct ExecParameters { @@ -255,6 +253,8 @@ struct ExecParameters { const char *runtime_prefix; + const char *confirm_spawn; + usec_t watchdog_usec; int *idle_pipe; 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/main.c b/src/core/main.c index f5f7df838d..5f9b1acad3 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -68,6 +68,7 @@ #include "mount-setup.h" #include "pager.h" #include "parse-util.h" +#include "path-util.h" #include "proc-cmdline.h" #include "process-util.h" #include "raw-clone.h" @@ -104,7 +105,7 @@ static bool arg_dump_core = true; static int arg_crash_chvt = -1; static bool arg_crash_shell = false; static bool arg_crash_reboot = false; -static bool arg_confirm_spawn = false; +static char *arg_confirm_spawn = NULL; static ShowStatus arg_show_status = _SHOW_STATUS_UNSET; static bool arg_switched_root = false; static bool arg_no_pager = false; @@ -294,6 +295,28 @@ static int parse_crash_chvt(const char *value) { return 0; } +static int parse_confirm_spawn(const char *value, char **console) { + char *s; + int r; + + r = value ? parse_boolean(value) : 1; + if (r == 0) { + *console = NULL; + return 0; + } + + if (r > 0) /* on with default tty */ + s = strdup("/dev/console"); + else if (is_path(value)) /* on with fully qualified path */ + s = strdup(value); + else /* on with only a tty file name, not a fully qualified path */ + s = strjoin("/dev/", value); + if (!s) + return -ENOMEM; + *console = s; + return 0; +} + static int set_machine_id(const char *m) { sd_id128_t t; assert(m); @@ -355,11 +378,11 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat } else if (streq(key, "systemd.confirm_spawn") && value) { - r = parse_boolean(value); + arg_confirm_spawn = mfree(arg_confirm_spawn); + + r = parse_confirm_spawn(value, &arg_confirm_spawn); if (r < 0) - log_warning("Failed to parse confirm spawn switch %s. Ignoring.", value); - else - arg_confirm_spawn = r; + log_warning_errno(r, "Failed to parse confirm_spawn switch %s. Ignoring.", value); } else if (streq(key, "systemd.show_status") && value) { @@ -952,12 +975,11 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_CONFIRM_SPAWN: - r = optarg ? parse_boolean(optarg) : 1; - if (r < 0) { - log_error("Failed to parse confirm spawn boolean %s.", optarg); - return r; - } - arg_confirm_spawn = r; + arg_confirm_spawn = mfree(arg_confirm_spawn); + + r = parse_confirm_spawn(optarg, &arg_confirm_spawn); + if (r < 0) + return log_error_errno(r, "Failed to parse confirm spawn option: %m"); break; case ARG_SHOW_STATUS: @@ -1991,6 +2013,7 @@ finish: arg_default_rlimit[j] = mfree(arg_default_rlimit[j]); arg_default_unit = mfree(arg_default_unit); + arg_confirm_spawn = mfree(arg_confirm_spawn); arg_join_controllers = strv_free_free(arg_join_controllers); arg_default_environment = strv_free(arg_default_environment); arg_syscall_archs = set_free(arg_syscall_archs); diff --git a/src/core/manager.c b/src/core/manager.c index 52174eac07..1f663d3c1d 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -111,6 +111,12 @@ static void manager_watch_jobs_in_progress(Manager *m) { assert(m); + /* We do not want to show the cylon animation if the user + * needs to confirm service executions otherwise confirmation + * messages will be screwed by the cylon animation. */ + if (!manager_is_confirm_spawn_disabled(m)) + return; + if (m->jobs_in_progress_event_source) return; @@ -981,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); @@ -996,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++; @@ -1015,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; } @@ -1035,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)); @@ -2228,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) @@ -2968,7 +2999,7 @@ void manager_check_finished(Manager *m) { manager_close_idle_pipe(m); /* Turn off confirm spawn now */ - m->confirm_spawn = false; + m->confirm_spawn = NULL; /* No need to update ask password status when we're going non-interactive */ manager_close_ask_password(m); @@ -3153,6 +3184,49 @@ static bool manager_get_show_status(Manager *m, StatusType type) { return false; } +const char *manager_get_confirm_spawn(Manager *m) { + static int last_errno = 0; + const char *vc = m->confirm_spawn; + struct stat st; + int r; + + /* Here's the deal: we want to test the validity of the console but don't want + * PID1 to go through the whole console process which might block. But we also + * want to warn the user only once if something is wrong with the console so we + * cannot do the sanity checks after spawning our children. So here we simply do + * really basic tests to hopefully trap common errors. + * + * If the console suddenly disappear at the time our children will really it + * then they will simply fail to acquire it and a positive answer will be + * assumed. New children will fallback to /dev/console though. + * + * Note: TTYs are devices that can come and go any time, and frequently aren't + * available yet during early boot (consider a USB rs232 dongle...). If for any + * reason the configured console is not ready, we fallback to the default + * console. */ + + if (!vc || path_equal(vc, "/dev/console")) + return vc; + + r = stat(vc, &st); + if (r < 0) + goto fail; + + if (!S_ISCHR(st.st_mode)) { + errno = ENOTTY; + goto fail; + } + + last_errno = 0; + return vc; +fail: + if (last_errno != errno) { + last_errno = errno; + log_warning_errno(errno, "Failed to open %s: %m, using default console", vc); + } + return "/dev/console"; +} + void manager_set_first_boot(Manager *m, bool b) { assert(m); @@ -3169,6 +3243,17 @@ void manager_set_first_boot(Manager *m, bool b) { m->first_boot = b; } +void manager_disable_confirm_spawn(void) { + (void) touch("/run/systemd/confirm_spawn_disabled"); +} + +bool manager_is_confirm_spawn_disabled(Manager *m) { + if (!m->confirm_spawn) + return true; + + return access("/run/systemd/confirm_spawn_disabled", F_OK) >= 0; +} + void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) { va_list ap; diff --git a/src/core/manager.h b/src/core/manager.h index 35172fdba9..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; @@ -246,7 +246,7 @@ struct Manager { uint8_t return_value; ShowStatus show_status; - bool confirm_spawn; + char *confirm_spawn; bool no_console_output; ExecOutput default_std_output, default_std_error; @@ -403,3 +403,7 @@ void manager_deserialize_gid_refs_one(Manager *m, const char *value); const char *manager_state_to_string(ManagerState m) _const_; ManagerState manager_state_from_string(const char *s) _pure_; + +const char *manager_get_confirm_spawn(Manager *m); +bool manager_is_confirm_spawn_disabled(Manager *m); +void manager_disable_confirm_spawn(void); diff --git a/src/core/mount.c b/src/core/mount.c index 43e0f1c746..1c2be28d55 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -747,7 +747,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { return r; exec_params.environment = UNIT(m)->manager->environment; - exec_params.flags |= UNIT(m)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; + exec_params.confirm_spawn = manager_get_confirm_spawn(UNIT(m)->manager); exec_params.cgroup_supported = UNIT(m)->manager->cgroup_supported; exec_params.cgroup_path = UNIT(m)->cgroup_path; exec_params.cgroup_delegate = m->cgroup_context.delegate; 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/service.c b/src/core/service.c index 7aa1fba572..9ad4cf5070 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1335,7 +1335,7 @@ static int service_spawn( exec_params.fds = fds; exec_params.fd_names = fd_names; exec_params.n_fds = n_fds; - exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; + exec_params.confirm_spawn = manager_get_confirm_spawn(UNIT(s)->manager); exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; exec_params.cgroup_path = path; exec_params.cgroup_delegate = s->cgroup_context.delegate; diff --git a/src/core/socket.c b/src/core/socket.c index ebacd74a47..1a53d47f21 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1778,7 +1778,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { exec_params.argv = argv; exec_params.environment = UNIT(s)->manager->environment; - exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; + exec_params.confirm_spawn = manager_get_confirm_spawn(UNIT(s)->manager); exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; exec_params.cgroup_path = UNIT(s)->cgroup_path; exec_params.cgroup_delegate = s->cgroup_context.delegate; diff --git a/src/core/swap.c b/src/core/swap.c index b870ac88e3..bf404db8c3 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -636,7 +636,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { goto fail; exec_params.environment = UNIT(s)->manager->environment; - exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; + exec_params.confirm_spawn = manager_get_confirm_spawn(UNIT(s)->manager); exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; exec_params.cgroup_path = UNIT(s)->cgroup_path; exec_params.cgroup_delegate = s->cgroup_context.delegate; diff --git a/src/core/unit.c b/src/core/unit.c index bba0f5d357..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); @@ -1515,6 +1511,17 @@ int unit_start_limit_test(Unit *u) { return emergency_action(u->manager, u->start_limit_action, u->reboot_arg, "unit failed"); } +bool unit_shall_confirm_spawn(Unit *u) { + + if (manager_is_confirm_spawn_disabled(u->manager)) + return false; + + /* For some reasons units remaining in the same process group + * as PID 1 fail to acquire the console even if it's not used + * by any process. So skip the confirmation question for them. */ + return !unit_get_exec_context(u)->same_pgrp; +} + /* Errors: * -EBADR: This unit type does not support starting. * -EALREADY: Unit is already started. @@ -3429,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 991543664b..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]; @@ -654,6 +657,8 @@ void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid); int unit_set_invocation_id(Unit *u, sd_id128_t id); int unit_acquire_invocation_id(Unit *u); +bool unit_shall_confirm_spawn(Unit *u); + /* Macros which append UNIT= or USER_UNIT= to the message */ #define log_unit_full(unit, level, error, ...) \ 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, ð_mac, arp_type, dhcp_hlen); + bcast_addr, ð_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 69b9efe320..a6adbbe879 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 @@ -4041,7 +4043,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 */ @@ -4056,8 +4058,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); @@ -4315,8 +4317,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 8fcbe97ba7..aeb79b131e 100644 --- a/src/shared/nsflags.c +++ b/src/shared/nsflags.c @@ -22,7 +22,6 @@ #include "alloc-util.h" #include "extract-word.h" #include "nsflags.h" -#include "seccomp-util.h" #include "string-util.h" const struct namespace_flag_map namespace_flag_map[] = { @@ -69,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]; |