diff options
70 files changed, 1460 insertions, 373 deletions
diff --git a/man/systemctl.xml b/man/systemctl.xml index 009aabacb9..e3776fb277 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -367,10 +367,9 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err <listitem> <para>Shows units required and wanted by the specified units. This recursively lists units following the - <varname>Requires=</varname>, - <varname>Requisite=</varname>, - <varname>ConsistsOf=</varname>, - <varname>Wants=</varname>, <varname>BindsTo=</varname> + <varname>Requires=</varname>, <varname>Requisite=</varname>, + <varname>Wants=</varname>, <varname>ConsistsOf=</varname>, + <varname>BindsTo=</varname>, and <varname>Upholds=</varname> dependencies. If no units are specified, <filename>default.target</filename> is implied.</para> @@ -1181,7 +1180,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err </listitem> </varlistentry> <varlistentry> - <term><command>cancel <replaceable>JOB</replaceable>…</command></term> + <term><command>cancel <optional><replaceable>JOB</replaceable>…</optional></command></term> <listitem> <para>Cancel one or more jobs specified on the command line @@ -1791,7 +1790,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err <para>Show reverse dependencies between units with <command>list-dependencies</command>, i.e. follow dependencies of type <varname>WantedBy=</varname>, - <varname>RequiredBy=</varname>, + <varname>RequiredBy=</varname>, <varname>UpheldBy=</varname>, <varname>PartOf=</varname>, <varname>BoundBy=</varname>, instead of <varname>Wants=</varname> and similar. </para> diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index c618e403f7..e28bfe845e 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -749,8 +749,7 @@ <para>When <varname>Upholds=b.service</varname> is used on <filename>a.service</filename>, this dependency will show as <varname>UpheldBy=a.service</varname> in the property listing of - <filename>b.service</filename>. The <varname>UpheldBy=</varname> dependency cannot be specified - directly.</para> + <filename>b.service</filename>.</para> </listitem> </varlistentry> @@ -1849,6 +1848,12 @@ <entry>[Install] section</entry> </row> <row> + <entry><varname>Upholds=</varname></entry> + <entry><varname>UpheldBy=</varname></entry> + <entry>[Unit] section</entry> + <entry>[Install] section</entry> + </row> + <row> <entry><varname>PartOf=</varname></entry> <entry><varname>ConsistsOf=</varname></entry> <entry>[Unit] section</entry> @@ -1895,10 +1900,10 @@ </tgroup> </table> - <para>Note: <varname>WantedBy=</varname> and <varname>RequiredBy=</varname> are - used in the [Install] section to create symlinks in <filename>.wants/</filename> - and <filename>.requires/</filename> directories. They cannot be used directly as a - unit configuration setting.</para> + <para>Note: <varname>WantedBy=</varname>, <varname>RequiredBy=</varname>, and <varname>UpheldBy=</varname> + are used in the [Install] section to create symlinks in <filename>.wants/</filename>, + <filename>.requires/</filename>, and <filename>.upholds/</filename> directories. They cannot be used + directly as a unit configuration setting.</para> <para>Note: <varname>ConsistsOf=</varname>, <varname>BoundBy=</varname>, <varname>RequisiteOf=</varname>, <varname>ConflictedBy=</varname> are created @@ -1947,23 +1952,23 @@ <varlistentry> <term><varname>WantedBy=</varname></term> <term><varname>RequiredBy=</varname></term> + <term><varname>UpheldBy=</varname></term> <listitem><para>This option may be used more than once, or a space-separated list of unit names may - be given. A symbolic link is created in the <filename>.wants/</filename> or - <filename>.requires/</filename> directory of each of the listed units when this unit is installed by - <command>systemctl enable</command>. This has the effect of a dependency of type - <varname>Wants=</varname> or <varname>Requires=</varname> being added from the listed unit to the - current unit. The primary result is that the current unit will be started when the listed unit is - started, see the description of <varname>Wants=</varname> and <varname>Requires=</varname> in the - [Unit] section for details.</para> + be given. A symbolic link is created in the <filename>.wants/</filename>, <filename>.requires/</filename>, + or <filename>.upholds/</filename> directory of each of the listed units when this unit is installed + by <command>systemctl enable</command>. This has the effect of a dependency of type + <varname>Wants=</varname>, <varname>Requires=</varname>, or <varname>Upholds=</varname> being added + from the listed unit to the current unit. See the description of the mentioned dependency types + in the [Unit] section for details.</para> <para>In case of template units listing non template units, the listing unit must have <varname>DefaultInstance=</varname> set, or <command>systemctl enable</command> must be called with an instance name. The instance (default or specified) will be added to the - <filename>.wants/</filename> or <filename>.requires/</filename> list of the listed unit. For example, - <command>WantedBy=getty.target</command> in a service <filename>getty@.service</filename> will result - in <command>systemctl enable getty@tty2.service</command> creating a - <filename>getty.target.wants/getty@tty2.service</filename> link to + <filename>.wants/</filename>, <filename>.requires/</filename>, or <filename>.upholds/</filename> + list of the listed unit. For example, <command>WantedBy=getty.target</command> in a service + <filename>getty@.service</filename> will result in <command>systemctl enable getty@tty2.service</command> + creating a <filename>getty.target.wants/getty@tty2.service</filename> link to <filename>getty@.service</filename>. This also applies to listing specific instances of templated units: this specific instance will gain the dependency. A template unit may also list a template unit, in which case a generic dependency will be added where each instance of the listing unit will diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 09a38e7930..b555c713fc 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -599,7 +599,7 @@ static int parse_argv(int argc, char *argv[]) { static int run(int argc, char *argv[]) { _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; - _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL; + _cleanup_(umount_and_freep) char *mounted_dir = NULL; static const Verb verbs[] = { { "help", VERB_ANY, VERB_ANY, 0, help }, @@ -660,13 +660,13 @@ static int run(int argc, char *argv[]) { DISSECT_IMAGE_GENERIC_ROOT | DISSECT_IMAGE_RELAX_VAR_CHECK | DISSECT_IMAGE_READ_ONLY, - &unlink_dir, + &mounted_dir, /* ret_dir_fd= */ NULL, &loop_device); if (r < 0) return r; - arg_root = strdup(unlink_dir); + arg_root = strdup(mounted_dir); if (!arg_root) return log_oom(); } diff --git a/src/basic/memfd-util.c b/src/basic/memfd-util.c index 285abd41d3..8e6946642b 100644 --- a/src/basic/memfd-util.c +++ b/src/basic/memfd-util.c @@ -92,9 +92,15 @@ int memfd_map(int fd, uint64_t offset, size_t size, void **p) { } int memfd_set_sealed(int fd) { + int r; + assert(fd >= 0); - return RET_NERRNO(fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL)); + r = RET_NERRNO(fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_EXEC | F_SEAL_SEAL)); + if (r == -EINVAL) /* old kernel ? */ + r = RET_NERRNO(fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL)); + + return r; } int memfd_get_sealed(int fd) { @@ -106,7 +112,8 @@ int memfd_get_sealed(int fd) { if (r < 0) return -errno; - return r == (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL); + /* We ignore F_SEAL_EXEC here to support older kernels. */ + return FLAGS_SET(r, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL); } int memfd_get_size(int fd, uint64_t *sz) { diff --git a/src/basic/missing_fcntl.h b/src/basic/missing_fcntl.h index 79e95a8f6f..24b2dc3119 100644 --- a/src/basic/missing_fcntl.h +++ b/src/basic/missing_fcntl.h @@ -25,6 +25,14 @@ #define F_SEAL_WRITE 0x0008 /* prevent writes */ #endif +#ifndef F_SEAL_FUTURE_WRITE +#define F_SEAL_FUTURE_WRITE 0x0010 /* prevent future writes while mapped */ +#endif + +#ifndef F_SEAL_EXEC +#define F_SEAL_EXEC 0x0020 /* prevent chmod modifying exec bits */ +#endif + #ifndef F_OFD_GETLK #define F_OFD_GETLK 36 #define F_OFD_SETLK 37 diff --git a/src/basic/unit-file.c b/src/basic/unit-file.c index 41422579d6..54f2137a36 100644 --- a/src/basic/unit-file.c +++ b/src/basic/unit-file.c @@ -250,9 +250,10 @@ bool lookup_paths_timestamp_hash_same(const LookupPaths *lp, uint64_t timestamp_ static int directory_name_is_valid(const char *name) { - /* Accept a directory whose name is a valid unit file name ending in .wants/, .requires/ or .d/ */ + /* Accept a directory whose name is a valid unit file name ending in .wants/, .requires/, + * .upholds/ or .d/ */ - FOREACH_STRING(suffix, ".wants", ".requires", ".d") { + FOREACH_STRING(suffix, ".wants", ".requires", ".upholds", ".d") { _cleanup_free_ char *chopped = NULL; const char *e; diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 0480e320c0..65608f5e83 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -445,7 +445,7 @@ static int bootctl_main(int argc, char *argv[]) { static int run(int argc, char *argv[]) { _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; - _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL; + _cleanup_(umount_and_freep) char *mounted_dir = NULL; int r; log_setup(); @@ -493,13 +493,13 @@ static int run(int argc, char *argv[]) { arg_image_policy, DISSECT_IMAGE_GENERIC_ROOT | DISSECT_IMAGE_RELAX_VAR_CHECK, - &unlink_dir, + &mounted_dir, /* ret_dir_fd= */ NULL, &loop_device); if (r < 0) return r; - arg_root = strdup(unlink_dir); + arg_root = strdup(mounted_dir); if (!arg_root) return log_oom(); } diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c index 90f20c05a1..820d27da20 100644 --- a/src/busctl/busctl.c +++ b/src/busctl/busctl.c @@ -77,6 +77,8 @@ static int acquire_bus(bool set_monitor, sd_bus **ret) { if (r < 0) return log_error_errno(r, "Failed to allocate bus: %m"); + (void) sd_bus_set_description(bus, "busctl"); + if (set_monitor) { r = sd_bus_set_monitor(bus, true); if (r < 0) diff --git a/src/core/dbus.c b/src/core/dbus.c index c41e1a6c74..7277696196 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -684,7 +684,7 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void return 0; } - nfd = -EBADF; + TAKE_FD(nfd); r = bus_check_peercred(bus); if (r < 0) { @@ -703,7 +703,8 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void r = sd_bus_negotiate_creds(bus, 1, SD_BUS_CREDS_PID|SD_BUS_CREDS_UID| SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS| - SD_BUS_CREDS_SELINUX_CONTEXT); + SD_BUS_CREDS_SELINUX_CONTEXT| + SD_BUS_CREDS_COMM|SD_BUS_CREDS_DESCRIPTION); if (r < 0) { log_warning_errno(r, "Failed to enable credentials for new connection: %m"); return 0; @@ -721,6 +722,23 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void return 0; } + if (DEBUG_LOGGING) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; + const char *comm = NULL, *description = NULL; + pid_t pid = 0; + + r = sd_bus_get_owner_creds(bus, SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|SD_BUS_CREDS_DESCRIPTION, &c); + if (r < 0) + log_warning_errno(r, "Failed to get peer creds, ignoring: %m"); + else { + (void) sd_bus_creds_get_pid(c, &pid); + (void) sd_bus_creds_get_comm(c, &comm); + (void) sd_bus_creds_get_description(c, &description); + } + + log_debug("Accepting direct incoming connection from " PID_FMT " (%s) [%s]", pid, strna(comm), strna(description)); + } + r = sd_bus_attach_event(bus, m->event, SD_EVENT_PRIORITY_NORMAL); if (r < 0) { log_warning_errno(r, "Failed to attach new connection bus to event loop: %m"); diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c index 53d2a3daa1..fd45744261 100644 --- a/src/core/load-dropin.c +++ b/src/core/load-dropin.c @@ -88,7 +88,7 @@ int unit_load_dropin(Unit *u) { assert(u); - /* Load dependencies from .wants and .requires directories */ + /* Load dependencies from .wants, .requires and .upholds directories */ r = process_deps(u, UNIT_WANTS, ".wants"); if (r < 0) return r; @@ -97,6 +97,10 @@ int unit_load_dropin(Unit *u) { if (r < 0) return r; + r = process_deps(u, UNIT_UPHOLDS, ".upholds"); + if (r < 0) + return r; + /* Load .conf dropins */ r = unit_find_dropin_paths(u, &l); if (r <= 0) diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in index 110bccb7ad..2a3c2c2cb8 100644 --- a/src/core/load-fragment-gperf.gperf.in +++ b/src/core/load-fragment-gperf.gperf.in @@ -580,5 +580,6 @@ Scope.OOMPolicy, config_parse_oom_policy, Install.Alias, NULL, 0, 0 Install.WantedBy, NULL, 0, 0 Install.RequiredBy, NULL, 0, 0 +Install.UpheldBy, NULL, 0, 0 Install.Also, NULL, 0, 0 Install.DefaultInstance, NULL, 0, 0 diff --git a/src/core/namespace.c b/src/core/namespace.c index 2fcc096217..1116ece59d 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -2166,10 +2166,11 @@ int setup_namespace( * in the root. The temporary directory prevents any mounts from being potentially obscured * my other mounts we already applied. We use the same mount point for all images, which is * safe, since they all live in their own namespaces after all, and hence won't see each - * other. */ + * other. (Note: this directory is also created by PID 1 early on, we create it here for + * similar reasons as /run/systemd/ first.) */ + root = "/run/systemd/mount-rootfs"; + (void) mkdir_label(root, 0555); - root = "/run/systemd/unit-root"; - (void) mkdir_label(root, 0700); require_prefix = true; } diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index bc52cc0b06..e34d74765e 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -1350,7 +1350,7 @@ static int coredumpctl_main(int argc, char *argv[]) { static int run(int argc, char *argv[]) { _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; - _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; + _cleanup_(umount_and_freep) char *mounted_dir = NULL; int r, units_active; setlocale(LC_ALL, ""); diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 71b1e25c9d..6d50054baf 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -1506,7 +1506,7 @@ static int parse_argv(int argc, char *argv[]) { static int run(int argc, char *argv[]) { _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; - _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL; + _cleanup_(umount_and_freep) char *mounted_dir = NULL; _cleanup_close_ int rfd = -EBADF; int r; @@ -1546,13 +1546,13 @@ static int run(int argc, char *argv[]) { DISSECT_IMAGE_RELAX_VAR_CHECK | DISSECT_IMAGE_FSCK | DISSECT_IMAGE_GROWFS, - &unlink_dir, + &mounted_dir, &rfd, &loop_device); if (r < 0) return r; - arg_root = strdup(unlink_dir); + arg_root = strdup(mounted_dir); if (!arg_root) return log_oom(); } else { diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 12119b302c..62f74551ad 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -2112,7 +2112,7 @@ static int wait_for_change(sd_journal *j, int poll_fd) { static int run(int argc, char *argv[]) { _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; - _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL; + _cleanup_(umount_and_freep) char *mounted_dir = NULL; bool previous_boot_id_valid = false, first_line = true, ellipsized = false, need_seek = false, since_seeked = false; bool use_cursor = false, after_cursor = false; _cleanup_(sd_journal_closep) sd_journal *j = NULL; @@ -2143,13 +2143,13 @@ static int run(int argc, char *argv[]) { DISSECT_IMAGE_VALIDATE_OS | DISSECT_IMAGE_RELAX_VAR_CHECK | (arg_action == ACTION_UPDATE_CATALOG ? DISSECT_IMAGE_FSCK|DISSECT_IMAGE_GROWFS : DISSECT_IMAGE_READ_ONLY), - &unlink_dir, + &mounted_dir, /* ret_dir_fd= */ NULL, &loop_device); if (r < 0) return r; - arg_root = strdup(unlink_dir); + arg_root = strdup(mounted_dir); if (!arg_root) return log_oom(); } diff --git a/src/libsystemd/meson.build b/src/libsystemd/meson.build index aa42497080..ee24240cd1 100644 --- a/src/libsystemd/meson.build +++ b/src/libsystemd/meson.build @@ -211,6 +211,10 @@ tests += [ 'dependencies' : threads, }, { + 'sources' : files('sd-bus/test-bus-peersockaddr.c'), + 'dependencies' : threads, + }, + { 'sources' : files('sd-bus/test-bus-queue-ref-cycle.c'), 'dependencies' : threads, }, diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c index 864acd73ac..287cde6d41 100644 --- a/src/libsystemd/sd-bus/bus-control.c +++ b/src/libsystemd/sd-bus/bus-control.c @@ -722,9 +722,51 @@ _public_ int sd_bus_get_name_creds( return 0; } +static int parse_sockaddr_string(const char *t, char **ret_comm, char **ret_description) { + _cleanup_free_ char *comm = NULL, *description = NULL; + const char *e, *sl; + + assert(t); + assert(ret_comm); + assert(ret_description); + + e = strstrafter(t, "/bus/"); + if (!e) { + log_debug("Didn't find /bus/ substring in peer socket address, ignoring."); + goto not_found; + } + + sl = strchr(e, '/'); + if (!sl) { + log_debug("Didn't find / substring after /bus/ in peer socket address, ignoring."); + goto not_found; + } + + if (sl - e > 0) { + comm = strndup(e, sl - e); + if (!comm) + return -ENOMEM; + } + + sl++; + if (!isempty(sl)) { + description = strdup(sl); + if (!description) + return -ENOMEM; + } + + *ret_comm = TAKE_PTR(comm); + *ret_description = TAKE_PTR(description); + return 0; + +not_found: + *ret_comm = *ret_description = NULL; + return 0; +} + _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; - bool do_label, do_groups; + bool do_label, do_groups, do_sockaddr_peer; pid_t pid = 0; int r; @@ -742,9 +784,12 @@ _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **r do_label = bus->label && (mask & SD_BUS_CREDS_SELINUX_CONTEXT); do_groups = bus->n_groups != SIZE_MAX && (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS); + do_sockaddr_peer = bus->sockaddr_size_peer >= offsetof(struct sockaddr_un, sun_path) + 1 && + bus->sockaddr_peer.sa.sa_family == AF_UNIX && + bus->sockaddr_peer.un.sun_path[0] == 0; /* Avoid allocating anything if we have no chance of returning useful data */ - if (!bus->ucred_valid && !do_label && !do_groups) + if (!bus->ucred_valid && !do_label && !do_groups && !do_sockaddr_peer) return -ENODATA; c = bus_creds_new(); @@ -786,6 +831,35 @@ _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **r c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; } + if (do_sockaddr_peer) { + _cleanup_free_ char *t = NULL; + + assert(bus->sockaddr_size_peer >= offsetof(struct sockaddr_un, sun_path) + 1); + assert(bus->sockaddr_peer.sa.sa_family == AF_UNIX); + assert(bus->sockaddr_peer.un.sun_path[0] == 0); + + /* So this is an abstract namespace socket, good. Now let's find the data we are interested in */ + r = make_cstring(bus->sockaddr_peer.un.sun_path + 1, + bus->sockaddr_size_peer - offsetof(struct sockaddr_un, sun_path) - 1, + MAKE_CSTRING_ALLOW_TRAILING_NUL, + &t); + if (r == -ENOMEM) + return r; + if (r < 0) + log_debug_errno(r, "Can't extract string from peer socket address, ignoring: %m"); + else { + r = parse_sockaddr_string(t, &c->comm, &c->description); + if (r < 0) + return r; + + if (c->comm) + c->mask |= SD_BUS_CREDS_COMM & mask; + + if (c->description) + c->mask |= SD_BUS_CREDS_DESCRIPTION & mask; + } + } + r = bus_creds_add_more(c, mask, pid, 0); if (r < 0 && r != -ESRCH) /* If the process vanished, then don't complain, just return what we got */ return r; diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h index 0e44897e0e..1cf6974bff 100644 --- a/src/libsystemd/sd-bus/bus-internal.h +++ b/src/libsystemd/sd-bus/bus-internal.h @@ -267,6 +267,8 @@ struct sd_bus { char *label; gid_t *groups; size_t n_groups; + union sockaddr_union sockaddr_peer; + socklen_t sockaddr_size_peer; uint64_t creds_mask; diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c index ceda20f289..741d54cabd 100644 --- a/src/libsystemd/sd-bus/bus-socket.c +++ b/src/libsystemd/sd-bus/bus-socket.c @@ -22,6 +22,7 @@ #include "memory-util.h" #include "path-util.h" #include "process-util.h" +#include "random-util.h" #include "signal-util.h" #include "stdio-util.h" #include "string-util.h" @@ -651,6 +652,17 @@ static void bus_get_peercred(sd_bus *b) { b->n_groups = (size_t) r; else if (!IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT)) log_debug_errno(r, "Failed to determine peer's group list: %m"); + + /* Let's query the peers socket address, it might carry information such as the peer's comm or + * description string */ + zero(b->sockaddr_peer); + b->sockaddr_size_peer = 0; + + socklen_t l = sizeof(b->sockaddr_peer) - 1; /* Leave space for a NUL */ + if (getpeername(b->input_fd, &b->sockaddr_peer.sa, &l) < 0) + log_debug_errno(errno, "Failed to get peer's socket address, ignoring: %m"); + else + b->sockaddr_size_peer = l; } static int bus_socket_start_auth_client(sd_bus *b) { @@ -889,6 +901,50 @@ fail: return r; } +static int bind_description(sd_bus *b, int fd, int family) { + _cleanup_free_ char *bind_name = NULL, *comm = NULL; + union sockaddr_union bsa; + const char *d = NULL; + int r; + + assert(b); + assert(fd >= 0); + + /* If this is an AF_UNIX socket, let's set our client's socket address to carry the description + * string for this bus connection. This is useful for debugging things, as the connection name is + * visible in various socket-related tools, and can even be queried by the server side. */ + + if (family != AF_UNIX) + return 0; + + (void) sd_bus_get_description(b, &d); + + /* Generate a recognizable source address in the abstract namespace. We'll include: + * - a random 64bit value (to avoid collisions) + * - our "comm" process name (suppressed if contains "/" to avoid parsing issues) + * - the description string of the bus connection. */ + (void) get_process_comm(0, &comm); + if (comm && strchr(comm, '/')) + comm = mfree(comm); + + if (!d && !comm) /* skip if we don't have either field, rely on kernel autobind instead */ + return 0; + + if (asprintf(&bind_name, "@%" PRIx64 "/bus/%s/%s", random_u64(), strempty(comm), strempty(d)) < 0) + return -ENOMEM; + + strshorten(bind_name, sizeof_field(struct sockaddr_un, sun_path)); + + r = sockaddr_un_set_path(&bsa.un, bind_name); + if (r < 0) + return r; + + if (bind(fd, &bsa.sa, r) < 0) + return -errno; + + return 0; +} + int bus_socket_connect(sd_bus *b) { bool inotify_done = false; int r; @@ -911,6 +967,10 @@ int bus_socket_connect(sd_bus *b) { if (b->input_fd < 0) return -errno; + r = bind_description(b, b->input_fd, b->sockaddr.sa.sa_family); + if (r < 0) + return r; + b->input_fd = fd_move_above_stdio(b->input_fd); b->output_fd = b->input_fd; diff --git a/src/libsystemd/sd-bus/test-bus-chat.c b/src/libsystemd/sd-bus/test-bus-chat.c index 8e66919b46..7abe129186 100644 --- a/src/libsystemd/sd-bus/test-bus-chat.c +++ b/src/libsystemd/sd-bus/test-bus-chat.c @@ -46,31 +46,25 @@ static int object_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_ return 0; } -static int server_init(sd_bus **_bus) { - sd_bus *bus = NULL; +static int server_init(sd_bus **ret_bus) { + _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; + const char *unique, *desc; sd_id128_t id; int r; - const char *unique, *desc; - assert_se(_bus); + assert_se(ret_bus); r = sd_bus_open_user_with_description(&bus, "my bus!"); - if (r < 0) { - log_error_errno(r, "Failed to connect to user bus: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to connect to user bus: %m"); r = sd_bus_get_bus_id(bus, &id); - if (r < 0) { - log_error_errno(r, "Failed to get server ID: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to get server ID: %m"); r = sd_bus_get_unique_name(bus, &unique); - if (r < 0) { - log_error_errno(r, "Failed to get unique name: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to get unique name: %m"); assert_se(sd_bus_get_description(bus, &desc) >= 0); assert_se(streq(desc, "my bus!")); @@ -80,48 +74,35 @@ static int server_init(sd_bus **_bus) { log_info("Can send file handles: %i", sd_bus_can_send(bus, 'h')); r = sd_bus_request_name(bus, "org.freedesktop.systemd.test", 0); - if (r < 0) { - log_error_errno(r, "Failed to acquire name: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to acquire name: %m"); r = sd_bus_add_fallback(bus, NULL, "/foo/bar", object_callback, NULL); - if (r < 0) { - log_error_errno(r, "Failed to add object: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to add object: %m"); r = sd_bus_match_signal(bus, NULL, NULL, NULL, "foo.bar", "Notify", match_callback, NULL); - if (r < 0) { - log_error_errno(r, "Failed to request match: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to request match: %m"); r = sd_bus_match_signal(bus, NULL, NULL, NULL, "foo.bar", "NotifyTo", match_callback, NULL); - if (r < 0) { - log_error_errno(r, "Failed to request match: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to request match: %m"); r = sd_bus_add_match(bus, NULL, "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'", match_callback, NULL); - if (r < 0) { - log_error_errno(r, "Failed to add match: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to add match: %m"); bus_match_dump(stdout, &bus->match_callbacks, 0); - *_bus = bus; + *ret_bus = TAKE_PTR(bus); return 0; - -fail: - sd_bus_unref(bus); - return r; } -static int server(sd_bus *bus) { - int r; +static int server(sd_bus *_bus) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = ASSERT_PTR(_bus); bool client1_gone = false, client2_gone = false; + int r; while (!client1_gone || !client2_gone) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; @@ -129,30 +110,26 @@ static int server(sd_bus *bus) { const char *label = NULL; r = sd_bus_process(bus, &m); - if (r < 0) { - log_error_errno(r, "Failed to process requests: %m"); - goto fail; - } - + if (r < 0) + return log_error_errno(r, "Failed to process requests: %m"); if (r == 0) { r = sd_bus_wait(bus, UINT64_MAX); - if (r < 0) { - log_error_errno(r, "Failed to wait: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to wait: %m"); continue; } - if (!m) continue; - sd_bus_creds_get_pid(sd_bus_message_get_creds(m), &pid); - sd_bus_creds_get_selinux_context(sd_bus_message_get_creds(m), &label); + (void) sd_bus_creds_get_pid(sd_bus_message_get_creds(m), &pid); + (void) sd_bus_creds_get_selinux_context(sd_bus_message_get_creds(m), &label); + log_info("Got message! member=%s pid="PID_FMT" label=%s", strna(sd_bus_message_get_member(m)), pid, strna(label)); + /* sd_bus_message_dump(m); */ /* sd_bus_message_rewind(m, true); */ @@ -161,40 +138,31 @@ static int server(sd_bus *bus) { _cleanup_free_ char *lowercase = NULL; r = sd_bus_message_read(m, "s", &hello); - if (r < 0) { - log_error_errno(r, "Failed to get parameter: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to get parameter: %m"); lowercase = strdup(hello); - if (!lowercase) { - r = log_oom(); - goto fail; - } + if (!lowercase) + return log_oom(); ascii_strlower(lowercase); r = sd_bus_reply_method_return(m, "s", lowercase); - if (r < 0) { - log_error_errno(r, "Failed to send reply: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to send reply: %m"); + } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient1")) { r = sd_bus_reply_method_return(m, NULL); - if (r < 0) { - log_error_errno(r, "Failed to send reply: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to send reply: %m"); client1_gone = true; } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient2")) { r = sd_bus_reply_method_return(m, NULL); - if (r < 0) { - log_error_errno(r, "Failed to send reply: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to send reply: %m"); client2_gone = true; } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "Slow")) { @@ -202,56 +170,40 @@ static int server(sd_bus *bus) { sleep(1); r = sd_bus_reply_method_return(m, NULL); - if (r < 0) { - log_error_errno(r, "Failed to send reply: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to send reply: %m"); } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "FileDescriptor")) { int fd; static const char x = 'X'; r = sd_bus_message_read(m, "h", &fd); - if (r < 0) { - log_error_errno(r, "Failed to get parameter: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to get parameter: %m"); log_info("Received fd=%d", fd); if (write(fd, &x, 1) < 0) { - log_error_errno(errno, "Failed to write to fd: %m"); + r = log_error_errno(errno, "Failed to write to fd: %m"); safe_close(fd); - goto fail; + return r; } r = sd_bus_reply_method_return(m, NULL); - if (r < 0) { - log_error_errno(r, "Failed to send reply: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to send reply: %m"); } else if (sd_bus_message_is_method_call(m, NULL, NULL)) { r = sd_bus_reply_method_error( m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method.")); - if (r < 0) { - log_error_errno(r, "Failed to send reply: %m"); - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to send reply: %m"); } } - r = 0; - -fail: - if (bus) { - sd_bus_flush(bus); - sd_bus_unref(bus); - } - - return r; + return 0; } static void* client1(void *p) { diff --git a/src/libsystemd/sd-bus/test-bus-peersockaddr.c b/src/libsystemd/sd-bus/test-bus-peersockaddr.c new file mode 100644 index 0000000000..8bab429624 --- /dev/null +++ b/src/libsystemd/sd-bus/test-bus-peersockaddr.c @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <pthread.h> +#include <unistd.h> + +#include "sd-bus.h" + +#include "fd-util.h" +#include "process-util.h" +#include "socket-util.h" +#include "tests.h" + +static void *server(void *p) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_close_ int listen_fd = PTR_TO_INT(p), fd = -EBADF; + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; + _cleanup_free_ char *our_comm = NULL; + sd_id128_t id; + int r; + + assert_se(sd_id128_randomize(&id) >= 0); + + fd = accept4(listen_fd, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK); + assert_se(fd >= 0); + + assert_se(sd_bus_new(&bus) >= 0); + assert_se(sd_bus_set_fd(bus, fd, fd) >= 0); + TAKE_FD(fd); + assert_se(sd_bus_set_server(bus, true, id) >= 0); + assert_se(sd_bus_negotiate_creds(bus, 1, SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|SD_BUS_CREDS_DESCRIPTION) >= 0); + + assert_se(sd_bus_start(bus) >= 0); + + assert_se(sd_bus_get_owner_creds(bus, SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|SD_BUS_CREDS_DESCRIPTION, &c) >= 0); + + uid_t u; + assert_se(sd_bus_creds_get_euid(c, &u) >= 0); + assert_se(u == getuid()); + + gid_t g; + assert_se(sd_bus_creds_get_egid(c, &g) >= 0); + assert_se(g == getgid()); + + pid_t pid; + assert_se(sd_bus_creds_get_pid(c, &pid) >= 0); + assert_se(pid == getpid_cached()); + + const char *comm; + assert_se(sd_bus_creds_get_comm(c, &comm) >= 0); + assert_se(get_process_comm(0, &our_comm) >= 0); + assert_se(streq_ptr(comm, our_comm)); + + const char *description; + assert_se(sd_bus_creds_get_description(c, &description) >= 0); + assert_se(streq_ptr(description, "wuffwuff")); + + for (;;) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + + r = sd_bus_process(bus, &m); + assert_se(r >= 0); + + if (r == 0) { + assert_se(sd_bus_wait(bus, UINT64_MAX) >= 0); + continue; + } + + if (sd_bus_message_is_method_call(m, "foo.foo", "Foo") > 0) { + assert_se(sd_bus_reply_method_return(m, "s", "bar") >= 0); + break; + } + } + + return NULL; +} + +static void* client(void *p) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + const char *z; + + assert_se(sd_bus_new(&bus) >= 0); + assert_se(sd_bus_set_description(bus, "wuffwuff") >= 0); + assert_se(sd_bus_set_address(bus, p) >= 0); + assert_se(sd_bus_start(bus) >= 0); + + assert_se(sd_bus_call_method(bus, "foo.foo", "/foo", "foo.foo", "Foo", NULL, &reply, "s", "foo") >= 0); + + assert_se(sd_bus_message_read(reply, "s", &z) >= 0); + assert_se(streq_ptr(z, "bar")); + + return NULL; +} + +TEST(description) { + _cleanup_free_ char *a = NULL; + _cleanup_close_ int fd = -EBADF; + union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + }; + socklen_t salen; + pthread_t s, c; + + fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); + assert_se(fd >= 0); + + assert_se(bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path)) >= 0); /* force auto-bind */ + + assert_se(listen(fd, 1) >= 0); + + salen = sizeof(sa); + assert_se(getsockname(fd, &sa.sa, &salen) >= 0); + assert_se(salen >= offsetof(struct sockaddr_un, sun_path)); + assert_se(sa.un.sun_path[0] == 0); + + assert_se(asprintf(&a, "unix:abstract=%s", sa.un.sun_path + 1) >= 0); + + assert_se(pthread_create(&s, NULL, server, INT_TO_PTR(fd)) == 0); + TAKE_FD(fd); + + assert_se(pthread_create(&c, NULL, client, a) == 0); + + assert_se(pthread_join(s, NULL) == 0); + assert_se(pthread_join(c, NULL) == 0); +} + +DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/libsystemd/sd-bus/test-bus-server.c b/src/libsystemd/sd-bus/test-bus-server.c index ab4045ee15..5024c1d4f5 100644 --- a/src/libsystemd/sd-bus/test-bus-server.c +++ b/src/libsystemd/sd-bus/test-bus-server.c @@ -22,8 +22,8 @@ struct context { }; static void *server(void *p) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; struct context *c = p; - sd_bus *bus = NULL; sd_id128_t id; bool quit = false; int r; @@ -97,11 +97,6 @@ static void *server(void *p) { r = 0; fail: - if (bus) { - sd_bus_flush(bus); - sd_bus_unref(bus); - } - return INT_TO_PTR(r); } diff --git a/src/libsystemd/sd-netlink/netlink-util.c b/src/libsystemd/sd-netlink/netlink-util.c index 6cd916a209..6ded66b935 100644 --- a/src/libsystemd/sd-netlink/netlink-util.c +++ b/src/libsystemd/sd-netlink/netlink-util.c @@ -11,44 +11,93 @@ #include "process-util.h" #include "strv.h" -int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) { +static int set_link_name(sd_netlink **rtnl, int ifindex, const char *name) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; - _cleanup_strv_free_ char **alternative_names = NULL; - bool altname_deleted = false; int r; assert(rtnl); assert(ifindex > 0); assert(name); - if (!ifname_valid(name)) + /* Assign the requested name. */ + r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex); + if (r < 0) + return r; + + r = sd_netlink_message_append_string(message, IFLA_IFNAME, name); + if (r < 0) + return r; + + return sd_netlink_call(*rtnl, message, 0, NULL); +} + +int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name, char* const *alternative_names) { + _cleanup_strv_free_ char **original_altnames = NULL, **new_altnames = NULL; + bool altname_deleted = false; + int r; + + assert(rtnl); + assert(ifindex > 0); + + if (isempty(name) && strv_isempty(alternative_names)) + return 0; + + if (name && !ifname_valid(name)) return -EINVAL; - r = rtnl_get_link_alternative_names(rtnl, ifindex, &alternative_names); + /* If the requested name is already assigned as an alternative name, then first drop it. */ + r = rtnl_get_link_alternative_names(rtnl, ifindex, &original_altnames); if (r < 0) log_debug_errno(r, "Failed to get alternative names on network interface %i, ignoring: %m", ifindex); - if (strv_contains(alternative_names, name)) { - r = rtnl_delete_link_alternative_names(rtnl, ifindex, STRV_MAKE(name)); - if (r < 0) - return log_debug_errno(r, "Failed to remove '%s' from alternative names on network interface %i: %m", - name, ifindex); + if (name) { + if (strv_contains(original_altnames, name)) { + r = rtnl_delete_link_alternative_names(rtnl, ifindex, STRV_MAKE(name)); + if (r < 0) + return log_debug_errno(r, "Failed to remove '%s' from alternative names on network interface %i: %m", + name, ifindex); + + altname_deleted = true; + } - altname_deleted = true; + r = set_link_name(rtnl, ifindex, name); + if (r < 0) + goto fail; } - r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex); - if (r < 0) - goto fail; + /* Filter out already assigned names from requested alternative names. Also, dedup the request. */ + STRV_FOREACH(a, alternative_names) { + if (streq_ptr(name, *a)) + continue; - r = sd_netlink_message_append_string(message, IFLA_IFNAME, name); - if (r < 0) - goto fail; + if (strv_contains(original_altnames, *a)) + continue; - r = sd_netlink_call(*rtnl, message, 0, NULL); - if (r < 0) - goto fail; + if (strv_contains(new_altnames, *a)) + continue; + + if (!ifname_valid_full(*a, IFNAME_VALID_ALTERNATIVE)) + continue; + + r = strv_extend(&new_altnames, *a); + if (r < 0) + return r; + } + + strv_sort(new_altnames); + + /* Finally, assign alternative names. */ + r = rtnl_set_link_alternative_names(rtnl, ifindex, new_altnames); + if (r == -EEXIST) /* Already assigned to another interface? */ + STRV_FOREACH(a, new_altnames) { + r = rtnl_set_link_alternative_names(rtnl, ifindex, STRV_MAKE(*a)); + if (r < 0) + log_debug_errno(r, "Failed to assign '%s' as an alternative name on network interface %i, ignoring: %m", + *a, ifindex); + } + else if (r < 0) + log_debug_errno(r, "Failed to assign alternative names on network interface %i, ignoring: %m", ifindex); return 0; diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h index 72d16fa9a9..2f9f7e9676 100644 --- a/src/libsystemd/sd-netlink/netlink-util.h +++ b/src/libsystemd/sd-netlink/netlink-util.h @@ -28,7 +28,10 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(MultipathRoute*, multipath_route_free); int multipath_route_dup(const MultipathRoute *m, MultipathRoute **ret); -int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name); +int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name, char* const* alternative_names); +static inline int rtnl_append_link_alternative_names(sd_netlink **rtnl, int ifindex, char* const *alternative_names) { + return rtnl_set_link_name(rtnl, ifindex, NULL, alternative_names); +} int rtnl_set_link_properties( sd_netlink **rtnl, int ifindex, diff --git a/src/libsystemd/sd-netlink/test-netlink.c b/src/libsystemd/sd-netlink/test-netlink.c index 9ad8ecf320..43124b99ae 100644 --- a/src/libsystemd/sd-netlink/test-netlink.c +++ b/src/libsystemd/sd-netlink/test-netlink.c @@ -662,12 +662,13 @@ TEST(rtnl_set_link_name) { assert_se(strv_contains(alternative_names, "testlongalternativename")); assert_se(strv_contains(alternative_names, "test-shortname")); - assert_se(rtnl_set_link_name(&rtnl, ifindex, "testlongalternativename") == -EINVAL); - assert_se(rtnl_set_link_name(&rtnl, ifindex, "test-shortname") >= 0); + assert_se(rtnl_set_link_name(&rtnl, ifindex, "testlongalternativename", NULL) == -EINVAL); + assert_se(rtnl_set_link_name(&rtnl, ifindex, "test-shortname", STRV_MAKE("testlongalternativename", "test-shortname", "test-additional-name")) >= 0); alternative_names = strv_free(alternative_names); assert_se(rtnl_get_link_alternative_names(&rtnl, ifindex, &alternative_names) >= 0); assert_se(strv_contains(alternative_names, "testlongalternativename")); + assert_se(strv_contains(alternative_names, "test-additional-name")); assert_se(!strv_contains(alternative_names, "test-shortname")); assert_se(rtnl_delete_link_alternative_names(&rtnl, ifindex, STRV_MAKE("testlongalternativename")) >= 0); @@ -675,6 +676,7 @@ TEST(rtnl_set_link_name) { alternative_names = strv_free(alternative_names); assert_se(rtnl_get_link_alternative_names(&rtnl, ifindex, &alternative_names) >= 0); assert_se(!strv_contains(alternative_names, "testlongalternativename")); + assert_se(strv_contains(alternative_names, "test-additional-name")); assert_se(!strv_contains(alternative_names, "test-shortname")); } diff --git a/src/machine-id-setup/machine-id-setup-main.c b/src/machine-id-setup/machine-id-setup-main.c index 38d66def06..59aad985f8 100644 --- a/src/machine-id-setup/machine-id-setup-main.c +++ b/src/machine-id-setup/machine-id-setup-main.c @@ -136,7 +136,7 @@ static int parse_argv(int argc, char *argv[]) { static int run(int argc, char *argv[]) { _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; - _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL; + _cleanup_(umount_and_freep) char *mounted_dir = NULL; int r; log_parse_environment(); @@ -157,13 +157,13 @@ static int run(int argc, char *argv[]) { DISSECT_IMAGE_RELAX_VAR_CHECK | DISSECT_IMAGE_FSCK | DISSECT_IMAGE_GROWFS, - &unlink_dir, + &mounted_dir, /* ret_dir_fd= */ NULL, &loop_device); if (r < 0) return r; - arg_root = strdup(unlink_dir); + arg_root = strdup(mounted_dir); if (!arg_root) return log_oom(); } diff --git a/src/nspawn/nspawn-oci.c b/src/nspawn/nspawn-oci.c index 3c6bfd3eaf..5e21538597 100644 --- a/src/nspawn/nspawn-oci.c +++ b/src/nspawn/nspawn-oci.c @@ -605,7 +605,7 @@ static int oci_namespace_type(const char *name, JsonVariant *v, JsonDispatchFlag *nsflags = CLONE_NEWCGROUP; else return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), - "Unknown cgroup type, refusing: %s", n); + "Unknown namespace type, refusing: %s", n); return 0; } @@ -663,7 +663,7 @@ static int oci_namespaces(const char *name, JsonVariant *v, JsonDispatchFlags fl if (!FLAGS_SET(n, CLONE_NEWNS)) return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP), - "Containers without file system namespace aren't supported."); + "Containers without a mount namespace aren't supported."); s->private_network = FLAGS_SET(n, CLONE_NEWNET); s->userns_mode = FLAGS_SET(n, CLONE_NEWUSER) ? USER_NAMESPACE_FIXED : USER_NAMESPACE_NO; @@ -819,7 +819,7 @@ static int oci_device_file_mode(const char *name, JsonVariant *v, JsonDispatchFl return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE), "fileMode out of range, refusing."); - *mode = m; + *mode = (*mode & ~07777) | m; return 0; } @@ -874,7 +874,7 @@ static int oci_devices(const char *name, JsonVariant *v, JsonDispatchFlags flags /* Suppress a couple of implicit device nodes */ r = devname_from_devnum(node->mode, makedev(node->major, node->minor), &path); if (r < 0) - json_log(e, flags|JSON_DEBUG, 0, "Failed to resolve device node %u:%u, ignoring: %m", node->major, node->minor); + json_log(e, flags|JSON_DEBUG, r, "Failed to resolve device node %u:%u, ignoring: %m", node->major, node->minor); else { if (PATH_IN_SET(path, "/dev/null", @@ -1177,13 +1177,13 @@ static int oci_cgroup_memory(const char *name, JsonVariant *v, JsonDispatchFlags }; static const JsonDispatch table[] = { - { "limit", JSON_VARIANT_NUMBER, oci_cgroup_memory_limit, offsetof(struct memory_data, limit), 0 }, - { "reservation", JSON_VARIANT_NUMBER, oci_cgroup_memory_limit, offsetof(struct memory_data, reservation), 0 }, - { "swap", JSON_VARIANT_NUMBER, oci_cgroup_memory_limit, offsetof(struct memory_data, swap), 0 }, - { "kernel", JSON_VARIANT_NUMBER, oci_unsupported, 0, JSON_PERMISSIVE }, - { "kernelTCP", JSON_VARIANT_NUMBER, oci_unsupported, 0, JSON_PERMISSIVE }, - { "swapiness", JSON_VARIANT_NUMBER, oci_unsupported, 0, JSON_PERMISSIVE }, - { "disableOOMKiller", JSON_VARIANT_NUMBER, oci_unsupported, 0, JSON_PERMISSIVE }, + { "limit", JSON_VARIANT_NUMBER, oci_cgroup_memory_limit, offsetof(struct memory_data, limit), 0 }, + { "reservation", JSON_VARIANT_NUMBER, oci_cgroup_memory_limit, offsetof(struct memory_data, reservation), 0 }, + { "swap", JSON_VARIANT_NUMBER, oci_cgroup_memory_limit, offsetof(struct memory_data, swap), 0 }, + { "kernel", JSON_VARIANT_NUMBER, oci_unsupported, 0, JSON_PERMISSIVE }, + { "kernelTCP", JSON_VARIANT_NUMBER, oci_unsupported, 0, JSON_PERMISSIVE }, + { "swapiness", JSON_VARIANT_NUMBER, oci_unsupported, 0, JSON_PERMISSIVE }, + { "disableOOMKiller", JSON_VARIANT_BOOLEAN, oci_unsupported, 0, JSON_PERMISSIVE }, {} }; @@ -1589,7 +1589,7 @@ static int oci_sysctl(const char *name, JsonVariant *v, JsonDispatchFlags flags, assert_se(m = json_variant_string(w)); - if (sysctl_key_valid(k)) + if (!sysctl_key_valid(k)) return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), "sysctl key invalid, refusing: %s", k); @@ -1829,6 +1829,7 @@ static int oci_seccomp_syscalls(const char *name, JsonVariant *v, JsonDispatchFl { "names", JSON_VARIANT_ARRAY, json_dispatch_strv, offsetof(struct syscall_rule, names), JSON_MANDATORY }, { "action", JSON_VARIANT_STRING, oci_seccomp_action, offsetof(struct syscall_rule, action), JSON_MANDATORY }, { "args", JSON_VARIANT_ARRAY, oci_seccomp_args, 0, 0 }, + {} }; struct syscall_rule rule = { .action = UINT32_MAX, @@ -2083,7 +2084,7 @@ static int oci_hooks_array(const char *name, JsonVariant *v, JsonDispatchFlags f .timeout = USEC_INFINITY, }; - r = json_dispatch(e, table, oci_unexpected, flags, userdata); + r = json_dispatch(e, table, oci_unexpected, flags, new_item); if (r < 0) { free(new_item->path); strv_free(new_item->args); @@ -2100,9 +2101,9 @@ static int oci_hooks_array(const char *name, JsonVariant *v, JsonDispatchFlags f static int oci_hooks(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { static const JsonDispatch table[] = { - { "prestart", JSON_VARIANT_OBJECT, oci_hooks_array, 0, 0 }, - { "poststart", JSON_VARIANT_OBJECT, oci_hooks_array, 0, 0 }, - { "poststop", JSON_VARIANT_OBJECT, oci_hooks_array, 0, 0 }, + { "prestart", JSON_VARIANT_ARRAY, oci_hooks_array, 0, 0 }, + { "poststart", JSON_VARIANT_ARRAY, oci_hooks_array, 0, 0 }, + { "poststop", JSON_VARIANT_ARRAY, oci_hooks_array, 0, 0 }, {} }; diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index 7500eabd18..161b1c1c70 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -97,27 +97,25 @@ int settings_load(FILE *f, const char *path, Settings **ret) { return 0; } -static void free_oci_hooks(OciHook *h, size_t n) { - size_t i; +static void free_oci_hooks(OciHook *hooks, size_t n) { + assert(hooks || n == 0); - assert(h || n == 0); - - for (i = 0; i < n; i++) { - free(h[i].path); - strv_free(h[i].args); - strv_free(h[i].env); + FOREACH_ARRAY(hook, hooks, n) { + free(hook->path); + strv_free(hook->args); + strv_free(hook->env); } - free(h); + free(hooks); } -void device_node_array_free(DeviceNode *node, size_t n) { - size_t i; +void device_node_array_free(DeviceNode *nodes, size_t n) { + assert(nodes || n == 0); - for (i = 0; i < n; i++) - free(node[i].path); + FOREACH_ARRAY(node, nodes, n) + free(node->path); - free(node); + free(nodes); } Settings* settings_free(Settings *s) { diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 49802d6fdf..5d49e05064 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -4651,6 +4651,7 @@ static int merge_settings(Settings *settings, const char *path) { device_node_array_free(arg_extra_nodes, arg_n_extra_nodes); arg_extra_nodes = TAKE_PTR(settings->extra_nodes); arg_n_extra_nodes = settings->n_extra_nodes; + settings->n_extra_nodes = 0; return 0; } diff --git a/src/partition/repart.c b/src/partition/repart.c index 1f3e78a84e..995a40655d 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -6669,7 +6669,7 @@ static int determine_auto_size(Context *c) { static int run(int argc, char *argv[]) { _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; - _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; + _cleanup_(umount_and_freep) char *mounted_dir = NULL; _cleanup_(context_freep) Context* context = NULL; bool node_is_our_loop = false; int r; diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c index 8a50cc6ebb..0244c92c79 100644 --- a/src/shared/base-filesystem.c +++ b/src/shared/base-filesystem.c @@ -40,6 +40,11 @@ static const BaseFilesystem table[] = { { "proc", 0555, NULL, NULL, true }, { "sys", 0555, NULL, NULL, true }, { "dev", 0555, NULL, NULL, true }, + { "run", 0555, NULL, NULL, true }, + /* We don't add /tmp/ here for now (even though it's necessary for regular operation), because we + * want to support both cases where /tmp/ is a mount of its own (in which case we probably should set + * the mode to 1555, to indicate that noone should write to it, not even root) and when it's part of + * the rootfs (in which case we should set mode 1777), and we simply don't know what's right. */ /* Various architecture ABIs define the path to the dynamic loader via the /lib64/ subdirectory of * the root directory. When booting from an otherwise empty root file system (where only /usr/ has diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index b84ef46442..39f75dd0dd 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -3667,8 +3667,7 @@ int mount_image_privately_interactively( _cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT; _cleanup_(loop_device_unrefp) LoopDevice *d = NULL; _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL; - _cleanup_(rmdir_and_freep) char *created_dir = NULL; - _cleanup_free_ char *temp = NULL; + _cleanup_free_ char *dir = NULL; int r; /* Mounts an OS image at a temporary place, inside a newly created mount namespace of our own. This @@ -3676,7 +3675,6 @@ int mount_image_privately_interactively( * easily. */ assert(image); - assert(ret_directory); assert(ret_loop_device); /* We intend to mount this right-away, hence add the partitions if needed and pin them. */ @@ -3687,10 +3685,6 @@ int mount_image_privately_interactively( if (r < 0) return log_error_errno(r, "Failed to load root hash data: %m"); - r = tempfn_random_child(NULL, program_invocation_short_name, &temp); - if (r < 0) - return log_error_errno(r, "Failed to generate temporary mount directory: %m"); - r = loop_device_make_by_path( image, FLAGS_SET(flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR, @@ -3723,13 +3717,16 @@ int mount_image_privately_interactively( if (r < 0) return log_error_errno(r, "Failed to detach mount namespace: %m"); - r = mkdir_p(temp, 0700); + r = mkdir_p("/run/systemd/mount-rootfs", 0555); if (r < 0) return log_error_errno(r, "Failed to create mount point: %m"); - created_dir = TAKE_PTR(temp); - - r = dissected_image_mount_and_warn(dissected_image, created_dir, UID_INVALID, UID_INVALID, flags); + r = dissected_image_mount_and_warn( + dissected_image, + "/run/systemd/mount-rootfs", + /* uid_shift= */ UID_INVALID, + /* uid_range= */ UID_INVALID, + flags); if (r < 0) return r; @@ -3741,19 +3738,26 @@ int mount_image_privately_interactively( if (r < 0) return log_error_errno(r, "Failed to relinquish DM and loopback block devices: %m"); + if (ret_directory) { + dir = strdup("/run/systemd/mount-rootfs"); + if (!dir) + return log_oom(); + } + if (ret_dir_fd) { _cleanup_close_ int dir_fd = -EBADF; - dir_fd = open(created_dir, O_CLOEXEC|O_DIRECTORY); + dir_fd = open("/run/systemd/mount-rootfs", O_CLOEXEC|O_DIRECTORY); if (dir_fd < 0) return log_error_errno(errno, "Failed to open mount point directory: %m"); *ret_dir_fd = TAKE_FD(dir_fd); } - *ret_directory = TAKE_PTR(created_dir); - *ret_loop_device = TAKE_PTR(d); + if (ret_directory) + *ret_directory = TAKE_PTR(dir); + *ret_loop_device = TAKE_PTR(d); return 0; } diff --git a/src/shared/install.c b/src/shared/install.c index 152e517ebc..7903de17b1 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -73,7 +73,8 @@ static bool install_info_has_rules(const InstallInfo *i) { return !strv_isempty(i->aliases) || !strv_isempty(i->wanted_by) || - !strv_isempty(i->required_by); + !strv_isempty(i->required_by) || + !strv_isempty(i->upheld_by); } static bool install_info_has_also(const InstallInfo *i) { @@ -942,7 +943,7 @@ static int find_symlinks( continue; suffix = strrchr(de->d_name, '.'); - if (!STRPTR_IN_SET(suffix, ".wants", ".requires")) + if (!STRPTR_IN_SET(suffix, ".wants", ".requires", ".upholds")) continue; path = path_join(config_path, de->d_name); @@ -967,7 +968,8 @@ static int find_symlinks( log_debug_errno(r, "Failed to look up symlinks in \"%s\": %m", path); } - /* We didn't find any suitable symlinks in .wants or .requires directories, let's look for linked unit files in this directory. */ + /* We didn't find any suitable symlinks in .wants, .requires or .upholds directories, + * let's look for linked unit files in this directory. */ rewinddir(config_dir); return find_symlinks_in_directory(config_dir, config_path, root_dir, i, /* ignore_destination= */ false, @@ -1081,6 +1083,7 @@ static void install_info_clear(InstallInfo *i) { i->aliases = strv_free(i->aliases); i->wanted_by = strv_free(i->wanted_by); i->required_by = strv_free(i->required_by); + i->upheld_by = strv_free(i->upheld_by); i->also = strv_free(i->also); i->default_instance = mfree(i->default_instance); i->symlink_target = mfree(i->symlink_target); @@ -1337,6 +1340,7 @@ static int unit_file_load( { "Install", "Alias", config_parse_alias, 0, &info->aliases }, { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by }, { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by }, + { "Install", "UpheldBy", config_parse_strv, 0, &info->upheld_by }, { "Install", "DefaultInstance", config_parse_default_instance, 0, info }, { "Install", "Also", config_parse_also, 0, ctx }, {} @@ -1440,7 +1444,8 @@ static int unit_file_load( return (int) strv_length(info->aliases) + (int) strv_length(info->wanted_by) + - (int) strv_length(info->required_by); + (int) strv_length(info->required_by) + + (int) strv_length(info->upheld_by); } static int unit_file_load_or_readlink( @@ -2113,6 +2118,10 @@ static int install_info_apply( if (r == 0) r = q; + q = install_info_symlink_wants(scope, file_flags, info, lp, config_path, info->upheld_by, ".upholds/", changes, n_changes); + if (r == 0) + r = q; + return r; } @@ -2733,8 +2742,10 @@ int unit_file_add_dependency( if (dep == UNIT_WANTS) l = &info->wanted_by; - else + else if (dep == UNIT_REQUIRES) l = &info->required_by; + else + l = &info->upheld_by; strv_free(*l); *l = strv_new(target_info->name); diff --git a/src/shared/install.h b/src/shared/install.h index 30b07a725f..bc0c6db828 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -92,6 +92,7 @@ struct InstallInfo { char **aliases; char **wanted_by; char **required_by; + char **upheld_by; char **also; char *default_instance; diff --git a/src/shared/mount-setup.c b/src/shared/mount-setup.c index a920e8a92a..6162a58d9a 100644 --- a/src/shared/mount-setup.c +++ b/src/shared/mount-setup.c @@ -114,6 +114,8 @@ static const MountPoint mount_table[] = { NULL, MNT_NONE, }, }; +assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table)); + bool mount_point_is_api(const char *path) { /* Checks if this mount point is considered "API", and hence * should be ignored */ @@ -186,13 +188,11 @@ static int mount_one(const MountPoint *p, bool relabel) { strna(p->options)); if (FLAGS_SET(p->mode, MNT_FOLLOW_SYMLINK)) - r = RET_NERRNO(mount(p->what, p->where, p->type, p->flags, p->options)); + r = mount_follow_verbose(priority, p->what, p->where, p->type, p->flags, p->options); else - r = mount_nofollow(p->what, p->where, p->type, p->flags, p->options); - if (r < 0) { - log_full_errno(priority, r, "Failed to mount %s at %s: %m", p->type, p->where); + r = mount_nofollow_verbose(priority, p->what, p->where, p->type, p->flags, p->options); + if (r < 0) return (p->mode & MNT_FATAL) ? r : 0; - } /* Relabel again, since we now mounted something fresh here */ if (relabel) @@ -205,7 +205,7 @@ static int mount_one(const MountPoint *p, bool relabel) { (void) umount2(p->where, UMOUNT_NOFOLLOW); (void) rmdir(p->where); - log_full_errno(priority, r, "Mount point %s not writable after mounting: %m", p->where); + log_full_errno(priority, r, "Mount point %s not writable after mounting, undoing: %m", p->where); return (p->mode & MNT_FATAL) ? r : 0; } } @@ -213,27 +213,23 @@ static int mount_one(const MountPoint *p, bool relabel) { return 1; } -static int mount_points_setup(unsigned n, bool loaded_policy) { - unsigned i; - int r = 0; +static int mount_points_setup(size_t n, bool loaded_policy) { + int ret = 0, r; - for (i = 0; i < n; i ++) { - int j; + assert(n <= ELEMENTSOF(mount_table)); - j = mount_one(mount_table + i, loaded_policy); - if (j != 0 && r >= 0) - r = j; + FOREACH_ARRAY(mp, mount_table, n) { + r = mount_one(mp, loaded_policy); + if (r != 0 && ret >= 0) + ret = r; } - return r; + return ret; } int mount_setup_early(void) { - assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table)); - - /* Do a minimal mount of /proc and friends to enable the most - * basic stuff, such as SELinux */ - return mount_points_setup(N_EARLY_MOUNT, false); + /* Do a minimal mount of /proc and friends to enable the most basic stuff, such as SELinux */ + return mount_points_setup(N_EARLY_MOUNT, /* loaded_policy= */ false); } static const char *join_with(const char *controller) { @@ -279,7 +275,7 @@ static int symlink_controller(const char *target, const char *alias) { p = strjoina("/sys/fs/cgroup/", target); r = mac_smack_copy(a, p); - if (r < 0 && r != -EOPNOTSUPP) + if (r < 0 && !ERRNO_IS_NOT_SUPPORTED(r)) return log_error_errno(r, "Failed to copy smack label from %s to %s: %m", p, a); #endif @@ -554,6 +550,11 @@ int mount_setup(bool loaded_policy, bool leave_propagation) { (void) mkdir_label("/run/systemd", 0755); (void) mkdir_label("/run/systemd/system", 0755); + /* Make sure there's always a place where sandboxed environments can mount root file systems they are + * about to move into, even when unprivileged, without having to create a temporary one in /tmp/ + * (which they then have to keep track of and clean) */ + (void) mkdir_label("/run/systemd/mount-rootfs", 0555); + /* Make sure we have a mount point to hide in sandboxes */ (void) mkdir_label("/run/credentials", 0755); diff --git a/src/shared/mount-util.h b/src/shared/mount-util.h index d63fddeb10..8a84d61622 100644 --- a/src/shared/mount-util.h +++ b/src/shared/mount-util.h @@ -83,6 +83,14 @@ static inline char* umount_and_rmdir_and_free(char *p) { } DEFINE_TRIVIAL_CLEANUP_FUNC(char*, umount_and_rmdir_and_free); +static inline char *umount_and_free(char *p) { + PROTECT_ERRNO; + if (p) + (void) umount_recursive(p, 0); + return mfree(p); +} +DEFINE_TRIVIAL_CLEANUP_FUNC(char*, umount_and_free); + int bind_mount_in_namespace(pid_t target, const char *propagate_path, const char *incoming_path, const char *src, const char *dest, bool read_only, bool make_file_or_directory); int mount_image_in_namespace(pid_t target, const char *propagate_path, const char *incoming_path, const char *src, const char *dest, bool read_only, bool make_file_or_directory, const MountOptions *options, const ImagePolicy *image_policy); diff --git a/src/systemctl/systemctl-enable.c b/src/systemctl/systemctl-enable.c index 6d3709705e..940f54607f 100644 --- a/src/systemctl/systemctl-enable.c +++ b/src/systemctl/systemctl-enable.c @@ -248,9 +248,9 @@ int verb_enable(int argc, char *argv[], void *userdata) { } if (carries_install_info == 0 && !ignore_carries_install_info) - log_notice("The unit files have no installation config (WantedBy=, RequiredBy=, Also=,\n" - "Alias= settings in the [Install] section, and DefaultInstance= for template\n" - "units). This means they are not meant to be enabled or disabled using systemctl.\n" + log_notice("The unit files have no installation config (WantedBy=, RequiredBy=, UpheldBy=,\n" + "Also=, or Alias= settings in the [Install] section, and DefaultInstance= for\n" + "template units). This means they are not meant to be enabled or disabled using systemctl.\n" " \n" /* trick: the space is needed so that the line does not get stripped from output */ "Possible reasons for having this kind of units are:\n" "%1$s A unit may be statically enabled by being symlinked from another unit's\n" diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 71c068b09e..b31a59785b 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -1224,7 +1224,7 @@ static int systemctl_main(int argc, char *argv[]) { static int run(int argc, char *argv[]) { _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; - _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; + _cleanup_(umount_and_freep) char *mounted_dir = NULL; int r; setlocale(LC_ALL, ""); diff --git a/src/sysupdate/sysupdate.c b/src/sysupdate/sysupdate.c index 29cd552ea8..76777dc08e 100644 --- a/src/sysupdate/sysupdate.c +++ b/src/sysupdate/sysupdate.c @@ -861,7 +861,7 @@ static int process_image( LoopDevice **ret_loop_device) { _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; - _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; + _cleanup_(umount_and_freep) char *mounted_dir = NULL; int r; assert(ret_mounted_dir); diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 58246b5d85..aa1f1356dc 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -2157,7 +2157,7 @@ static int read_credential_lines(void) { static int run(int argc, char *argv[]) { #ifndef STANDALONE _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; - _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL; + _cleanup_(umount_and_freep) char *mounted_dir = NULL; #endif _cleanup_close_ int lock = -EBADF; Item *i; @@ -2191,13 +2191,13 @@ static int run(int argc, char *argv[]) { DISSECT_IMAGE_RELAX_VAR_CHECK | DISSECT_IMAGE_FSCK | DISSECT_IMAGE_GROWFS, - &unlink_dir, + &mounted_dir, /* ret_dir_fd= */ NULL, &loop_device); if (r < 0) return r; - arg_root = strdup(unlink_dir); + arg_root = strdup(mounted_dir); if (!arg_root) return log_oom(); } diff --git a/src/test/meson.build b/src/test/meson.build index 8e76df624d..7f8de2a2ce 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -112,6 +112,7 @@ simple_tests += files( 'test-log.c', 'test-logarithm.c', 'test-macro.c', + 'test-memfd-util.c', 'test-memory-util.c', 'test-mempool.c', 'test-mkdir.c', diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c index 55b8894ecc..80166b17c6 100644 --- a/src/test/test-install-root.c +++ b/src/test/test-install-root.c @@ -1135,6 +1135,9 @@ TEST(verify_alias) { verify_one(&plain_service, "alias.socket", -EXDEV, NULL); verify_one(&plain_service, "alias@.service", -EXDEV, NULL); verify_one(&plain_service, "alias@inst.service", -EXDEV, NULL); + + /* Setting WantedBy= and RequiredBy= through Alias= is supported for the sake of backwards + * compatibility. */ verify_one(&plain_service, "foo.target.wants/plain.service", 0, NULL); verify_one(&plain_service, "foo.target.wants/plain.socket", -EXDEV, NULL); verify_one(&plain_service, "foo.target.wants/plain@.service", -EXDEV, NULL); @@ -1143,9 +1146,14 @@ TEST(verify_alias) { verify_one(&plain_service, "foo.target.requires/plain.socket", -EXDEV, NULL); verify_one(&plain_service, "foo.target.requires/plain@.service", -EXDEV, NULL); verify_one(&plain_service, "foo.target.requires/service", -EXDEV, NULL); - verify_one(&plain_service, "foo.target.conf/plain.service", -EXDEV, NULL); - verify_one(&plain_service, "foo.service/plain.service", -EXDEV, NULL); /* missing dir suffix */ verify_one(&plain_service, "asdf.requires/plain.service", -EXDEV, NULL); /* invalid unit name component */ + /* The newly-added UpheldBy= (.upholds/) and other suffixes should be rejected */ + verify_one(&plain_service, "foo.target.upholds/plain.service", -EXDEV, NULL); + verify_one(&plain_service, "foo.target.upholds/plain.socket", -EXDEV, NULL); + verify_one(&plain_service, "foo.target.upholds/plain@.service", -EXDEV, NULL); + verify_one(&plain_service, "foo.target.upholds/service", -EXDEV, NULL); + verify_one(&plain_service, "foo.service/plain.service", -EXDEV, NULL); /* missing dir suffix */ + verify_one(&plain_service, "foo.target.conf/plain.service", -EXDEV, NULL); verify_one(&bare_template, "alias.service", -EXDEV, NULL); verify_one(&bare_template, "alias.socket", -EXDEV, NULL); diff --git a/src/test/test-memfd-util.c b/src/test/test-memfd-util.c new file mode 100644 index 0000000000..f8e1b46075 --- /dev/null +++ b/src/test/test-memfd-util.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <unistd.h> + +#include "errno-util.h" +#include "fd-util.h" +#include "memfd-util.h" +#include "string-util.h" +#include "tests.h" + +TEST(memfd_get_sealed) { +#define TEST_TEXT "this is some random test text we are going to write to a memfd" + _cleanup_close_ int fd = -EBADF; + + fd = memfd_new("test-memfd-get-sealed"); + if (fd < 0) { + assert_se(ERRNO_IS_NOT_SUPPORTED(fd)); + return; + } + + assert_se(write(fd, TEST_TEXT, strlen(TEST_TEXT)) == strlen(TEST_TEXT)); + /* we'll leave the read offset at the end of the memfd, the fdopen_independent() descriptors should + * start at the beginning anyway */ + + assert_se(memfd_get_sealed(fd) == 0); + assert_se(memfd_set_sealed(fd) >= 0); + assert_se(memfd_get_sealed(fd) > 0); +} + +DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 382fa8b56a..be04b25653 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -4223,7 +4223,7 @@ DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(item_array_hash_ops, char, string_ static int run(int argc, char *argv[]) { #ifndef STANDALONE _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; - _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL; + _cleanup_(umount_and_freep) char *mounted_dir = NULL; #endif _cleanup_strv_free_ char **config_dirs = NULL; bool invalid_config = false; @@ -4314,13 +4314,13 @@ static int run(int argc, char *argv[]) { DISSECT_IMAGE_RELAX_VAR_CHECK | DISSECT_IMAGE_FSCK | DISSECT_IMAGE_GROWFS, - &unlink_dir, + &mounted_dir, /* ret_dir_fd= */ NULL, &loop_device); if (r < 0) return r; - arg_root = strdup(unlink_dir); + arg_root = strdup(mounted_dir); if (!arg_root) return log_oom(); } diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 4c83eede7a..b554a6827d 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -365,6 +365,7 @@ Link *link_free(Link *link) { sd_device_unref(link->device); free(link->kind); free(link->driver); + strv_free(link->altnames); return mfree(link); } @@ -724,7 +725,7 @@ static int link_generate_new_name(Link *link) { config = link->config; device = link->device; - if (link->action == SD_DEVICE_MOVE) { + if (link->action != SD_DEVICE_ADD) { log_link_debug(link, "Skipping to apply Name= and NamePolicy= on '%s' uevent.", device_action_to_string(link->action)); goto no_rename; @@ -793,19 +794,22 @@ no_rename: return 0; } -static int link_apply_alternative_names(Link *link, sd_netlink **rtnl) { - _cleanup_strv_free_ char **altnames = NULL, **current_altnames = NULL; +static int link_generate_alternative_names(Link *link) { + _cleanup_strv_free_ char **altnames = NULL; LinkConfig *config; sd_device *device; int r; assert(link); - assert(link->config); - assert(link->device); - assert(rtnl); + config = ASSERT_PTR(link->config); + device = ASSERT_PTR(link->device); + assert(!link->altnames); - config = link->config; - device = link->device; + if (link->action != SD_DEVICE_ADD) { + log_link_debug(link, "Skipping to apply AlternativeNames= and AlternativeNamesPolicy= on '%s' uevent.", + device_action_to_string(link->action)); + return 0; + } if (config->alternative_names) { altnames = strv_copy(config->alternative_names); @@ -836,29 +840,14 @@ static int link_apply_alternative_names(Link *link, sd_netlink **rtnl) { default: assert_not_reached(); } - if (!isempty(n)) { + if (ifname_valid_full(n, IFNAME_VALID_ALTERNATIVE)) { r = strv_extend(&altnames, n); if (r < 0) return log_oom(); } } - strv_remove(altnames, link->ifname); - - r = rtnl_get_link_alternative_names(rtnl, link->ifindex, ¤t_altnames); - if (r < 0) - log_link_debug_errno(link, r, "Failed to get alternative names, ignoring: %m"); - - STRV_FOREACH(p, current_altnames) - strv_remove(altnames, *p); - - strv_uniq(altnames); - strv_sort(altnames); - r = rtnl_set_link_alternative_names(rtnl, link->ifindex, altnames); - if (r < 0) - log_link_full_errno(link, r == -EOPNOTSUPP ? LOG_DEBUG : LOG_WARNING, r, - "Could not set AlternativeName= or apply AlternativeNamesPolicy=, ignoring: %m"); - + link->altnames = TAKE_PTR(altnames); return 0; } @@ -960,7 +949,7 @@ int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link) { if (r < 0) return r; - r = link_apply_alternative_names(link, rtnl); + r = link_generate_alternative_names(link); if (r < 0) return r; diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index ed0896e9de..713d41040a 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -27,6 +27,7 @@ typedef struct Link { int ifindex; const char *ifname; const char *new_name; + char **altnames; LinkConfig *config; sd_device *device; diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c index 2b2205513b..11419a3e61 100644 --- a/src/udev/udev-builtin-blkid.c +++ b/src/udev/udev-builtin-blkid.c @@ -315,7 +315,8 @@ notloop: return 0; } -static int builtin_blkid(sd_device *dev, sd_netlink **rtnl, int argc, char *argv[], bool test) { +static int builtin_blkid(UdevEvent *event, int argc, char *argv[], bool test) { + sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); const char *devnode, *root_partition = NULL, *data, *name; _cleanup_(blkid_free_probep) blkid_probe pr = NULL; _cleanup_free_ char *backing_fname = NULL; diff --git a/src/udev/udev-builtin-btrfs.c b/src/udev/udev-builtin-btrfs.c index 79f91ea2ae..9b12aebb3a 100644 --- a/src/udev/udev-builtin-btrfs.c +++ b/src/udev/udev-builtin-btrfs.c @@ -12,7 +12,8 @@ #include "strxcpyx.h" #include "udev-builtin.h" -static int builtin_btrfs(sd_device *dev, sd_netlink **rtnl, int argc, char *argv[], bool test) { +static int builtin_btrfs(UdevEvent *event, int argc, char *argv[], bool test) { + sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); struct btrfs_ioctl_vol_args args = {}; _cleanup_close_ int fd = -EBADF; int r; diff --git a/src/udev/udev-builtin-hwdb.c b/src/udev/udev-builtin-hwdb.c index 8d652e46fe..19e07e734f 100644 --- a/src/udev/udev-builtin-hwdb.c +++ b/src/udev/udev-builtin-hwdb.c @@ -118,7 +118,7 @@ next: return r; } -static int builtin_hwdb(sd_device *dev, sd_netlink **rtnl, int argc, char *argv[], bool test) { +static int builtin_hwdb(UdevEvent *event, int argc, char *argv[], bool test) { static const struct option options[] = { { "filter", required_argument, NULL, 'f' }, { "device", required_argument, NULL, 'd' }, @@ -131,6 +131,7 @@ static int builtin_hwdb(sd_device *dev, sd_netlink **rtnl, int argc, char *argv[ const char *subsystem = NULL; const char *prefix = NULL; _cleanup_(sd_device_unrefp) sd_device *srcdev = NULL; + sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); int r; if (!hwdb) diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c index 540f390530..295e8d2159 100644 --- a/src/udev/udev-builtin-input_id.c +++ b/src/udev/udev-builtin-input_id.c @@ -369,8 +369,8 @@ static bool test_key(sd_device *dev, return found; } -static int builtin_input_id(sd_device *dev, sd_netlink **rtnl, int argc, char *argv[], bool test) { - sd_device *pdev; +static int builtin_input_id(UdevEvent *event, int argc, char *argv[], bool test) { + sd_device *pdev, *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); unsigned long bitmask_ev[NBITS(EV_MAX)]; unsigned long bitmask_abs[NBITS(ABS_MAX)]; unsigned long bitmask_key[NBITS(KEY_MAX)]; @@ -380,8 +380,6 @@ static int builtin_input_id(sd_device *dev, sd_netlink **rtnl, int argc, char *a bool is_pointer; bool is_key; - assert(dev); - /* walk up the parental chain until we find the real input device; the * argument is very likely a subdevice of this, like eventN */ for (pdev = dev; pdev; ) { diff --git a/src/udev/udev-builtin-keyboard.c b/src/udev/udev-builtin-keyboard.c index 80cfdee0c1..da67c2be1c 100644 --- a/src/udev/udev-builtin-keyboard.c +++ b/src/udev/udev-builtin-keyboard.c @@ -159,7 +159,8 @@ static int set_trackpoint_sensitivity(sd_device *dev, const char *value) { return 0; } -static int builtin_keyboard(sd_device *dev, sd_netlink **rtnl, int argc, char *argv[], bool test) { +static int builtin_keyboard(UdevEvent *event, int argc, char *argv[], bool test) { + sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); unsigned release[1024]; unsigned release_count = 0; _cleanup_close_ int fd = -EBADF; diff --git a/src/udev/udev-builtin-kmod.c b/src/udev/udev-builtin-kmod.c index eade042f35..3ab5c485f8 100644 --- a/src/udev/udev-builtin-kmod.c +++ b/src/udev/udev-builtin-kmod.c @@ -22,11 +22,10 @@ _printf_(6,0) static void udev_kmod_log(void *data, int priority, const char *fi log_internalv(priority, 0, file, line, fn, format, args); } -static int builtin_kmod(sd_device *dev, sd_netlink **rtnl, int argc, char *argv[], bool test) { +static int builtin_kmod(UdevEvent *event, int argc, char *argv[], bool test) { + sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); int r; - assert(dev); - if (!ctx) return 0; diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c index 5e2b69d6f7..998478ec4e 100644 --- a/src/udev/udev-builtin-net_id.c +++ b/src/udev/udev-builtin-net_id.c @@ -1096,7 +1096,8 @@ static int get_link_info(sd_device *dev, LinkInfo *info) { return 0; } -static int builtin_net_id(sd_device *dev, sd_netlink **rtnl, int argc, char *argv[], bool test) { +static int builtin_net_id(UdevEvent *event, int argc, char *argv[], bool test) { + sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); const char *prefix; NetNames names = {}; LinkInfo info = {}; diff --git a/src/udev/udev-builtin-net_setup_link.c b/src/udev/udev-builtin-net_setup_link.c index 18450536b5..5bc965f6d8 100644 --- a/src/udev/udev-builtin-net_setup_link.c +++ b/src/udev/udev-builtin-net_setup_link.c @@ -12,7 +12,8 @@ static LinkConfigContext *ctx = NULL; -static int builtin_net_setup_link(sd_device *dev, sd_netlink **rtnl, int argc, char **argv, bool test) { +static int builtin_net_setup_link(UdevEvent *event, int argc, char **argv, bool test) { + sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); _cleanup_(link_freep) Link *link = NULL; _cleanup_free_ char *joined = NULL; int r; @@ -20,7 +21,7 @@ static int builtin_net_setup_link(sd_device *dev, sd_netlink **rtnl, int argc, c if (argc > 1) return log_device_error_errno(dev, SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments."); - r = link_new(ctx, rtnl, dev, &link); + r = link_new(ctx, &event->rtnl, dev, &link); if (r == -ENODEV) { log_device_debug_errno(dev, r, "Link vanished while getting information, ignoring."); return 0; @@ -41,7 +42,7 @@ static int builtin_net_setup_link(sd_device *dev, sd_netlink **rtnl, int argc, c return log_device_error_errno(dev, r, "Failed to get link config: %m"); } - r = link_apply_config(ctx, rtnl, link); + r = link_apply_config(ctx, &event->rtnl, link); if (r == -ENODEV) log_device_debug_errno(dev, r, "Link vanished while applying configuration, ignoring."); else if (r < 0) @@ -51,6 +52,8 @@ static int builtin_net_setup_link(sd_device *dev, sd_netlink **rtnl, int argc, c if (link->new_name) udev_builtin_add_property(dev, test, "ID_NET_NAME", link->new_name); + event->altnames = TAKE_PTR(link->altnames); + STRV_FOREACH(d, link->config->dropins) { _cleanup_free_ char *escaped = NULL; diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c index ff6682cad9..404196f409 100644 --- a/src/udev/udev-builtin-path_id.c +++ b/src/udev/udev-builtin-path_id.c @@ -589,15 +589,14 @@ static int find_real_nvme_parent(sd_device *dev, sd_device **ret) { return 0; } -static int builtin_path_id(sd_device *dev, sd_netlink **rtnl, int argc, char *argv[], bool test) { +static int builtin_path_id(UdevEvent *event, int argc, char *argv[], bool test) { + sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); _cleanup_(sd_device_unrefp) sd_device *dev_other_branch = NULL; _cleanup_free_ char *path = NULL, *compat_path = NULL; bool supported_transport = false, supported_parent = false; const char *subsystem; int r; - assert(dev); - /* walk up the chain of devices and compose path */ for (sd_device *parent = dev; parent; ) { const char *subsys, *sysname; diff --git a/src/udev/udev-builtin-uaccess.c b/src/udev/udev-builtin-uaccess.c index 6e73d99375..36c993cbb0 100644 --- a/src/udev/udev-builtin-uaccess.c +++ b/src/udev/udev-builtin-uaccess.c @@ -16,7 +16,8 @@ #include "log.h" #include "udev-builtin.h" -static int builtin_uaccess(sd_device *dev, sd_netlink **rtnl, int argc, char *argv[], bool test) { +static int builtin_uaccess(UdevEvent *event, int argc, char *argv[], bool test) { + sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); const char *path = NULL, *seat; bool changed_acl = false; uid_t uid; diff --git a/src/udev/udev-builtin-usb_id.c b/src/udev/udev-builtin-usb_id.c index 91deb64292..8e83c9c342 100644 --- a/src/udev/udev-builtin-usb_id.c +++ b/src/udev/udev-builtin-usb_id.c @@ -224,7 +224,8 @@ static int dev_if_packed_info(sd_device *dev, char *ifs_str, size_t len) { * 6.) If the device supplies a serial number, this number * is concatenated with the identification with an underscore '_'. */ -static int builtin_usb_id(sd_device *dev, sd_netlink **rtnl, int argc, char *argv[], bool test) { +static int builtin_usb_id(UdevEvent *event, int argc, char *argv[], bool test) { + sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); char vendor_str[64] = ""; char vendor_str_enc[256]; const char *vendor_id; @@ -250,8 +251,6 @@ static int builtin_usb_id(sd_device *dev, sd_netlink **rtnl, int argc, char *arg const char *syspath, *sysname, *devtype, *interface_syspath; int r; - assert(dev); - r = sd_device_get_syspath(dev, &syspath); if (r < 0) return r; diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c index 566641e400..a8dd656b00 100644 --- a/src/udev/udev-builtin.c +++ b/src/udev/udev-builtin.c @@ -98,11 +98,12 @@ UdevBuiltinCommand udev_builtin_lookup(const char *command) { return _UDEV_BUILTIN_INVALID; } -int udev_builtin_run(sd_device *dev, sd_netlink **rtnl, UdevBuiltinCommand cmd, const char *command, bool test) { +int udev_builtin_run(UdevEvent *event, UdevBuiltinCommand cmd, const char *command, bool test) { _cleanup_strv_free_ char **argv = NULL; int r; - assert(dev); + assert(event); + assert(event->dev); assert(cmd >= 0 && cmd < _UDEV_BUILTIN_MAX); assert(command); @@ -115,7 +116,7 @@ int udev_builtin_run(sd_device *dev, sd_netlink **rtnl, UdevBuiltinCommand cmd, /* we need '0' here to reset the internal state */ optind = 0; - return builtins[cmd]->cmd(dev, rtnl, strv_length(argv), argv, test); + return builtins[cmd]->cmd(event, strv_length(argv), argv, test); } int udev_builtin_add_property(sd_device *dev, bool test, const char *key, const char *val) { diff --git a/src/udev/udev-builtin.h b/src/udev/udev-builtin.h index 490b2dbe96..b2cf81ad31 100644 --- a/src/udev/udev-builtin.h +++ b/src/udev/udev-builtin.h @@ -7,6 +7,7 @@ #include "sd-netlink.h" #include "macro.h" +#include "udev-event.h" typedef enum UdevBuiltinCommand { #if HAVE_BLKID @@ -32,7 +33,7 @@ typedef enum UdevBuiltinCommand { typedef struct UdevBuiltin { const char *name; - int (*cmd)(sd_device *dev, sd_netlink **rtnl, int argc, char *argv[], bool test); + int (*cmd)(UdevEvent *event, int argc, char *argv[], bool test); const char *help; int (*init)(void); void (*exit)(void); @@ -76,7 +77,7 @@ void udev_builtin_exit(void); UdevBuiltinCommand udev_builtin_lookup(const char *command); const char *udev_builtin_name(UdevBuiltinCommand cmd); bool udev_builtin_run_once(UdevBuiltinCommand cmd); -int udev_builtin_run(sd_device *dev, sd_netlink **rtnl, UdevBuiltinCommand cmd, const char *command, bool test); +int udev_builtin_run(UdevEvent *event, UdevBuiltinCommand cmd, const char *command, bool test); void udev_builtin_list(void); bool udev_builtin_should_reload(void); int udev_builtin_add_property(sd_device *dev, bool test, const char *key, const char *val); diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index a8d7db40b4..b3cdb32a63 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -87,6 +87,7 @@ UdevEvent *udev_event_free(UdevEvent *event) { ordered_hashmap_free_free_free(event->seclabel_list); free(event->program_result); free(event->name); + strv_free(event->altnames); return mfree(event); } @@ -914,9 +915,6 @@ static int rename_netif(UdevEvent *event) { dev = ASSERT_PTR(event->dev); - if (!device_for_action(dev, SD_DEVICE_ADD)) - return 0; /* Rename the interface only when it is added. */ - r = sd_device_get_ifindex(dev, &ifindex); if (r == -ENOENT) return 0; /* Device is not a network interface. */ @@ -976,7 +974,7 @@ static int rename_netif(UdevEvent *event) { goto revert; } - r = rtnl_set_link_name(&event->rtnl, ifindex, event->name); + r = rtnl_set_link_name(&event->rtnl, ifindex, event->name, event->altnames); if (r < 0) { if (r == -EBUSY) { log_device_info(dev, "Network interface '%s' is already up, cannot rename to '%s'.", @@ -1007,6 +1005,35 @@ revert: return r; } +static int assign_altnames(UdevEvent *event) { + sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); + int ifindex, r; + const char *s; + + if (strv_isempty(event->altnames)) + return 0; + + r = sd_device_get_ifindex(dev, &ifindex); + if (r == -ENOENT) + return 0; /* Device is not a network interface. */ + if (r < 0) + return log_device_warning_errno(dev, r, "Failed to get ifindex: %m"); + + r = sd_device_get_sysname(dev, &s); + if (r < 0) + return log_device_warning_errno(dev, r, "Failed to get sysname: %m"); + + /* Filter out the current interface name. */ + strv_remove(event->altnames, s); + + r = rtnl_append_link_alternative_names(&event->rtnl, ifindex, event->altnames); + if (r < 0) + log_device_full_errno(dev, r == -EOPNOTSUPP ? LOG_DEBUG : LOG_WARNING, r, + "Could not set AlternativeName= or apply AlternativeNamesPolicy=, ignoring: %m"); + + return 0; +} + static int update_devnode(UdevEvent *event) { sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); int r; @@ -1148,9 +1175,13 @@ int udev_event_execute_rules( DEVICE_TRACE_POINT(rules_finished, dev); - r = rename_netif(event); - if (r < 0) - return r; + if (action == SD_DEVICE_ADD) { + r = rename_netif(event); + if (r < 0) + return r; + if (r == 0) + (void) assign_altnames(event); + } r = update_devnode(event); if (r < 0) @@ -1185,7 +1216,7 @@ void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec, int timeout_s if (builtin_cmd != _UDEV_BUILTIN_INVALID) { log_device_debug(event->dev, "Running built-in command \"%s\"", command); - r = udev_builtin_run(event->dev, &event->rtnl, builtin_cmd, command, false); + r = udev_builtin_run(event, builtin_cmd, command, false); if (r < 0) log_device_debug_errno(event->dev, r, "Failed to run built-in command \"%s\", ignoring: %m", command); } else { diff --git a/src/udev/udev-event.h b/src/udev/udev-event.h index c54c7891b6..b99559ca2e 100644 --- a/src/udev/udev-event.h +++ b/src/udev/udev-event.h @@ -22,6 +22,7 @@ typedef struct UdevEvent { sd_device *dev_parent; sd_device *dev_db_clone; char *name; + char **altnames; char *program_result; mode_t mode; uid_t uid; diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index d6e701f3cc..fc1ef34964 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -2193,7 +2193,7 @@ static int udev_rule_apply_token_to_event( log_event_debug(dev, token, "Importing properties from results of builtin command '%s'", buf); - r = udev_builtin_run(dev, &event->rtnl, cmd, buf, false); + r = udev_builtin_run(event, cmd, buf, false); if (r < 0) { /* remember failure */ log_event_debug_errno(dev, token, r, "Failed to run builtin '%s': %m", buf); diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c index 81b633611e..5d1fafbd03 100644 --- a/src/udev/udevadm-test-builtin.c +++ b/src/udev/udevadm-test-builtin.c @@ -72,7 +72,7 @@ static int parse_argv(int argc, char *argv[]) { } int builtin_main(int argc, char *argv[], void *userdata) { - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + _cleanup_(udev_event_freep) UdevEvent *event = NULL; _cleanup_(sd_device_unrefp) sd_device *dev = NULL; UdevBuiltinCommand cmd; int r; @@ -87,8 +87,7 @@ int builtin_main(int argc, char *argv[], void *userdata) { cmd = udev_builtin_lookup(arg_command); if (cmd < 0) { - log_error("Unknown command '%s'", arg_command); - r = -EINVAL; + r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown command '%s'", arg_command); goto finish; } @@ -98,7 +97,13 @@ int builtin_main(int argc, char *argv[], void *userdata) { goto finish; } - r = udev_builtin_run(dev, &rtnl, cmd, arg_command, true); + event = udev_event_new(dev, 0, NULL, LOG_DEBUG); + if (!event) { + r = log_oom(); + goto finish; + } + + r = udev_builtin_run(event, cmd, arg_command, true); if (r < 0) log_debug_errno(r, "Builtin command '%s' fails: %m", arg_command); diff --git a/test/fuzz/fuzz-nspawn-oci/basic.json b/test/fuzz/fuzz-nspawn-oci/basic.json index f42739e03a..24bacf39c1 100644 --- a/test/fuzz/fuzz-nspawn-oci/basic.json +++ b/test/fuzz/fuzz-nspawn-oci/basic.json @@ -1,6 +1,8 @@ { "ociVersion": "1.0.0", + "hostname" : "foo", + "root": { "path": "rootfs", "readonly": true @@ -33,11 +35,42 @@ "cwd": "/tmp/src", - "rlimits": [ + "noNewPrivileges" : true, + "oomScoreAdj" : 20, + "capabilities" : { + "bounding" : [ + "CAP_AUDIT_WRITE", + "CAP_KILL", + "CAP_NET_BIND_SERVICE" + ], + "permitted" : [ + "CAP_AUDIT_WRITE", + "CAP_KILL", + "CAP_NET_BIND_SERVICE" + ], + "inheritable" : [ + "CAP_AUDIT_WRITE", + "CAP_KILL", + "CAP_NET_BIND_SERVICE" + ], + "effective" : [ + "CAP_AUDIT_WRITE", + "CAP_KILL" + ], + "ambient" : [ + "CAP_NET_BIND_SERVICE" + ] + }, + "rlimits" : [ { - "type": "RLIMIT_NOFILE", - "hard": 1020, - "soft": 1020 + "type" : "RLIMIT_NOFILE", + "soft" : 1024, + "hard" : 1024 + }, + { + "type" : "RLIMIT_RTPRIO", + "soft" : 5, + "hard" : 10 } ] }, @@ -110,32 +143,206 @@ } ], - "hooks": {}, - - "linux": { - "resources": { - "devices": [ + "linux" : { + "namespaces" : [ + { + "type" : "mount" + }, + { + "type" : "network", + "path" : "$NETNS" + }, + { + "type" : "pid" + }, + { + "type" : "uts" + } + ], + "uidMappings" : [ + { + "containerID" : 0, + "hostID" : 1000, + "size" : 100 + } + ], + "gidMappings" : [ + { + "containerID" : 0, + "hostID" : 1000, + "size" : 100 + } + ], + "devices" : [ + { + "type" : "c", + "path" : "/dev/zero", + "major" : 1, + "minor" : 5, + "fileMode" : 444 + }, + { + "type" : "b", + "path" : "$DEV", + "major" : 4, + "minor" : 2, + "fileMode" : 666, + "uid" : 0, + "gid" : 0 + } + ], + "resources" : { + "devices" : [ + { + "allow" : false, + "access" : "m" + }, + { + "allow" : true, + "type" : "b", + "major" : 4, + "minor" : 2, + "access" : "rwm" + } + ], + "memory" : { + "limit" : 134217728, + "reservation" : 33554432, + "swap" : 268435456 + }, + "cpu" : { + "shares" : 1024, + "quota" : 1000000, + "period" : 500000, + "cpus" : "0-7" + }, + "blockIO" : { + "weight" : 10, + "weightDevice" : [ + { + "major" : 4, + "minor" : 2, + "weight" : 500 + } + ], + "throttleReadBpsDevice" : [ + { + "major" : 4, + "minor" : 2, + "rate" : 500 + } + ], + "throttleWriteBpsDevice" : [ + { + "major" : 4, + "minor" : 2, + "rate" : 500 + } + ], + "throttleReadIOPSDevice" : [ + { + "major" : 4, + "minor" : 2, + "rate" : 500 + } + ], + "throttleWriteIOPSDevice" : [ + { + "major" : 4, + "minor" : 2, + "rate" : 500 + } + ] + }, + "pids" : { + "limit" : 1024 + } + }, + "sysctl" : { + "kernel.domainname" : "foo.bar", + "vm.swappiness" : "60" + }, + "seccomp" : { + "defaultAction" : "SCMP_ACT_ALLOW", + "architectures" : [ + "SCMP_ARCH_ARM", + "SCMP_ARCH_X86_64" + ], + "syscalls" : [ { - "allow": false, - "access": "rwm" + "names" : [ + "lchown", + "chmod" + ], + "action" : "SCMP_ACT_ERRNO", + "args" : [ + { + "index" : 0, + "value" : 1, + "op" : "SCMP_CMP_NE" + }, + { + "index" : 1, + "value" : 2, + "valueTwo" : 3, + "op" : "SCMP_CMP_MASKED_EQ" + } + ] } ] }, - "namespaces": [ + "rootfsPropagation" : "shared", + "maskedPaths" : [ + "/proc/kcore", + "/root/nonexistent" + ], + "readonlyPaths" : [ + "/proc/sys", + "/opt/readonly" + ] + }, + "hooks" : { + "prestart" : [ { - "type": "pid" + "path" : "/bin/sh", + "args" : [ + "-xec", + "echo $PRESTART_FOO >/prestart" + ], + "env" : [ + "PRESTART_FOO=prestart_bar", + "ALSO_FOO=also_bar" + ], + "timeout" : 666 }, { - "type": "ipc" - }, + "path" : "/bin/touch", + "args" : [ + "/tmp/also-prestart" + ] + } + ], + "poststart" : [ { - "type": "mount" + "path" : "/bin/sh", + "args" : [ + "touch", + "/poststart" + ] + } + ], + "poststop" : [ + { + "path" : "/bin/sh", + "args" : [ + "touch", + "/poststop" + ] } ] }, - - "annotations": { - "com.example.key1": "value1", - "com.example.key2": "value2" + "annotations" : { + "hello.world" : "1", + "foo" : "bar" } } diff --git a/test/fuzz/fuzz-unit-file/directives-all.service b/test/fuzz/fuzz-unit-file/directives-all.service index 8450d024e6..818fb28dbf 100644 --- a/test/fuzz/fuzz-unit-file/directives-all.service +++ b/test/fuzz/fuzz-unit-file/directives-all.service @@ -269,6 +269,8 @@ Type= USBFunctionDescriptors= USBFunctionStrings= Unit= +UpheldBy= +Upholds= User= WakeSystem= WantedBy= diff --git a/test/testsuite-23.units/testsuite-23-upheldby-install.service b/test/testsuite-23.units/testsuite-23-upheldby-install.service new file mode 100644 index 0000000000..a4562077db --- /dev/null +++ b/test/testsuite-23.units/testsuite-23-upheldby-install.service @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=Unit that sets UpheldBy= through [Install] + +[Service] +ExecStart=/bin/sleep infinity + +[Install] +UpheldBy=testsuite-23-retry-uphold.service diff --git a/test/units/testsuite-13.nspawn-oci.sh b/test/units/testsuite-13.nspawn-oci.sh new file mode 100755 index 0000000000..cbfdb18290 --- /dev/null +++ b/test/units/testsuite-13.nspawn-oci.sh @@ -0,0 +1,383 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2016 +set -eux +set -o pipefail + +export SYSTEMD_LOG_LEVEL=debug +export SYSTEMD_LOG_TARGET=journal +CREATE_BB_CONTAINER="/usr/lib/systemd/tests/testdata/create-busybox-container" + +# shellcheck disable=SC2317 +at_exit() { + set +e + + mountpoint -q /var/lib/machines && umount /var/lib/machines + [[ -n "${DEV:-}" ]] && rm -f "$DEV" + [[ -n "${NETNS:-}" ]] && umount "$NETNS" && rm -f "$NETNS" + [[ -n "${TMPDIR:-}" ]] && rm -fr "$TMPDIR" +} + +trap at_exit EXIT + +# Mount tmpfs over /var/lib/machines to not pollute the image +mkdir -p /var/lib/machines +mount -t tmpfs tmpfs /var/lib/machines + +# Setup a couple of dirs/devices for the OCI containers +DEV="$(mktemp -u /dev/oci-dev-XXX)" +mknod -m 666 "$DEV" b 42 42 +NETNS="$(mktemp /var/tmp/netns.XXX)" +mount --bind /proc/self/ns/net "$NETNS" +TMPDIR="$(mktemp -d)" +touch "$TMPDIR/hello" +OCI="$(mktemp -d /var/lib/machines/testsuite-13.oci-bundle.XXX)" +"$CREATE_BB_CONTAINER" "$OCI/rootfs" +mkdir -p "$OCI/rootfs/opt/var" +mkdir -p "$OCI/rootfs/opt/readonly" + +# Let's start with a simple config +cat >"$OCI/config.json" <<EOF +{ + "ociVersion" : "1.0.0", + "root" : { + "path" : "rootfs" + }, + "mounts" : [ + { + "destination" : "/root", + "type" : "tmpfs", + "source" : "tmpfs" + } + ] +} +EOF +systemd-nspawn --oci-bundle="$OCI" sh -xec 'mountpoint /root' + +# And now for something a bit more involved +# Notes: +# - the hooks are parsed & processed, but never executed +# - set sysctl's are parsed but never used? +# - same goes for arg_sysctl in nspawn.c +cat >"$OCI/config.json" <<EOF +{ + "ociVersion" : "1.0.0", + "hostname" : "my-oci-container", + "root" : { + "path" : "rootfs", + "readonly" : false + }, + "mounts" : [ + { + "destination" : "/root", + "type" : "tmpfs", + "source" : "tmpfs" + }, + { + "destination" : "/var", + "type" : "none", + "source" : "$TMPDIR", + "options" : ["rbind", "rw"] + } + ], + "process" : { + "terminal" : false, + "consoleSize" : { + "height" : 25, + "width" : 80 + }, + "user" : { + "uid" : 0, + "gid" : 0, + "additionalGids" : [5, 6] + }, + "env" : [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "FOO=bar" + ], + "cwd" : "/root", + "args" : [ + "sh", + "-xe", + "/entrypoint.sh" + ], + "noNewPrivileges" : true, + "oomScoreAdj" : 20, + "capabilities" : { + "bounding" : [ + "CAP_AUDIT_WRITE", + "CAP_KILL", + "CAP_NET_BIND_SERVICE" + ], + "permitted" : [ + "CAP_AUDIT_WRITE", + "CAP_KILL", + "CAP_NET_BIND_SERVICE" + ], + "inheritable" : [ + "CAP_AUDIT_WRITE", + "CAP_KILL", + "CAP_NET_BIND_SERVICE" + ], + "effective" : [ + "CAP_AUDIT_WRITE", + "CAP_KILL" + ], + "ambient" : [ + "CAP_NET_BIND_SERVICE" + ] + }, + "rlimits" : [ + { + "type" : "RLIMIT_NOFILE", + "soft" : 1024, + "hard" : 1024 + }, + { + "type" : "RLIMIT_RTPRIO", + "soft" : 5, + "hard" : 10 + } + ] + }, + "linux" : { + "namespaces" : [ + { + "type" : "mount" + }, + { + "type" : "network", + "path" : "$NETNS" + }, + { + "type" : "pid" + }, + { + "type" : "uts" + } + ], + "uidMappings" : [ + { + "containerID" : 0, + "hostID" : 1000, + "size" : 100 + } + ], + "gidMappings" : [ + { + "containerID" : 0, + "hostID" : 1000, + "size" : 100 + } + ], + "devices" : [ + { + "type" : "c", + "path" : "/dev/zero", + "major" : 1, + "minor" : 5, + "fileMode" : 444 + }, + { + "type" : "b", + "path" : "$DEV", + "major" : 4, + "minor" : 2, + "fileMode" : 666, + "uid" : 0, + "gid" : 0 + } + ], + "resources" : { + "devices" : [ + { + "allow" : false, + "access" : "m" + }, + { + "allow" : true, + "type" : "b", + "major" : 4, + "minor" : 2, + "access" : "rwm" + } + ], + "memory" : { + "limit" : 134217728, + "reservation" : 33554432, + "swap" : 268435456 + }, + "cpu" : { + "shares" : 1024, + "quota" : 1000000, + "period" : 500000, + "cpus" : "0-7" + }, + "blockIO" : { + "weight" : 10, + "weightDevice" : [ + { + "major" : 4, + "minor" : 2, + "weight" : 500 + } + ], + "throttleReadBpsDevice" : [ + { + "major" : 4, + "minor" : 2, + "rate" : 500 + } + ], + "throttleWriteBpsDevice" : [ + { + "major" : 4, + "minor" : 2, + "rate" : 500 + } + ], + "throttleReadIOPSDevice" : [ + { + "major" : 4, + "minor" : 2, + "rate" : 500 + } + ], + "throttleWriteIOPSDevice" : [ + { + "major" : 4, + "minor" : 2, + "rate" : 500 + } + ] + }, + "pids" : { + "limit" : 1024 + } + }, + "sysctl" : { + "kernel.domainname" : "foo.bar", + "vm.swappiness" : "60" + }, + "seccomp" : { + "defaultAction" : "SCMP_ACT_ALLOW", + "architectures" : [ + "SCMP_ARCH_ARM", + "SCMP_ARCH_X86_64" + ], + "syscalls" : [ + { + "names" : [ + "lchown", + "chmod" + ], + "action" : "SCMP_ACT_ERRNO", + "args" : [ + { + "index" : 0, + "value" : 1, + "op" : "SCMP_CMP_NE" + }, + { + "index" : 1, + "value" : 2, + "valueTwo" : 3, + "op" : "SCMP_CMP_MASKED_EQ" + } + ] + } + ] + }, + "rootfsPropagation" : "shared", + "maskedPaths" : [ + "/proc/kcore", + "/root/nonexistent" + ], + "readonlyPaths" : [ + "/proc/sys", + "/opt/readonly" + ] + }, + "hooks" : { + "prestart" : [ + { + "path" : "/bin/sh", + "args" : [ + "-xec", + "echo \$PRESTART_FOO >/prestart" + ], + "env" : [ + "PRESTART_FOO=prestart_bar", + "ALSO_FOO=also_bar" + ], + "timeout" : 666 + }, + { + "path" : "/bin/touch", + "args" : [ + "/tmp/also-prestart" + ] + } + ], + "poststart" : [ + { + "path" : "/bin/sh", + "args" : [ + "touch", + "/poststart" + ] + } + ], + "poststop" : [ + { + "path" : "/bin/sh", + "args" : [ + "touch", + "/poststop" + ] + } + ] + }, + "annotations" : { + "hello.world" : "1", + "foo" : "bar" + } +} +EOF +# Create a simple "entrypoint" script that validates that the container +# is created correctly according to the OCI config +cat >"$OCI/rootfs/entrypoint.sh" <<EOF +#!/bin/sh -e + +# Mounts +mountpoint /root +mountpoint /var +test -e /var/hello + +# Process +[[ "\$PWD" == /root ]] +[[ "\$FOO" == bar ]] + +# Process - rlimits +[[ "\$(ulimit -S -n)" -eq 1024 ]] +[[ "\$(ulimit -H -n)" -eq 1024 ]] +[[ "\$(ulimit -S -r)" -eq 5 ]] +[[ "\$(ulimit -H -r)" -eq 10 ]] +[[ "\$(hostname)" == my-oci-container ]] + +# Linux - devices +test -c /dev/zero +test -b "$DEV" +[[ "\$(stat -c '%t:%T' "$DEV")" == 4:2 ]] + +# Linux - maskedPaths +test -e /proc/kcore +cat /proc/kcore && exit 1 +test ! -e /root/nonexistent + +# Linux - readonlyPaths +touch /opt/readonly/foo && exit 1 + +exit 0 +EOF +systemd-nspawn --oci-bundle="$OCI" diff --git a/test/units/testsuite-13.nspawn.sh b/test/units/testsuite-13.nspawn.sh index 76ba143751..8f8daf05cc 100755 --- a/test/units/testsuite-13.nspawn.sh +++ b/test/units/testsuite-13.nspawn.sh @@ -46,7 +46,7 @@ mkdir -p /var/lib/machines mount -t tmpfs tmpfs /var/lib/machines testcase_sanity_check() { - local template root image oci uuid tmpdir + local template root image uuid tmpdir tmpdir="$(mktemp -d)" template="$(mktemp -d /tmp/nspawn-template.XXX)" @@ -59,24 +59,6 @@ testcase_sanity_check() { mount -o loop "$image" /mnt cp -r "$template"/* /mnt/ umount /mnt - # Create a simple OCI bundle - oci="$(mktemp -d /var/lib/machines/testsuite-13.oci-bundle.XXX)" - "$CREATE_BB_CONTAINER" "$oci/rootfs" - cat >"$oci/config.json" <<EOF -{ - "ociVersion" : "1.0.0", - "root" : { - "path" : "rootfs" - }, - "mounts" : [ - { - "destination" : "/root", - "type" : "tmpfs", - "source" : "tmpfs" - } - ] -} -EOF systemd-nspawn --help --no-pager systemd-nspawn --version @@ -101,7 +83,6 @@ EOF sh -xec 'touch /nope') test ! -e "$root/nope" systemd-nspawn --image="$image" sh -xec 'echo hello' - systemd-nspawn --oci-bundle="$oci" sh -xec 'mountpoint /root' # --volatile= touch "$root/usr/has-usr" diff --git a/test/units/testsuite-17.12.sh b/test/units/testsuite-17.12.sh new file mode 100755 index 0000000000..df74d356ee --- /dev/null +++ b/test/units/testsuite-17.12.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +# shellcheck source=test/units/assert.sh +. "$(dirname "$0")"/assert.sh + +create_link_file() { + name=${1?} + + mkdir -p /run/systemd/network/ + cat >/run/systemd/network/10-test.link <<EOF +[Match] +Kind=dummy +MACAddress=00:50:56:c0:00:18 + +[Link] +Name=$name +AlternativeName=test1 test2 test3 test4 +EOF + udevadm control --reload +} + +udevadm control --log-level=debug + +create_link_file test1 +ip link add address 00:50:56:c0:00:18 type dummy +udevadm wait --settle --timeout=30 /sys/class/net/test1 +output=$(ip link show dev test1) +if ! [[ "$output" =~ altname ]]; then + echo "alternative name for network interface not supported, skipping test." + exit 0 +fi +assert_not_in "altname test1" "$output" +assert_in "altname test2" "$output" +assert_in "altname test3" "$output" +assert_in "altname test4" "$output" + +# By triggering add event, Name= and AlternativeNames= are re-applied +create_link_file test2 +udevadm trigger --action add --settle /sys/class/net/test1 +udevadm wait --settle --timeout=30 /sys/class/net/test2 +output=$(ip link show dev test2) +assert_in "altname test1" "$output" +assert_not_in "altname test2" "$output" +assert_in "altname test3" "$output" +assert_in "altname test4" "$output" + +# Name= and AlternativeNames= are not applied on move event +create_link_file test3 +udevadm trigger --action move --settle /sys/class/net/test2 +udevadm wait --settle --timeout=30 /sys/class/net/test2 +output=$(ip link show dev test2) +assert_in "altname test1" "$output" +assert_not_in "altname test2" "$output" +assert_in "altname test3" "$output" +assert_in "altname test4" "$output" + +# Test move event triggered by manual renaming +ip link set dev test2 name hoge +udevadm wait --settle --timeout=30 /sys/class/net/hoge +output=$(ip link show dev hoge) +assert_in "altname test1" "$output" +assert_not_in "altname test2" "$output" +assert_in "altname test3" "$output" +assert_in "altname test4" "$output" +assert_not_in "altname hoge" "$output" + +# Re-test add event +udevadm trigger --action add --settle /sys/class/net/hoge +udevadm wait --settle --timeout=30 /sys/class/net/test3 +output=$(ip link show dev test3) +assert_in "altname test1" "$output" +assert_in "altname test2" "$output" +assert_not_in "altname test3" "$output" +assert_in "altname test4" "$output" +assert_not_in "altname hoge" "$output" + +# cleanup +ip link del dev test3 + +rm -f /run/systemd/network/10-test.link +udevadm control --reload --log-level=info + +exit 0 diff --git a/test/units/testsuite-23.Upholds.sh b/test/units/testsuite-23.Upholds.sh index 21db258e1d..bce4d7205b 100755 --- a/test/units/testsuite-23.Upholds.sh +++ b/test/units/testsuite-23.Upholds.sh @@ -30,6 +30,8 @@ done systemctl stop testsuite-23-uphold.service +systemctl enable testsuite-23-upheldby-install.service + # Idea is this: # 1. we start testsuite-23-retry-uphold.service # 2. which through Uphold= starts testsuite-23-retry-upheld.service @@ -42,12 +44,13 @@ systemctl stop testsuite-23-uphold.service rm -f /tmp/testsuite-23-retry-fail systemctl start testsuite-23-retry-uphold.service +systemctl is-active testsuite-23-upheldby-install.service while ! systemctl is-failed testsuite-23-retry-fail.service ; do sleep .5 done -systemctl is-active testsuite-23-retry-upheld.service && { echo 'unexpected success'; exit 1; } +(! systemctl is-active testsuite-23-retry-upheld.service) touch /tmp/testsuite-23-retry-fail |