core: move several source files to src/shared
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 31 Dec 2020 20:38:06 +0000 (05:38 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 18 Jan 2021 22:04:19 +0000 (07:04 +0900)
As, the files are used by many executables, fstab-generator, remount-fs,
machine-id-setup, and etc.
With this change, the total size of the relevant executables and
libshared slightly decreases.

Before:
```
$ ll systemd-fstab-generator systemd-remount-fs systemd-machine-id-setup systemd-shutdown systemd-nspawn systemd src/shared/libsystemd-shared-247.so
-rwxrwxr-x 1 watanabe watanabe 7577800 Jan  5 13:35 src/shared/libsystemd-shared-247.so
-rwxrwxr-x 1 watanabe watanabe 4608360 Jan  5 13:35 systemd
-rwxrwxr-x 1 watanabe watanabe  117240 Jan  5 13:35 systemd-fstab-generator
-rwxrwxr-x 1 watanabe watanabe   61576 Jan  5 13:35 systemd-machine-id-setup
-rwxrwxr-x 1 watanabe watanabe  853080 Jan  5 13:35 systemd-nspawn
-rwxrwxr-x 1 watanabe watanabe   70600 Jan  5 13:35 systemd-remount-fs
-rwxrwxr-x 1 watanabe watanabe  172624 Jan  5 13:35 systemd-shutdown
```
Total: 13461280

After:
```
$ ll systemd-fstab-generator systemd-remount-fs systemd-machine-id-setup systemd-shutdown systemd-nspawn systemd src/shared/libsystemd-shared-247.so
-rwxrwxr-x 1 watanabe watanabe 7658336 Jan  5 13:32 src/shared/libsystemd-shared-247.so
-rwxrwxr-x 1 watanabe watanabe 4523560 Jan  5 13:32 systemd
-rwxrwxr-x 1 watanabe watanabe   78288 Jan  5 13:32 systemd-fstab-generator
-rwxrwxr-x 1 watanabe watanabe   30984 Jan  5 13:32 systemd-machine-id-setup
-rwxrwxr-x 1 watanabe watanabe  840384 Jan  5 13:32 systemd-nspawn
-rwxrwxr-x 1 watanabe watanabe   39104 Jan  5 13:32 systemd-remount-fs
-rwxrwxr-x 1 watanabe watanabe  117160 Jan  5 13:32 systemd-shutdown
```
Total: 13287816

20 files changed:
meson.build
src/core/killall.c [deleted file]
src/core/killall.h [deleted file]
src/core/loopback-setup.c [deleted file]
src/core/loopback-setup.h [deleted file]
src/core/machine-id-setup.c [deleted file]
src/core/machine-id-setup.h [deleted file]
src/core/meson.build
src/core/mount-setup.c [deleted file]
src/core/mount-setup.h [deleted file]
src/shared/killall.c [new file with mode: 0644]
src/shared/killall.h [new file with mode: 0644]
src/shared/loopback-setup.c [new file with mode: 0644]
src/shared/loopback-setup.h [new file with mode: 0644]
src/shared/machine-id-setup.c [new file with mode: 0644]
src/shared/machine-id-setup.h [new file with mode: 0644]
src/shared/meson.build
src/shared/mount-setup.c [new file with mode: 0644]
src/shared/mount-setup.h [new file with mode: 0644]
src/test/meson.build

index 4fbcb9533e4f578d13f46b0b565dc56585ebc708..80cf510020ff07534b73319dbb4f8962d5742762 100644 (file)
@@ -1914,8 +1914,7 @@ executable(
         'systemd-fstab-generator',
         'src/fstab-generator/fstab-generator.c',
         include_directories : includes,
-        link_with : [libcore_shared,
-                     libshared],
+        link_with : [libshared],
         install_rpath : rootlibexecdir,
         install : true,
         install_dir : systemgeneratordir)
@@ -2925,8 +2924,7 @@ executable(
         'systemd-remount-fs',
         'src/remount-fs/remount-fs.c',
         include_directories : includes,
-        link_with : [libcore_shared,
-                     libshared],
+        link_with : [libshared],
         install_rpath : rootlibexecdir,
         install : true,
         install_dir : rootlibexecdir)
@@ -2935,8 +2933,7 @@ executable(
         'systemd-machine-id-setup',
         'src/machine-id-setup/machine-id-setup-main.c',
         include_directories : includes,
-        link_with : [libcore_shared,
-                     libshared],
+        link_with : [libshared],
         install_rpath : rootlibexecdir,
         install : true,
         install_dir : rootbindir)
@@ -3287,8 +3284,7 @@ executable(
         'systemd-shutdown',
         systemd_shutdown_sources,
         include_directories : includes,
-        link_with : [libcore_shared,
-                     libshared],
+        link_with : [libshared],
         dependencies : [libmount],
         install_rpath : rootlibexecdir,
         install : true,
@@ -3336,8 +3332,7 @@ public_programs += executable(
         'systemd-nspawn',
         systemd_nspawn_sources,
         include_directories : includes,
-        link_with : [libcore_shared,
-                     libnspawn_core,
+        link_with : [libnspawn_core,
                      libshared],
         dependencies : [libblkid,
                         libseccomp],
diff --git a/src/core/killall.c b/src/core/killall.c
deleted file mode 100644 (file)
index d9fcfc2..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/***
-  Copyright © 2010 ProFUSION embedded systems
-***/
-
-#include <errno.h>
-#include <signal.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include "alloc-util.h"
-#include "def.h"
-#include "dirent-util.h"
-#include "fd-util.h"
-#include "format-util.h"
-#include "killall.h"
-#include "parse-util.h"
-#include "process-util.h"
-#include "set.h"
-#include "string-util.h"
-#include "terminal-util.h"
-#include "util.h"
-
-static bool ignore_proc(pid_t pid, bool warn_rootfs) {
-        _cleanup_fclose_ FILE *f = NULL;
-        const char *p;
-        char c = 0;
-        uid_t uid;
-        int r;
-
-        /* We are PID 1, let's not commit suicide */
-        if (pid <= 1)
-                return true;
-
-        /* Ignore kernel threads */
-        r = is_kernel_thread(pid);
-        if (r != 0)
-                return true; /* also ignore processes where we can't determine this */
-
-        r = get_process_uid(pid, &uid);
-        if (r < 0)
-                return true; /* not really, but better safe than sorry */
-
-        /* Non-root processes otherwise are always subject to be killed */
-        if (uid != 0)
-                return false;
-
-        p = procfs_file_alloca(pid, "cmdline");
-        f = fopen(p, "re");
-        if (!f)
-                return true; /* not really, but has the desired effect */
-
-        /* Try to read the first character of the command line. If the cmdline is empty (which might be the case for
-         * kernel threads but potentially also other stuff), this line won't do anything, but we don't care much, as
-         * actual kernel threads are already filtered out above. */
-        (void) fread(&c, 1, 1, f);
-
-        /* Processes with argv[0][0] = '@' we ignore from the killing spree.
-         *
-         * https://systemd.io/ROOT_STORAGE_DAEMONS */
-        if (c != '@')
-                return false;
-
-        if (warn_rootfs &&
-            pid_from_same_root_fs(pid) == 0) {
-
-                _cleanup_free_ char *comm = NULL;
-
-                (void) get_process_comm(pid, &comm);
-
-                log_notice("Process " PID_FMT " (%s) has been marked to be excluded from killing. It is "
-                           "running from the root file system, and thus likely to block re-mounting of the "
-                           "root file system to read-only. Please consider moving it into an initrd file "
-                           "system instead.", pid, strna(comm));
-        }
-
-        return true;
-}
-
-static void log_children_no_yet_killed(Set *pids) {
-        _cleanup_free_ char *lst_child = NULL;
-        void *p;
-
-        SET_FOREACH(p, pids) {
-                _cleanup_free_ char *s = NULL;
-
-                if (get_process_comm(PTR_TO_PID(p), &s) < 0)
-                        (void) asprintf(&s, PID_FMT, PTR_TO_PID(p));
-
-                if (!strextend(&lst_child, ", ", s)) {
-                        log_oom();
-                        return;
-                }
-        }
-
-        if (isempty(lst_child))
-                return;
-
-        log_warning("Waiting for process: %s", lst_child + 2);
-}
-
-static int wait_for_children(Set *pids, sigset_t *mask, usec_t timeout) {
-        usec_t until, date_log_child, n;
-
-        assert(mask);
-
-        /* Return the number of children remaining in the pids set: That correspond to the number
-         * of processes still "alive" after the timeout */
-
-        if (set_isempty(pids))
-                return 0;
-
-        n = now(CLOCK_MONOTONIC);
-        until = usec_add(n, timeout);
-        date_log_child = usec_add(n, 10u * USEC_PER_SEC);
-        if (date_log_child > until)
-                date_log_child = usec_add(n, timeout / 2u);
-
-        for (;;) {
-                struct timespec ts;
-                int k;
-                void *p;
-
-                /* First, let the kernel inform us about killed
-                 * children. Most processes will probably be our
-                 * children, but some are not (might be our
-                 * grandchildren instead...). */
-                for (;;) {
-                        pid_t pid;
-
-                        pid = waitpid(-1, NULL, WNOHANG);
-                        if (pid == 0)
-                                break;
-                        if (pid < 0) {
-                                if (errno == ECHILD)
-                                        break;
-
-                                return log_error_errno(errno, "waitpid() failed: %m");
-                        }
-
-                        (void) set_remove(pids, PID_TO_PTR(pid));
-                }
-
-                /* Now explicitly check who might be remaining, who
-                 * might not be our child. */
-                SET_FOREACH(p, pids) {
-
-                        /* kill(pid, 0) sends no signal, but it tells
-                         * us whether the process still exists. */
-                        if (kill(PTR_TO_PID(p), 0) == 0)
-                                continue;
-
-                        if (errno != ESRCH)
-                                continue;
-
-                        set_remove(pids, p);
-                }
-
-                if (set_isempty(pids))
-                        return 0;
-
-                n = now(CLOCK_MONOTONIC);
-                if (date_log_child > 0 && n >= date_log_child) {
-                        log_children_no_yet_killed(pids);
-                        /* Log the children not yet killed only once */
-                        date_log_child = 0;
-                }
-
-                if (n >= until)
-                        return set_size(pids);
-
-                if (date_log_child > 0)
-                        timespec_store(&ts, MIN(until - n, date_log_child - n));
-                else
-                        timespec_store(&ts, until - n);
-
-                k = sigtimedwait(mask, NULL, &ts);
-                if (k != SIGCHLD) {
-
-                        if (k < 0 && errno != EAGAIN)
-                                return log_error_errno(errno, "sigtimedwait() failed: %m");
-
-                        if (k >= 0)
-                                log_warning("sigtimedwait() returned unexpected signal.");
-                }
-        }
-}
-
-static int killall(int sig, Set *pids, bool send_sighup) {
-        _cleanup_closedir_ DIR *dir = NULL;
-        struct dirent *d;
-        int n_killed = 0;
-
-        /* Send the specified signal to all remaining processes, if not excluded by ignore_proc().
-         * Returns the number of processes to which the specified signal was sent */
-
-        dir = opendir("/proc");
-        if (!dir)
-                return log_warning_errno(errno, "opendir(/proc) failed: %m");
-
-        FOREACH_DIRENT_ALL(d, dir, break) {
-                pid_t pid;
-                int r;
-
-                if (!IN_SET(d->d_type, DT_DIR, DT_UNKNOWN))
-                        continue;
-
-                if (parse_pid(d->d_name, &pid) < 0)
-                        continue;
-
-                if (ignore_proc(pid, sig == SIGKILL && !in_initrd()))
-                        continue;
-
-                if (sig == SIGKILL) {
-                        _cleanup_free_ char *s = NULL;
-
-                        (void) get_process_comm(pid, &s);
-                        log_notice("Sending SIGKILL to PID "PID_FMT" (%s).", pid, strna(s));
-                }
-
-                if (kill(pid, sig) >= 0) {
-                        n_killed++;
-                        if (pids) {
-                                r = set_put(pids, PID_TO_PTR(pid));
-                                if (r < 0)
-                                        log_oom();
-                        }
-                } else if (errno != ENOENT)
-                        log_warning_errno(errno, "Could not kill %d: %m", pid);
-
-                if (send_sighup) {
-                        /* Optionally, also send a SIGHUP signal, but
-                        only if the process has a controlling
-                        tty. This is useful to allow handling of
-                        shells which ignore SIGTERM but react to
-                        SIGHUP. We do not send this to processes that
-                        have no controlling TTY since we don't want to
-                        trigger reloads of daemon processes. Also we
-                        make sure to only send this after SIGTERM so
-                        that SIGTERM is always first in the queue. */
-
-                        if (get_ctty_devnr(pid, NULL) >= 0)
-                                /* it's OK if the process is gone, just ignore the result */
-                                (void) kill(pid, SIGHUP);
-                }
-        }
-
-        return n_killed;
-}
-
-int broadcast_signal(int sig, bool wait_for_exit, bool send_sighup, usec_t timeout) {
-        int n_children_left;
-        sigset_t mask, oldmask;
-        _cleanup_set_free_ Set *pids = NULL;
-
-        /* Send the specified signal to all remaining processes, if not excluded by ignore_proc().
-         * Return:
-         *  - The number of processes still "alive" after the timeout (that should have been killed)
-         *    if the function needs to wait for the end of the processes (wait_for_exit).
-         *  - Otherwise, the number of processes to which the specified signal was sent */
-
-        if (wait_for_exit)
-                pids = set_new(NULL);
-
-        assert_se(sigemptyset(&mask) == 0);
-        assert_se(sigaddset(&mask, SIGCHLD) == 0);
-        assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
-
-        if (kill(-1, SIGSTOP) < 0 && errno != ESRCH)
-                log_warning_errno(errno, "kill(-1, SIGSTOP) failed: %m");
-
-        n_children_left = killall(sig, pids, send_sighup);
-
-        if (kill(-1, SIGCONT) < 0 && errno != ESRCH)
-                log_warning_errno(errno, "kill(-1, SIGCONT) failed: %m");
-
-        if (wait_for_exit && n_children_left > 0)
-                n_children_left = wait_for_children(pids, &mask, timeout);
-
-        assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
-
-        return n_children_left;
-}
diff --git a/src/core/killall.h b/src/core/killall.h
deleted file mode 100644 (file)
index d8ef96f..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include "time-util.h"
-
-int broadcast_signal(int sig, bool wait_for_exit, bool send_sighup, usec_t timeout);
diff --git a/src/core/loopback-setup.c b/src/core/loopback-setup.c
deleted file mode 100644 (file)
index 76022ce..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <net/if.h>
-#include <stdlib.h>
-
-#include "sd-netlink.h"
-
-#include "loopback-setup.h"
-#include "missing_network.h"
-#include "netlink-util.h"
-#include "time-util.h"
-
-#define LOOPBACK_SETUP_TIMEOUT_USEC (5 * USEC_PER_SEC)
-
-struct state {
-        unsigned n_messages;
-        int rcode;
-        const char *error_message;
-        const char *success_message;
-};
-
-static int generic_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
-        struct state *s = userdata;
-        int r;
-
-        assert(s);
-        assert(s->n_messages > 0);
-        s->n_messages--;
-
-        errno = 0;
-
-        r = sd_netlink_message_get_errno(m);
-        if (r < 0)
-                log_debug_errno(r, "%s: %m", s->error_message);
-        else
-                log_debug("%s", s->success_message);
-
-        s->rcode = r;
-        return 0;
-}
-
-static int start_loopback(sd_netlink *rtnl, struct state *s) {
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
-        int r;
-
-        assert(rtnl);
-        assert(s);
-
-        r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, LOOPBACK_IFINDEX);
-        if (r < 0)
-                return r;
-
-        r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_call_async(rtnl, NULL, req, generic_handler, NULL, s, LOOPBACK_SETUP_TIMEOUT_USEC, "systemd-start-loopback");
-        if (r < 0)
-                return r;
-
-        s->n_messages ++;
-        return 0;
-}
-
-static int add_ipv4_address(sd_netlink *rtnl, struct state *s) {
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
-        int r;
-
-        assert(rtnl);
-        assert(s);
-
-        r = sd_rtnl_message_new_addr(rtnl, &req, RTM_NEWADDR, LOOPBACK_IFINDEX, AF_INET);
-        if (r < 0)
-                return r;
-
-        r = sd_rtnl_message_addr_set_prefixlen(req, 8);
-        if (r < 0)
-                return r;
-
-        r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT);
-        if (r < 0)
-                return r;
-
-        r = sd_rtnl_message_addr_set_scope(req, RT_SCOPE_HOST);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &(struct in_addr) { .s_addr = htobe32(INADDR_LOOPBACK) } );
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_call_async(rtnl, NULL, req, generic_handler, NULL, s, USEC_INFINITY, "systemd-loopback-ipv4");
-        if (r < 0)
-                return r;
-
-        s->n_messages ++;
-        return 0;
-}
-
-static int add_ipv6_address(sd_netlink *rtnl, struct state *s) {
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
-        int r;
-
-        assert(rtnl);
-        assert(s);
-
-        r = sd_rtnl_message_new_addr(rtnl, &req, RTM_NEWADDR, LOOPBACK_IFINDEX, AF_INET6);
-        if (r < 0)
-                return r;
-
-        r = sd_rtnl_message_addr_set_prefixlen(req, 128);
-        if (r < 0)
-                return r;
-
-        r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT);
-        if (r < 0)
-                return r;
-
-        r = sd_rtnl_message_addr_set_scope(req, RT_SCOPE_HOST);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_message_append_in6_addr(req, IFA_LOCAL, &in6addr_loopback);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_call_async(rtnl, NULL, req, generic_handler, NULL, s, USEC_INFINITY, "systemd-loopback-ipv6");
-        if (r < 0)
-                return r;
-
-        s->n_messages ++;
-        return 0;
-}
-
-static bool check_loopback(sd_netlink *rtnl) {
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
-        unsigned flags;
-        int r;
-
-        r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, LOOPBACK_IFINDEX);
-        if (r < 0)
-                return false;
-
-        r = sd_netlink_call(rtnl, req, USEC_INFINITY, &reply);
-        if (r < 0)
-                return false;
-
-        r = sd_rtnl_message_link_get_flags(reply, &flags);
-        if (r < 0)
-                return false;
-
-        return flags & IFF_UP;
-}
-
-int loopback_setup(void) {
-        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
-        struct state state_4 = {
-                .error_message = "Failed to add address 127.0.0.1 to loopback interface",
-                .success_message = "Successfully added address 127.0.0.1 to loopback interface",
-        }, state_6 = {
-                .error_message = "Failed to add address ::1 to loopback interface",
-                .success_message = "Successfully added address ::1 to loopback interface",
-        }, state_up = {
-                .error_message = "Failed to bring loopback interface up",
-                .success_message = "Successfully brought loopback interface up",
-        };
-        int r;
-
-        r = sd_netlink_open(&rtnl);
-        if (r < 0)
-                return log_error_errno(r, "Failed to open netlink: %m");
-
-        /* Note that we add the IP addresses here explicitly even though the kernel does that too implicitly when
-         * setting up the loopback device. The reason we do this here a second time (and possibly race against the
-         * kernel) is that we want to synchronously wait until the IP addresses are set up correctly, see
-         *
-         * https://github.com/systemd/systemd/issues/5641 */
-
-        r = add_ipv4_address(rtnl, &state_4);
-        if (r < 0)
-                return log_error_errno(r, "Failed to enqueue IPv4 loopback address add request: %m");
-
-        r = add_ipv6_address(rtnl, &state_6);
-        if (r < 0)
-                return log_error_errno(r, "Failed to enqueue IPv6 loopback address add request: %m");
-
-        r = start_loopback(rtnl, &state_up);
-        if (r < 0)
-                return log_error_errno(r, "Failed to enqueue loopback interface start request: %m");
-
-        while (state_4.n_messages + state_6.n_messages + state_up.n_messages > 0) {
-                r = sd_netlink_wait(rtnl, LOOPBACK_SETUP_TIMEOUT_USEC);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to wait for netlink event: %m");
-
-                r = sd_netlink_process(rtnl, NULL);
-                if (r < 0)
-                        return log_warning_errno(r, "Failed to process netlink event: %m");
-        }
-
-        /* Note that we don't really care whether the addresses could be added or not */
-        if (state_up.rcode != 0) {
-                /* If we lack the permissions to configure the loopback device,
-                 * but we find it to be already configured, let's exit cleanly,
-                 * in order to supported unprivileged containers. */
-                if (state_up.rcode == -EPERM && check_loopback(rtnl))
-                        return 0;
-
-                return log_warning_errno(state_up.rcode, "Failed to configure loopback device: %m");
-        }
-
-        return 0;
-}
diff --git a/src/core/loopback-setup.h b/src/core/loopback-setup.h
deleted file mode 100644 (file)
index a7ee2da..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-int loopback_setup(void);
diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c
deleted file mode 100644 (file)
index 6d15f9c..0000000
+++ /dev/null
@@ -1,282 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <fcntl.h>
-#include <sched.h>
-#include <sys/mount.h>
-#include <unistd.h>
-
-#include "sd-id128.h"
-
-#include "alloc-util.h"
-#include "fd-util.h"
-#include "fs-util.h"
-#include "id128-util.h"
-#include "io-util.h"
-#include "log.h"
-#include "machine-id-setup.h"
-#include "macro.h"
-#include "mkdir.h"
-#include "mount-util.h"
-#include "mountpoint-util.h"
-#include "namespace-util.h"
-#include "path-util.h"
-#include "process-util.h"
-#include "stat-util.h"
-#include "string-util.h"
-#include "umask-util.h"
-#include "util.h"
-#include "virt.h"
-
-static int generate_machine_id(const char *root, sd_id128_t *ret) {
-        const char *dbus_machine_id;
-        _cleanup_close_ int fd = -1;
-        int r;
-
-        assert(ret);
-
-        /* First, try reading the D-Bus machine id, unless it is a symlink */
-        dbus_machine_id = prefix_roota(root, "/var/lib/dbus/machine-id");
-        fd = open(dbus_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
-        if (fd >= 0) {
-                if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0) {
-                        log_info("Initializing machine ID from D-Bus machine ID.");
-                        return 0;
-                }
-
-                fd = safe_close(fd);
-        }
-
-        if (isempty(root) && running_in_chroot() <= 0) {
-                /* If that didn't work, see if we are running in a container,
-                 * and a machine ID was passed in via $container_uuid the way
-                 * libvirt/LXC does it */
-
-                if (detect_container() > 0) {
-                        _cleanup_free_ char *e = NULL;
-
-                        if (getenv_for_pid(1, "container_uuid", &e) > 0 &&
-                            sd_id128_from_string(e, ret) >= 0) {
-                                log_info("Initializing machine ID from container UUID.");
-                                return 0;
-                        }
-
-                } else if (detect_vm() == VIRTUALIZATION_KVM) {
-
-                        /* If we are not running in a container, see if we are
-                         * running in qemu/kvm and a machine ID was passed in
-                         * via -uuid on the qemu/kvm command line */
-
-                        if (id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, ret) >= 0) {
-                                log_info("Initializing machine ID from KVM UUID.");
-                                return 0;
-                        }
-                        /* on POWER, it's exported here instead */
-                        if (id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, ret) >= 0) {
-                                log_info("Initializing machine ID from KVM UUID.");
-                                return 0;
-                        }
-                }
-        }
-
-        /* If that didn't work, generate a random machine id */
-        r = sd_id128_randomize(ret);
-        if (r < 0)
-                return log_error_errno(r, "Failed to generate randomized machine ID: %m");
-
-        log_info("Initializing machine ID from random generator.");
-        return 0;
-}
-
-int machine_id_setup(const char *root, bool force_transient, sd_id128_t machine_id, sd_id128_t *ret) {
-        const char *etc_machine_id, *run_machine_id;
-        _cleanup_close_ int fd = -1;
-        bool writable;
-        int r;
-
-        etc_machine_id = prefix_roota(root, "/etc/machine-id");
-
-        RUN_WITH_UMASK(0000) {
-                /* We create this 0444, to indicate that this isn't really
-                 * something you should ever modify. Of course, since the file
-                 * will be owned by root it doesn't matter much, but maybe
-                 * people look. */
-
-                (void) mkdir_parents(etc_machine_id, 0755);
-                fd = open(etc_machine_id, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
-                if (fd < 0) {
-                        int old_errno = errno;
-
-                        fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY);
-                        if (fd < 0) {
-                                if (old_errno == EROFS && errno == ENOENT)
-                                        return log_error_errno(errno,
-                                                  "System cannot boot: Missing /etc/machine-id and /etc is mounted read-only.\n"
-                                                  "Booting up is supported only when:\n"
-                                                  "1) /etc/machine-id exists and is populated.\n"
-                                                  "2) /etc/machine-id exists and is empty.\n"
-                                                  "3) /etc/machine-id is missing and /etc is writable.\n");
-                                else
-                                        return log_error_errno(errno, "Cannot open %s: %m", etc_machine_id);
-                        }
-
-                        writable = false;
-                } else
-                        writable = true;
-        }
-
-        /* A we got a valid machine ID argument, that's what counts */
-        if (sd_id128_is_null(machine_id)) {
-
-                /* Try to read any existing machine ID */
-                if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0)
-                        return 0;
-
-                /* Hmm, so, the id currently stored is not useful, then let's generate one */
-                r = generate_machine_id(root, &machine_id);
-                if (r < 0)
-                        return r;
-        }
-
-        if (writable) {
-                if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
-                        return log_error_errno(errno, "Failed to seek %s: %m", etc_machine_id);
-
-                if (ftruncate(fd, 0) < 0)
-                        return log_error_errno(errno, "Failed to truncate %s: %m", etc_machine_id);
-
-                /* If the caller requested a transient machine-id, write the string "uninitialized\n" to
-                 * disk and overmount it with a transient file.
-                 *
-                 * Otherwise write the machine-id directly to disk. */
-                if (force_transient) {
-                        r = loop_write(fd, "uninitialized\n", strlen("uninitialized\n"), false);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to write uninitialized %s: %m", etc_machine_id);
-
-                        r = fsync_full(fd);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to sync %s: %m", etc_machine_id);
-                } else {
-                        r = id128_write_fd(fd, ID128_PLAIN, machine_id, true);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to write %s: %m", etc_machine_id);
-                        else
-                                goto finish;
-                }
-        }
-
-        fd = safe_close(fd);
-
-        /* Hmm, we couldn't or shouldn't write the machine-id to /etc?
-         * So let's write it to /run/machine-id as a replacement */
-
-        run_machine_id = prefix_roota(root, "/run/machine-id");
-
-        RUN_WITH_UMASK(0022)
-                r = id128_write(run_machine_id, ID128_PLAIN, machine_id, false);
-        if (r < 0) {
-                (void) unlink(run_machine_id);
-                return log_error_errno(r, "Cannot write %s: %m", run_machine_id);
-        }
-
-        /* And now, let's mount it over */
-        r = mount_follow_verbose(LOG_ERR, run_machine_id, etc_machine_id, NULL, MS_BIND, NULL);
-        if (r < 0) {
-                (void) unlink(run_machine_id);
-                return r;
-        }
-
-        log_full(force_transient ? LOG_DEBUG : LOG_INFO, "Installed transient %s file.", etc_machine_id);
-
-        /* Mark the mount read-only */
-        r = mount_follow_verbose(LOG_WARNING, NULL, etc_machine_id, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL);
-        if (r < 0)
-                return r;
-
-finish:
-        if (ret)
-                *ret = machine_id;
-
-        return 0;
-}
-
-int machine_id_commit(const char *root) {
-        _cleanup_close_ int fd = -1, initial_mntns_fd = -1;
-        const char *etc_machine_id, *sync_path;
-        sd_id128_t id;
-        int r;
-
-        /* Before doing anything, sync everything to ensure any changes by first-boot units are persisted.
-         *
-         * First, explicitly sync the file systems we care about and check if it worked. */
-        FOREACH_STRING(sync_path, "/etc/", "/var/") {
-                r = syncfs_path(AT_FDCWD, sync_path);
-                if (r < 0)
-                        return log_error_errno(r, "Cannot sync %s: %m", sync_path);
-        }
-
-        /* Afterwards, sync() the rest too, but we can't check the return value for these. */
-        sync();
-
-        /* Replaces a tmpfs bind mount of /etc/machine-id by a proper file, atomically. For this, the umount is removed
-         * in a mount namespace, a new file is created at the right place. Afterwards the mount is also removed in the
-         * original mount namespace, thus revealing the file that was just created. */
-
-        etc_machine_id = prefix_roota(root, "/etc/machine-id");
-
-        r = path_is_mount_point(etc_machine_id, NULL, 0);
-        if (r < 0)
-                return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", etc_machine_id);
-        if (r == 0) {
-                log_debug("%s is not a mount point. Nothing to do.", etc_machine_id);
-                return 0;
-        }
-
-        /* Read existing machine-id */
-        fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY);
-        if (fd < 0)
-                return log_error_errno(errno, "Cannot open %s: %m", etc_machine_id);
-
-        r = fd_is_temporary_fs(fd);
-        if (r < 0)
-                return log_error_errno(r, "Failed to determine whether %s is on a temporary file system: %m", etc_machine_id);
-        if (r == 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EROFS),
-                                       "%s is not on a temporary file system.",
-                                       etc_machine_id);
-
-        r = id128_read_fd(fd, ID128_PLAIN, &id);
-        if (r < 0)
-                return log_error_errno(r, "We didn't find a valid machine ID in %s: %m", etc_machine_id);
-
-        fd = safe_close(fd);
-
-        /* Store current mount namespace */
-        r = namespace_open(0, NULL, &initial_mntns_fd, NULL, NULL, NULL);
-        if (r < 0)
-                return log_error_errno(r, "Can't fetch current mount namespace: %m");
-
-        /* Switch to a new mount namespace, isolate ourself and unmount etc_machine_id in our new namespace */
-        r = detach_mount_namespace();
-        if (r < 0)
-                return log_error_errno(r, "Failed to set up new mount namespace: %m");
-
-        r = umount_verbose(LOG_ERR, etc_machine_id, 0);
-        if (r < 0)
-                return r;
-
-        /* Update a persistent version of etc_machine_id */
-        r = id128_write(etc_machine_id, ID128_PLAIN, id, true);
-        if (r < 0)
-                return log_error_errno(r, "Cannot write %s. This is mandatory to get a persistent machine ID: %m", etc_machine_id);
-
-        /* Return to initial namespace and proceed a lazy tmpfs unmount */
-        r = namespace_enter(-1, initial_mntns_fd, -1, -1, -1);
-        if (r < 0)
-                return log_warning_errno(r, "Failed to switch back to initial mount namespace: %m.\nWe'll keep transient %s file until next reboot.", etc_machine_id);
-
-        if (umount2(etc_machine_id, MNT_DETACH) < 0)
-                return log_warning_errno(errno, "Failed to unmount transient %s file: %m.\nWe keep that mount until next reboot.", etc_machine_id);
-
-        return 0;
-}
diff --git a/src/core/machine-id-setup.h b/src/core/machine-id-setup.h
deleted file mode 100644 (file)
index cce5819..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include <stdbool.h>
-
-int machine_id_commit(const char *root);
-int machine_id_setup(const char *root, bool force_transient, sd_id128_t requested, sd_id128_t *ret);
index 3482bd44721999cb140a04f097090cea52b2394d..1aaf7cd92939497f52073c0810f3e1568293d6bf 100644 (file)
@@ -1,16 +1,5 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
-libcore_shared_sources = '''
-        killall.c
-        killall.h
-        loopback-setup.c
-        loopback-setup.h
-        machine-id-setup.c
-        machine-id-setup.h
-        mount-setup.c
-        mount-setup.h
-'''.split()
-
 libcore_sources = '''
         apparmor-setup.c
         apparmor-setup.h
@@ -151,22 +140,12 @@ load_fragment_gperf_nulstr_c = custom_target(
         command : [awk, '-f', '@INPUT0@', '@INPUT1@'],
         capture : true)
 
-# A convenience library to share code with other binaries:
-# systemd-shutdown, systemd-remount-fs, systemd-machine-id-setup, …
-libcore_shared = static_library(
-        'core-shared',
-        libcore_shared_sources,
-        include_directories : includes,
-        dependencies : [versiondep,
-                        libmount])
-
 libcore = static_library(
         'core',
         libcore_sources,
         load_fragment_gperf_c,
         load_fragment_gperf_nulstr_c,
         include_directories : includes,
-        link_whole : libcore_shared,
         dependencies : [versiondep,
                         threads,
                         librt,
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
deleted file mode 100644 (file)
index 1e4bbfb..0000000
+++ /dev/null
@@ -1,561 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <errno.h>
-#include <ftw.h>
-#include <stdlib.h>
-#include <sys/mount.h>
-#include <sys/statvfs.h>
-#include <unistd.h>
-
-#include "alloc-util.h"
-#include "bus-util.h"
-#include "cgroup-util.h"
-#include "conf-files.h"
-#include "cgroup-setup.h"
-#include "dev-setup.h"
-#include "dirent-util.h"
-#include "efi-loader.h"
-#include "fd-util.h"
-#include "fileio.h"
-#include "fs-util.h"
-#include "label.h"
-#include "log.h"
-#include "macro.h"
-#include "mkdir.h"
-#include "mount-setup.h"
-#include "mount-util.h"
-#include "mountpoint-util.h"
-#include "nulstr-util.h"
-#include "path-util.h"
-#include "set.h"
-#include "smack-util.h"
-#include "strv.h"
-#include "user-util.h"
-#include "virt.h"
-
-typedef enum MountMode {
-        MNT_NONE           = 0,
-        MNT_FATAL          = 1 << 0,
-        MNT_IN_CONTAINER   = 1 << 1,
-        MNT_CHECK_WRITABLE = 1 << 2,
-        MNT_FOLLOW_SYMLINK = 1 << 3,
-} MountMode;
-
-typedef struct MountPoint {
-        const char *what;
-        const char *where;
-        const char *type;
-        const char *options;
-        unsigned long flags;
-        bool (*condition_fn)(void);
-        MountMode mode;
-} MountPoint;
-
-/* The first three entries we might need before SELinux is up. The
- * fourth (securityfs) is needed by IMA to load a custom policy. The
- * other ones we can delay until SELinux and IMA are loaded. When
- * SMACK is enabled we need smackfs, too, so it's a fifth one. */
-#if ENABLE_SMACK
-#define N_EARLY_MOUNT 5
-#else
-#define N_EARLY_MOUNT 4
-#endif
-
-static const MountPoint mount_table[] = {
-        { "proc",        "/proc",                     "proc",       NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
-          NULL,          MNT_FATAL|MNT_IN_CONTAINER|MNT_FOLLOW_SYMLINK },
-        { "sysfs",       "/sys",                      "sysfs",      NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
-          NULL,          MNT_FATAL|MNT_IN_CONTAINER },
-        { "devtmpfs",    "/dev",                      "devtmpfs",   "mode=755" TMPFS_LIMITS_DEV,               MS_NOSUID|MS_STRICTATIME,
-          NULL,          MNT_FATAL|MNT_IN_CONTAINER },
-        { "securityfs",  "/sys/kernel/security",      "securityfs", NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
-          NULL,          MNT_NONE                   },
-#if ENABLE_SMACK
-        { "smackfs",     "/sys/fs/smackfs",           "smackfs",    "smackfsdef=*",                            MS_NOSUID|MS_NOEXEC|MS_NODEV,
-          mac_smack_use, MNT_FATAL                  },
-        { "tmpfs",       "/dev/shm",                  "tmpfs",      "mode=1777,smackfsroot=*",                 MS_NOSUID|MS_NODEV|MS_STRICTATIME,
-          mac_smack_use, MNT_FATAL                  },
-#endif
-        { "tmpfs",       "/dev/shm",                  "tmpfs",      "mode=1777",                               MS_NOSUID|MS_NODEV|MS_STRICTATIME,
-          NULL,          MNT_FATAL|MNT_IN_CONTAINER },
-        { "devpts",      "/dev/pts",                  "devpts",     "mode=620,gid=" STRINGIFY(TTY_GID),        MS_NOSUID|MS_NOEXEC,
-          NULL,          MNT_IN_CONTAINER           },
-#if ENABLE_SMACK
-        { "tmpfs",       "/run",                      "tmpfs",      "mode=755,smackfsroot=*" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
-          mac_smack_use, MNT_FATAL                  },
-#endif
-        { "tmpfs",       "/run",                      "tmpfs",      "mode=755" TMPFS_LIMITS_RUN,               MS_NOSUID|MS_NODEV|MS_STRICTATIME,
-          NULL,          MNT_FATAL|MNT_IN_CONTAINER },
-        { "cgroup2",     "/sys/fs/cgroup",            "cgroup2",    "nsdelegate,memory_recursiveprot",         MS_NOSUID|MS_NOEXEC|MS_NODEV,
-          cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
-        { "cgroup2",     "/sys/fs/cgroup",            "cgroup2",    "nsdelegate",                              MS_NOSUID|MS_NOEXEC|MS_NODEV,
-          cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
-        { "cgroup2",     "/sys/fs/cgroup",            "cgroup2",    NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
-          cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
-        { "tmpfs",       "/sys/fs/cgroup",            "tmpfs",      "mode=755" TMPFS_LIMITS_SYS_FS_CGROUP,     MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
-          cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
-        { "cgroup2",     "/sys/fs/cgroup/unified",    "cgroup2",    "nsdelegate",                              MS_NOSUID|MS_NOEXEC|MS_NODEV,
-          cg_is_hybrid_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
-        { "cgroup2",     "/sys/fs/cgroup/unified",    "cgroup2",    NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
-          cg_is_hybrid_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
-        { "cgroup",      "/sys/fs/cgroup/systemd",    "cgroup",     "none,name=systemd,xattr",                 MS_NOSUID|MS_NOEXEC|MS_NODEV,
-          cg_is_legacy_wanted, MNT_IN_CONTAINER     },
-        { "cgroup",      "/sys/fs/cgroup/systemd",    "cgroup",     "none,name=systemd",                       MS_NOSUID|MS_NOEXEC|MS_NODEV,
-          cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
-        { "pstore",      "/sys/fs/pstore",            "pstore",     NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
-          NULL,          MNT_NONE                   },
-#if ENABLE_EFI
-        { "efivarfs",    "/sys/firmware/efi/efivars", "efivarfs",   NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
-          is_efi_boot,   MNT_NONE                   },
-#endif
-        { "bpf",         "/sys/fs/bpf",               "bpf",        "mode=700",                                MS_NOSUID|MS_NOEXEC|MS_NODEV,
-          NULL,          MNT_NONE,                  },
-};
-
-bool mount_point_is_api(const char *path) {
-        unsigned i;
-
-        /* Checks if this mount point is considered "API", and hence
-         * should be ignored */
-
-        for (i = 0; i < ELEMENTSOF(mount_table); i ++)
-                if (path_equal(path, mount_table[i].where))
-                        return true;
-
-        return path_startswith(path, "/sys/fs/cgroup/");
-}
-
-bool mount_point_ignore(const char *path) {
-
-        const char *i;
-
-        /* These are API file systems that might be mounted by other software, we just list them here so that
-         * we know that we should ignore them. */
-        FOREACH_STRING(i,
-                       /* SELinux file systems */
-                       "/sys/fs/selinux",
-                       /* Container bind mounts */
-                       "/dev/console",
-                       "/proc/kmsg",
-                       "/proc/sys",
-                       "/proc/sys/kernel/random/boot_id")
-                if (path_equal(path, i))
-                        return true;
-
-        if (path_startswith(path, "/run/host")) /* All mounts passed in from the container manager are
-                                                 * something we better ignore. */
-                return true;
-
-        return false;
-}
-
-static int mount_one(const MountPoint *p, bool relabel) {
-        int r, priority;
-
-        assert(p);
-
-        priority = (p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG;
-
-        if (p->condition_fn && !p->condition_fn())
-                return 0;
-
-        /* Relabel first, just in case */
-        if (relabel)
-                (void) label_fix(p->where, LABEL_IGNORE_ENOENT|LABEL_IGNORE_EROFS);
-
-        r = path_is_mount_point(p->where, NULL, AT_SYMLINK_FOLLOW);
-        if (r < 0 && r != -ENOENT) {
-                log_full_errno(priority, r, "Failed to determine whether %s is a mount point: %m", p->where);
-                return (p->mode & MNT_FATAL) ? r : 0;
-        }
-        if (r > 0)
-                return 0;
-
-        /* Skip securityfs in a container */
-        if (!(p->mode & MNT_IN_CONTAINER) && detect_container() > 0)
-                return 0;
-
-        /* The access mode here doesn't really matter too much, since
-         * the mounted file system will take precedence anyway. */
-        if (relabel)
-                (void) mkdir_p_label(p->where, 0755);
-        else
-                (void) mkdir_p(p->where, 0755);
-
-        log_debug("Mounting %s to %s of type %s with options %s.",
-                  p->what,
-                  p->where,
-                  p->type,
-                  strna(p->options));
-
-        if (FLAGS_SET(p->mode, MNT_FOLLOW_SYMLINK))
-                r = mount(p->what, p->where, p->type, p->flags, p->options) < 0 ? -errno : 0;
-        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);
-                return (p->mode & MNT_FATAL) ? r : 0;
-        }
-
-        /* Relabel again, since we now mounted something fresh here */
-        if (relabel)
-                (void) label_fix(p->where, 0);
-
-        if (p->mode & MNT_CHECK_WRITABLE) {
-                if (access(p->where, W_OK) < 0) {
-                        r = -errno;
-
-                        (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);
-                        return (p->mode & MNT_FATAL) ? r : 0;
-                }
-        }
-
-        return 1;
-}
-
-static int mount_points_setup(unsigned n, bool loaded_policy) {
-        unsigned i;
-        int r = 0;
-
-        for (i = 0; i < n; i ++) {
-                int j;
-
-                j = mount_one(mount_table + i, loaded_policy);
-                if (j != 0 && r >= 0)
-                        r = j;
-        }
-
-        return r;
-}
-
-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);
-}
-
-static const char *join_with(const char *controller) {
-
-        static const char* const pairs[] = {
-                "cpu", "cpuacct",
-                "net_cls", "net_prio",
-                NULL
-        };
-
-        const char *const *x, *const *y;
-
-        assert(controller);
-
-        /* This will lookup which controller to mount another controller with. Input is a controller name, and output
-         * is the other controller name. The function works both ways: you can input one and get the other, and input
-         * the other to get the one. */
-
-        STRV_FOREACH_PAIR(x, y, pairs) {
-                if (streq(controller, *x))
-                        return *y;
-                if (streq(controller, *y))
-                        return *x;
-        }
-
-        return NULL;
-}
-
-static int symlink_controller(const char *target, const char *alias) {
-        const char *a;
-        int r;
-
-        assert(target);
-        assert(alias);
-
-        a = strjoina("/sys/fs/cgroup/", alias);
-
-        r = symlink_idempotent(target, a, false);
-        if (r < 0)
-                return log_error_errno(r, "Failed to create symlink %s: %m", a);
-
-#ifdef SMACK_RUN_LABEL
-        const char *p;
-
-        p = strjoina("/sys/fs/cgroup/", target);
-
-        r = mac_smack_copy(a, p);
-        if (r < 0 && r != -EOPNOTSUPP)
-                return log_error_errno(r, "Failed to copy smack label from %s to %s: %m", p, a);
-#endif
-
-        return 0;
-}
-
-int mount_cgroup_controllers(void) {
-        _cleanup_set_free_free_ Set *controllers = NULL;
-        int r;
-
-        if (!cg_is_legacy_wanted())
-                return 0;
-
-        /* Mount all available cgroup controllers that are built into the kernel. */
-        r = cg_kernel_controllers(&controllers);
-        if (r < 0)
-                return log_error_errno(r, "Failed to enumerate cgroup controllers: %m");
-
-        for (;;) {
-                _cleanup_free_ char *options = NULL, *controller = NULL, *where = NULL;
-                const char *other_controller;
-                MountPoint p = {
-                        .what = "cgroup",
-                        .type = "cgroup",
-                        .flags = MS_NOSUID|MS_NOEXEC|MS_NODEV,
-                        .mode = MNT_IN_CONTAINER,
-                };
-
-                controller = set_steal_first(controllers);
-                if (!controller)
-                        break;
-
-                /* Check if we shall mount this together with another controller */
-                other_controller = join_with(controller);
-                if (other_controller) {
-                        _cleanup_free_ char *c = NULL;
-
-                        /* Check if the other controller is actually available in the kernel too */
-                        c = set_remove(controllers, other_controller);
-                        if (c) {
-
-                                /* Join the two controllers into one string, and maintain a stable ordering */
-                                if (strcmp(controller, other_controller) < 0)
-                                        options = strjoin(controller, ",", other_controller);
-                                else
-                                        options = strjoin(other_controller, ",", controller);
-                                if (!options)
-                                        return log_oom();
-                        }
-                }
-
-                /* The simple case, where there's only one controller to mount together */
-                if (!options)
-                        options = TAKE_PTR(controller);
-
-                where = path_join("/sys/fs/cgroup", options);
-                if (!where)
-                        return log_oom();
-
-                p.where = where;
-                p.options = options;
-
-                r = mount_one(&p, true);
-                if (r < 0)
-                        return r;
-
-                /* Create symlinks from the individual controller names, in case we have a joined mount */
-                if (controller)
-                        (void) symlink_controller(options, controller);
-                if (other_controller)
-                        (void) symlink_controller(options, other_controller);
-        }
-
-        /* Now that we mounted everything, let's make the tmpfs the cgroup file systems are mounted into read-only. */
-        (void) mount_nofollow("tmpfs", "/sys/fs/cgroup", "tmpfs", MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755" TMPFS_LIMITS_SYS_FS_CGROUP);
-
-        return 0;
-}
-
-#if HAVE_SELINUX || ENABLE_SMACK
-static int nftw_cb(
-                const char *fpath,
-                const struct stat *sb,
-                int tflag,
-                struct FTW *ftwbuf) {
-
-        /* No need to label /dev twice in a row... */
-        if (_unlikely_(ftwbuf->level == 0))
-                return FTW_CONTINUE;
-
-        (void) label_fix(fpath, 0);
-
-        /* /run/initramfs is static data and big, no need to
-         * dynamically relabel its contents at boot... */
-        if (_unlikely_(ftwbuf->level == 1 &&
-                      tflag == FTW_D &&
-                      streq(fpath, "/run/initramfs")))
-                return FTW_SKIP_SUBTREE;
-
-        return FTW_CONTINUE;
-};
-
-static int relabel_cgroup_filesystems(void) {
-        int r;
-        struct statfs st;
-
-        r = cg_all_unified();
-        if (r == 0) {
-                /* Temporarily remount the root cgroup filesystem to give it a proper label. Do this
-                   only when the filesystem has been already populated by a previous instance of systemd
-                   running from initrd. Otherwise don't remount anything and leave the filesystem read-write
-                   for the cgroup filesystems to be mounted inside. */
-                if (statfs("/sys/fs/cgroup", &st) < 0)
-                        return log_error_errno(errno, "Failed to determine mount flags for /sys/fs/cgroup: %m");
-
-                if (st.f_flags & ST_RDONLY)
-                        (void) mount_nofollow(NULL, "/sys/fs/cgroup", NULL, MS_REMOUNT, NULL);
-
-                (void) label_fix("/sys/fs/cgroup", 0);
-                (void) nftw("/sys/fs/cgroup", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
-
-                if (st.f_flags & ST_RDONLY)
-                        (void) mount_nofollow(NULL, "/sys/fs/cgroup", NULL, MS_REMOUNT|MS_RDONLY, NULL);
-
-        } else if (r < 0)
-                return log_error_errno(r, "Failed to determine whether we are in all unified mode: %m");
-
-        return 0;
-}
-
-static int relabel_extra(void) {
-        _cleanup_strv_free_ char **files = NULL;
-        char **file;
-        int r, c = 0;
-
-        /* Support for relabelling additional files or directories after loading the policy. For this, code in the
-         * initrd simply has to drop in *.relabel files into /run/systemd/relabel-extra.d/. We'll read all such files
-         * expecting one absolute path by line and will relabel each (and everyone below that in case the path refers
-         * to a directory). These drop-in files are supposed to be absolutely minimal, and do not understand comments
-         * and such. After the operation succeeded the files are removed, and the drop-in directory as well, if
-         * possible.
-         */
-
-        r = conf_files_list(&files, ".relabel", NULL,
-                            CONF_FILES_FILTER_MASKED | CONF_FILES_REGULAR,
-                            "/run/systemd/relabel-extra.d/");
-        if (r < 0)
-                return log_error_errno(r, "Failed to enumerate /run/systemd/relabel-extra.d/, ignoring: %m");
-
-        STRV_FOREACH(file, files) {
-                _cleanup_fclose_ FILE *f = NULL;
-
-                f = fopen(*file, "re");
-                if (!f) {
-                        log_warning_errno(errno, "Failed to open %s, ignoring: %m", *file);
-                        continue;
-                }
-
-                for (;;) {
-                        _cleanup_free_ char *line = NULL;
-
-                        r = read_line(f, LONG_LINE_MAX, &line);
-                        if (r < 0) {
-                                log_warning_errno(r, "Failed to read %s, ignoring: %m", *file);
-                                break;
-                        }
-                        if (r == 0) /* EOF */
-                                break;
-
-                        path_simplify(line, true);
-
-                        if (!path_is_normalized(line)) {
-                                log_warning("Path to relabel is not normalized, ignoring: %s", line);
-                                continue;
-                        }
-
-                        if (!path_is_absolute(line)) {
-                                log_warning("Path to relabel is not absolute, ignoring: %s", line);
-                                continue;
-                        }
-
-                        log_debug("Relabelling additional file/directory '%s'.", line);
-                        (void) label_fix(line, 0);
-                        (void) nftw(line, nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
-                        c++;
-                }
-
-                if (unlink(*file) < 0)
-                        log_warning_errno(errno, "Failed to remove %s, ignoring: %m", *file);
-        }
-
-        /* Remove when we complete things. */
-        if (rmdir("/run/systemd/relabel-extra.d") < 0 &&
-            errno != ENOENT)
-                log_warning_errno(errno, "Failed to remove /run/systemd/relabel-extra.d/ directory: %m");
-
-        return c;
-}
-#endif
-
-int mount_setup(bool loaded_policy, bool leave_propagation) {
-        int r;
-
-        r = mount_points_setup(ELEMENTSOF(mount_table), loaded_policy);
-        if (r < 0)
-                return r;
-
-#if HAVE_SELINUX || ENABLE_SMACK
-        /* Nodes in devtmpfs and /run need to be manually updated for
-         * the appropriate labels, after mounting. The other virtual
-         * API file systems like /sys and /proc do not need that, they
-         * use the same label for all their files. */
-        if (loaded_policy) {
-                usec_t before_relabel, after_relabel;
-                char timespan[FORMAT_TIMESPAN_MAX];
-                const char *i;
-                int n_extra;
-
-                before_relabel = now(CLOCK_MONOTONIC);
-
-                FOREACH_STRING(i, "/dev", "/dev/shm", "/run")
-                        (void) nftw(i, nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
-
-                (void) relabel_cgroup_filesystems();
-
-                n_extra = relabel_extra();
-
-                after_relabel = now(CLOCK_MONOTONIC);
-
-                log_info("Relabelled /dev, /dev/shm, /run, /sys/fs/cgroup%s in %s.",
-                         n_extra > 0 ? ", additional files" : "",
-                         format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel, 0));
-        }
-#endif
-
-        /* Create a few default symlinks, which are normally created
-         * by udevd, but some scripts might need them before we start
-         * udevd. */
-        dev_setup(NULL, UID_INVALID, GID_INVALID);
-
-        /* Mark the root directory as shared in regards to mount propagation. The kernel defaults to "private", but we
-         * think it makes more sense to have a default of "shared" so that nspawn and the container tools work out of
-         * the box. If specific setups need other settings they can reset the propagation mode to private if
-         * needed. Note that we set this only when we are invoked directly by the kernel. If we are invoked by a
-         * container manager we assume the container manager knows what it is doing (for example, because it set up
-         * some directories with different propagation modes). */
-        if (detect_container() <= 0 && !leave_propagation)
-                if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0)
-                        log_warning_errno(errno, "Failed to set up the root directory for shared mount propagation: %m");
-
-        /* Create a few directories we always want around, Note that sd_booted() checks for /run/systemd/system, so
-         * this mkdir really needs to stay for good, otherwise software that copied sd-daemon.c into their sources will
-         * misdetect systemd. */
-        (void) mkdir_label("/run/systemd", 0755);
-        (void) mkdir_label("/run/systemd/system", 0755);
-
-        /* Make sure we have a mount point to hide in sandboxes */
-        (void) mkdir_label("/run/credentials", 0755);
-
-        /* Also create /run/systemd/inaccessible nodes, so that we always have something to mount
-         * inaccessible nodes from. If we run in a container the host might have created these for us already
-         * in /run/host/inaccessible/. Use those if we can, since that way we likely get access to block/char
-         * device nodes that are inaccessible, and if userns is used to nodes that are on mounts owned by a
-         * userns outside the container and thus nicely read-only and not remountable. */
-        if (access("/run/host/inaccessible/", F_OK) < 0) {
-                if (errno != ENOENT)
-                        log_debug_errno(errno, "Failed to check if /run/host/inaccessible exists, ignoring: %m");
-
-                (void) make_inaccessible_nodes("/run/systemd", UID_INVALID, GID_INVALID);
-        } else
-                (void) symlink("../host/inaccessible", "/run/systemd/inaccessible");
-
-        return 0;
-}
diff --git a/src/core/mount-setup.h b/src/core/mount-setup.h
deleted file mode 100644 (file)
index 29bd62f..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include <stdbool.h>
-
-int mount_setup_early(void);
-int mount_setup(bool loaded_policy, bool leave_propagation);
-
-int mount_cgroup_controllers(void);
-
-bool mount_point_is_api(const char *path);
-bool mount_point_ignore(const char *path);
diff --git a/src/shared/killall.c b/src/shared/killall.c
new file mode 100644 (file)
index 0000000..d9fcfc2
--- /dev/null
@@ -0,0 +1,283 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/***
+  Copyright © 2010 ProFUSION embedded systems
+***/
+
+#include <errno.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "def.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "format-util.h"
+#include "killall.h"
+#include "parse-util.h"
+#include "process-util.h"
+#include "set.h"
+#include "string-util.h"
+#include "terminal-util.h"
+#include "util.h"
+
+static bool ignore_proc(pid_t pid, bool warn_rootfs) {
+        _cleanup_fclose_ FILE *f = NULL;
+        const char *p;
+        char c = 0;
+        uid_t uid;
+        int r;
+
+        /* We are PID 1, let's not commit suicide */
+        if (pid <= 1)
+                return true;
+
+        /* Ignore kernel threads */
+        r = is_kernel_thread(pid);
+        if (r != 0)
+                return true; /* also ignore processes where we can't determine this */
+
+        r = get_process_uid(pid, &uid);
+        if (r < 0)
+                return true; /* not really, but better safe than sorry */
+
+        /* Non-root processes otherwise are always subject to be killed */
+        if (uid != 0)
+                return false;
+
+        p = procfs_file_alloca(pid, "cmdline");
+        f = fopen(p, "re");
+        if (!f)
+                return true; /* not really, but has the desired effect */
+
+        /* Try to read the first character of the command line. If the cmdline is empty (which might be the case for
+         * kernel threads but potentially also other stuff), this line won't do anything, but we don't care much, as
+         * actual kernel threads are already filtered out above. */
+        (void) fread(&c, 1, 1, f);
+
+        /* Processes with argv[0][0] = '@' we ignore from the killing spree.
+         *
+         * https://systemd.io/ROOT_STORAGE_DAEMONS */
+        if (c != '@')
+                return false;
+
+        if (warn_rootfs &&
+            pid_from_same_root_fs(pid) == 0) {
+
+                _cleanup_free_ char *comm = NULL;
+
+                (void) get_process_comm(pid, &comm);
+
+                log_notice("Process " PID_FMT " (%s) has been marked to be excluded from killing. It is "
+                           "running from the root file system, and thus likely to block re-mounting of the "
+                           "root file system to read-only. Please consider moving it into an initrd file "
+                           "system instead.", pid, strna(comm));
+        }
+
+        return true;
+}
+
+static void log_children_no_yet_killed(Set *pids) {
+        _cleanup_free_ char *lst_child = NULL;
+        void *p;
+
+        SET_FOREACH(p, pids) {
+                _cleanup_free_ char *s = NULL;
+
+                if (get_process_comm(PTR_TO_PID(p), &s) < 0)
+                        (void) asprintf(&s, PID_FMT, PTR_TO_PID(p));
+
+                if (!strextend(&lst_child, ", ", s)) {
+                        log_oom();
+                        return;
+                }
+        }
+
+        if (isempty(lst_child))
+                return;
+
+        log_warning("Waiting for process: %s", lst_child + 2);
+}
+
+static int wait_for_children(Set *pids, sigset_t *mask, usec_t timeout) {
+        usec_t until, date_log_child, n;
+
+        assert(mask);
+
+        /* Return the number of children remaining in the pids set: That correspond to the number
+         * of processes still "alive" after the timeout */
+
+        if (set_isempty(pids))
+                return 0;
+
+        n = now(CLOCK_MONOTONIC);
+        until = usec_add(n, timeout);
+        date_log_child = usec_add(n, 10u * USEC_PER_SEC);
+        if (date_log_child > until)
+                date_log_child = usec_add(n, timeout / 2u);
+
+        for (;;) {
+                struct timespec ts;
+                int k;
+                void *p;
+
+                /* First, let the kernel inform us about killed
+                 * children. Most processes will probably be our
+                 * children, but some are not (might be our
+                 * grandchildren instead...). */
+                for (;;) {
+                        pid_t pid;
+
+                        pid = waitpid(-1, NULL, WNOHANG);
+                        if (pid == 0)
+                                break;
+                        if (pid < 0) {
+                                if (errno == ECHILD)
+                                        break;
+
+                                return log_error_errno(errno, "waitpid() failed: %m");
+                        }
+
+                        (void) set_remove(pids, PID_TO_PTR(pid));
+                }
+
+                /* Now explicitly check who might be remaining, who
+                 * might not be our child. */
+                SET_FOREACH(p, pids) {
+
+                        /* kill(pid, 0) sends no signal, but it tells
+                         * us whether the process still exists. */
+                        if (kill(PTR_TO_PID(p), 0) == 0)
+                                continue;
+
+                        if (errno != ESRCH)
+                                continue;
+
+                        set_remove(pids, p);
+                }
+
+                if (set_isempty(pids))
+                        return 0;
+
+                n = now(CLOCK_MONOTONIC);
+                if (date_log_child > 0 && n >= date_log_child) {
+                        log_children_no_yet_killed(pids);
+                        /* Log the children not yet killed only once */
+                        date_log_child = 0;
+                }
+
+                if (n >= until)
+                        return set_size(pids);
+
+                if (date_log_child > 0)
+                        timespec_store(&ts, MIN(until - n, date_log_child - n));
+                else
+                        timespec_store(&ts, until - n);
+
+                k = sigtimedwait(mask, NULL, &ts);
+                if (k != SIGCHLD) {
+
+                        if (k < 0 && errno != EAGAIN)
+                                return log_error_errno(errno, "sigtimedwait() failed: %m");
+
+                        if (k >= 0)
+                                log_warning("sigtimedwait() returned unexpected signal.");
+                }
+        }
+}
+
+static int killall(int sig, Set *pids, bool send_sighup) {
+        _cleanup_closedir_ DIR *dir = NULL;
+        struct dirent *d;
+        int n_killed = 0;
+
+        /* Send the specified signal to all remaining processes, if not excluded by ignore_proc().
+         * Returns the number of processes to which the specified signal was sent */
+
+        dir = opendir("/proc");
+        if (!dir)
+                return log_warning_errno(errno, "opendir(/proc) failed: %m");
+
+        FOREACH_DIRENT_ALL(d, dir, break) {
+                pid_t pid;
+                int r;
+
+                if (!IN_SET(d->d_type, DT_DIR, DT_UNKNOWN))
+                        continue;
+
+                if (parse_pid(d->d_name, &pid) < 0)
+                        continue;
+
+                if (ignore_proc(pid, sig == SIGKILL && !in_initrd()))
+                        continue;
+
+                if (sig == SIGKILL) {
+                        _cleanup_free_ char *s = NULL;
+
+                        (void) get_process_comm(pid, &s);
+                        log_notice("Sending SIGKILL to PID "PID_FMT" (%s).", pid, strna(s));
+                }
+
+                if (kill(pid, sig) >= 0) {
+                        n_killed++;
+                        if (pids) {
+                                r = set_put(pids, PID_TO_PTR(pid));
+                                if (r < 0)
+                                        log_oom();
+                        }
+                } else if (errno != ENOENT)
+                        log_warning_errno(errno, "Could not kill %d: %m", pid);
+
+                if (send_sighup) {
+                        /* Optionally, also send a SIGHUP signal, but
+                        only if the process has a controlling
+                        tty. This is useful to allow handling of
+                        shells which ignore SIGTERM but react to
+                        SIGHUP. We do not send this to processes that
+                        have no controlling TTY since we don't want to
+                        trigger reloads of daemon processes. Also we
+                        make sure to only send this after SIGTERM so
+                        that SIGTERM is always first in the queue. */
+
+                        if (get_ctty_devnr(pid, NULL) >= 0)
+                                /* it's OK if the process is gone, just ignore the result */
+                                (void) kill(pid, SIGHUP);
+                }
+        }
+
+        return n_killed;
+}
+
+int broadcast_signal(int sig, bool wait_for_exit, bool send_sighup, usec_t timeout) {
+        int n_children_left;
+        sigset_t mask, oldmask;
+        _cleanup_set_free_ Set *pids = NULL;
+
+        /* Send the specified signal to all remaining processes, if not excluded by ignore_proc().
+         * Return:
+         *  - The number of processes still "alive" after the timeout (that should have been killed)
+         *    if the function needs to wait for the end of the processes (wait_for_exit).
+         *  - Otherwise, the number of processes to which the specified signal was sent */
+
+        if (wait_for_exit)
+                pids = set_new(NULL);
+
+        assert_se(sigemptyset(&mask) == 0);
+        assert_se(sigaddset(&mask, SIGCHLD) == 0);
+        assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
+
+        if (kill(-1, SIGSTOP) < 0 && errno != ESRCH)
+                log_warning_errno(errno, "kill(-1, SIGSTOP) failed: %m");
+
+        n_children_left = killall(sig, pids, send_sighup);
+
+        if (kill(-1, SIGCONT) < 0 && errno != ESRCH)
+                log_warning_errno(errno, "kill(-1, SIGCONT) failed: %m");
+
+        if (wait_for_exit && n_children_left > 0)
+                n_children_left = wait_for_children(pids, &mask, timeout);
+
+        assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
+
+        return n_children_left;
+}
diff --git a/src/shared/killall.h b/src/shared/killall.h
new file mode 100644 (file)
index 0000000..d8ef96f
--- /dev/null
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "time-util.h"
+
+int broadcast_signal(int sig, bool wait_for_exit, bool send_sighup, usec_t timeout);
diff --git a/src/shared/loopback-setup.c b/src/shared/loopback-setup.c
new file mode 100644 (file)
index 0000000..76022ce
--- /dev/null
@@ -0,0 +1,213 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <net/if.h>
+#include <stdlib.h>
+
+#include "sd-netlink.h"
+
+#include "loopback-setup.h"
+#include "missing_network.h"
+#include "netlink-util.h"
+#include "time-util.h"
+
+#define LOOPBACK_SETUP_TIMEOUT_USEC (5 * USEC_PER_SEC)
+
+struct state {
+        unsigned n_messages;
+        int rcode;
+        const char *error_message;
+        const char *success_message;
+};
+
+static int generic_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+        struct state *s = userdata;
+        int r;
+
+        assert(s);
+        assert(s->n_messages > 0);
+        s->n_messages--;
+
+        errno = 0;
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0)
+                log_debug_errno(r, "%s: %m", s->error_message);
+        else
+                log_debug("%s", s->success_message);
+
+        s->rcode = r;
+        return 0;
+}
+
+static int start_loopback(sd_netlink *rtnl, struct state *s) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+        int r;
+
+        assert(rtnl);
+        assert(s);
+
+        r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, LOOPBACK_IFINDEX);
+        if (r < 0)
+                return r;
+
+        r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_call_async(rtnl, NULL, req, generic_handler, NULL, s, LOOPBACK_SETUP_TIMEOUT_USEC, "systemd-start-loopback");
+        if (r < 0)
+                return r;
+
+        s->n_messages ++;
+        return 0;
+}
+
+static int add_ipv4_address(sd_netlink *rtnl, struct state *s) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+        int r;
+
+        assert(rtnl);
+        assert(s);
+
+        r = sd_rtnl_message_new_addr(rtnl, &req, RTM_NEWADDR, LOOPBACK_IFINDEX, AF_INET);
+        if (r < 0)
+                return r;
+
+        r = sd_rtnl_message_addr_set_prefixlen(req, 8);
+        if (r < 0)
+                return r;
+
+        r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT);
+        if (r < 0)
+                return r;
+
+        r = sd_rtnl_message_addr_set_scope(req, RT_SCOPE_HOST);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &(struct in_addr) { .s_addr = htobe32(INADDR_LOOPBACK) } );
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_call_async(rtnl, NULL, req, generic_handler, NULL, s, USEC_INFINITY, "systemd-loopback-ipv4");
+        if (r < 0)
+                return r;
+
+        s->n_messages ++;
+        return 0;
+}
+
+static int add_ipv6_address(sd_netlink *rtnl, struct state *s) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+        int r;
+
+        assert(rtnl);
+        assert(s);
+
+        r = sd_rtnl_message_new_addr(rtnl, &req, RTM_NEWADDR, LOOPBACK_IFINDEX, AF_INET6);
+        if (r < 0)
+                return r;
+
+        r = sd_rtnl_message_addr_set_prefixlen(req, 128);
+        if (r < 0)
+                return r;
+
+        r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT);
+        if (r < 0)
+                return r;
+
+        r = sd_rtnl_message_addr_set_scope(req, RT_SCOPE_HOST);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_append_in6_addr(req, IFA_LOCAL, &in6addr_loopback);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_call_async(rtnl, NULL, req, generic_handler, NULL, s, USEC_INFINITY, "systemd-loopback-ipv6");
+        if (r < 0)
+                return r;
+
+        s->n_messages ++;
+        return 0;
+}
+
+static bool check_loopback(sd_netlink *rtnl) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+        unsigned flags;
+        int r;
+
+        r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, LOOPBACK_IFINDEX);
+        if (r < 0)
+                return false;
+
+        r = sd_netlink_call(rtnl, req, USEC_INFINITY, &reply);
+        if (r < 0)
+                return false;
+
+        r = sd_rtnl_message_link_get_flags(reply, &flags);
+        if (r < 0)
+                return false;
+
+        return flags & IFF_UP;
+}
+
+int loopback_setup(void) {
+        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+        struct state state_4 = {
+                .error_message = "Failed to add address 127.0.0.1 to loopback interface",
+                .success_message = "Successfully added address 127.0.0.1 to loopback interface",
+        }, state_6 = {
+                .error_message = "Failed to add address ::1 to loopback interface",
+                .success_message = "Successfully added address ::1 to loopback interface",
+        }, state_up = {
+                .error_message = "Failed to bring loopback interface up",
+                .success_message = "Successfully brought loopback interface up",
+        };
+        int r;
+
+        r = sd_netlink_open(&rtnl);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open netlink: %m");
+
+        /* Note that we add the IP addresses here explicitly even though the kernel does that too implicitly when
+         * setting up the loopback device. The reason we do this here a second time (and possibly race against the
+         * kernel) is that we want to synchronously wait until the IP addresses are set up correctly, see
+         *
+         * https://github.com/systemd/systemd/issues/5641 */
+
+        r = add_ipv4_address(rtnl, &state_4);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enqueue IPv4 loopback address add request: %m");
+
+        r = add_ipv6_address(rtnl, &state_6);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enqueue IPv6 loopback address add request: %m");
+
+        r = start_loopback(rtnl, &state_up);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enqueue loopback interface start request: %m");
+
+        while (state_4.n_messages + state_6.n_messages + state_up.n_messages > 0) {
+                r = sd_netlink_wait(rtnl, LOOPBACK_SETUP_TIMEOUT_USEC);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to wait for netlink event: %m");
+
+                r = sd_netlink_process(rtnl, NULL);
+                if (r < 0)
+                        return log_warning_errno(r, "Failed to process netlink event: %m");
+        }
+
+        /* Note that we don't really care whether the addresses could be added or not */
+        if (state_up.rcode != 0) {
+                /* If we lack the permissions to configure the loopback device,
+                 * but we find it to be already configured, let's exit cleanly,
+                 * in order to supported unprivileged containers. */
+                if (state_up.rcode == -EPERM && check_loopback(rtnl))
+                        return 0;
+
+                return log_warning_errno(state_up.rcode, "Failed to configure loopback device: %m");
+        }
+
+        return 0;
+}
diff --git a/src/shared/loopback-setup.h b/src/shared/loopback-setup.h
new file mode 100644 (file)
index 0000000..a7ee2da
--- /dev/null
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int loopback_setup(void);
diff --git a/src/shared/machine-id-setup.c b/src/shared/machine-id-setup.c
new file mode 100644 (file)
index 0000000..6d15f9c
--- /dev/null
@@ -0,0 +1,282 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fcntl.h>
+#include <sched.h>
+#include <sys/mount.h>
+#include <unistd.h>
+
+#include "sd-id128.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "id128-util.h"
+#include "io-util.h"
+#include "log.h"
+#include "machine-id-setup.h"
+#include "macro.h"
+#include "mkdir.h"
+#include "mount-util.h"
+#include "mountpoint-util.h"
+#include "namespace-util.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "umask-util.h"
+#include "util.h"
+#include "virt.h"
+
+static int generate_machine_id(const char *root, sd_id128_t *ret) {
+        const char *dbus_machine_id;
+        _cleanup_close_ int fd = -1;
+        int r;
+
+        assert(ret);
+
+        /* First, try reading the D-Bus machine id, unless it is a symlink */
+        dbus_machine_id = prefix_roota(root, "/var/lib/dbus/machine-id");
+        fd = open(dbus_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+        if (fd >= 0) {
+                if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0) {
+                        log_info("Initializing machine ID from D-Bus machine ID.");
+                        return 0;
+                }
+
+                fd = safe_close(fd);
+        }
+
+        if (isempty(root) && running_in_chroot() <= 0) {
+                /* If that didn't work, see if we are running in a container,
+                 * and a machine ID was passed in via $container_uuid the way
+                 * libvirt/LXC does it */
+
+                if (detect_container() > 0) {
+                        _cleanup_free_ char *e = NULL;
+
+                        if (getenv_for_pid(1, "container_uuid", &e) > 0 &&
+                            sd_id128_from_string(e, ret) >= 0) {
+                                log_info("Initializing machine ID from container UUID.");
+                                return 0;
+                        }
+
+                } else if (detect_vm() == VIRTUALIZATION_KVM) {
+
+                        /* If we are not running in a container, see if we are
+                         * running in qemu/kvm and a machine ID was passed in
+                         * via -uuid on the qemu/kvm command line */
+
+                        if (id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, ret) >= 0) {
+                                log_info("Initializing machine ID from KVM UUID.");
+                                return 0;
+                        }
+                        /* on POWER, it's exported here instead */
+                        if (id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, ret) >= 0) {
+                                log_info("Initializing machine ID from KVM UUID.");
+                                return 0;
+                        }
+                }
+        }
+
+        /* If that didn't work, generate a random machine id */
+        r = sd_id128_randomize(ret);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate randomized machine ID: %m");
+
+        log_info("Initializing machine ID from random generator.");
+        return 0;
+}
+
+int machine_id_setup(const char *root, bool force_transient, sd_id128_t machine_id, sd_id128_t *ret) {
+        const char *etc_machine_id, *run_machine_id;
+        _cleanup_close_ int fd = -1;
+        bool writable;
+        int r;
+
+        etc_machine_id = prefix_roota(root, "/etc/machine-id");
+
+        RUN_WITH_UMASK(0000) {
+                /* We create this 0444, to indicate that this isn't really
+                 * something you should ever modify. Of course, since the file
+                 * will be owned by root it doesn't matter much, but maybe
+                 * people look. */
+
+                (void) mkdir_parents(etc_machine_id, 0755);
+                fd = open(etc_machine_id, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
+                if (fd < 0) {
+                        int old_errno = errno;
+
+                        fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                        if (fd < 0) {
+                                if (old_errno == EROFS && errno == ENOENT)
+                                        return log_error_errno(errno,
+                                                  "System cannot boot: Missing /etc/machine-id and /etc is mounted read-only.\n"
+                                                  "Booting up is supported only when:\n"
+                                                  "1) /etc/machine-id exists and is populated.\n"
+                                                  "2) /etc/machine-id exists and is empty.\n"
+                                                  "3) /etc/machine-id is missing and /etc is writable.\n");
+                                else
+                                        return log_error_errno(errno, "Cannot open %s: %m", etc_machine_id);
+                        }
+
+                        writable = false;
+                } else
+                        writable = true;
+        }
+
+        /* A we got a valid machine ID argument, that's what counts */
+        if (sd_id128_is_null(machine_id)) {
+
+                /* Try to read any existing machine ID */
+                if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0)
+                        return 0;
+
+                /* Hmm, so, the id currently stored is not useful, then let's generate one */
+                r = generate_machine_id(root, &machine_id);
+                if (r < 0)
+                        return r;
+        }
+
+        if (writable) {
+                if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
+                        return log_error_errno(errno, "Failed to seek %s: %m", etc_machine_id);
+
+                if (ftruncate(fd, 0) < 0)
+                        return log_error_errno(errno, "Failed to truncate %s: %m", etc_machine_id);
+
+                /* If the caller requested a transient machine-id, write the string "uninitialized\n" to
+                 * disk and overmount it with a transient file.
+                 *
+                 * Otherwise write the machine-id directly to disk. */
+                if (force_transient) {
+                        r = loop_write(fd, "uninitialized\n", strlen("uninitialized\n"), false);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to write uninitialized %s: %m", etc_machine_id);
+
+                        r = fsync_full(fd);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to sync %s: %m", etc_machine_id);
+                } else {
+                        r = id128_write_fd(fd, ID128_PLAIN, machine_id, true);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to write %s: %m", etc_machine_id);
+                        else
+                                goto finish;
+                }
+        }
+
+        fd = safe_close(fd);
+
+        /* Hmm, we couldn't or shouldn't write the machine-id to /etc?
+         * So let's write it to /run/machine-id as a replacement */
+
+        run_machine_id = prefix_roota(root, "/run/machine-id");
+
+        RUN_WITH_UMASK(0022)
+                r = id128_write(run_machine_id, ID128_PLAIN, machine_id, false);
+        if (r < 0) {
+                (void) unlink(run_machine_id);
+                return log_error_errno(r, "Cannot write %s: %m", run_machine_id);
+        }
+
+        /* And now, let's mount it over */
+        r = mount_follow_verbose(LOG_ERR, run_machine_id, etc_machine_id, NULL, MS_BIND, NULL);
+        if (r < 0) {
+                (void) unlink(run_machine_id);
+                return r;
+        }
+
+        log_full(force_transient ? LOG_DEBUG : LOG_INFO, "Installed transient %s file.", etc_machine_id);
+
+        /* Mark the mount read-only */
+        r = mount_follow_verbose(LOG_WARNING, NULL, etc_machine_id, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL);
+        if (r < 0)
+                return r;
+
+finish:
+        if (ret)
+                *ret = machine_id;
+
+        return 0;
+}
+
+int machine_id_commit(const char *root) {
+        _cleanup_close_ int fd = -1, initial_mntns_fd = -1;
+        const char *etc_machine_id, *sync_path;
+        sd_id128_t id;
+        int r;
+
+        /* Before doing anything, sync everything to ensure any changes by first-boot units are persisted.
+         *
+         * First, explicitly sync the file systems we care about and check if it worked. */
+        FOREACH_STRING(sync_path, "/etc/", "/var/") {
+                r = syncfs_path(AT_FDCWD, sync_path);
+                if (r < 0)
+                        return log_error_errno(r, "Cannot sync %s: %m", sync_path);
+        }
+
+        /* Afterwards, sync() the rest too, but we can't check the return value for these. */
+        sync();
+
+        /* Replaces a tmpfs bind mount of /etc/machine-id by a proper file, atomically. For this, the umount is removed
+         * in a mount namespace, a new file is created at the right place. Afterwards the mount is also removed in the
+         * original mount namespace, thus revealing the file that was just created. */
+
+        etc_machine_id = prefix_roota(root, "/etc/machine-id");
+
+        r = path_is_mount_point(etc_machine_id, NULL, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", etc_machine_id);
+        if (r == 0) {
+                log_debug("%s is not a mount point. Nothing to do.", etc_machine_id);
+                return 0;
+        }
+
+        /* Read existing machine-id */
+        fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+        if (fd < 0)
+                return log_error_errno(errno, "Cannot open %s: %m", etc_machine_id);
+
+        r = fd_is_temporary_fs(fd);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine whether %s is on a temporary file system: %m", etc_machine_id);
+        if (r == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EROFS),
+                                       "%s is not on a temporary file system.",
+                                       etc_machine_id);
+
+        r = id128_read_fd(fd, ID128_PLAIN, &id);
+        if (r < 0)
+                return log_error_errno(r, "We didn't find a valid machine ID in %s: %m", etc_machine_id);
+
+        fd = safe_close(fd);
+
+        /* Store current mount namespace */
+        r = namespace_open(0, NULL, &initial_mntns_fd, NULL, NULL, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Can't fetch current mount namespace: %m");
+
+        /* Switch to a new mount namespace, isolate ourself and unmount etc_machine_id in our new namespace */
+        r = detach_mount_namespace();
+        if (r < 0)
+                return log_error_errno(r, "Failed to set up new mount namespace: %m");
+
+        r = umount_verbose(LOG_ERR, etc_machine_id, 0);
+        if (r < 0)
+                return r;
+
+        /* Update a persistent version of etc_machine_id */
+        r = id128_write(etc_machine_id, ID128_PLAIN, id, true);
+        if (r < 0)
+                return log_error_errno(r, "Cannot write %s. This is mandatory to get a persistent machine ID: %m", etc_machine_id);
+
+        /* Return to initial namespace and proceed a lazy tmpfs unmount */
+        r = namespace_enter(-1, initial_mntns_fd, -1, -1, -1);
+        if (r < 0)
+                return log_warning_errno(r, "Failed to switch back to initial mount namespace: %m.\nWe'll keep transient %s file until next reboot.", etc_machine_id);
+
+        if (umount2(etc_machine_id, MNT_DETACH) < 0)
+                return log_warning_errno(errno, "Failed to unmount transient %s file: %m.\nWe keep that mount until next reboot.", etc_machine_id);
+
+        return 0;
+}
diff --git a/src/shared/machine-id-setup.h b/src/shared/machine-id-setup.h
new file mode 100644 (file)
index 0000000..cce5819
--- /dev/null
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdbool.h>
+
+int machine_id_commit(const char *root);
+int machine_id_setup(const char *root, bool force_transient, sd_id128_t requested, sd_id128_t *ret);
index 3032a9efd1a21280707fe4883de15646e431e42c..6db9065794a48168c47ec15720d6c3d57b6334b8 100644 (file)
@@ -145,6 +145,8 @@ shared_sources = files('''
         json-internal.h
         json.c
         json.h
+        killall.c
+        killall.h
         libcrypt-util.c
         libcrypt-util.h
         libfido2-util.c
@@ -165,6 +167,10 @@ shared_sources = files('''
         logs-show.h
         loop-util.c
         loop-util.h
+        loopback-setup.c
+        loopback-setup.h
+        machine-id-setup.c
+        machine-id-setup.h
         machine-image.c
         machine-image.h
         machine-pool.c
@@ -175,6 +181,8 @@ shared_sources = files('''
         mkfs-util.c
         mkfs-util.h
         module-util.h
+        mount-setup.c
+        mount-setup.h
         mount-util.c
         mount-util.h
         net-condition.c
diff --git a/src/shared/mount-setup.c b/src/shared/mount-setup.c
new file mode 100644 (file)
index 0000000..1e4bbfb
--- /dev/null
@@ -0,0 +1,561 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+#include <ftw.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/statvfs.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "bus-util.h"
+#include "cgroup-util.h"
+#include "conf-files.h"
+#include "cgroup-setup.h"
+#include "dev-setup.h"
+#include "dirent-util.h"
+#include "efi-loader.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "label.h"
+#include "log.h"
+#include "macro.h"
+#include "mkdir.h"
+#include "mount-setup.h"
+#include "mount-util.h"
+#include "mountpoint-util.h"
+#include "nulstr-util.h"
+#include "path-util.h"
+#include "set.h"
+#include "smack-util.h"
+#include "strv.h"
+#include "user-util.h"
+#include "virt.h"
+
+typedef enum MountMode {
+        MNT_NONE           = 0,
+        MNT_FATAL          = 1 << 0,
+        MNT_IN_CONTAINER   = 1 << 1,
+        MNT_CHECK_WRITABLE = 1 << 2,
+        MNT_FOLLOW_SYMLINK = 1 << 3,
+} MountMode;
+
+typedef struct MountPoint {
+        const char *what;
+        const char *where;
+        const char *type;
+        const char *options;
+        unsigned long flags;
+        bool (*condition_fn)(void);
+        MountMode mode;
+} MountPoint;
+
+/* The first three entries we might need before SELinux is up. The
+ * fourth (securityfs) is needed by IMA to load a custom policy. The
+ * other ones we can delay until SELinux and IMA are loaded. When
+ * SMACK is enabled we need smackfs, too, so it's a fifth one. */
+#if ENABLE_SMACK
+#define N_EARLY_MOUNT 5
+#else
+#define N_EARLY_MOUNT 4
+#endif
+
+static const MountPoint mount_table[] = {
+        { "proc",        "/proc",                     "proc",       NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+          NULL,          MNT_FATAL|MNT_IN_CONTAINER|MNT_FOLLOW_SYMLINK },
+        { "sysfs",       "/sys",                      "sysfs",      NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+          NULL,          MNT_FATAL|MNT_IN_CONTAINER },
+        { "devtmpfs",    "/dev",                      "devtmpfs",   "mode=755" TMPFS_LIMITS_DEV,               MS_NOSUID|MS_STRICTATIME,
+          NULL,          MNT_FATAL|MNT_IN_CONTAINER },
+        { "securityfs",  "/sys/kernel/security",      "securityfs", NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+          NULL,          MNT_NONE                   },
+#if ENABLE_SMACK
+        { "smackfs",     "/sys/fs/smackfs",           "smackfs",    "smackfsdef=*",                            MS_NOSUID|MS_NOEXEC|MS_NODEV,
+          mac_smack_use, MNT_FATAL                  },
+        { "tmpfs",       "/dev/shm",                  "tmpfs",      "mode=1777,smackfsroot=*",                 MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+          mac_smack_use, MNT_FATAL                  },
+#endif
+        { "tmpfs",       "/dev/shm",                  "tmpfs",      "mode=1777",                               MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+          NULL,          MNT_FATAL|MNT_IN_CONTAINER },
+        { "devpts",      "/dev/pts",                  "devpts",     "mode=620,gid=" STRINGIFY(TTY_GID),        MS_NOSUID|MS_NOEXEC,
+          NULL,          MNT_IN_CONTAINER           },
+#if ENABLE_SMACK
+        { "tmpfs",       "/run",                      "tmpfs",      "mode=755,smackfsroot=*" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+          mac_smack_use, MNT_FATAL                  },
+#endif
+        { "tmpfs",       "/run",                      "tmpfs",      "mode=755" TMPFS_LIMITS_RUN,               MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+          NULL,          MNT_FATAL|MNT_IN_CONTAINER },
+        { "cgroup2",     "/sys/fs/cgroup",            "cgroup2",    "nsdelegate,memory_recursiveprot",         MS_NOSUID|MS_NOEXEC|MS_NODEV,
+          cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
+        { "cgroup2",     "/sys/fs/cgroup",            "cgroup2",    "nsdelegate",                              MS_NOSUID|MS_NOEXEC|MS_NODEV,
+          cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
+        { "cgroup2",     "/sys/fs/cgroup",            "cgroup2",    NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+          cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
+        { "tmpfs",       "/sys/fs/cgroup",            "tmpfs",      "mode=755" TMPFS_LIMITS_SYS_FS_CGROUP,     MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
+          cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
+        { "cgroup2",     "/sys/fs/cgroup/unified",    "cgroup2",    "nsdelegate",                              MS_NOSUID|MS_NOEXEC|MS_NODEV,
+          cg_is_hybrid_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
+        { "cgroup2",     "/sys/fs/cgroup/unified",    "cgroup2",    NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+          cg_is_hybrid_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
+        { "cgroup",      "/sys/fs/cgroup/systemd",    "cgroup",     "none,name=systemd,xattr",                 MS_NOSUID|MS_NOEXEC|MS_NODEV,
+          cg_is_legacy_wanted, MNT_IN_CONTAINER     },
+        { "cgroup",      "/sys/fs/cgroup/systemd",    "cgroup",     "none,name=systemd",                       MS_NOSUID|MS_NOEXEC|MS_NODEV,
+          cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
+        { "pstore",      "/sys/fs/pstore",            "pstore",     NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+          NULL,          MNT_NONE                   },
+#if ENABLE_EFI
+        { "efivarfs",    "/sys/firmware/efi/efivars", "efivarfs",   NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+          is_efi_boot,   MNT_NONE                   },
+#endif
+        { "bpf",         "/sys/fs/bpf",               "bpf",        "mode=700",                                MS_NOSUID|MS_NOEXEC|MS_NODEV,
+          NULL,          MNT_NONE,                  },
+};
+
+bool mount_point_is_api(const char *path) {
+        unsigned i;
+
+        /* Checks if this mount point is considered "API", and hence
+         * should be ignored */
+
+        for (i = 0; i < ELEMENTSOF(mount_table); i ++)
+                if (path_equal(path, mount_table[i].where))
+                        return true;
+
+        return path_startswith(path, "/sys/fs/cgroup/");
+}
+
+bool mount_point_ignore(const char *path) {
+
+        const char *i;
+
+        /* These are API file systems that might be mounted by other software, we just list them here so that
+         * we know that we should ignore them. */
+        FOREACH_STRING(i,
+                       /* SELinux file systems */
+                       "/sys/fs/selinux",
+                       /* Container bind mounts */
+                       "/dev/console",
+                       "/proc/kmsg",
+                       "/proc/sys",
+                       "/proc/sys/kernel/random/boot_id")
+                if (path_equal(path, i))
+                        return true;
+
+        if (path_startswith(path, "/run/host")) /* All mounts passed in from the container manager are
+                                                 * something we better ignore. */
+                return true;
+
+        return false;
+}
+
+static int mount_one(const MountPoint *p, bool relabel) {
+        int r, priority;
+
+        assert(p);
+
+        priority = (p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG;
+
+        if (p->condition_fn && !p->condition_fn())
+                return 0;
+
+        /* Relabel first, just in case */
+        if (relabel)
+                (void) label_fix(p->where, LABEL_IGNORE_ENOENT|LABEL_IGNORE_EROFS);
+
+        r = path_is_mount_point(p->where, NULL, AT_SYMLINK_FOLLOW);
+        if (r < 0 && r != -ENOENT) {
+                log_full_errno(priority, r, "Failed to determine whether %s is a mount point: %m", p->where);
+                return (p->mode & MNT_FATAL) ? r : 0;
+        }
+        if (r > 0)
+                return 0;
+
+        /* Skip securityfs in a container */
+        if (!(p->mode & MNT_IN_CONTAINER) && detect_container() > 0)
+                return 0;
+
+        /* The access mode here doesn't really matter too much, since
+         * the mounted file system will take precedence anyway. */
+        if (relabel)
+                (void) mkdir_p_label(p->where, 0755);
+        else
+                (void) mkdir_p(p->where, 0755);
+
+        log_debug("Mounting %s to %s of type %s with options %s.",
+                  p->what,
+                  p->where,
+                  p->type,
+                  strna(p->options));
+
+        if (FLAGS_SET(p->mode, MNT_FOLLOW_SYMLINK))
+                r = mount(p->what, p->where, p->type, p->flags, p->options) < 0 ? -errno : 0;
+        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);
+                return (p->mode & MNT_FATAL) ? r : 0;
+        }
+
+        /* Relabel again, since we now mounted something fresh here */
+        if (relabel)
+                (void) label_fix(p->where, 0);
+
+        if (p->mode & MNT_CHECK_WRITABLE) {
+                if (access(p->where, W_OK) < 0) {
+                        r = -errno;
+
+                        (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);
+                        return (p->mode & MNT_FATAL) ? r : 0;
+                }
+        }
+
+        return 1;
+}
+
+static int mount_points_setup(unsigned n, bool loaded_policy) {
+        unsigned i;
+        int r = 0;
+
+        for (i = 0; i < n; i ++) {
+                int j;
+
+                j = mount_one(mount_table + i, loaded_policy);
+                if (j != 0 && r >= 0)
+                        r = j;
+        }
+
+        return r;
+}
+
+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);
+}
+
+static const char *join_with(const char *controller) {
+
+        static const char* const pairs[] = {
+                "cpu", "cpuacct",
+                "net_cls", "net_prio",
+                NULL
+        };
+
+        const char *const *x, *const *y;
+
+        assert(controller);
+
+        /* This will lookup which controller to mount another controller with. Input is a controller name, and output
+         * is the other controller name. The function works both ways: you can input one and get the other, and input
+         * the other to get the one. */
+
+        STRV_FOREACH_PAIR(x, y, pairs) {
+                if (streq(controller, *x))
+                        return *y;
+                if (streq(controller, *y))
+                        return *x;
+        }
+
+        return NULL;
+}
+
+static int symlink_controller(const char *target, const char *alias) {
+        const char *a;
+        int r;
+
+        assert(target);
+        assert(alias);
+
+        a = strjoina("/sys/fs/cgroup/", alias);
+
+        r = symlink_idempotent(target, a, false);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create symlink %s: %m", a);
+
+#ifdef SMACK_RUN_LABEL
+        const char *p;
+
+        p = strjoina("/sys/fs/cgroup/", target);
+
+        r = mac_smack_copy(a, p);
+        if (r < 0 && r != -EOPNOTSUPP)
+                return log_error_errno(r, "Failed to copy smack label from %s to %s: %m", p, a);
+#endif
+
+        return 0;
+}
+
+int mount_cgroup_controllers(void) {
+        _cleanup_set_free_free_ Set *controllers = NULL;
+        int r;
+
+        if (!cg_is_legacy_wanted())
+                return 0;
+
+        /* Mount all available cgroup controllers that are built into the kernel. */
+        r = cg_kernel_controllers(&controllers);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enumerate cgroup controllers: %m");
+
+        for (;;) {
+                _cleanup_free_ char *options = NULL, *controller = NULL, *where = NULL;
+                const char *other_controller;
+                MountPoint p = {
+                        .what = "cgroup",
+                        .type = "cgroup",
+                        .flags = MS_NOSUID|MS_NOEXEC|MS_NODEV,
+                        .mode = MNT_IN_CONTAINER,
+                };
+
+                controller = set_steal_first(controllers);
+                if (!controller)
+                        break;
+
+                /* Check if we shall mount this together with another controller */
+                other_controller = join_with(controller);
+                if (other_controller) {
+                        _cleanup_free_ char *c = NULL;
+
+                        /* Check if the other controller is actually available in the kernel too */
+                        c = set_remove(controllers, other_controller);
+                        if (c) {
+
+                                /* Join the two controllers into one string, and maintain a stable ordering */
+                                if (strcmp(controller, other_controller) < 0)
+                                        options = strjoin(controller, ",", other_controller);
+                                else
+                                        options = strjoin(other_controller, ",", controller);
+                                if (!options)
+                                        return log_oom();
+                        }
+                }
+
+                /* The simple case, where there's only one controller to mount together */
+                if (!options)
+                        options = TAKE_PTR(controller);
+
+                where = path_join("/sys/fs/cgroup", options);
+                if (!where)
+                        return log_oom();
+
+                p.where = where;
+                p.options = options;
+
+                r = mount_one(&p, true);
+                if (r < 0)
+                        return r;
+
+                /* Create symlinks from the individual controller names, in case we have a joined mount */
+                if (controller)
+                        (void) symlink_controller(options, controller);
+                if (other_controller)
+                        (void) symlink_controller(options, other_controller);
+        }
+
+        /* Now that we mounted everything, let's make the tmpfs the cgroup file systems are mounted into read-only. */
+        (void) mount_nofollow("tmpfs", "/sys/fs/cgroup", "tmpfs", MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755" TMPFS_LIMITS_SYS_FS_CGROUP);
+
+        return 0;
+}
+
+#if HAVE_SELINUX || ENABLE_SMACK
+static int nftw_cb(
+                const char *fpath,
+                const struct stat *sb,
+                int tflag,
+                struct FTW *ftwbuf) {
+
+        /* No need to label /dev twice in a row... */
+        if (_unlikely_(ftwbuf->level == 0))
+                return FTW_CONTINUE;
+
+        (void) label_fix(fpath, 0);
+
+        /* /run/initramfs is static data and big, no need to
+         * dynamically relabel its contents at boot... */
+        if (_unlikely_(ftwbuf->level == 1 &&
+                      tflag == FTW_D &&
+                      streq(fpath, "/run/initramfs")))
+                return FTW_SKIP_SUBTREE;
+
+        return FTW_CONTINUE;
+};
+
+static int relabel_cgroup_filesystems(void) {
+        int r;
+        struct statfs st;
+
+        r = cg_all_unified();
+        if (r == 0) {
+                /* Temporarily remount the root cgroup filesystem to give it a proper label. Do this
+                   only when the filesystem has been already populated by a previous instance of systemd
+                   running from initrd. Otherwise don't remount anything and leave the filesystem read-write
+                   for the cgroup filesystems to be mounted inside. */
+                if (statfs("/sys/fs/cgroup", &st) < 0)
+                        return log_error_errno(errno, "Failed to determine mount flags for /sys/fs/cgroup: %m");
+
+                if (st.f_flags & ST_RDONLY)
+                        (void) mount_nofollow(NULL, "/sys/fs/cgroup", NULL, MS_REMOUNT, NULL);
+
+                (void) label_fix("/sys/fs/cgroup", 0);
+                (void) nftw("/sys/fs/cgroup", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
+
+                if (st.f_flags & ST_RDONLY)
+                        (void) mount_nofollow(NULL, "/sys/fs/cgroup", NULL, MS_REMOUNT|MS_RDONLY, NULL);
+
+        } else if (r < 0)
+                return log_error_errno(r, "Failed to determine whether we are in all unified mode: %m");
+
+        return 0;
+}
+
+static int relabel_extra(void) {
+        _cleanup_strv_free_ char **files = NULL;
+        char **file;
+        int r, c = 0;
+
+        /* Support for relabelling additional files or directories after loading the policy. For this, code in the
+         * initrd simply has to drop in *.relabel files into /run/systemd/relabel-extra.d/. We'll read all such files
+         * expecting one absolute path by line and will relabel each (and everyone below that in case the path refers
+         * to a directory). These drop-in files are supposed to be absolutely minimal, and do not understand comments
+         * and such. After the operation succeeded the files are removed, and the drop-in directory as well, if
+         * possible.
+         */
+
+        r = conf_files_list(&files, ".relabel", NULL,
+                            CONF_FILES_FILTER_MASKED | CONF_FILES_REGULAR,
+                            "/run/systemd/relabel-extra.d/");
+        if (r < 0)
+                return log_error_errno(r, "Failed to enumerate /run/systemd/relabel-extra.d/, ignoring: %m");
+
+        STRV_FOREACH(file, files) {
+                _cleanup_fclose_ FILE *f = NULL;
+
+                f = fopen(*file, "re");
+                if (!f) {
+                        log_warning_errno(errno, "Failed to open %s, ignoring: %m", *file);
+                        continue;
+                }
+
+                for (;;) {
+                        _cleanup_free_ char *line = NULL;
+
+                        r = read_line(f, LONG_LINE_MAX, &line);
+                        if (r < 0) {
+                                log_warning_errno(r, "Failed to read %s, ignoring: %m", *file);
+                                break;
+                        }
+                        if (r == 0) /* EOF */
+                                break;
+
+                        path_simplify(line, true);
+
+                        if (!path_is_normalized(line)) {
+                                log_warning("Path to relabel is not normalized, ignoring: %s", line);
+                                continue;
+                        }
+
+                        if (!path_is_absolute(line)) {
+                                log_warning("Path to relabel is not absolute, ignoring: %s", line);
+                                continue;
+                        }
+
+                        log_debug("Relabelling additional file/directory '%s'.", line);
+                        (void) label_fix(line, 0);
+                        (void) nftw(line, nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
+                        c++;
+                }
+
+                if (unlink(*file) < 0)
+                        log_warning_errno(errno, "Failed to remove %s, ignoring: %m", *file);
+        }
+
+        /* Remove when we complete things. */
+        if (rmdir("/run/systemd/relabel-extra.d") < 0 &&
+            errno != ENOENT)
+                log_warning_errno(errno, "Failed to remove /run/systemd/relabel-extra.d/ directory: %m");
+
+        return c;
+}
+#endif
+
+int mount_setup(bool loaded_policy, bool leave_propagation) {
+        int r;
+
+        r = mount_points_setup(ELEMENTSOF(mount_table), loaded_policy);
+        if (r < 0)
+                return r;
+
+#if HAVE_SELINUX || ENABLE_SMACK
+        /* Nodes in devtmpfs and /run need to be manually updated for
+         * the appropriate labels, after mounting. The other virtual
+         * API file systems like /sys and /proc do not need that, they
+         * use the same label for all their files. */
+        if (loaded_policy) {
+                usec_t before_relabel, after_relabel;
+                char timespan[FORMAT_TIMESPAN_MAX];
+                const char *i;
+                int n_extra;
+
+                before_relabel = now(CLOCK_MONOTONIC);
+
+                FOREACH_STRING(i, "/dev", "/dev/shm", "/run")
+                        (void) nftw(i, nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
+
+                (void) relabel_cgroup_filesystems();
+
+                n_extra = relabel_extra();
+
+                after_relabel = now(CLOCK_MONOTONIC);
+
+                log_info("Relabelled /dev, /dev/shm, /run, /sys/fs/cgroup%s in %s.",
+                         n_extra > 0 ? ", additional files" : "",
+                         format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel, 0));
+        }
+#endif
+
+        /* Create a few default symlinks, which are normally created
+         * by udevd, but some scripts might need them before we start
+         * udevd. */
+        dev_setup(NULL, UID_INVALID, GID_INVALID);
+
+        /* Mark the root directory as shared in regards to mount propagation. The kernel defaults to "private", but we
+         * think it makes more sense to have a default of "shared" so that nspawn and the container tools work out of
+         * the box. If specific setups need other settings they can reset the propagation mode to private if
+         * needed. Note that we set this only when we are invoked directly by the kernel. If we are invoked by a
+         * container manager we assume the container manager knows what it is doing (for example, because it set up
+         * some directories with different propagation modes). */
+        if (detect_container() <= 0 && !leave_propagation)
+                if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0)
+                        log_warning_errno(errno, "Failed to set up the root directory for shared mount propagation: %m");
+
+        /* Create a few directories we always want around, Note that sd_booted() checks for /run/systemd/system, so
+         * this mkdir really needs to stay for good, otherwise software that copied sd-daemon.c into their sources will
+         * misdetect systemd. */
+        (void) mkdir_label("/run/systemd", 0755);
+        (void) mkdir_label("/run/systemd/system", 0755);
+
+        /* Make sure we have a mount point to hide in sandboxes */
+        (void) mkdir_label("/run/credentials", 0755);
+
+        /* Also create /run/systemd/inaccessible nodes, so that we always have something to mount
+         * inaccessible nodes from. If we run in a container the host might have created these for us already
+         * in /run/host/inaccessible/. Use those if we can, since that way we likely get access to block/char
+         * device nodes that are inaccessible, and if userns is used to nodes that are on mounts owned by a
+         * userns outside the container and thus nicely read-only and not remountable. */
+        if (access("/run/host/inaccessible/", F_OK) < 0) {
+                if (errno != ENOENT)
+                        log_debug_errno(errno, "Failed to check if /run/host/inaccessible exists, ignoring: %m");
+
+                (void) make_inaccessible_nodes("/run/systemd", UID_INVALID, GID_INVALID);
+        } else
+                (void) symlink("../host/inaccessible", "/run/systemd/inaccessible");
+
+        return 0;
+}
diff --git a/src/shared/mount-setup.h b/src/shared/mount-setup.h
new file mode 100644 (file)
index 0000000..29bd62f
--- /dev/null
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdbool.h>
+
+int mount_setup_early(void);
+int mount_setup(bool loaded_policy, bool leave_propagation);
+
+int mount_cgroup_controllers(void);
+
+bool mount_point_is_api(const char *path);
+bool mount_point_ignore(const char *path);
index 5d185eb77f32c05e2db42e5a0f613a3f60bab088..6ea45997918e28f51daa311c611c97bcdadbe5bc 100644 (file)
@@ -775,8 +775,7 @@ tests += [
         [['src/test/test-umount.c',
           'src/shutdown/umount.c',
           'src/shutdown/umount.h'],
-         [libcore_shared,
-          libshared],
+         [libshared],
          [libmount]],
 
         [['src/test/test-bus-util.c'],