Merge pull request #18038 from yuwata/meson-split

meson: various cleanups
This commit is contained in:
Yu Watanabe
2021-01-19 12:22:34 +09:00
committed by GitHub
160 changed files with 1324 additions and 1735 deletions
+256
View File
@@ -0,0 +1,256 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
#include "sd-device.h"
#include "acl-util.h"
#include "alloc-util.h"
#include "device-util.h"
#include "devnode-acl.h"
#include "dirent-util.h"
#include "escape.h"
#include "fd-util.h"
#include "format-util.h"
#include "set.h"
#include "string-util.h"
#include "util.h"
static int flush_acl(acl_t acl) {
acl_entry_t i;
int found;
bool changed = false;
assert(acl);
for (found = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
found > 0;
found = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
acl_tag_t tag;
if (acl_get_tag_type(i, &tag) < 0)
return -errno;
if (tag != ACL_USER)
continue;
if (acl_delete_entry(acl, i) < 0)
return -errno;
changed = true;
}
if (found < 0)
return -errno;
return changed;
}
int devnode_acl(const char *path,
bool flush,
bool del, uid_t old_uid,
bool add, uid_t new_uid) {
acl_t acl;
int r = 0;
bool changed = false;
assert(path);
acl = acl_get_file(path, ACL_TYPE_ACCESS);
if (!acl)
return -errno;
if (flush) {
r = flush_acl(acl);
if (r < 0)
goto finish;
if (r > 0)
changed = true;
} else if (del && old_uid > 0) {
acl_entry_t entry;
r = acl_find_uid(acl, old_uid, &entry);
if (r < 0)
goto finish;
if (r > 0) {
if (acl_delete_entry(acl, entry) < 0) {
r = -errno;
goto finish;
}
changed = true;
}
}
if (add && new_uid > 0) {
acl_entry_t entry;
acl_permset_t permset;
int rd, wt;
r = acl_find_uid(acl, new_uid, &entry);
if (r < 0)
goto finish;
if (r == 0) {
if (acl_create_entry(&acl, &entry) < 0) {
r = -errno;
goto finish;
}
if (acl_set_tag_type(entry, ACL_USER) < 0 ||
acl_set_qualifier(entry, &new_uid) < 0) {
r = -errno;
goto finish;
}
}
if (acl_get_permset(entry, &permset) < 0) {
r = -errno;
goto finish;
}
rd = acl_get_perm(permset, ACL_READ);
if (rd < 0) {
r = -errno;
goto finish;
}
wt = acl_get_perm(permset, ACL_WRITE);
if (wt < 0) {
r = -errno;
goto finish;
}
if (!rd || !wt) {
if (acl_add_perm(permset, ACL_READ|ACL_WRITE) < 0) {
r = -errno;
goto finish;
}
changed = true;
}
}
if (!changed)
goto finish;
if (acl_calc_mask(&acl) < 0) {
r = -errno;
goto finish;
}
if (acl_set_file(path, ACL_TYPE_ACCESS, acl) < 0) {
r = -errno;
goto finish;
}
r = 0;
finish:
acl_free(acl);
return r;
}
int devnode_acl_all(const char *seat,
bool flush,
bool del, uid_t old_uid,
bool add, uid_t new_uid) {
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
_cleanup_set_free_free_ Set *nodes = NULL;
_cleanup_closedir_ DIR *dir = NULL;
struct dirent *dent;
sd_device *d;
char *n;
int r;
nodes = set_new(&path_hash_ops);
if (!nodes)
return -ENOMEM;
r = sd_device_enumerator_new(&e);
if (r < 0)
return r;
if (isempty(seat))
seat = "seat0";
/* We can only match by one tag in libudev. We choose
* "uaccess" for that. If we could match for two tags here we
* could add the seat name as second match tag, but this would
* be hardly optimizable in libudev, and hence checking the
* second tag manually in our loop is a good solution. */
r = sd_device_enumerator_add_match_tag(e, "uaccess");
if (r < 0)
return r;
FOREACH_DEVICE(e, d) {
const char *node, *sn;
/* Make sure the tag is still in place */
if (sd_device_has_current_tag(d, "uaccess") <= 0)
continue;
if (sd_device_get_property_value(d, "ID_SEAT", &sn) < 0 || isempty(sn))
sn = "seat0";
if (!streq(seat, sn))
continue;
/* In case people mistag devices with nodes, we need to ignore this */
if (sd_device_get_devname(d, &node) < 0)
continue;
log_device_debug(d, "Found udev node %s for seat %s", node, seat);
r = set_put_strdup(&nodes, node);
if (r < 0)
return r;
}
/* udev exports "dead" device nodes to allow module on-demand loading,
* these devices are not known to the kernel at this moment */
dir = opendir("/run/udev/static_node-tags/uaccess");
if (dir) {
FOREACH_DIRENT(dent, dir, return -errno) {
_cleanup_free_ char *unescaped_devname = NULL;
if (cunescape(dent->d_name, UNESCAPE_RELAX, &unescaped_devname) < 0)
return -ENOMEM;
n = path_join("/dev", unescaped_devname);
if (!n)
return -ENOMEM;
log_debug("Found static node %s for seat %s", n, seat);
r = set_consume(nodes, n);
if (r == -EEXIST)
continue;
if (r < 0)
return r;
}
}
r = 0;
SET_FOREACH(n, nodes) {
int k;
log_debug("Changing ACLs at %s for seat %s (uid "UID_FMT"→"UID_FMT"%s%s)",
n, seat, old_uid, new_uid,
del ? " del" : "", add ? " add" : "");
k = devnode_acl(n, flush, del, old_uid, add, new_uid);
if (k == -ENOENT)
log_debug("Device %s disappeared while setting ACLs", n);
else if (k < 0 && r == 0)
r = k;
}
return r;
}
+34
View File
@@ -0,0 +1,34 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stdbool.h>
#include <sys/types.h>
#if HAVE_ACL
int devnode_acl(const char *path,
bool flush,
bool del, uid_t old_uid,
bool add, uid_t new_uid);
int devnode_acl_all(const char *seat,
bool flush,
bool del, uid_t old_uid,
bool add, uid_t new_uid);
#else
static inline int devnode_acl(const char *path,
bool flush,
bool del, uid_t old_uid,
bool add, uid_t new_uid) {
return 0;
}
static inline int devnode_acl_all(const char *seat,
bool flush,
bool del, uid_t old_uid,
bool add, uid_t new_uid) {
return 0;
}
#endif
+1 -15
View File
@@ -6,24 +6,10 @@
#include <stddef.h>
#include <stdint.h>
#include "dns-def.h"
#include "hashmap.h"
#include "in-addr-util.h"
/* Length of a single label, with all escaping removed, excluding any trailing dot or NUL byte */
#define DNS_LABEL_MAX 63
/* Worst case length of a single label, with all escaping applied and room for a trailing NUL byte. */
#define DNS_LABEL_ESCAPED_MAX (DNS_LABEL_MAX*4+1)
/* Maximum length of a full hostname, consisting of a series of unescaped labels, and no trailing dot or NUL byte */
#define DNS_HOSTNAME_MAX 253
/* Maximum length of a full hostname, on the wire, including the final NUL byte */
#define DNS_WIRE_FORMAT_HOSTNAME_MAX 255
/* Maximum number of labels per valid hostname */
#define DNS_N_LABELS_MAX 127
typedef enum DNSLabelFlags {
DNS_LABEL_LDH = 1 << 0, /* Follow the "LDH" rule — only letters, digits, and internal hyphens. */
DNS_LABEL_NO_ESCAPES = 1 << 1, /* Do not treat backslashes specially */
+1
View File
@@ -0,0 +1 @@
../boot/efi/loader-features.h
+3 -2
View File
@@ -1,10 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "efivars.h"
#include <sys/stat.h>
#include "efi-loader-features.h"
#include "efivars.h"
#if ENABLE_EFI
int efi_reboot_to_firmware_supported(void);
+283
View File
@@ -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;
}
+6
View File
@@ -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);
+213
View File
@@ -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;
}
+4
View File
@@ -0,0 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
int loopback_setup(void);
+282
View File
@@ -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;
}
+7
View File
@@ -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);
+23 -13
View File
@@ -78,6 +78,7 @@ shared_sources = files('''
daemon-util.h
dev-setup.c
dev-setup.h
devnode-acl.h
dissect-image.c
dissect-image.h
dm-util.c
@@ -86,6 +87,7 @@ shared_sources = files('''
dns-domain.h
dropin.c
dropin.h
efi-loader-features.h
efi-loader.c
efi-loader.h
enable-mempool.c
@@ -144,6 +146,8 @@ shared_sources = files('''
json-internal.h
json.c
json.h
killall.c
killall.h
libcrypt-util.c
libcrypt-util.h
libfido2-util.c
@@ -164,6 +168,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
@@ -174,6 +182,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
@@ -243,8 +253,6 @@ shared_sources = files('''
udev-util.h
uid-range.c
uid-range.h
unit-file.c
unit-file.h
user-record-nss.c
user-record-nss.h
user-record-show.c
@@ -273,12 +281,13 @@ shared_sources = files('''
'''.split())
if get_option('tests') != 'false'
shared_sources += files('tests.c', 'tests.h')
shared_sources += files('''
test-tables.h
tests.c
tests.h
'''.split())
endif
test_tables_h = files('test-tables.h')
shared_sources += test_tables_h
generate_syscall_list = find_program('generate-syscall-list.py')
fname = 'syscall-list.h'
syscall_list_h = custom_target(
@@ -290,7 +299,10 @@ syscall_list_h = custom_target(
capture : true)
if conf.get('HAVE_ACL') == 1
shared_sources += files('acl-util.c')
shared_sources += files('''
acl-util.c
devnode-acl.c
'''.split())
endif
if conf.get('ENABLE_UTMP') == 1
@@ -311,10 +323,9 @@ if conf.get('HAVE_KMOD') == 1
endif
if conf.get('HAVE_PAM') == 1
shared_sources += files('''
pam-util.c
pam-util.h
'''.split())
shared_sources += files(
'pam-util.c',
'pam-util.h')
endif
if conf.get('ENABLE_NSCD') == 1
@@ -399,8 +410,7 @@ libshared = shared_library(
link_whole : [libshared_static,
libbasic,
libbasic_gcrypt,
libsystemd_static,
libjournal_client],
libsystemd_static],
c_args : ['-fvisibility=default'],
dependencies : libshared_deps,
install : true,
+561
View File
@@ -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;
}
+12
View File
@@ -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);
-164
View File
@@ -1,164 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "fd-util.h"
#include "fs-util.h"
#include "offline-passwd.h"
#include "path-util.h"
#include "user-util.h"
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(uid_gid_hash_ops, char, string_hash_func, string_compare_func, free);
static int open_passwd_file(const char *root, const char *fname, FILE **ret_file) {
_cleanup_free_ char *p = NULL;
_cleanup_close_ int fd = -1;
fd = chase_symlinks_and_open(fname, root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC, &p);
if (fd < 0)
return fd;
FILE *f = fdopen(fd, "r");
if (!f)
return -errno;
TAKE_FD(fd);
log_debug("Reading %s entries from %s...", basename(fname), p);
*ret_file = f;
return 0;
}
static int populate_uid_cache(const char *root, Hashmap **ret) {
_cleanup_(hashmap_freep) Hashmap *cache = NULL;
int r;
cache = hashmap_new(&uid_gid_hash_ops);
if (!cache)
return -ENOMEM;
/* The directory list is hardcoded here: /etc is the standard, and rpm-ostree uses /usr/lib. This
* could be made configurable, but I don't see the point right now. */
const char *fname;
FOREACH_STRING(fname, "/etc/passwd", "/usr/lib/passwd") {
_cleanup_fclose_ FILE *f = NULL;
r = open_passwd_file(root, fname, &f);
if (r == -ENOENT)
continue;
if (r < 0)
return r;
struct passwd *pw;
while ((r = fgetpwent_sane(f, &pw)) > 0) {
_cleanup_free_ char *n = NULL;
n = strdup(pw->pw_name);
if (!n)
return -ENOMEM;
r = hashmap_put(cache, n, UID_TO_PTR(pw->pw_uid));
if (IN_SET(r, 0 -EEXIST))
continue;
if (r < 0)
return r;
TAKE_PTR(n);
}
}
*ret = TAKE_PTR(cache);
return 0;
}
static int populate_gid_cache(const char *root, Hashmap **ret) {
_cleanup_(hashmap_freep) Hashmap *cache = NULL;
int r;
cache = hashmap_new(&uid_gid_hash_ops);
if (!cache)
return -ENOMEM;
const char *fname;
FOREACH_STRING(fname, "/etc/group", "/usr/lib/group") {
_cleanup_fclose_ FILE *f = NULL;
r = open_passwd_file(root, fname, &f);
if (r == -ENOENT)
continue;
if (r < 0)
return r;
struct group *gr;
while ((r = fgetgrent_sane(f, &gr)) > 0) {
_cleanup_free_ char *n = NULL;
n = strdup(gr->gr_name);
if (!n)
return -ENOMEM;
r = hashmap_put(cache, n, GID_TO_PTR(gr->gr_gid));
if (IN_SET(r, 0, -EEXIST))
continue;
if (r < 0)
return r;
TAKE_PTR(n);
}
}
*ret = TAKE_PTR(cache);
return 0;
}
int name_to_uid_offline(
const char *root,
const char *user,
uid_t *ret_uid,
Hashmap **cache) {
void *found;
int r;
assert(user);
assert(ret_uid);
assert(cache);
if (!*cache) {
r = populate_uid_cache(root, cache);
if (r < 0)
return r;
}
found = hashmap_get(*cache, user);
if (!found)
return -ESRCH;
*ret_uid = PTR_TO_UID(found);
return 0;
}
int name_to_gid_offline(
const char *root,
const char *group,
gid_t *ret_gid,
Hashmap **cache) {
void *found;
int r;
assert(group);
assert(ret_gid);
assert(cache);
if (!*cache) {
r = populate_gid_cache(root, cache);
if (r < 0)
return r;
}
found = hashmap_get(*cache, group);
if (!found)
return -ESRCH;
*ret_gid = PTR_TO_GID(found);
return 0;
}
-9
View File
@@ -1,9 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <sys/types.h>
#include "hashmap.h"
int name_to_uid_offline(const char *root, const char *user, uid_t *ret_uid, Hashmap **cache);
int name_to_gid_offline(const char *root, const char *group, gid_t *ret_gid, Hashmap **cache);
+38
View File
@@ -2151,3 +2151,41 @@ uint32_t scmp_act_kill_process(void) {
return SCMP_ACT_KILL; /* same as SCMP_ACT_KILL_THREAD */
}
int parse_syscall_and_errno(const char *in, char **name, int *error) {
_cleanup_free_ char *n = NULL;
char *p;
int e = -1;
assert(in);
assert(name);
assert(error);
/*
* This parse "syscall:errno" like "uname:EILSEQ", "@sync:255".
* If errno is omitted, then error is set to -1.
* Empty syscall name is not allowed.
* Here, we do not check that the syscall name is valid or not.
*/
p = strchr(in, ':');
if (p) {
e = seccomp_parse_errno_or_action(p + 1);
if (e < 0)
return e;
n = strndup(in, p - in);
} else
n = strdup(in);
if (!n)
return -ENOMEM;
if (isempty(n))
return -EINVAL;
*error = e;
*name = TAKE_PTR(n);
return 0;
}
+2
View File
@@ -148,3 +148,5 @@ static inline const char *seccomp_errno_or_action_to_string(int num) {
return "kill";
return errno_to_name(num);
}
int parse_syscall_and_errno(const char *in, char **name, int *error);
-601
View File
@@ -1,601 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "sd-id128.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "macro.h"
#include "path-lookup.h"
#include "set.h"
#include "special.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "unit-file.h"
bool unit_type_may_alias(UnitType type) {
return IN_SET(type,
UNIT_SERVICE,
UNIT_SOCKET,
UNIT_TARGET,
UNIT_DEVICE,
UNIT_TIMER,
UNIT_PATH);
}
bool unit_type_may_template(UnitType type) {
return IN_SET(type,
UNIT_SERVICE,
UNIT_SOCKET,
UNIT_TARGET,
UNIT_TIMER,
UNIT_PATH);
}
int unit_symlink_name_compatible(const char *symlink, const char *target, bool instance_propagation) {
_cleanup_free_ char *template = NULL;
int r, un_type1, un_type2;
un_type1 = unit_name_classify(symlink);
/* The straightforward case: the symlink name matches the target and we have a valid unit */
if (streq(symlink, target) &&
(un_type1 & (UNIT_NAME_PLAIN | UNIT_NAME_INSTANCE)))
return 1;
r = unit_name_template(symlink, &template);
if (r == -EINVAL)
return 0; /* Not a template */
if (r < 0)
return r;
un_type2 = unit_name_classify(target);
/* An instance name points to a target that is just the template name */
if (un_type1 == UNIT_NAME_INSTANCE &&
un_type2 == UNIT_NAME_TEMPLATE &&
streq(template, target))
return 1;
/* foo@.target.requires/bar@.service: instance will be propagated */
if (instance_propagation &&
un_type1 == UNIT_NAME_TEMPLATE &&
un_type2 == UNIT_NAME_TEMPLATE &&
streq(template, target))
return 1;
return 0;
}
int unit_validate_alias_symlink_and_warn(const char *filename, const char *target) {
const char *src, *dst;
_cleanup_free_ char *src_instance = NULL, *dst_instance = NULL;
UnitType src_unit_type, dst_unit_type;
int src_name_type, dst_name_type;
/* Check if the *alias* symlink is valid. This applies to symlinks like
* /etc/systemd/system/dbus.service → dbus-broker.service, but not to .wants or .requires symlinks
* and such. Neither does this apply to symlinks which *link* units, i.e. symlinks to outside of the
* unit lookup path.
*
* -EINVAL is returned if the something is wrong with the source filename or the source unit type is
* not allowed to symlink,
* -EXDEV if the target filename is not a valid unit name or doesn't match the source.
*/
src = basename(filename);
dst = basename(target);
/* src checks */
src_name_type = unit_name_to_instance(src, &src_instance);
if (src_name_type < 0)
return log_notice_errno(src_name_type,
"%s: not a valid unit name \"%s\": %m", filename, src);
src_unit_type = unit_name_to_type(src);
assert(src_unit_type >= 0); /* unit_name_to_instance() checked the suffix already */
if (!unit_type_may_alias(src_unit_type))
return log_notice_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: symlinks are not allowed for units of this type, rejecting.",
filename);
if (src_name_type != UNIT_NAME_PLAIN &&
!unit_type_may_template(src_unit_type))
return log_notice_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: templates not allowed for %s units, rejecting.",
filename, unit_type_to_string(src_unit_type));
/* dst checks */
dst_name_type = unit_name_to_instance(dst, &dst_instance);
if (dst_name_type < 0)
return log_notice_errno(dst_name_type == -EINVAL ? SYNTHETIC_ERRNO(EXDEV) : dst_name_type,
"%s points to \"%s\" which is not a valid unit name: %m",
filename, dst);
if (!(dst_name_type == src_name_type ||
(src_name_type == UNIT_NAME_INSTANCE && dst_name_type == UNIT_NAME_TEMPLATE)))
return log_notice_errno(SYNTHETIC_ERRNO(EXDEV),
"%s: symlink target name type \"%s\" does not match source, rejecting.",
filename, dst);
if (dst_name_type == UNIT_NAME_INSTANCE) {
assert(src_instance);
assert(dst_instance);
if (!streq(src_instance, dst_instance))
return log_notice_errno(SYNTHETIC_ERRNO(EXDEV),
"%s: unit symlink target \"%s\" instance name doesn't match, rejecting.",
filename, dst);
}
dst_unit_type = unit_name_to_type(dst);
if (dst_unit_type != src_unit_type)
return log_notice_errno(SYNTHETIC_ERRNO(EXDEV),
"%s: symlink target \"%s\" has incompatible suffix, rejecting.",
filename, dst);
return 0;
}
#define FOLLOW_MAX 8
static int unit_ids_map_get(
Hashmap *unit_ids_map,
const char *unit_name,
const char **ret_fragment_path) {
/* Resolve recursively until we hit an absolute path, i.e. a non-aliased unit.
*
* We distinguish the case where unit_name was not found in the hashmap at all, and the case where
* some symlink was broken.
*
* If a symlink target points to an instance name, then we also check for the template. */
const char *id = NULL;
int r;
for (unsigned n = 0; n < FOLLOW_MAX; n++) {
const char *t = hashmap_get(unit_ids_map, id ?: unit_name);
if (!t) {
_cleanup_free_ char *template = NULL;
if (!id)
return -ENOENT;
r = unit_name_template(id, &template);
if (r == -EINVAL)
return -ENXIO; /* we failed to find the symlink target */
if (r < 0)
return log_error_errno(r, "Failed to determine template name for %s: %m", id);
t = hashmap_get(unit_ids_map, template);
if (!t)
return -ENXIO;
/* We successfully switched from instanced name to a template, let's continue */
}
if (path_is_absolute(t)) {
if (ret_fragment_path)
*ret_fragment_path = t;
return 0;
}
id = t;
}
return -ELOOP;
}
static bool lookup_paths_mtime_exclude(const LookupPaths *lp, const char *path) {
/* Paths that are under our exclusive control. Users shall not alter those directly. */
return streq_ptr(path, lp->generator) ||
streq_ptr(path, lp->generator_early) ||
streq_ptr(path, lp->generator_late) ||
streq_ptr(path, lp->transient) ||
streq_ptr(path, lp->persistent_control) ||
streq_ptr(path, lp->runtime_control);
}
#define HASH_KEY SD_ID128_MAKE(4e,86,1b,e3,39,b3,40,46,98,5d,b8,11,34,8f,c3,c1)
bool lookup_paths_timestamp_hash_same(const LookupPaths *lp, uint64_t timestamp_hash, uint64_t *ret_new) {
struct siphash state;
siphash24_init(&state, HASH_KEY.bytes);
char **dir;
STRV_FOREACH(dir, (char**) lp->search_path) {
struct stat st;
if (lookup_paths_mtime_exclude(lp, *dir))
continue;
/* Determine the latest lookup path modification time */
if (stat(*dir, &st) < 0) {
if (errno == ENOENT)
continue;
log_debug_errno(errno, "Failed to stat %s, ignoring: %m", *dir);
continue;
}
siphash24_compress_usec_t(timespec_load(&st.st_mtim), &state);
}
uint64_t updated = siphash24_finalize(&state);
if (ret_new)
*ret_new = updated;
if (updated != timestamp_hash)
log_debug("Modification times have changed, need to update cache.");
return updated == timestamp_hash;
}
int unit_file_build_name_map(
const LookupPaths *lp,
uint64_t *cache_timestamp_hash,
Hashmap **unit_ids_map,
Hashmap **unit_names_map,
Set **path_cache) {
/* Build two mappings: any name → main unit (i.e. the end result of symlink resolution), unit name →
* all aliases (i.e. the entry for a given key is a a list of all names which point to this key). The
* key is included in the value iff we saw a file or symlink with that name. In other words, if we
* have a key, but it is not present in the value for itself, there was an alias pointing to it, but
* the unit itself is not loadable.
*
* At the same, build a cache of paths where to find units. The non-const parameters are for input
* and output. Existing contents will be freed before the new contents are stored.
*/
_cleanup_hashmap_free_ Hashmap *ids = NULL, *names = NULL;
_cleanup_set_free_free_ Set *paths = NULL;
uint64_t timestamp_hash;
char **dir;
int r;
/* Before doing anything, check if the timestamp hash that was passed is still valid.
* If yes, do nothing. */
if (cache_timestamp_hash &&
lookup_paths_timestamp_hash_same(lp, *cache_timestamp_hash, &timestamp_hash))
return 0;
/* The timestamp hash is now set based on the mtimes from before when we start reading files.
* If anything is modified concurrently, we'll consider the cache outdated. */
if (path_cache) {
paths = set_new(&path_hash_ops_free);
if (!paths)
return log_oom();
}
STRV_FOREACH(dir, (char**) lp->search_path) {
struct dirent *de;
_cleanup_closedir_ DIR *d = NULL;
d = opendir(*dir);
if (!d) {
if (errno != ENOENT)
log_warning_errno(errno, "Failed to open \"%s\", ignoring: %m", *dir);
continue;
}
FOREACH_DIRENT_ALL(de, d, log_warning_errno(errno, "Failed to read \"%s\", ignoring: %m", *dir)) {
char *filename;
_cleanup_free_ char *_filename_free = NULL, *simplified = NULL;
const char *suffix, *dst = NULL;
bool valid_unit_name;
valid_unit_name = unit_name_is_valid(de->d_name, UNIT_NAME_ANY);
/* We only care about valid units and dirs with certain suffixes, let's ignore the
* rest. */
if (!valid_unit_name &&
!ENDSWITH_SET(de->d_name, ".wants", ".requires", ".d"))
continue;
filename = path_join(*dir, de->d_name);
if (!filename)
return log_oom();
if (paths) {
r = set_consume(paths, filename);
if (r < 0)
return log_oom();
/* We will still use filename below. This is safe because we know the set
* holds a reference. */
} else
_filename_free = filename; /* Make sure we free the filename. */
if (!valid_unit_name)
continue;
assert_se(suffix = strrchr(de->d_name, '.'));
/* search_path is ordered by priority (highest first). If the name is already mapped
* to something (incl. itself), it means that we have already seen it, and we should
* ignore it here. */
if (hashmap_contains(ids, de->d_name))
continue;
dirent_ensure_type(d, de);
if (de->d_type == DT_LNK) {
/* We don't explicitly check for alias loops here. unit_ids_map_get() which
* limits the number of hops should be used to access the map. */
_cleanup_free_ char *target = NULL;
r = readlinkat_malloc(dirfd(d), de->d_name, &target);
if (r < 0) {
log_warning_errno(r, "Failed to read symlink %s/%s, ignoring: %m",
*dir, de->d_name);
continue;
}
const bool is_abs = path_is_absolute(target);
if (lp->root_dir || !is_abs) {
char *target_abs = path_join(is_abs ? lp->root_dir : *dir, target);
if (!target_abs)
return log_oom();
free_and_replace(target, target_abs);
}
/* Get rid of "." and ".." components in target path */
r = chase_symlinks(target, lp->root_dir, CHASE_NOFOLLOW | CHASE_NONEXISTENT, &simplified, NULL);
if (r < 0) {
log_warning_errno(r, "Failed to resolve symlink %s pointing to %s, ignoring: %m",
filename, target);
continue;
}
/* Check if the symlink goes outside of our search path.
* If yes, it's a linked unit file or mask, and we don't care about the target name.
* Let's just store the link destination directly.
* If not, let's verify that it's a good symlink. */
char *tail = path_startswith_strv(simplified, lp->search_path);
if (tail) {
bool self_alias;
dst = basename(simplified);
self_alias = streq(dst, de->d_name);
if (is_path(tail))
log_full(self_alias ? LOG_DEBUG : LOG_WARNING,
"Suspicious symlink %s→%s, treating as alias.",
filename, simplified);
r = unit_validate_alias_symlink_and_warn(filename, simplified);
if (r < 0)
continue;
if (self_alias) {
/* A self-alias that has no effect */
log_debug("%s: self-alias: %s/%s → %s, ignoring.",
__func__, *dir, de->d_name, dst);
continue;
}
log_debug("%s: alias: %s/%s → %s", __func__, *dir, de->d_name, dst);
} else {
dst = simplified;
log_debug("%s: linked unit file: %s/%s → %s", __func__, *dir, de->d_name, dst);
}
} else {
dst = filename;
log_debug("%s: normal unit file: %s", __func__, dst);
}
r = hashmap_put_strdup(&ids, de->d_name, dst);
if (r < 0)
return log_warning_errno(r, "Failed to add entry to hashmap (%s→%s): %m",
de->d_name, dst);
}
}
/* Let's also put the names in the reverse db. */
const char *dummy, *src;
HASHMAP_FOREACH_KEY(dummy, src, ids) {
const char *dst;
r = unit_ids_map_get(ids, src, &dst);
if (r < 0)
continue;
if (null_or_empty_path(dst) != 0)
continue;
/* Do not treat instance symlinks that point to the template as aliases */
if (unit_name_is_valid(basename(dst), UNIT_NAME_TEMPLATE) &&
unit_name_is_valid(src, UNIT_NAME_INSTANCE))
continue;
r = string_strv_hashmap_put(&names, basename(dst), src);
if (r < 0)
return log_warning_errno(r, "Failed to add entry to hashmap (%s→%s): %m",
basename(dst), src);
}
if (cache_timestamp_hash)
*cache_timestamp_hash = timestamp_hash;
hashmap_free_and_replace(*unit_ids_map, ids);
hashmap_free_and_replace(*unit_names_map, names);
if (path_cache)
set_free_and_replace(*path_cache, paths);
return 1;
}
int unit_file_find_fragment(
Hashmap *unit_ids_map,
Hashmap *unit_name_map,
const char *unit_name,
const char **ret_fragment_path,
Set **ret_names) {
const char *fragment = NULL;
_cleanup_free_ char *template = NULL, *instance = NULL;
_cleanup_set_free_free_ Set *names = NULL;
char **t, **nnn;
int r, name_type;
/* Finds a fragment path, and returns the set of names:
* if we have …/foo.service and …/foo-alias.service→foo.service,
* and …/foo@.service and …/foo-alias@.service→foo@.service,
* and …/foo@inst.service,
* this should return:
* foo.service → …/foo.service, {foo.service, foo-alias.service},
* foo-alias.service → …/foo.service, {foo.service, foo-alias.service},
* foo@.service → …/foo@.service, {foo@.service, foo-alias@.service},
* foo-alias@.service → …/foo@.service, {foo@.service, foo-alias@.service},
* foo@bar.service → …/foo@.service, {foo@bar.service, foo-alias@bar.service},
* foo-alias@bar.service → …/foo@.service, {foo@bar.service, foo-alias@bar.service},
* foo-alias@inst.service → …/foo@inst.service, {foo@inst.service, foo-alias@inst.service}.
*/
name_type = unit_name_to_instance(unit_name, &instance);
if (name_type < 0)
return name_type;
names = set_new(&string_hash_ops);
if (!names)
return -ENOMEM;
/* The unit always has its own name if it's not a template. */
if (IN_SET(name_type, UNIT_NAME_PLAIN, UNIT_NAME_INSTANCE)) {
r = set_put_strdup(&names, unit_name);
if (r < 0)
return r;
}
/* First try to load fragment under the original name */
r = unit_ids_map_get(unit_ids_map, unit_name, &fragment);
if (r < 0 && !IN_SET(r, -ENOENT, -ENXIO))
return log_debug_errno(r, "Cannot load unit %s: %m", unit_name);
if (fragment) {
/* Add any aliases of the original name to the set of names */
nnn = hashmap_get(unit_name_map, basename(fragment));
STRV_FOREACH(t, nnn) {
if (name_type == UNIT_NAME_INSTANCE && unit_name_is_valid(*t, UNIT_NAME_TEMPLATE)) {
char *inst;
r = unit_name_replace_instance(*t, instance, &inst);
if (r < 0)
return log_debug_errno(r, "Cannot build instance name %s+%s: %m", *t, instance);
if (!streq(unit_name, inst))
log_debug("%s: %s has alias %s", __func__, unit_name, inst);
log_info("%s: %s+%s → %s", __func__, *t, instance, inst);
r = set_consume(names, inst);
} else {
if (!streq(unit_name, *t))
log_debug("%s: %s has alias %s", __func__, unit_name, *t);
r = set_put_strdup(&names, *t);
}
if (r < 0)
return r;
}
}
if (!fragment && name_type == UNIT_NAME_INSTANCE) {
/* Look for a fragment under the template name */
r = unit_name_template(unit_name, &template);
if (r < 0)
return log_debug_errno(r, "Failed to determine template name: %m");
r = unit_ids_map_get(unit_ids_map, template, &fragment);
if (r < 0 && !IN_SET(r, -ENOENT, -ENXIO))
return log_debug_errno(r, "Cannot load template %s: %m", template);
if (fragment) {
/* Add any aliases of the original name to the set of names */
nnn = hashmap_get(unit_name_map, basename(fragment));
STRV_FOREACH(t, nnn) {
_cleanup_free_ char *inst = NULL;
const char *inst_fragment = NULL;
r = unit_name_replace_instance(*t, instance, &inst);
if (r < 0)
return log_debug_errno(r, "Cannot build instance name %s+%s: %m", template, instance);
/* Exclude any aliases that point in some other direction. */
r = unit_ids_map_get(unit_ids_map, inst, &inst_fragment);
if (r < 0 && !IN_SET(r, -ENOENT, -ENXIO))
return log_debug_errno(r, "Cannot find instance fragment %s: %m", inst);
if (inst_fragment &&
!streq(basename(inst_fragment), basename(fragment))) {
log_debug("Instance %s has fragment %s and is not an alias of %s.",
inst, inst_fragment, unit_name);
continue;
}
if (!streq(unit_name, inst))
log_debug("%s: %s has alias %s", __func__, unit_name, inst);
r = set_consume(names, TAKE_PTR(inst));
if (r < 0)
return r;
}
}
}
*ret_fragment_path = fragment;
*ret_names = TAKE_PTR(names);
// FIXME: if instance, consider any unit names with different template name
return 0;
}
static const char * const rlmap[] = {
"emergency", SPECIAL_EMERGENCY_TARGET,
"-b", SPECIAL_EMERGENCY_TARGET,
"rescue", SPECIAL_RESCUE_TARGET,
"single", SPECIAL_RESCUE_TARGET,
"-s", SPECIAL_RESCUE_TARGET,
"s", SPECIAL_RESCUE_TARGET,
"S", SPECIAL_RESCUE_TARGET,
"1", SPECIAL_RESCUE_TARGET,
"2", SPECIAL_MULTI_USER_TARGET,
"3", SPECIAL_MULTI_USER_TARGET,
"4", SPECIAL_MULTI_USER_TARGET,
"5", SPECIAL_GRAPHICAL_TARGET,
NULL
};
static const char * const rlmap_initrd[] = {
"emergency", SPECIAL_EMERGENCY_TARGET,
"rescue", SPECIAL_RESCUE_TARGET,
NULL
};
const char* runlevel_to_target(const char *word) {
const char * const *rlmap_ptr;
size_t i;
if (!word)
return NULL;
if (in_initrd()) {
word = startswith(word, "rd.");
if (!word)
return NULL;
}
rlmap_ptr = in_initrd() ? rlmap_initrd : rlmap;
for (i = 0; rlmap_ptr[i]; i += 2)
if (streq(word, rlmap_ptr[i]))
return rlmap_ptr[i+1];
return NULL;
}
-61
View File
@@ -1,61 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stdbool.h>
#include "hashmap.h"
#include "time-util.h"
#include "unit-name.h"
typedef enum UnitFileState UnitFileState;
typedef enum UnitFileScope UnitFileScope;
typedef struct LookupPaths LookupPaths;
enum UnitFileState {
UNIT_FILE_ENABLED,
UNIT_FILE_ENABLED_RUNTIME,
UNIT_FILE_LINKED,
UNIT_FILE_LINKED_RUNTIME,
UNIT_FILE_ALIAS,
UNIT_FILE_MASKED,
UNIT_FILE_MASKED_RUNTIME,
UNIT_FILE_STATIC,
UNIT_FILE_DISABLED,
UNIT_FILE_INDIRECT,
UNIT_FILE_GENERATED,
UNIT_FILE_TRANSIENT,
UNIT_FILE_BAD,
_UNIT_FILE_STATE_MAX,
_UNIT_FILE_STATE_INVALID = -1
};
enum UnitFileScope {
UNIT_FILE_SYSTEM,
UNIT_FILE_GLOBAL,
UNIT_FILE_USER,
_UNIT_FILE_SCOPE_MAX,
_UNIT_FILE_SCOPE_INVALID = -1
};
bool unit_type_may_alias(UnitType type) _const_;
bool unit_type_may_template(UnitType type) _const_;
int unit_symlink_name_compatible(const char *symlink, const char *target, bool instance_propagation);
int unit_validate_alias_symlink_and_warn(const char *filename, const char *target);
bool lookup_paths_timestamp_hash_same(const LookupPaths *lp, uint64_t timestamp_hash, uint64_t *ret_new);
int unit_file_build_name_map(
const LookupPaths *lp,
uint64_t *cache_timestamp_hash,
Hashmap **unit_ids_map,
Hashmap **unit_names_map,
Set **path_cache);
int unit_file_find_fragment(
Hashmap *unit_ids_map,
Hashmap *unit_name_map,
const char *unit_name,
const char **ret_fragment_path,
Set **ret_names);
const char* runlevel_to_target(const char *rl);