Merge pull request #24018 from keszybz/generator-cleanups

Make generators easier to test, fix various corner issues
This commit is contained in:
Yu Watanabe
2022-07-16 05:11:49 +09:00
committed by GitHub
16 changed files with 201 additions and 184 deletions
+3
View File
@@ -51,6 +51,9 @@ All tools:
* `$SYSTEMD_FSTAB` — if set, use this path instead of `/etc/fstab`. Only useful
for debugging.
* `$SYSTEMD_SYSROOT_FSTAB` — if set, use this path instead of
`/sysroot/etc/fstab`. Only useful for debugging `systemd-fstab-generator`.
* `$SYSTEMD_CRYPTTAB` — if set, use this path instead of `/etc/crypttab`. Only
useful for debugging. Currently only supported by
`systemd-cryptsetup-generator`.
+9 -8
View File
@@ -272,13 +272,14 @@
<varlistentry>
<term>BARRIER=1</term>
<listitem><para>Tells the service manager that the client is explicitly requesting synchronization by means of
closing the file descriptor sent with this command. The service manager guarantees that the processing of a <varname>
BARRIER=1</varname> command will only happen after all previous notification messages sent before this command
have been processed. Hence, this command accompanied with a single file descriptor can be used to synchronize
against reception of all previous status messages. Note that this command cannot be mixed with other notifications,
and has to be sent in a separate message to the service manager, otherwise all assignments will be ignored. Note that
sending 0 or more than 1 file descriptor with this command is a violation of the protocol.</para></listitem>
<listitem><para>Tells the service manager that the client is explicitly requesting synchronization by
means of closing the file descriptor sent with this command. The service manager guarantees that the
processing of a <varname>BARRIER=1</varname> command will only happen after all previous notification
messages sent before this command have been processed. Hence, this command accompanied with a single
file descriptor can be used to synchronize against reception of all previous status messages. Note
that this command cannot be mixed with other notifications, and has to be sent in a separate message
to the service manager, otherwise all assignments will be ignored. Note that sending 0 or more than 1
file descriptor with this command is a violation of the protocol.</para></listitem>
</varlistentry>
</variablelist>
@@ -341,7 +342,7 @@
<para><function>sd_notify_barrier()</function> allows the caller to
synchronize against reception of previously sent notification messages
and uses the <literal>BARRIER=1</literal> command. It takes a relative
and uses the <varname>BARRIER=1</varname> command. It takes a relative
<varname>timeout</varname> value in microseconds which is passed to
<citerefentry><refentrytitle>ppoll</refentrytitle><manvolnum>2</manvolnum>
</citerefentry>. A value of UINT64_MAX is interpreted as infinite timeout.
+3 -3
View File
@@ -46,7 +46,7 @@
for more information about special <filename>/etc/fstab</filename>
mount options this generator understands.</para>
<para>One special topic is handling of symbolic links. Historical init
<para>One special topic is handling of symbolic links. Historical init
implementations supported symlinks in <filename>/etc/fstab</filename>.
Because mount units will refuse mounts where the target is a symbolic link,
this generator will resolve any symlinks as far as possible when processing
@@ -251,8 +251,8 @@
<citerefentry><refentrytitle>systemd.swap</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-cryptsetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry>
<citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
<ulink url="https://systemd.io/ENVIRONMENT/">Known Environment Variables</ulink>
</para>
</refsect1>
</refentry>
+11 -10
View File
@@ -26,8 +26,8 @@
<cmdsynopsis>
<command index='false'>/path/to/generator</command>
<arg choice="plain"><replaceable>normal-dir</replaceable></arg>
<arg choice="plain"><replaceable>early-dir</replaceable></arg>
<arg choice="plain"><replaceable>late-dir</replaceable></arg>
<arg choice="option"><replaceable>early-dir</replaceable></arg>
<arg choice="option"><replaceable>late-dir</replaceable></arg>
</cmdsynopsis>
<para>
@@ -56,13 +56,13 @@
that they can extend the unit file hierarchy the service manager subsequently loads and operates
on.</para>
<para>Each generator is called with three directory paths that are to be used for generator output. In
these three directories, generators may dynamically generate unit files (regular ones, instances, as well
as templates), unit file <filename>.d/</filename> drop-ins, and create symbolic links to unit files to
add additional dependencies, create aliases, or instantiate existing templates. Those directories are
included in the unit load path of
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, allowing
generated configuration to extend or override existing definitions.</para>
<para><command>systemd</command> will call each generator with three directory paths that are to be used
for generator output. In these three directories, generators may dynamically generate unit files (regular
ones, instances, as well as templates), unit file <filename>.d/</filename> drop-ins, and create symbolic
links to unit files to add additional dependencies, create aliases, or instantiate existing templates.
Those directories are included in the unit load path, allowing generated configuration to extend or
override existing definitions. For tests, generators may be called with just one argument; the generator
should assume that all three paths are the same in that case.</para>
<para>Directory paths for generator output differ by priority: <filename>…/generator.early</filename> has
priority higher than the admin configuration in <filename>/etc/</filename>, while
@@ -96,7 +96,8 @@
<para>Generators are invoked with three arguments: paths to directories where generators can place their
generated unit files or symlinks. By default those paths are runtime directories that are included in the
search path of <command>systemd</command>, but a generator may be called with different paths for
debugging purposes.</para>
debugging purposes. If only one argument is provided, the generator should use the same directory as the
the three output paths.</para>
<orderedlist>
<listitem>
+2 -20
View File
@@ -1153,30 +1153,12 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
}
static bool should_parse_proc_cmdline(void) {
const char *e;
pid_t p;
/* PID1 always reads the kernel command line. */
if (getpid_cached() == 1)
return true;
/* If the process is directly executed by PID1 (e.g. ExecStart= or generator), systemd-importd,
* or systemd-homed, then $SYSTEMD_EXEC_PID= is set, and read the command line. */
e = getenv("SYSTEMD_EXEC_PID");
if (!e)
return false;
if (streq(e, "*"))
/* For testing. */
return true;
if (parse_pid(e, &p) < 0) {
/* We know that systemd sets the variable correctly. Something else must have set it. */
log_debug("Failed to parse \"$SYSTEMD_EXEC_PID=%s\". Ignoring.", e);
return false;
}
return getpid_cached() == p;
/* Otherwise, parse the commandline if invoked directly by systemd. */
return invoked_by_systemd();
}
void log_parse_environment_variables(void) {
+24
View File
@@ -1579,6 +1579,30 @@ bool invoked_as(char *argv[], const char *token) {
return strstr(last_path_component(argv[0]), token);
}
bool invoked_by_systemd(void) {
int r;
/* If the process is directly executed by PID1 (e.g. ExecStart= or generator), systemd-importd,
* or systemd-homed, then $SYSTEMD_EXEC_PID= is set, and read the command line. */
const char *e = getenv("SYSTEMD_EXEC_PID");
if (!e)
return false;
if (streq(e, "*"))
/* For testing. */
return true;
pid_t p;
r = parse_pid(e, &p);
if (r < 0) {
/* We know that systemd sets the variable correctly. Something else must have set it. */
log_debug_errno(r, "Failed to parse \"SYSTEMD_EXEC_PID=%s\", ignoring: %m", e);
return false;
}
return getpid_cached() == p;
}
_noreturn_ void freeze(void) {
log_close();
+2
View File
@@ -190,6 +190,8 @@ int setpriority_closest(int priority);
bool invoked_as(char *argv[], const char *token);
bool invoked_by_systemd(void);
_noreturn_ void freeze(void);
bool argv_looks_like_help(int argc, char **argv);
+17 -33
View File
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include "efi-loader.h"
@@ -10,62 +9,47 @@
#include "mkdir.h"
#include "special.h"
#include "string-util.h"
#include "util.h"
#include "virt.h"
/* This generator pulls systemd-bless-boot.service into the initial transaction if the "LoaderBootCountPath" EFI
* variable is set, i.e. the system boots up with boot counting in effect, which means we should mark the boot as
* "good" if we manage to boot up far enough. */
/* This generator pulls systemd-bless-boot.service into the initial transaction if the "LoaderBootCountPath"
* EFI variable is set, i.e. the system boots up with boot counting in effect, which means we should mark the
* boot as "good" if we manage to boot up far enough. */
static const char *arg_dest = "/tmp";
int main(int argc, char *argv[]) {
const char *p;
log_setup_generator();
if (argc > 1 && argc != 4) {
log_error("This program takes three or no arguments.");
return EXIT_FAILURE;
}
if (argc > 1)
arg_dest = argv[2];
static int run(const char *dest, const char *dest_early, const char *dest_late) {
if (in_initrd() > 0) {
log_debug("Skipping generator, running in the initrd.");
return EXIT_SUCCESS;
return 0;
}
if (detect_container() > 0) {
log_debug("Skipping generator, running in a container.");
return EXIT_SUCCESS;
return 0;
}
if (!is_efi_boot()) {
log_debug("Skipping generator, not an EFI boot.");
return EXIT_SUCCESS;
return 0;
}
if (access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderBootCountPath)), F_OK) < 0) {
if (errno == ENOENT) {
log_debug_errno(errno, "Skipping generator, not booted with boot counting in effect.");
return EXIT_SUCCESS;
return 0;
}
log_error_errno(errno, "Failed to check if LoaderBootCountPath EFI variable exists: %m");
return EXIT_FAILURE;
return log_error_errno(errno, "Failed to check if LoaderBootCountPath EFI variable exists: %m");
}
/* We pull this in from basic.target so that it ends up in all "regular" boot ups, but not in rescue.target or
* even emergency.target. */
p = strjoina(arg_dest, "/" SPECIAL_BASIC_TARGET ".wants/systemd-bless-boot.service");
/* We pull this in from basic.target so that it ends up in all "regular" boot ups, but not in
* rescue.target or even emergency.target. */
const char *p = strjoina(dest_early, "/" SPECIAL_BASIC_TARGET ".wants/systemd-bless-boot.service");
(void) mkdir_parents(p, 0755);
if (symlink(SYSTEM_DATA_UNIT_DIR "/systemd-bless-boot.service", p) < 0) {
log_error_errno(errno, "Failed to create symlink '%s': %m", p);
return EXIT_FAILURE;
}
if (symlink(SYSTEM_DATA_UNIT_DIR "/systemd-bless-boot.service", p) < 0)
return log_error_errno(errno, "Failed to create symlink '%s': %m", p);
return EXIT_SUCCESS;
return 0;
}
DEFINE_MAIN_GENERATOR_FUNCTION(run);
+38 -35
View File
@@ -2530,14 +2530,14 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
return 0;
}
/* As extra safety check, let's make sure the string we get doesn't contain embedded NUL bytes. We permit one
* trailing NUL byte in the message, but don't expect it. */
/* As extra safety check, let's make sure the string we get doesn't contain embedded NUL bytes.
* We permit one trailing NUL byte in the message, but don't expect it. */
if (n > 1 && memchr(buf, 0, n-1)) {
log_warning("Received notify message with embedded NUL bytes. Ignoring.");
return 0;
}
/* Make sure it's NUL-terminated, then parse it to obtain the tags list */
/* Make sure it's NUL-terminated, then parse it to obtain the tags list. */
buf[n] = 0;
tags = strv_split_newlines(buf);
if (!tags) {
@@ -2545,7 +2545,7 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
return 0;
}
/* possibly a barrier fd, let's see */
/* Possibly a barrier fd, let's see. */
if (manager_process_barrier_fd(tags, fds))
return 0;
@@ -2566,8 +2566,8 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
if (!array_copy)
log_oom();
}
/* And now invoke the per-unit callbacks. Note that manager_invoke_notify_message() will handle duplicate units
* make sure we only invoke each unit's handler once. */
/* And now invoke the per-unit callbacks. Note that manager_invoke_notify_message() will handle
* duplicate units make sure we only invoke each unit's handler once. */
if (u1) {
manager_invoke_notify_message(m, u1, ucred, tags, fds);
found = true;
@@ -2620,8 +2620,8 @@ static int manager_dispatch_sigchld(sd_event_source *source, void *userdata) {
assert(source);
assert(m);
/* First we call waitid() for a PID and do not reap the zombie. That way we can still access /proc/$PID for it
* while it is a zombie. */
/* First we call waitid() for a PID and do not reap the zombie. That way we can still access
* /proc/$PID for it while it is a zombie. */
if (waitid(P_ALL, 0, &si, WEXITED|WNOHANG|WNOWAIT) < 0) {
@@ -2988,15 +2988,15 @@ static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32
assert(m);
assert(m->idle_pipe[2] == fd);
/* There's at least one Type=idle child that just gave up on us waiting for the boot process to complete. Let's
* now turn off any further console output if there's at least one service that needs console access, so that
* from now on our own output should not spill into that service's output anymore. After all, we support
* Type=idle only to beautify console output and it generally is set on services that want to own the console
* exclusively without our interference. */
/* There's at least one Type=idle child that just gave up on us waiting for the boot process to
* complete. Let's now turn off any further console output if there's at least one service that needs
* console access, so that from now on our own output should not spill into that service's output
* anymore. After all, we support Type=idle only to beautify console output and it generally is set
* on services that want to own the console exclusively without our interference. */
m->no_console_output = m->n_on_console > 0;
/* Acknowledge the child's request, and let all all other children know too that they shouldn't wait any longer
* by closing the pipes towards them, which is what they are waiting for. */
/* Acknowledge the child's request, and let all all other children know too that they shouldn't wait
* any longer by closing the pipes towards them, which is what they are waiting for. */
manager_close_idle_pipe(m);
return 0;
@@ -3092,8 +3092,8 @@ int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e,
if (r < 0)
return r;
/* Permit addressing units by invocation ID: if the passed bus path is suffixed by a 128bit ID then we use it
* as invocation ID. */
/* Permit addressing units by invocation ID: if the passed bus path is suffixed by a 128bit ID then
* we use it as invocation ID. */
r = sd_id128_from_string(n, &invocation_id);
if (r >= 0) {
u = hashmap_get(m->units_by_invocation_id, &invocation_id);
@@ -3915,10 +3915,10 @@ int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit) {
void manager_recheck_dbus(Manager *m) {
assert(m);
/* Connects to the bus if the dbus service and socket are running. If we are running in user mode this is all
* it does. In system mode we'll also connect to the system bus (which will most likely just reuse the
* connection of the API bus). That's because the system bus after all runs as service of the system instance,
* while in the user instance we can assume it's already there. */
/* Connects to the bus if the dbus service and socket are running. If we are running in user mode
* this is all it does. In system mode we'll also connect to the system bus (which will most likely
* just reuse the connection of the API bus). That's because the system bus after all runs as service
* of the system instance, while in the user instance we can assume it's already there. */
if (MANAGER_IS_RELOADING(m))
return; /* don't check while we are reloading… */
@@ -3990,9 +3990,9 @@ void manager_recheck_journal(Manager *m) {
if (MANAGER_IS_RELOADING(m))
return;
/* The journal is fully and entirely up? If so, let's permit logging to it, if that's configured. If the
* journal is down, don't ever log to it, otherwise we might end up deadlocking ourselves as we might trigger
* an activation ourselves we can't fulfill. */
/* The journal is fully and entirely up? If so, let's permit logging to it, if that's configured. If
* the journal is down, don't ever log to it, otherwise we might end up deadlocking ourselves as we
* might trigger an activation ourselves we can't fulfill. */
log_set_prohibit_ipc(!manager_journal_is_running(m));
log_open();
}
@@ -4264,13 +4264,14 @@ static void manager_unref_uid_internal(
assert(uid_is_valid(uid));
assert(_clean_ipc);
/* A generic implementation, covering both manager_unref_uid() and manager_unref_gid(), under the assumption
* that uid_t and gid_t are actually defined the same way, with the same validity rules.
/* A generic implementation, covering both manager_unref_uid() and manager_unref_gid(), under the
* assumption that uid_t and gid_t are actually defined the same way, with the same validity rules.
*
* We store a hashmap where the key is the UID/GID and the value is a 32bit reference counter, whose highest
* bit is used as flag for marking UIDs/GIDs whose IPC objects to remove when the last reference to the UID/GID
* is dropped. The flag is set to on, once at least one reference from a unit where RemoveIPC= is set is added
* on a UID/GID. It is reset when the UID's/GID's reference counter drops to 0 again. */
* We store a hashmap where the key is the UID/GID and the value is a 32bit reference counter, whose
* highest bit is used as flag for marking UIDs/GIDs whose IPC objects to remove when the last
* reference to the UID/GID is dropped. The flag is set to on, once at least one reference from a
* unit where RemoveIPC= is set is added on a UID/GID. It is reset when the UID's/GID's reference
* counter drops to 0 again. */
assert_cc(sizeof(uid_t) == sizeof(gid_t));
assert_cc(UID_INVALID == (uid_t) GID_INVALID);
@@ -4318,8 +4319,9 @@ static int manager_ref_uid_internal(
assert(uid_refs);
assert(uid_is_valid(uid));
/* A generic implementation, covering both manager_ref_uid() and manager_ref_gid(), under the assumption
* that uid_t and gid_t are actually defined the same way, with the same validity rules. */
/* A generic implementation, covering both manager_ref_uid() and manager_ref_gid(), under the
* assumption that uid_t and gid_t are actually defined the same way, with the same validity
* rules. */
assert_cc(sizeof(uid_t) == sizeof(gid_t));
assert_cc(UID_INVALID == (uid_t) GID_INVALID);
@@ -4419,9 +4421,10 @@ int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t re
assert_se(source);
assert_se(m);
/* Invoked whenever a child process succeeded resolving its user/group to use and sent us the resulting UID/GID
* in a datagram. We parse the datagram here and pass it off to the unit, so that it can add a reference to the
* UID/GID so that it can destroy the UID/GID's IPC objects when the reference counter drops to 0. */
/* Invoked whenever a child process succeeded resolving its user/group to use and sent us the
* resulting UID/GID in a datagram. We parse the datagram here and pass it off to the unit, so that
* it can add a reference to the UID/GID so that it can destroy the UID/GID's IPC objects when the
* reference counter drops to 0. */
l = recv(fd, &buffer, sizeof(buffer), MSG_DONTWAIT);
if (l < 0) {
@@ -8,6 +8,7 @@
#include "escape.h"
#include "glyph-util.h"
#include "log.h"
#include "main-func.h"
#include "path-lookup.h"
#include "strv.h"
@@ -80,20 +81,19 @@ static int load_and_print(void) {
return 0;
}
int main(int argc, char *argv[]) {
static int run(int argc, char *argv[]) {
int r;
log_parse_environment();
log_open();
if (argc > 1) {
log_error("This program takes no arguments.");
return EXIT_FAILURE;
}
if (argc > 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
r = load_and_print();
if (r < 0)
log_error_errno(r, "Failed to load environment.d: %m");
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
return log_error_errno(r, "Failed to load environment.d: %m");
return 0;
}
DEFINE_MAIN_FUNCTION(run);
+1 -1
View File
@@ -54,7 +54,7 @@ static void start_target(const char *target, const char *mode) {
log_info("Running request %s/start/%s", target, mode);
/* Start these units only if we can replace base.target with it */
/* Start this unit only if we can replace basic.target with it */
r = sd_bus_call_method(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
+61 -43
View File
@@ -92,6 +92,7 @@ static int write_what(FILE *f, const char *what) {
}
static int add_swap(
const char *source,
const char *what,
struct mntent *me,
MountPointFlags flags) {
@@ -122,7 +123,7 @@ static int add_swap(
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
r = generator_open_unit_file(arg_dest, fstab_path(), name, &f);
r = generator_open_unit_file(arg_dest, source, name, &f);
if (r < 0)
return r;
@@ -130,7 +131,7 @@ static int add_swap(
"[Unit]\n"
"Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n"
"SourcePath=%s\n",
fstab_path());
source);
r = generator_write_blockdev_dependency(f, what);
if (r < 0)
@@ -188,7 +189,7 @@ static bool mount_in_initrd(struct mntent *me) {
assert(me);
return fstab_test_option(me->mnt_opts, "x-initrd.mount\0") ||
streq(me->mnt_dir, "/usr");
path_equal(me->mnt_dir, "/usr");
}
static int write_timeout(
@@ -339,6 +340,7 @@ static int write_extra_dependencies(FILE *f, const char *opts) {
}
static int add_mount(
const char *source,
const char *dest,
const char *what,
const char *where,
@@ -347,8 +349,7 @@ static int add_mount(
const char *opts,
int passno,
MountPointFlags flags,
const char *post,
const char *source) {
const char *target_unit) {
_cleanup_free_ char
*name = NULL,
@@ -362,7 +363,7 @@ static int add_mount(
assert(what);
assert(where);
assert(opts);
assert(post);
assert(target_unit);
assert(source);
if (streq_ptr(fstype, "autofs"))
@@ -406,7 +407,7 @@ static int add_mount(
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
r = generator_open_unit_file(dest, fstab_path(), name, &f);
r = generator_open_unit_file(dest, source, name, &f);
if (r < 0)
return r;
@@ -434,10 +435,10 @@ static int add_mount(
if (r < 0)
return r;
/* Order the mount unit we generate relative to the post unit, so that DefaultDependencies= on the
/* Order the mount unit we generate relative to target_unit, so that DefaultDependencies= on the
* target unit won't affect us. */
if (post && !FLAGS_SET(flags, MOUNT_NOFAIL))
fprintf(f, "Before=%s\n", post);
if (!FLAGS_SET(flags, MOUNT_NOFAIL))
fprintf(f, "Before=%s\n", target_unit);
if (passno != 0) {
r = generator_write_fsck_deps(f, dest, what, where, fstype);
@@ -505,14 +506,14 @@ static int add_mount(
}
if (flags & MOUNT_GROWFS) {
r = generator_hook_up_growfs(dest, where, post);
r = generator_hook_up_growfs(dest, where, target_unit);
if (r < 0)
return r;
}
if (!FLAGS_SET(flags, MOUNT_AUTOMOUNT)) {
if (!FLAGS_SET(flags, MOUNT_NOAUTO) && strv_isempty(wanted_by) && strv_isempty(required_by)) {
r = generator_add_symlink(dest, post,
r = generator_add_symlink(dest, target_unit,
(flags & MOUNT_NOFAIL) ? "wants" : "requires", name);
if (r < 0)
return r;
@@ -536,7 +537,7 @@ static int add_mount(
f = safe_fclose(f);
r = generator_open_unit_file(dest, fstab_path(), automount_name, &f);
r = generator_open_unit_file(dest, source, automount_name, &f);
if (r < 0)
return r;
@@ -560,7 +561,7 @@ static int add_mount(
if (r < 0)
return log_error_errno(r, "Failed to write unit file %s: %m", automount_name);
r = generator_add_symlink(dest, post,
r = generator_add_symlink(dest, target_unit,
(flags & MOUNT_NOFAIL) ? "wants" : "requires", automount_name);
if (r < 0)
return r;
@@ -569,13 +570,21 @@ static int add_mount(
return 0;
}
static const char* sysroot_fstab_path(void) {
return getenv("SYSTEMD_SYSROOT_FSTAB") ?: "/sysroot/etc/fstab";
}
static int parse_fstab(bool initrd) {
_cleanup_endmntent_ FILE *f = NULL;
const char *fstab;
struct mntent *me;
int r = 0;
fstab = initrd ? "/sysroot/etc/fstab" : fstab_path();
if (initrd)
fstab = sysroot_fstab_path();
else
fstab = fstab_path();
log_debug("Parsing %s...", fstab);
f = setmntent(fstab, "re");
@@ -622,13 +631,25 @@ static int parse_fstab(bool initrd) {
* mount units, but causes problems since it historically worked to have symlinks in e.g.
* /etc/fstab. So we canonicalize here. Note that we use CHASE_NONEXISTENT to handle the case
* where a symlink refers to another mount target; this works assuming the sub-mountpoint
* target is the final directory. */
* target is the final directory.
*
* FIXME: when chase_symlinks() learns to chase non-existent paths, use this here and
* drop the prefixing with /sysroot on error below.
*/
k = chase_symlinks(where, initrd ? "/sysroot" : NULL,
CHASE_PREFIX_ROOT | CHASE_NONEXISTENT,
&canonical_where, NULL);
if (k < 0) /* If we can't canonicalize we continue on as if it wasn't a symlink */
log_debug_errno(k, "Failed to read symlink target for %s, ignoring: %m", where);
else if (streq(canonical_where, where)) /* If it was fully canonicalized, suppress the change */
if (k < 0) {
/* If we can't canonicalize, continue as if it wasn't a symlink */
log_debug_errno(k, "Failed to read symlink target for %s, using as-is: %m", where);
if (initrd) {
canonical_where = path_join("/sysroot", where);
if (!canonical_where)
return log_oom();
}
} else if (streq(canonical_where, where)) /* If it was fully canonicalized, suppress the change */
canonical_where = mfree(canonical_where);
else
log_debug("Canonicalized what=%s where=%s to %s", what, where, canonical_where);
@@ -650,10 +671,9 @@ static int parse_fstab(bool initrd) {
nofail * MOUNT_NOFAIL;
if (streq(me->mnt_type, "swap"))
k = add_swap(what, me, flags);
k = add_swap(fstab, what, me, flags);
else {
bool rw_only, automount;
const char *post;
rw_only = fstab_test_option(me->mnt_opts, "x-systemd.rw-only\0");
automount = fstab_test_option(me->mnt_opts,
@@ -663,14 +683,13 @@ static int parse_fstab(bool initrd) {
flags |= rw_only * MOUNT_RW_ONLY |
automount * MOUNT_AUTOMOUNT;
if (initrd)
post = SPECIAL_INITRD_FS_TARGET;
else if (mount_is_network(me))
post = SPECIAL_REMOTE_FS_TARGET;
else
post = SPECIAL_LOCAL_FS_TARGET;
const char *target_unit =
initrd ? SPECIAL_INITRD_FS_TARGET :
mount_is_network(me) ? SPECIAL_REMOTE_FS_TARGET :
SPECIAL_LOCAL_FS_TARGET;
k = add_mount(arg_dest,
k = add_mount(fstab,
arg_dest,
what,
canonical_where ?: where,
canonical_where ? where: NULL,
@@ -678,8 +697,7 @@ static int parse_fstab(bool initrd) {
me->mnt_opts,
me->mnt_passno,
flags,
post,
fstab);
target_unit);
}
if (r >= 0 && k < 0)
@@ -752,7 +770,7 @@ static int add_sysroot_mount(void) {
}
if (streq(arg_root_what, "gpt-auto")) {
/* This is handled by the gpt-auto generator */
/* This is handled by gpt-auto-generator */
log_debug("Skipping root directory handling, as gpt-auto was requested.");
return 0;
}
@@ -818,7 +836,8 @@ static int add_sysroot_mount(void) {
return r;
}
return add_mount(arg_dest,
return add_mount("/proc/cmdline",
arg_dest,
what,
"/sysroot",
NULL,
@@ -826,8 +845,7 @@ static int add_sysroot_mount(void) {
opts,
is_device_path(what) ? 1 : 0, /* passno */
0, /* makefs off, growfs off, noauto off, nofail off, automount off */
SPECIAL_INITRD_ROOT_FS_TARGET,
"/proc/cmdline");
SPECIAL_INITRD_ROOT_FS_TARGET);
}
static int add_sysroot_usr_mount(void) {
@@ -898,7 +916,8 @@ static int add_sysroot_usr_mount(void) {
log_debug("Found entry what=%s where=/sysusr/usr type=%s opts=%s", what, strna(arg_usr_fstype), strempty(opts));
r = add_mount(arg_dest,
r = add_mount("/proc/cmdline",
arg_dest,
what,
"/sysusr/usr",
NULL,
@@ -906,14 +925,14 @@ static int add_sysroot_usr_mount(void) {
opts,
is_device_path(what) ? 1 : 0, /* passno */
0,
SPECIAL_INITRD_USR_FS_TARGET,
"/proc/cmdline");
SPECIAL_INITRD_USR_FS_TARGET);
if (r < 0)
return r;
log_debug("Synthesizing entry what=/sysusr/usr where=/sysrootr/usr opts=bind");
r = add_mount(arg_dest,
r = add_mount("/proc/cmdline",
arg_dest,
"/sysusr/usr",
"/sysroot/usr",
NULL,
@@ -921,8 +940,7 @@ static int add_sysroot_usr_mount(void) {
"bind",
0,
0,
SPECIAL_INITRD_FS_TARGET,
"/proc/cmdline");
SPECIAL_INITRD_FS_TARGET);
if (r < 0)
return r;
@@ -966,7 +984,8 @@ static int add_volatile_var(void) {
/* If requested, mount /var as tmpfs, but do so only if there's nothing else defined for this. */
return add_mount(arg_dest_late,
return add_mount("/proc/cmdline",
arg_dest_late,
"tmpfs",
"/var",
NULL,
@@ -974,8 +993,7 @@ static int add_volatile_var(void) {
"mode=0755" TMPFS_LIMITS_VAR,
0,
0,
SPECIAL_LOCAL_FS_TARGET,
"/proc/cmdline");
SPECIAL_LOCAL_FS_TARGET);
}
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
@@ -16,7 +16,7 @@
#include "string-util.h"
#include "unit-name.h"
static const char *arg_dest = "/tmp";
static const char *arg_dest = NULL;
static char *arg_resume_device = NULL;
static char *arg_resume_options = NULL;
static char *arg_root_options = NULL;
@@ -109,17 +109,10 @@ static int process_resume(void) {
return 0;
}
static int run(int argc, char *argv[]) {
static int run(const char *dest, const char *dest_early, const char *dest_late) {
int r = 0;
log_setup_generator();
if (argc > 1 && argc != 4)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"This program takes three or no arguments.");
if (argc > 1)
arg_dest = argv[1];
arg_dest = ASSERT_PTR(dest);
/* Don't even consider resuming outside of initramfs. */
if (!in_initrd()) {
@@ -139,4 +132,4 @@ static int run(int argc, char *argv[]) {
return process_resume();
}
DEFINE_MAIN_FUNCTION(run);
DEFINE_MAIN_GENERATOR_FUNCTION(run);
+9 -4
View File
@@ -15,6 +15,7 @@
#include "macro.h"
#include "mkdir-label.h"
#include "path-util.h"
#include "process-util.h"
#include "special.h"
#include "specifier.h"
#include "string-util.h"
@@ -737,11 +738,15 @@ int generator_write_veritysetup_service_section(
}
void log_setup_generator(void) {
/* Disable talking to syslog/journal (i.e. the two IPC-based loggers) if we run in system context. */
if (cg_pid_get_owner_uid(0, NULL) == -ENXIO /* not running in a per-user slice */)
log_set_prohibit_ipc(true);
if (invoked_by_systemd()) {
/* Disable talking to syslog/journal (i.e. the two IPC-based loggers) if we run in system context. */
if (cg_pid_get_owner_uid(0, NULL) == -ENXIO /* not running in a per-user slice */)
log_set_prohibit_ipc(true);
/* This effectively means: journal for per-user generators, kmsg otherwise */
log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
}
log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); /* This effectively means: journal for per-user generators, kmsg otherwise */
log_parse_environment();
(void) log_open();
}
+6 -5
View File
@@ -3,6 +3,7 @@
#include <stdio.h>
#include "macro.h"
#include "main-func.h"
int generator_open_unit_file(
@@ -86,11 +87,11 @@ void log_setup_generator(void);
_DEFINE_MAIN_FUNCTION( \
({ \
log_setup_generator(); \
if (argc > 1 && argc != 4) \
if (!IN_SET(argc, 2, 4)) \
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), \
"This program takes zero or three arguments."); \
"This program takes one or three arguments."); \
}), \
impl(argc > 1 ? argv[1] : "/tmp", \
argc > 1 ? argv[2] : "/tmp", \
argc > 1 ? argv[3] : "/tmp"), \
impl(argv[1], \
argv[argc == 4 ? 2 : 1], \
argv[argc == 4 ? 3 : 1]), \
r < 0 ? EXIT_FAILURE : EXIT_SUCCESS)
+3 -3
View File
@@ -49,9 +49,9 @@ static int start_default_target(sd_bus *bus) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
log_info("Starting default target");
log_info("Starting "SPECIAL_DEFAULT_TARGET);
/* Start these units only if we can replace base.target with it */
/* Start this unit only if we can replace basic.target with it */
r = sd_bus_call_method(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
@@ -62,7 +62,7 @@ static int start_default_target(sd_bus *bus) {
"ss", SPECIAL_DEFAULT_TARGET, "isolate");
if (r < 0)
return log_error_errno(r, "Failed to start default target: %s", bus_error_message(&error, r));
return log_error_errno(r, "Failed to start "SPECIAL_DEFAULT_TARGET": %s", bus_error_message(&error, r));
return 0;
}