diff options
Diffstat (limited to 'src')
39 files changed, 545 insertions, 220 deletions
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/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(); } |