Merge pull request #4879 from poettering/systemd

This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2017-01-14 19:48:04 -05:00
commit 6b3d378331
87 changed files with 2788 additions and 1142 deletions

3
.gitignore vendored
View File

@ -126,6 +126,9 @@
/systemd-update-utmp
/systemd-user-sessions
/systemd-vconsole-setup
/systemd-veritysetup
/systemd-veritysetup-generator
/systemd-volatile-root
/tags
/test-acd
/test-acl-util

1
.mkosi/Makefile Symbolic link
View File

@ -0,0 +1 @@
../src/Makefile

View File

@ -64,7 +64,6 @@ BuildPackages=
libxslt
lz4-devel
make
diffutils
pam-devel
pkgconfig
python3-devel

View File

@ -21,6 +21,11 @@ All tools:
* `$SD_EVENT_PROFILE_DELAYS=1` — if set, the sd-event event loop implementation
will print latency information at runtime.
* `$SYSTEMD_PROC_CMDLINE` — if set, may contain a string that is used as kernel
command line instead of the actual one readable from /proc/cmdline. This is
useful for debugging, in order to test generators and other code against
specific kernel command lines.
systemctl:
* `$SYSTEMCTL_FORCE_BUS=1` — if set, do not connect to PID1's private D-Bus

View File

@ -142,6 +142,7 @@ MANPAGES += \
man/systemd-tty-ask-password-agent.1 \
man/systemd-udevd.service.8 \
man/systemd-update-done.service.8 \
man/systemd-volatile-root.service.8 \
man/systemd.1 \
man/systemd.automount.5 \
man/systemd.device.5 \
@ -482,6 +483,7 @@ MANPAGES_ALIAS += \
man/systemd-udevd.8 \
man/systemd-update-done.8 \
man/systemd-user.conf.5 \
man/systemd-volatile-root.8 \
man/udev_device_get_action.3 \
man/udev_device_get_devlinks_list_entry.3 \
man/udev_device_get_devnode.3 \
@ -837,6 +839,7 @@ man/systemd-udevd-kernel.socket.8: man/systemd-udevd.service.8
man/systemd-udevd.8: man/systemd-udevd.service.8
man/systemd-update-done.8: man/systemd-update-done.service.8
man/systemd-user.conf.5: man/systemd-system.conf.5
man/systemd-volatile-root.8: man/systemd-volatile-root.service.8
man/udev_device_get_action.3: man/udev_device_get_syspath.3
man/udev_device_get_devlinks_list_entry.3: man/udev_device_has_tag.3
man/udev_device_get_devnode.3: man/udev_device_get_syspath.3
@ -1790,6 +1793,9 @@ man/systemd-update-done.html: man/systemd-update-done.service.html
man/systemd-user.conf.html: man/systemd-system.conf.html
$(html-alias)
man/systemd-volatile-root.html: man/systemd-volatile-root.service.html
$(html-alias)
man/udev_device_get_action.html: man/udev_device_get_syspath.html
$(html-alias)
@ -2270,13 +2276,20 @@ if HAVE_LIBCRYPTSETUP
MANPAGES += \
man/crypttab.5 \
man/systemd-cryptsetup-generator.8 \
man/systemd-cryptsetup@.service.8
man/systemd-cryptsetup@.service.8 \
man/systemd-veritysetup-generator.8 \
man/systemd-veritysetup@.service.8
MANPAGES_ALIAS += \
man/systemd-cryptsetup.8
man/systemd-cryptsetup.8 \
man/systemd-veritysetup.8
man/systemd-cryptsetup.8: man/systemd-cryptsetup@.service.8
man/systemd-veritysetup.8: man/systemd-veritysetup@.service.8
man/systemd-cryptsetup.html: man/systemd-cryptsetup@.service.html
$(html-alias)
man/systemd-veritysetup.html: man/systemd-veritysetup@.service.html
$(html-alias)
endif
if HAVE_MICROHTTPD
@ -2804,6 +2817,9 @@ EXTRA_DIST += \
man/systemd-update-utmp.service.xml \
man/systemd-user-sessions.service.xml \
man/systemd-vconsole-setup.service.xml \
man/systemd-veritysetup-generator.xml \
man/systemd-veritysetup@.service.xml \
man/systemd-volatile-root.service.xml \
man/systemd.automount.xml \
man/systemd.device.xml \
man/systemd.exec.xml \

View File

@ -397,6 +397,7 @@ rootlibexec_PROGRAMS = \
systemd-initctl \
systemd-shutdown \
systemd-remount-fs \
systemd-volatile-root \
systemd-reply-password \
systemd-fsck \
systemd-ac-power \
@ -538,6 +539,7 @@ nodist_systemunit_DATA = \
units/system-update-cleanup.service \
units/systemd-initctl.service \
units/systemd-remount-fs.service \
units/systemd-volatile-root.service \
units/systemd-ask-password-wall.service \
units/systemd-ask-password-console.service \
units/systemd-sysctl.service \
@ -602,6 +604,7 @@ EXTRA_DIST += \
units/system-update-cleanup.service.in \
units/systemd-initctl.service.in \
units/systemd-remount-fs.service.in \
units/systemd-volatile-root.service.in \
units/systemd-update-utmp.service.in \
units/systemd-update-utmp-runlevel.service.in \
units/systemd-ask-password-wall.service.in \
@ -1065,7 +1068,9 @@ libshared_la_SOURCES = \
src/shared/nsflags.h \
src/shared/nsflags.c \
src/shared/dissect-image.c \
src/shared/dissect-image.h
src/shared/dissect-image.h \
src/shared/volatile-util.c \
src/shared/volatile-util.h
if HAVE_UTMP
libshared_la_SOURCES += \
@ -3065,6 +3070,13 @@ systemd_remount_fs_SOURCES = \
systemd_remount_fs_LDADD = \
libsystemd-shared.la
# ------------------------------------------------------------------------------
systemd_volatile_root_SOURCES = \
src/volatile-root/volatile-root.c
systemd_volatile_root_LDADD = \
libsystemd-shared.la
# ------------------------------------------------------------------------------
systemd_cgroups_agent_SOURCES = \
src/cgroups-agent/cgroups-agent.c
@ -4821,10 +4833,12 @@ EXTRA_DIST += \
# ------------------------------------------------------------------------------
if HAVE_LIBCRYPTSETUP
rootlibexec_PROGRAMS += \
systemd-cryptsetup
systemd-cryptsetup \
systemd-veritysetup
systemgenerator_PROGRAMS += \
systemd-cryptsetup-generator
systemd-cryptsetup-generator \
systemd-veritysetup-generator
dist_systemunit_DATA += \
units/cryptsetup.target \
@ -4847,6 +4861,23 @@ systemd_cryptsetup_generator_SOURCES = \
systemd_cryptsetup_generator_LDADD = \
libsystemd-shared.la
systemd_veritysetup_SOURCES = \
src/veritysetup/veritysetup.c
systemd_veritysetup_CFLAGS = \
$(AM_CFLAGS) \
$(LIBCRYPTSETUP_CFLAGS)
systemd_veritysetup_LDADD = \
libsystemd-shared.la \
$(LIBCRYPTSETUP_LIBS)
systemd_veritysetup_generator_SOURCES = \
src/veritysetup/veritysetup-generator.c
systemd_veritysetup_generator_LDADD = \
libsystemd-shared.la
SYSINIT_TARGET_WANTS += \
cryptsetup.target

5
NEWS
View File

@ -6,6 +6,11 @@ CHANGES WITH 233 in spe
sure your system has dbus >= 1.9.18 running before upgrading to this
version, or override the install path with --with-dbuspolicydir= .
* The shell invoked by debug-shell.service now defaults to /bin/sh in
all cases. If distributions want to use a different shell for this
purpose (for example Fedora's /sbin/sushell) they need to specify
this explicitly at configure time using --with-debug-shell=.
* The confirmation spawn prompt has been reworked to offer the
following choices:

17
TODO
View File

@ -24,6 +24,19 @@ Janitorial Clean-ups:
Features:
* Maybe add a small tool invoked early at boot, that adds in or resizes
partitions automatically, to be used when the media used is actually larger
than the image written onto it is.
* change the dependency Set* objects in Unit structures to become Hashmap*, and
then store a bit mask who created a specific dependency: the source unit via
fragment configuration, the destination unit via fragment configuration, or
the source unit via udev rules (in case of .device units), or any combination
thereof. This information can then be used to flush out old udev-created
dependencies when the udev properties change, and eventually to implement a
"systemctl refresh" operation for reloading the configuration of individual
units without reloading the whole set.
* Add ExecMonitor= setting. May be used multiple times. Forks off a process in
the service cgroup, which is supposed to monitor the service, and when it
exits the service is considered failed by its monitor.
@ -55,8 +68,6 @@ Features:
partition, that is mounted to / and is writable, and where the actual root's
/usr is mounted into.
* add dm-verity boots, and in nspawn (libcryptsetup knows this, should be relatively straight-forward)
* machined: add apis to query /etc/machine-info data of a container
* .mount and .swap units: add Format=yes|no option that formats the partition before mounting/enabling it, implicitly
@ -295,7 +306,6 @@ Features:
* support empty /etc boots nicely:
- nspawn/gpt-generator: introduce new gpt partition type for /usr
- fstab-generator: support systemd.volatile=yes|no|state on the kernel cmdline, too, similar to nspawn's --volatile=
* generator that automatically discovers btrfs subvolumes, identifies their purpose based on some xattr on them.
@ -311,7 +321,6 @@ Features:
* add bus api to query unit file's X fields.
* gpt-auto-generator:
- Support LUKS for root devices
- Define new partition type for encrypted swap? Support probed LUKS for encrypted swap?
- Make /home automount rather than mount?

View File

@ -554,8 +554,8 @@ AS_IF([test "x$enable_wheel_group" != "xno"], [
AC_ARG_WITH(debug-shell,
AS_HELP_STRING([--with-debug-shell=PATH],
[path to debug shell binary]),
[SUSHELL="$withval"],[
AS_IF([test "x${have_selinux}" != "xno"], [SUSHELL="/sbin/sushell"] , [SUSHELL="/bin/sh"])])
[SUSHELL="$withval"],
[SUSHELL="/bin/sh"])
AC_SUBST(SUSHELL)

View File

@ -102,7 +102,7 @@
<varlistentry>
<term><varname>systemd.mask=</varname></term>
<term><varname>systemd.wants=</varname></term>
<term><varname>systemd.debug-shell</varname></term>
<term><varname>systemd.debug_shell</varname></term>
<listitem>
<para>Additional parameters understood by
<citerefentry><refentrytitle>systemd-debug-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
@ -124,6 +124,28 @@
</listitem>
</varlistentry>
<varlistentry>
<term><varname>systemd.volatile=</varname></term>
<listitem>
<para>This parameter controls whether the system shall boot up in volatile mode. Takes a boolean argument, or
the special value <literal>state</literal>. If false (the default), normal boot mode is selected, the root
directory and <filename>/var</filename> are mounted as specified on the kernel command line or
<filename>/etc/fstab</filename>, or otherwise configured. If true, full state-less boot mode is selected. In
this case the root directory is mounted as volatile memory file system (<literal>tmpfs</literal>), and only
<filename>/usr</filename> is mounted from the file system configured as root device, in read-only mode. This
enables fully state-less boots were the vendor-supplied OS is used as shipped, with only default
configuration and no stored state in effect, as <filename>/etc</filename> and <filename>/var</filename> (as
well as all other resources shipped in the root file system) are reset at boot and lost on shutdown. If this
setting is set to <literal>state</literal> the root file system is mounted as usual, however
<filename>/var</filename> is mounted as a volatile memory file system (<literal>tmpfs</literal>), so that the
system boots up with the normal configuration applied, but all state reset at boot and lost at shutdown. For details,
see
<citerefentry><refentrytitle>systemd-volatile-root.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
and
<citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>quiet</varname></term>
<listitem>
@ -236,14 +258,14 @@
</varlistentry>
<varlistentry>
<term><varname>udev.log-priority=</varname></term>
<term><varname>rd.udev.log-priority=</varname></term>
<term><varname>udev.children-max=</varname></term>
<term><varname>rd.udev.children-max=</varname></term>
<term><varname>udev.exec-delay=</varname></term>
<term><varname>rd.udev.exec-delay=</varname></term>
<term><varname>udev.event-timeout=</varname></term>
<term><varname>rd.udev.event-timeout=</varname></term>
<term><varname>udev.log_priority=</varname></term>
<term><varname>rd.udev.log_priority=</varname></term>
<term><varname>udev.children_max=</varname></term>
<term><varname>rd.udev.children_max=</varname></term>
<term><varname>udev.exec_delay=</varname></term>
<term><varname>rd.udev.exec_delay=</varname></term>
<term><varname>udev.event_timeout=</varname></term>
<term><varname>rd.udev.event_timeout=</varname></term>
<term><varname>net.ifnames=</varname></term>
<listitem>
@ -311,6 +333,19 @@
</listitem>
</varlistentry>
<varlistentry>
<term><varname>roothash=</varname></term>
<term><varname>systemd.verity=</varname></term>
<term><varname>rd.systemd.verity=</varname></term>
<term><varname>systemd.verity_root_data=</varname></term>
<term><varname>systemd.verity_root_hash=</varname></term>
<listitem>
<para>Configures the integrity protection root hash for the root file system, and other related
parameters. For details, see
<citerefentry><refentrytitle>systemd-veritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>systemd.gpt_auto=</varname></term>
<term><varname>rd.systemd.gpt_auto=</varname></term>
@ -332,8 +367,8 @@
</varlistentry>
<varlistentry>
<term><varname>modules-load=</varname></term>
<term><varname>rd.modules-load=</varname></term>
<term><varname>modules_load=</varname></term>
<term><varname>rd.modules_load=</varname></term>
<listitem>
<para>Load a specific kernel module early at boot. For
@ -353,6 +388,15 @@
<citerefentry><refentrytitle>systemd-hibernate-resume-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>systemd.firstboot=</varname></term>
<listitem><para>Takes a boolean argument, defaults to on. If off,
<citerefentry><refentrytitle>systemd-firstboot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
will not query the user for basic system settings, even if the system boots up for the first time and the
relevant settings are not initialized yet.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
@ -371,12 +415,15 @@
<citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry project='die-net'><refentrytitle>plymouth</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-cryptsetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-veritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-volatile-root.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-modules-load.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-backlight@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-rfkill.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-hibernate-resume-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
<citerefentry><refentrytitle>systemd-hibernate-resume-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-firstboot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
</para>
</refsect1>

View File

@ -66,9 +66,8 @@
password using the right mechanism at boot and during
runtime.</para>
<para>At early boot and when the system manager configuration is
reloaded this <filename>/etc/crypttab</filename> is translated
into <filename>systemd-cryptsetup@.service</filename> units by
<para>At early boot and when the system manager configuration is reloaded, <filename>/etc/crypttab</filename> is
translated into <filename>systemd-cryptsetup@.service</filename> units by
<citerefentry><refentrytitle>systemd-cryptsetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
</refsect1>

View File

@ -70,7 +70,7 @@
the initial transaction. This is useful to start one or more
additional units at boot. May be specified more than once.</para>
<para>If the <option>systemd.debug-shell</option> option is
<para>If the <option>systemd.debug_shell</option> option is
specified, the debug shell service
<literal>debug-shell.service</literal> is pulled into the boot
transaction. It will spawn a debug shell on tty9 during early

View File

@ -240,6 +240,20 @@
otherwise.</para>
</refsect1>
<refsect1>
<title>Kernel Command Line</title>
<variablelist class='kernel-commandline-options'>
<varlistentry>
<term><varname>systemd.firstboot=</varname></term>
<listitem><para>Takes a boolean argument, defaults to on. If off, <filename>systemd-firstboot.service</filename>
won't interactively query the user for basic settings at first boot, even if those settings are not
initialized yet.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>See Also</title>
<para>

View File

@ -89,12 +89,13 @@
<listitem><para>Takes a boolean argument. Defaults to
<literal>yes</literal>. If <literal>no</literal>, causes the
generator to ignore any mounts or swaps configured in
generator to ignore any mounts or swap devices configured in
<filename>/etc/fstab</filename>. <varname>rd.fstab=</varname>
is honored only by initial RAM disk (initrd) while
is honored only by the initial RAM disk (initrd) while
<varname>fstab=</varname> is honored by both the main system
and the initrd.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>root=</varname></term>
@ -102,6 +103,7 @@
initrd. <varname>root=</varname> is honored by the
initrd.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>rootfstype=</varname></term>
@ -109,6 +111,7 @@
passed to the mount command. <varname>rootfstype=</varname> is
honored by the initrd.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>rootflags=</varname></term>
@ -116,6 +119,7 @@
use. <varname>rootflags=</varname> is honored by the
initrd.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>mount.usr=</varname></term>
@ -133,6 +137,7 @@
<para><varname>mount.usr=</varname> is honored by the initrd.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>mount.usrfstype=</varname></term>
@ -150,6 +155,7 @@
<para><varname>mount.usrfstype=</varname> is honored by the
initrd.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>mount.usrflags=</varname></term>
@ -166,6 +172,39 @@
<para><varname>mount.usrflags=</varname> is honored by the
initrd.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>systemd.volatile=</varname></term>
<listitem><para>Controls whether the system shall boot up in volatile mode. Takes a boolean argument or the
special value <option>state</option>.</para>
<para>If false (the default), this generator makes no changes to the mount tree and the system is booted up in
normal mode.</para>
<para>If true the generator ensures
<citerefentry><refentrytitle>systemd-volatile-root.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
is run as part of the initial RAM disk ("initrd"). This service changes the mount table before transitioning to
the host system, so that a volatile memory file system (<literal>tmpfs</literal>) is used as root directory,
with only <filename>/usr</filename> mounted into it from the configured root file system, in read-only
mode. This way the system operates in fully stateless mode, with all configuration and state reset at boot and
lost at shutdown, as <filename>/etc</filename> and <filename>/var</filename> will be served from the (initially
unpopulated) volatile memory file system.</para>
<para>If set to <option>state</option> the generator will leave the root
directory mount point unaltered, however will mount a <literal>tmpfs</literal> file system to
<filename>/var</filename>. In this mode the normal system configuration (i.e the contents of
<literal>/etc</literal>) is in effect (and may be modified during system runtime), however the system state
(i.e. the contents of <literal>/var</literal>) is reset at boot and lost at shutdown.</para>
<para>Note that in none of these modes the root directory, <filename>/etc</filename>, <filename>/var</filename>
or any other resources stored in the root file system are physically removed. It's thus safe to boot a system
that is normally operated in non-volatile mode temporarily into volatile mode, without losing data.</para>
<para>Note that enabling this setting will only work correctly on operating systems that can boot up with only
<filename>/usr</filename> mounted, and are able to automatically populate <filename>/etc</filename>, and also
<filename>/var</filename> in case of <literal>systemd.volatile=yes</literal>.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
@ -176,7 +215,8 @@
<citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.swap</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-cryptsetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
<citerefentry><refentrytitle>systemd-cryptsetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry>
</para>
</refsect1>

View File

@ -73,8 +73,8 @@
<variablelist class='kernel-commandline-options'>
<varlistentry>
<term><varname>modules-load=</varname></term>
<term><varname>rd.modules-load=</varname></term>
<term><varname>modules_load=</varname></term>
<term><varname>rd.modules_load=</varname></term>
<listitem><para>Takes a comma-separated list of kernel modules
to statically load during early boot. The option prefixed with

View File

@ -939,12 +939,15 @@
<option>no</option> (the default), the whole OS tree is made
available writable.</para>
<para>Note that setting this to <option>yes</option> or
<option>state</option> will only work correctly with
operating systems in the container that can boot up with only
<filename>/usr</filename> mounted, and are able to populate
<filename>/var</filename> automatically, as
needed.</para></listitem>
<para>This option provides similar functionality for containers as the <literal>systemd.volatile=</literal>
kernel command line switch provides for host systems. See
<citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
details.</para>
<para>Note that enabling this setting will only work correctly with operating systems in the container that can
boot up with only <filename>/usr</filename> mounted, and are able to automatically populate
<filename>/var</filename>, and also <filename>/etc</filename> in case of
<literal>--volatile=yes</literal>.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -97,12 +97,10 @@
<term><varname>DefaultStandardOutput=journal</varname></term>
<term><varname>DefaultStandardError=inherit</varname></term>
<listitem><para>Configures various parameters of basic manager
operation. These options may be overridden by the respective
command line arguments. See
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for details about these command line
arguments.</para></listitem>
<listitem><para>Configures various parameters of basic manager operation. These options may be overridden by
the respective process and kernel command line arguments. See
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
details.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -133,22 +133,22 @@
<para>Parameters starting with "rd." will be read when
<command>systemd-udevd</command> is used in an initrd.</para>
<varlistentry>
<term><varname>udev.log-priority=</varname></term>
<term><varname>rd.udev.log-priority=</varname></term>
<term><varname>udev.log_priority=</varname></term>
<term><varname>rd.udev.log_priority=</varname></term>
<listitem>
<para>Set the log level.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>udev.children-max=</varname></term>
<term><varname>rd.udev.children-max=</varname></term>
<term><varname>udev.children_max=</varname></term>
<term><varname>rd.udev.children_max=</varname></term>
<listitem>
<para>Limit the number of events executed in parallel.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>udev.exec-delay=</varname></term>
<term><varname>rd.udev.exec-delay=</varname></term>
<term><varname>udev.exec_delay=</varname></term>
<term><varname>rd.udev.exec_delay=</varname></term>
<listitem>
<para>Delay the execution of <varname>RUN</varname> instructions by the given
number of seconds. This option might be useful when
@ -157,8 +157,8 @@
</listitem>
</varlistentry>
<varlistentry>
<term><varname>udev.event-timeout=</varname></term>
<term><varname>rd.udev.event-timeout=</varname></term>
<term><varname>udev.event_timeout=</varname></term>
<term><varname>rd.udev.event_timeout=</varname></term>
<listitem>
<para>Wait for events to finish up to the given number
of seconds. This option might be useful if events are

View File

@ -0,0 +1,122 @@
<?xml version="1.0"?>
<!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!--
This file is part of systemd.
Copyright 2016 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
-->
<refentry id="systemd-veritysetup-generator" conditional='HAVE_LIBCRYPTSETUP'>
<refentryinfo>
<title>systemd-veritysetup-generator</title>
<productname>systemd</productname>
<authorgroup>
<author>
<contrib>Developer</contrib>
<firstname>Lennart</firstname>
<surname>Poettering</surname>
<email>lennart@poettering.net</email>
</author>
</authorgroup>
</refentryinfo>
<refmeta>
<refentrytitle>systemd-veritysetup-generator</refentrytitle>
<manvolnum>8</manvolnum>
</refmeta>
<refnamediv>
<refname>systemd-veritysetup-generator</refname>
<refpurpose>Unit generator for integrity protected block devices</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><filename>/usr/lib/systemd/system-generators/systemd-veritysetup-generator</filename></para>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><filename>systemd-veritysetup-generator</filename> is a generator that translates kernel command line options
configuring integrity protected block devices (verity) into native systemd units early at boot and when
configuration of the system manager is reloaded. This will create
<citerefentry><refentrytitle>systemd-veritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
units as necessary.</para>
<para>Currently, only a single verity device may be se up with this generator, backing the root file system of the
OS.</para>
<para><filename>systemd-veritysetup-generator</filename> implements
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
</refsect1>
<refsect1>
<title>Kernel Command Line</title>
<para><filename>systemd-veritysetup-generator</filename>
understands the following kernel command line parameters:</para>
<variablelist class='kernel-commandline-options'>
<varlistentry>
<term><varname>systemd.verity=</varname></term>
<term><varname>rd.systemd.verity=</varname></term>
<listitem><para>Takes a boolean argument. Defaults to <literal>yes</literal>. If <literal>no</literal>,
disables the generator entirely. <varname>rd.systemd.verity=</varname> is honored only by the initial RAM disk
(initrd) while <varname>systemd.verity=</varname> is honored by both the host system and the
initrd. </para></listitem>
</varlistentry>
<varlistentry>
<term><varname>roothash=</varname></term>
<listitem><para>Takes a root hash value for the root file system. Expects a hash value formatted in hexadecimal
characters, of the appropriate length (i.e. most likely 256 bit/64 characters, or longer). If not specified via
<varname>systemd.verity_root_data=</varname> and <varname>systemd.verity_root_hash=</varname>, the hash and
data devices to use are automatically derived from the specified hash value. Specifically, the data partition
device is looked for under a GPT partition UUID derived from the first 128bit of the root hash, the hash
partition device is looked for under a GPT partition UUID derived from the last 128bit of the root hash. Hence
it is usually sufficient to specify the root hash to boot from an integrity protected root file system, as
device paths are automatically determined from it — as long as the partition table is properly set up.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>systemd.verity_root_data=</varname></term>
<term><varname>systemd.verity_root_hash=</varname></term>
<listitem><para>These two settings take block device paths as arguments, and may be use to explicitly configure
the data partition and hash partition to use for setting up the integrity protection for the root file
system. If not specified, these paths are automatically derived from the <varname>roothash=</varname> argument
(see above).</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-veritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry project='die-net'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
</para>
</refsect1>
</refentry>

View File

@ -0,0 +1,75 @@
<?xml version="1.0"?>
<!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!--
This file is part of systemd.
Copyright 2016 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
-->
<refentry id="systemd-veritysetup@.service" conditional='HAVE_LIBCRYPTSETUP'>
<refentryinfo>
<title>systemd-veritysetup@.service</title>
<productname>systemd</productname>
<authorgroup>
<author>
<contrib>Developer</contrib>
<firstname>Lennart</firstname>
<surname>Poettering</surname>
<email>lennart@poettering.net</email>
</author>
</authorgroup>
</refentryinfo>
<refmeta>
<refentrytitle>systemd-veritysetup@.service</refentrytitle>
<manvolnum>8</manvolnum>
</refmeta>
<refnamediv>
<refname>systemd-veritysetup@.service</refname>
<refname>systemd-veritysetup</refname>
<refpurpose>Disk integrity protection logic</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><filename>systemd-veritysetup@.service</filename></para>
<para><filename>/usr/lib/systemd/systemd-veritysetup</filename></para>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><filename>systemd-veritysetup@.service</filename> is a service responsible for setting up integrity
protection (verity) block devices. It should be instantiated for each device that requires integrity
protection.</para>
<para>At early boot and when the system manager configuration is reloaded kernel command line configuration for
integrity protected block devices is translated into <filename>systemd-veritysetup@.service</filename> units by
<citerefentry><refentrytitle>systemd-veritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-veritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry project='die-net'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>
</para>
</refsect1>
</refentry>

View File

@ -0,0 +1,79 @@
<?xml version="1.0"?>
<!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!--
This file is part of systemd.
Copyright 2016 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
-->
<refentry id="systemd-volatile-root.service">
<refentryinfo>
<title>systemd-volatile-root.service</title>
<productname>systemd</productname>
<authorgroup>
<author>
<contrib>Developer</contrib>
<firstname>Lennart</firstname>
<surname>Poettering</surname>
<email>lennart@poettering.net</email>
</author>
</authorgroup>
</refentryinfo>
<refmeta>
<refentrytitle>systemd-volatile-root.service</refentrytitle>
<manvolnum>8</manvolnum>
</refmeta>
<refnamediv>
<refname>systemd-volatile-root.service</refname>
<refname>systemd-volatile-root</refname>
<refpurpose>Make the root file system volatile</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><filename>systemd-volatile-root.service</filename></para>
<para><filename>/usr/lib/systemd/systemd-volatile-root</filename></para>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><filename>systemd-volatile-root.service</filename> is a service that replaces the root directory with a
volatile memory file system (<literal>tmpfs</literal>), mounting the original (non-volatile)
<filename>/usr</filename> inside it read-only. This way, vendor data from <filename>/usr</filename> is available as
usual, but all configuration data in <filename>/etc</filename>, all state data in <filename>/var</filename> and all
other resources stored directly under the root directory are reset on boot and lost at shutdown, enabling fully
stateless systems.</para>
<para>This service is only enabled if full volatile mode is selected, for example by specifying
<literal>systemd.volatile=yes</literal> on the kernel command line. This service runs only in the initial RAM disk
("initrd"), before the system transitions to the host's root directory. Note that this service is not used if
<literal>systemd.volatile=state</literal> is used, as in that mode the root directory is non-volatile.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry>
</para>
</refsect1>
</refentry>

View File

@ -284,7 +284,9 @@
providing multiple command lines in the same directive, or alternatively, this directive may be specified more
than once with the same effect. If the empty string is assigned to this option, the list of commands to start
is reset, prior assignments of this option will have no effect. If no <varname>ExecStart=</varname> is
specified, then the service must have <varname>RemainAfterExit=yes</varname> set.</para>
specified, then the service must have <varname>RemainAfterExit=yes</varname> and at least one
<varname>ExecStop=</varname> line set. (Services lacking both <varname>ExecStart=</varname> and
<varname>ExecStop=</varname> are not valid.)</para>
<para>For each of the specified commands, the first argument must be an absolute path to an
executable. Optionally, if this file name is prefixed with <literal>@</literal>, the second token will be

View File

@ -183,10 +183,14 @@
<varlistentry>
<term><option>--show-status=</option></term>
<listitem><para>Show terse service status information while
booting. This switch has no effect when run as user instance.
Takes a boolean argument which may be omitted which is
interpreted as <option>true</option>.</para></listitem>
<listitem><para>Takes a boolean argument or the special value <constant>auto</constant>. If on, terse unit
status information is shown on the console during boot-up and shutdown. If off, no such status information is
shown. If set to <constant>auto</constant> behavior is similar to off, except that it is automatically switched
to on, as soon as the first unit failure or significant boot delay is encountered. This switch has no effect
when invoked as user instance. If specified, overrides both the kernel command line setting
<varname>systemd.show_status=</varname> (see below) and the configuration file option
<option>ShowStatus=</option>, see
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--log-target=</option></term>
@ -953,15 +957,15 @@
<varlistentry>
<term><varname>systemd.show_status=</varname></term>
<listitem><para>Takes a boolean argument or the constant
<constant>auto</constant>. If <option>yes</option>, the
systemd manager (PID 1) shows terse service status updates on
the console during bootup. <constant>auto</constant> behaves
like <option>false</option> until a service fails or there is
a significant delay in boot. Defaults to
<option>yes</option>, unless <option>quiet</option> is passed
as kernel command line option, in which case it defaults to
<constant>auto</constant>.</para></listitem>
<listitem><para>Takes a boolean argument or the constant <constant>auto</constant>. If <option>yes</option>,
the systemd manager (PID 1) shows terse service status updates on the console during bootup.
<constant>auto</constant> behaves like <option>false</option> until a unit fails or there is a significant
delay in boot. Defaults to <option>yes</option>, unless <option>quiet</option> is passed as kernel command
line option, in which case it defaults to <constant>auto</constant>. If specified overrides the system manager
configuration file option <option>ShowStatus=</option>, see
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>. However,
the process command line option <option>--show-status=</option> takes precedence over both this kernel command
line option and the configuration file option.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -88,7 +88,4 @@ ENV{DEVTYPE}=="partition", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-i
ENV{ID_PART_ENTRY_UUID}=="?*", SYMLINK+="disk/by-partuuid/$env{ID_PART_ENTRY_UUID}"
ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_NAME}=="?*", SYMLINK+="disk/by-partlabel/$env{ID_PART_ENTRY_NAME}"
# add symlink to GPT root disk
ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_GPT_AUTO_ROOT}=="1", SYMLINK+="gpt-auto-root"
LABEL="persistent_storage_end"

View File

@ -17,6 +17,11 @@ SUBSYSTEM=="block", ACTION=="add", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", E
# we are probably still calling mke2fs or mkswap on it.
SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{ID_PART_TABLE_TYPE}=="", ENV{ID_FS_USAGE}=="", ENV{SYSTEMD_READY}="0"
# add symlink to GPT root disk
SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}!="crypto_LUKS", SYMLINK+="gpt-auto-root"
SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}=="crypto_LUKS", SYMLINK+="gpt-auto-root-luks"
SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{DM_NAME}=="root", SYMLINK+="gpt-auto-root"
# Ignore raid devices that are not yet assembled and started
SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="md*", TEST!="md/array_state", ENV{SYSTEMD_READY}="0"
SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="md*", ATTR{md/array_state}=="|clear|inactive", ENV{SYSTEMD_READY}="0"

View File

@ -2361,6 +2361,7 @@ int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
bool cg_is_unified_wanted(void) {
static thread_local int wanted = -1;
int r, unified;
bool b;
/* If the hierarchy is already mounted, then follow whatever
* was chosen for it. */
@ -2374,20 +2375,11 @@ bool cg_is_unified_wanted(void) {
if (wanted >= 0)
return wanted;
r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy", NULL);
if (r > 0)
return (wanted = true);
else {
_cleanup_free_ char *value = NULL;
r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b);
if (r < 0)
return false;
r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy=", &value);
if (r < 0)
return false;
if (r == 0)
return (wanted = false);
return (wanted = parse_boolean(value) > 0);
}
return (wanted = r > 0 ? b : false);
}
bool cg_is_legacy_wanted(void) {
@ -2397,6 +2389,7 @@ bool cg_is_legacy_wanted(void) {
bool cg_is_unified_systemd_controller_wanted(void) {
static thread_local int wanted = -1;
int r, unified;
bool b;
/* If the unified hierarchy is requested in full, no need to
* bother with this. */
@ -2415,23 +2408,11 @@ bool cg_is_unified_systemd_controller_wanted(void) {
if (wanted >= 0)
return wanted;
r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller", NULL);
if (r > 0)
wanted = false;
else {
_cleanup_free_ char *value = NULL;
r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b);
if (r < 0)
return false;
r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller=", &value);
if (r < 0)
return false;
if (r == 0)
wanted = false;
else
wanted = parse_boolean(value) <= 0;
}
return wanted;
return (wanted = r > 0 ? b : false);
}
bool cg_is_legacy_systemd_controller_wanted(void) {

View File

@ -799,8 +799,10 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
return -ENOMEM;
}
*ret = done;
done = NULL;
if (ret) {
*ret = done;
done = NULL;
}
return exists;
}

View File

@ -97,6 +97,9 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
assert(len);
assert(p);
if (l % 2 != 0)
return -EINVAL;
z = r = malloc((l + 1) / 2 + 1);
if (!r)
return -ENOMEM;
@ -107,12 +110,10 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
a = unhexchar(x[0]);
if (a < 0)
return a;
else if (x+1 < p + l) {
b = unhexchar(x[1]);
if (b < 0)
return b;
} else
b = 0;
b = unhexchar(x[1]);
if (b < 0)
return b;
*(z++) = (uint8_t) a << 4 | (uint8_t) b;
}

View File

@ -981,24 +981,30 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (streq(key, "debug") && !value)
log_set_max_level(LOG_DEBUG);
else if (streq(key, "systemd.log_target") && value) {
else if (proc_cmdline_key_streq(key, "systemd.log_target")) {
if (proc_cmdline_value_missing(key, value))
return 0;
if (log_set_target_from_string(value) < 0)
log_warning("Failed to parse log target '%s'. Ignoring.", value);
} else if (streq(key, "systemd.log_level") && value) {
} else if (proc_cmdline_key_streq(key, "systemd.log_level")) {
if (proc_cmdline_value_missing(key, value))
return 0;
if (log_set_max_level_from_string(value) < 0)
log_warning("Failed to parse log level '%s'. Ignoring.", value);
} else if (streq(key, "systemd.log_color") && value) {
} else if (proc_cmdline_key_streq(key, "systemd.log_color")) {
if (log_show_color_from_string(value) < 0)
if (log_show_color_from_string(value ?: "1") < 0)
log_warning("Failed to parse log color setting '%s'. Ignoring.", value);
} else if (streq(key, "systemd.log_location") && value) {
} else if (proc_cmdline_key_streq(key, "systemd.log_location")) {
if (log_show_location_from_string(value) < 0)
if (log_show_location_from_string(value ?: "1") < 0)
log_warning("Failed to parse log location setting '%s'. Ignoring.", value);
}
@ -1009,10 +1015,9 @@ void log_parse_environment(void) {
const char *e;
if (get_ctty_devnr(0, NULL) < 0)
/* Only try to read the command line in daemons.
We assume that anything that has a controlling
tty is user stuff. */
(void) parse_proc_cmdline(parse_proc_cmdline_item, NULL, true);
/* Only try to read the command line in daemons. We assume that anything that has a controlling tty is
user stuff. */
(void) proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
e = secure_getenv("SYSTEMD_LOG_TARGET");
if (e && log_set_target_from_string(e) < 0)

View File

@ -673,6 +673,9 @@ int mount_verbose(
else if ((flags & MS_BIND) && !type)
log_debug("Bind-mounting %s on %s (%s \"%s\")...",
what, where, strnull(fl), strempty(options));
else if (flags & MS_MOVE)
log_debug("Moving mount %s → %s (%s \"%s\")...",
what, where, strnull(fl), strempty(options));
else
log_debug("Mounting %s on %s (%s \"%s\")...",
strna(type), where, strnull(fl), strempty(options));

View File

@ -34,17 +34,30 @@
#include "virt.h"
int proc_cmdline(char **ret) {
const char *e;
assert(ret);
/* For testing purposes it is sometimes useful to be able to override what we consider /proc/cmdline to be */
e = secure_getenv("SYSTEMD_PROC_CMDLINE");
if (e) {
char *m;
m = strdup(e);
if (!m)
return -ENOMEM;
*ret = m;
return 0;
}
if (detect_container() > 0)
return get_process_cmdline(1, 0, false, ret);
else
return read_one_line_file("/proc/cmdline", ret);
}
int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, void *data),
void *data,
bool strip_prefix) {
int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned flags) {
_cleanup_free_ char *line = NULL;
const char *p;
int r;
@ -58,7 +71,7 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, voi
p = line;
for (;;) {
_cleanup_free_ char *word = NULL;
char *value = NULL, *unprefixed;
char *value, *key, *q;
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
if (r < 0)
@ -66,17 +79,23 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, voi
if (r == 0)
break;
/* Filter out arguments that are intended only for the
* initrd */
unprefixed = startswith(word, "rd.");
if (unprefixed && !in_initrd())
continue;
key = word;
value = strchr(word, '=');
/* Filter out arguments that are intended only for the initrd */
q = startswith(word, "rd.");
if (q) {
if (!in_initrd())
continue;
if (flags & PROC_CMDLINE_STRIP_RD_PREFIX)
key = q;
}
value = strchr(key, '=');
if (value)
*(value++) = 0;
r = parse_item(strip_prefix && unprefixed ? unprefixed : word, value, data);
r = parse_item(key, value, data);
if (r < 0)
return r;
}
@ -84,13 +103,64 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, voi
return 0;
}
int get_proc_cmdline_key(const char *key, char **value) {
static bool relaxed_equal_char(char a, char b) {
return a == b ||
(a == '_' && b == '-') ||
(a == '-' && b == '_');
}
char *proc_cmdline_key_startswith(const char *s, const char *prefix) {
assert(s);
assert(prefix);
/* Much like startswith(), but considers "-" and "_" the same */
for (; *prefix != 0; s++, prefix++)
if (!relaxed_equal_char(*s, *prefix))
return NULL;
return (char*) s;
}
bool proc_cmdline_key_streq(const char *x, const char *y) {
assert(x);
assert(y);
/* Much like streq(), but considers "-" and "_" the same */
for (; *x != 0 || *y != 0; x++, y++)
if (!relaxed_equal_char(*x, *y))
return false;
return true;
}
int proc_cmdline_get_key(const char *key, unsigned flags, char **value) {
_cleanup_free_ char *line = NULL, *ret = NULL;
bool found = false;
const char *p;
int r;
assert(key);
/* Looks for a specific key on the kernel command line. Supports two modes:
*
* a) The "value" parameter is used. In this case a parameter beginning with the "key" string followed by "="
* is searched, and the value following this is returned in "value".
*
* b) as above, but the PROC_CMDLINE_VALUE_OPTIONAL flag is set. In this case if the the key is found as a
* separate word (i.e. not followed by "=" but instead by whitespace or the end of the command line), then
* this is also accepted, and "value" is returned as NULL.
*
* c) The "value" parameter is NULL. In this case a search for the exact "key" parameter is performed.
*
* In all three cases, > 0 is returned if the key is found, 0 if not.*/
if (isempty(key))
return -EINVAL;
if ((flags & PROC_CMDLINE_VALUE_OPTIONAL) && !value)
return -EINVAL;
r = proc_cmdline(&line);
if (r < 0)
@ -107,21 +177,26 @@ int get_proc_cmdline_key(const char *key, char **value) {
if (r == 0)
break;
/* Filter out arguments that are intended only for the
* initrd */
/* Automatically filter out arguments that are intended only for the initrd, if we are not in the
* initrd. */
if (!in_initrd() && startswith(word, "rd."))
continue;
if (value) {
e = startswith(word, key);
e = proc_cmdline_key_startswith(word, key);
if (!e)
continue;
r = free_and_strdup(&ret, e);
if (r < 0)
return r;
if (*e == '=') {
r = free_and_strdup(&ret, e+1);
if (r < 0)
return r;
found = true;
} else if (*e == 0 && (flags & PROC_CMDLINE_VALUE_OPTIONAL))
found = true;
found = true;
} else {
if (streq(word, key))
found = true;
@ -134,20 +209,42 @@ int get_proc_cmdline_key(const char *key, char **value) {
}
return found;
}
int proc_cmdline_get_bool(const char *key, bool *ret) {
_cleanup_free_ char *v = NULL;
int r;
assert(ret);
r = proc_cmdline_get_key(key, PROC_CMDLINE_VALUE_OPTIONAL, &v);
if (r < 0)
return r;
if (r == 0) {
*ret = false;
return 0;
}
if (v) { /* parameter passed */
r = parse_boolean(v);
if (r < 0)
return r;
*ret = r;
} else /* no parameter passed */
*ret = true;
return 1;
}
int shall_restore_state(void) {
_cleanup_free_ char *value = NULL;
bool ret;
int r;
r = get_proc_cmdline_key("systemd.restore_state=", &value);
r = proc_cmdline_get_bool("systemd.restore_state", &ret);
if (r < 0)
return r;
if (r == 0)
return true;
return parse_boolean(value);
return r > 0 ? ret : true;
}
static const char * const rlmap[] = {

View File

@ -19,11 +19,36 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include "log.h"
enum {
PROC_CMDLINE_STRIP_RD_PREFIX = 1,
PROC_CMDLINE_VALUE_OPTIONAL = 2,
};
typedef int (*proc_cmdline_parse_t)(const char *key, const char *value, void *data);
int proc_cmdline(char **ret);
int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, void *data),
void *data,
bool strip_prefix);
int get_proc_cmdline_key(const char *parameter, char **value);
int proc_cmdline_parse(const proc_cmdline_parse_t parse, void *userdata, unsigned flags);
int proc_cmdline_get_key(const char *parameter, unsigned flags, char **value);
int proc_cmdline_get_bool(const char *key, bool *ret);
char *proc_cmdline_key_startswith(const char *s, const char *prefix);
bool proc_cmdline_key_streq(const char *x, const char *y);
int shall_restore_state(void);
const char* runlevel_to_target(const char *rl);
/* A little helper call, to be used in proc_cmdline_parse_t callbacks */
static inline bool proc_cmdline_value_missing(const char *key, const char *value) {
if (!value) {
log_warning("Missing argument for %s= kernel command line switch, ignoring.", key);
return true;
}
return false;
}

View File

@ -28,6 +28,7 @@
#include "dirent-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "macro.h"
#include "missing.h"
#include "stat-util.h"
@ -143,22 +144,29 @@ int path_is_read_only_fs(const char *path) {
}
int path_is_os_tree(const char *path) {
char *p;
int r;
assert(path);
/* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir
* always results in -ENOENT, and we can properly distuingish the case where the whole root doesn't exist from
* the case where just the os-release file is missing. */
if (laccess(path, F_OK) < 0)
return -errno;
/* We use /usr/lib/os-release as flag file if something is an OS */
p = strjoina(path, "/usr/lib/os-release");
r = access(p, F_OK);
if (r >= 0)
return 1;
r = chase_symlinks("/usr/lib/os-release", path, CHASE_PREFIX_ROOT, NULL);
if (r == -ENOENT) {
/* Also check for the old location in /etc, just in case. */
p = strjoina(path, "/etc/os-release");
r = access(p, F_OK);
/* Also check for the old location in /etc, just in case. */
r = chase_symlinks("/etc/os-release", path, CHASE_PREFIX_ROOT, NULL);
if (r == -ENOENT)
return 0; /* We got nothing */
}
if (r < 0)
return r;
return r >= 0;
return 1;
}
int files_same(const char *filea, const char *fileb) {
@ -196,7 +204,7 @@ int fd_check_fstype(int fd, statfs_f_type_t magic_value) {
int path_check_fstype(const char *path, statfs_f_type_t magic_value) {
_cleanup_close_ int fd = -1;
fd = open(path, O_RDONLY);
fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
if (fd < 0)
return -errno;
@ -216,3 +224,13 @@ int fd_is_temporary_fs(int fd) {
return is_temporary_fs(&s);
}
int path_is_temporary_fs(const char *path) {
_cleanup_close_ int fd = -1;
fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
if (fd < 0)
return -errno;
return fd_is_temporary_fs(fd);
}

View File

@ -61,6 +61,7 @@ int path_check_fstype(const char *path, statfs_f_type_t magic_value);
bool is_temporary_fs(const struct statfs *s) _pure_;
int fd_is_temporary_fs(int fd);
int path_is_temporary_fs(const char *path);
/* Because statfs.t_type can be int on some architectures, we have to cast
* the const magic to the type, otherwise the compiler warns about

View File

@ -35,6 +35,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
#include "install.h"
#include "log.h"
#include "path-util.h"
@ -1484,25 +1485,36 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er
if (r < 0)
return r;
if (path_equal(root, "/") || !path_is_absolute(root))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid switch root path %s", root);
if (isempty(root))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New root directory may not be the empty string.");
if (!path_is_absolute(root))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New root path '%s' is not absolute.", root);
if (path_equal(root, "/"))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New root directory cannot be the old root directory.");
/* Safety check */
if (isempty(init)) {
if (!path_is_os_tree(root))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified switch root path %s does not seem to be an OS tree. os-release file is missing.", root);
r = path_is_os_tree(root);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to determine whether root path '%s' contains an OS tree: %m", root);
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified switch root path '%s' does not seem to be an OS tree. os-release file is missing.", root);
} else {
_cleanup_free_ char *p = NULL;
_cleanup_free_ char *chased = NULL;
if (!path_is_absolute(init))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid init path %s", init);
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path to init binary '%s' not absolute.", init);
p = strappend(root, init);
if (!p)
return -ENOMEM;
r = chase_symlinks(init, root, CHASE_PREFIX_ROOT, &chased);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Could not resolve init executable %s: %m", init);
if (access(p, X_OK) < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified init binary %s does not exist.", p);
if (laccess(chased, X_OK) < 0) {
if (errno == EACCES)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Init binary %s is not executable.", init);
return sd_bus_error_set_errnof(error, r, "Could not check whether init binary %s is executable: %m", init);
}
}
rt = strdup(root);

View File

@ -645,7 +645,7 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR
[JOB_DEPENDENCY] = "Dependency failed for %s.",
[JOB_ASSERT] = "Assertion failed for %s.",
[JOB_UNSUPPORTED] = "Starting of %s not supported.",
[JOB_COLLECTED] = "Unecessary job for %s was removed.",
[JOB_COLLECTED] = "Unnecessary job for %s was removed.",
};
static const char *const generic_finished_stop_job[_JOB_RESULT_MAX] = {
[JOB_DONE] = "Stopped %s.",

View File

@ -49,6 +49,7 @@
#include "cpu-set-util.h"
#include "dbus-manager.h"
#include "def.h"
#include "emergency-action.h"
#include "env-util.h"
#include "fd-util.h"
#include "fdset.h"
@ -90,7 +91,6 @@
#include "user-util.h"
#include "virt.h"
#include "watchdog.h"
#include "emergency-action.h"
static enum {
ACTION_RUN,
@ -337,60 +337,73 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
assert(key);
if (streq(key, "systemd.unit") && value) {
if (STR_IN_SET(key, "systemd.unit", "rd.systemd.unit")) {
if (!in_initrd())
return free_and_strdup(&arg_default_unit, value);
if (proc_cmdline_value_missing(key, value))
return 0;
} else if (streq(key, "rd.systemd.unit") && value) {
if (!unit_name_is_valid(value, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
log_warning("Unit name specified on %s= is not valid, ignoring: %s", key, value);
else if (in_initrd() == !!startswith(key, "rd.")) {
if (free_and_strdup(&arg_default_unit, value) < 0)
return log_oom();
}
if (in_initrd())
return free_and_strdup(&arg_default_unit, value);
} else if (proc_cmdline_key_streq(key, "systemd.dump_core")) {
} else if (streq(key, "systemd.dump_core") && value) {
r = parse_boolean(value);
r = value ? parse_boolean(value) : true;
if (r < 0)
log_warning("Failed to parse dump core switch %s. Ignoring.", value);
else
arg_dump_core = r;
} else if (streq(key, "systemd.crash_chvt") && value) {
} else if (proc_cmdline_key_streq(key, "systemd.crash_chvt")) {
if (parse_crash_chvt(value) < 0)
if (!value)
arg_crash_chvt = 0; /* turn on */
else if (parse_crash_chvt(value) < 0)
log_warning("Failed to parse crash chvt switch %s. Ignoring.", value);
} else if (streq(key, "systemd.crash_shell") && value) {
} else if (proc_cmdline_key_streq(key, "systemd.crash_shell")) {
r = parse_boolean(value);
r = value ? parse_boolean(value) : true;
if (r < 0)
log_warning("Failed to parse crash shell switch %s. Ignoring.", value);
else
arg_crash_shell = r;
} else if (streq(key, "systemd.crash_reboot") && value) {
} else if (proc_cmdline_key_streq(key, "systemd.crash_reboot")) {
r = parse_boolean(value);
r = value ? parse_boolean(value) : true;
if (r < 0)
log_warning("Failed to parse crash reboot switch %s. Ignoring.", value);
else
arg_crash_reboot = r;
} else if (streq(key, "systemd.confirm_spawn") && value) {
} else if (proc_cmdline_key_streq(key, "systemd.confirm_spawn")) {
char *s;
arg_confirm_spawn = mfree(arg_confirm_spawn);
r = parse_confirm_spawn(value, &arg_confirm_spawn);
r = parse_confirm_spawn(value, &s);
if (r < 0)
log_warning_errno(r, "Failed to parse confirm_spawn switch %s. Ignoring.", value);
else {
free(arg_confirm_spawn);
arg_confirm_spawn = s;
}
} else if (streq(key, "systemd.show_status") && value) {
} else if (proc_cmdline_key_streq(key, "systemd.show_status")) {
r = parse_show_status(value, &arg_show_status);
if (r < 0)
log_warning("Failed to parse show status switch %s. Ignoring.", value);
if (value) {
r = parse_show_status(value, &arg_show_status);
if (r < 0)
log_warning("Failed to parse show status switch %s. Ignoring.", value);
} else
arg_show_status = SHOW_STATUS_YES;
} else if (streq(key, "systemd.default_standard_output") && value) {
} else if (proc_cmdline_key_streq(key, "systemd.default_standard_output")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = exec_output_from_string(value);
if (r < 0)
@ -398,7 +411,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
else
arg_default_std_output = r;
} else if (streq(key, "systemd.default_standard_error") && value) {
} else if (proc_cmdline_key_streq(key, "systemd.default_standard_error")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = exec_output_from_string(value);
if (r < 0)
@ -406,24 +422,42 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
else
arg_default_std_error = r;
} else if (streq(key, "systemd.setenv") && value) {
} else if (streq(key, "systemd.setenv")) {
if (proc_cmdline_value_missing(key, value))
return 0;
if (env_assignment_is_valid(value)) {
char **env;
env = strv_env_set(arg_default_environment, value);
if (env)
arg_default_environment = env;
else
log_warning_errno(ENOMEM, "Setting environment variable '%s' failed, ignoring: %m", value);
if (!env)
return log_oom();
arg_default_environment = env;
} else
log_warning("Environment variable name '%s' is not valid. Ignoring.", value);
} else if (streq(key, "systemd.machine_id") && value) {
} else if (proc_cmdline_key_streq(key, "systemd.machine_id")) {
r = set_machine_id(value);
if (r < 0)
log_warning("MachineID '%s' is not valid. Ignoring.", value);
if (proc_cmdline_value_missing(key, value))
return 0;
r = set_machine_id(value);
if (r < 0)
log_warning("MachineID '%s' is not valid. Ignoring.", value);
} else if (proc_cmdline_key_streq(key, "systemd.default_timeout_start_sec")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = parse_sec(value, &arg_default_timeout_start_usec);
if (r < 0)
log_warning_errno(r, "Failed to parse default start timeout: %s, ignoring.", value);
if (arg_default_timeout_start_usec <= 0)
arg_default_timeout_start_usec = USEC_INFINITY;
} else if (streq(key, "quiet") && !value) {
@ -445,15 +479,6 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
target = runlevel_to_target(key);
if (target)
return free_and_strdup(&arg_default_unit, target);
} else if (streq(key, "systemd.default_timeout_start_sec") && value) {
r = parse_sec(value, &arg_default_timeout_start_usec);
if (r < 0)
log_warning_errno(r, "Failed to parse default start timeout: %s, ignoring.", value);
if (arg_default_timeout_start_usec <= 0)
arg_default_timeout_start_usec = USEC_INFINITY;
}
return 0;
@ -1341,10 +1366,9 @@ static int fixup_environment(void) {
* However if TERM was configured through the kernel
* command line then leave it alone. */
r = get_proc_cmdline_key("TERM=", &term);
r = proc_cmdline_get_key("TERM", 0, &term);
if (r < 0)
return r;
if (r == 0) {
term = strdup(default_term_for_tty("/dev/console"));
if (!term)
@ -1411,7 +1435,7 @@ int main(int argc, char *argv[]) {
called 'systemd'. That is confusing, hence let's call us
systemd right-away. */
program_invocation_short_name = systemd;
prctl(PR_SET_NAME, systemd);
(void) prctl(PR_SET_NAME, systemd);
saved_argv = argv;
saved_argc = argc;
@ -1435,9 +1459,10 @@ int main(int argc, char *argv[]) {
if (!skip_setup) {
r = mount_setup_early();
if (r < 0) {
error_message = "Failed to early mount API filesystems";
error_message = "Failed to mount early API filesystems";
goto finish;
}
dual_timestamp_get(&security_start_timestamp);
if (mac_selinux_setup(&loaded_policy) < 0) {
error_message = "Failed to load SELinux policy";
@ -1513,7 +1538,7 @@ int main(int argc, char *argv[]) {
log_close_console(); /* force reopen of /dev/console */
log_open();
/* For the later on, see above... */
/* For later on, see above... */
log_set_target(LOG_TARGET_JOURNAL);
/* clear the kernel timestamp,
@ -1590,7 +1615,7 @@ int main(int argc, char *argv[]) {
}
if (arg_system) {
r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, false);
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
}

View File

@ -360,7 +360,6 @@ int mount_setup(bool loaded_policy) {
int r = 0;
r = mount_points_setup(ELEMENTSOF(mount_table), loaded_policy);
if (r < 0)
return r;
@ -391,25 +390,24 @@ int mount_setup(bool loaded_policy) {
* 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. */
/* 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)
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. */
/* 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);
(void) mkdir_label("/run/systemd/inaccessible", 0000);
/* Set up inaccessible items */
(void) mkdir_label("/run/systemd/inaccessible", 0000);
(void) mknod("/run/systemd/inaccessible/reg", S_IFREG | 0000, 0);
(void) mkdir_label("/run/systemd/inaccessible/dir", 0000);
(void) mknod("/run/systemd/inaccessible/chr", S_IFCHR | 0000, makedev(0, 0));

View File

@ -102,18 +102,17 @@ static int create_disk(
if (!f)
return log_error_errno(errno, "Failed to create unit file %s: %m", p);
fputs(
"# Automatically generated by systemd-cryptsetup-generator\n\n"
"[Unit]\n"
"Description=Cryptography Setup for %I\n"
"Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
"SourcePath=/etc/crypttab\n"
"DefaultDependencies=no\n"
"Conflicts=umount.target\n"
"BindsTo=dev-mapper-%i.device\n"
"IgnoreOnIsolate=true\n"
"After=cryptsetup-pre.target\n",
f);
fputs("# Automatically generated by systemd-cryptsetup-generator\n\n"
"[Unit]\n"
"Description=Cryptography Setup for %I\n"
"Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
"SourcePath=/etc/crypttab\n"
"DefaultDependencies=no\n"
"Conflicts=umount.target\n"
"BindsTo=dev-mapper-%i.device\n"
"IgnoreOnIsolate=true\n"
"After=cryptsetup-pre.target\n",
f);
if (!nofail)
fprintf(f,
@ -278,27 +277,30 @@ static crypto_device *get_crypto_device(const char *uuid) {
}
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
int r;
crypto_device *d;
_cleanup_free_ char *uuid = NULL, *uuid_value = NULL;
crypto_device *d;
int r;
if (streq(key, "luks") && value) {
if (streq(key, "luks")) {
r = parse_boolean(value);
r = value ? parse_boolean(value) : 1;
if (r < 0)
log_warning("Failed to parse luks switch %s. Ignoring.", value);
log_warning("Failed to parse luks= kernel command line switch %s. Ignoring.", value);
else
arg_enabled = r;
} else if (streq(key, "luks.crypttab") && value) {
} else if (streq(key, "luks.crypttab")) {
r = parse_boolean(value);
r = value ? parse_boolean(value) : 1;
if (r < 0)
log_warning("Failed to parse luks crypttab switch %s. Ignoring.", value);
log_warning("Failed to parse luks.crypttab= kernel command line switch %s. Ignoring.", value);
else
arg_read_crypttab = r;
} else if (streq(key, "luks.uuid") && value) {
} else if (streq(key, "luks.uuid")) {
if (proc_cmdline_value_missing(key, value))
return 0;
d = get_crypto_device(startswith(value, "luks-") ? value+5 : value);
if (!d)
@ -306,7 +308,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
d->create = arg_whitelist = true;
} else if (streq(key, "luks.options") && value) {
} else if (streq(key, "luks.options")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
if (r == 2) {
@ -314,13 +319,14 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (!d)
return log_oom();
free(d->options);
d->options = uuid_value;
uuid_value = NULL;
free_and_replace(d->options, uuid_value);
} else if (free_and_strdup(&arg_default_options, value) < 0)
return log_oom();
} else if (streq(key, "luks.key") && value) {
} else if (streq(key, "luks.key")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
if (r == 2) {
@ -328,13 +334,14 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (!d)
return log_oom();
free(d->keyfile);
d->keyfile = uuid_value;
uuid_value = NULL;
free_and_replace(d->keyfile, uuid_value);
} else if (free_and_strdup(&arg_default_keyfile, value) < 0)
return log_oom();
} else if (streq(key, "luks.name") && value) {
} else if (streq(key, "luks.name")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
if (r == 2) {
@ -349,7 +356,6 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
uuid_value = NULL;
} else
log_warning("Failed to parse luks name switch %s. Ignoring.", value);
}
return 0;
@ -458,7 +464,7 @@ static int add_proc_cmdline_devices(void) {
}
int main(int argc, char *argv[]) {
int r = EXIT_FAILURE;
int r;
if (argc > 1 && argc != 4) {
log_error("This program takes three or no arguments.");
@ -475,32 +481,36 @@ int main(int argc, char *argv[]) {
umask(0022);
arg_disks = hashmap_new(&string_hash_ops);
if (!arg_disks)
goto cleanup;
if (!arg_disks) {
r = log_oom();
goto finish;
}
r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, true);
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
if (r < 0) {
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
r = EXIT_FAILURE;
log_warning_errno(r, "Failed to parse kernel command line: %m");
goto finish;
}
if (!arg_enabled) {
r = EXIT_SUCCESS;
goto cleanup;
r = 0;
goto finish;
}
if (add_crypttab_devices() < 0)
goto cleanup;
r = add_crypttab_devices();
if (r < 0)
goto finish;
if (add_proc_cmdline_devices() < 0)
goto cleanup;
r = add_proc_cmdline_devices();
if (r < 0)
goto finish;
r = EXIT_SUCCESS;
r = 0;
cleanup:
finish:
free_arg_disks();
free(arg_default_options);
free(arg_default_keyfile);
return r;
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}

View File

@ -310,7 +310,7 @@ static char *disk_mount_point(const char *label) {
if (asprintf(&device, "/dev/mapper/%s", label) < 0)
return NULL;
f = setmntent("/etc/fstab", "r");
f = setmntent("/etc/fstab", "re");
if (!f)
return NULL;
@ -593,17 +593,18 @@ static int help(void) {
}
int main(int argc, char *argv[]) {
int r = EXIT_FAILURE;
struct crypt_device *cd = NULL;
int r;
if (argc <= 1) {
help();
return EXIT_SUCCESS;
r = help();
goto finish;
}
if (argc < 3) {
log_error("This program requires at least two arguments.");
return EXIT_FAILURE;
r = -EINVAL;
goto finish;
}
log_set_target(LOG_TARGET_AUTO);
@ -614,7 +615,6 @@ int main(int argc, char *argv[]) {
if (streq(argv[1], "attach")) {
uint32_t flags = 0;
int k;
unsigned tries;
usec_t until;
crypt_status_info status;
@ -648,11 +648,11 @@ int main(int argc, char *argv[]) {
if (arg_header) {
log_debug("LUKS header: %s", arg_header);
k = crypt_init(&cd, arg_header);
r = crypt_init(&cd, arg_header);
} else
k = crypt_init(&cd, argv[3]);
if (k != 0) {
log_error_errno(k, "crypt_init() failed: %m");
r = crypt_init(&cd, argv[3]);
if (r < 0) {
log_error_errno(r, "crypt_init() failed: %m");
goto finish;
}
@ -661,7 +661,7 @@ int main(int argc, char *argv[]) {
status = crypt_status(cd, argv[2]);
if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
log_info("Volume %s already active.", argv[2]);
r = EXIT_SUCCESS;
r = 0;
goto finish;
}
@ -691,29 +691,30 @@ int main(int argc, char *argv[]) {
_cleanup_strv_free_erase_ char **passwords = NULL;
if (!key_file) {
k = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords);
if (k == -EAGAIN)
r = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords);
if (r == -EAGAIN)
continue;
else if (k < 0)
if (r < 0)
goto finish;
}
if (streq_ptr(arg_type, CRYPT_TCRYPT))
k = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
r = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
else
k = attach_luks_or_plain(cd,
r = attach_luks_or_plain(cd,
argv[2],
key_file,
arg_header ? argv[3] : NULL,
passwords,
flags);
if (k >= 0)
if (r >= 0)
break;
else if (k == -EAGAIN) {
if (r == -EAGAIN) {
key_file = NULL;
continue;
} else if (k != -EPERM) {
log_error_errno(k, "Failed to activate: %m");
}
if (r != -EPERM) {
log_error_errno(r, "Failed to activate: %m");
goto finish;
}
@ -722,28 +723,28 @@ int main(int argc, char *argv[]) {
if (arg_tries != 0 && tries >= arg_tries) {
log_error("Too many attempts; giving up.");
r = EXIT_FAILURE;
r = -EPERM;
goto finish;
}
} else if (streq(argv[1], "detach")) {
int k;
k = crypt_init_by_name(&cd, argv[2]);
if (k == -ENODEV) {
r = crypt_init_by_name(&cd, argv[2]);
if (r == -ENODEV) {
log_info("Volume %s already inactive.", argv[2]);
r = EXIT_SUCCESS;
r = 0;
goto finish;
} else if (k) {
log_error_errno(k, "crypt_init_by_name() failed: %m");
}
if (r < 0) {
log_error_errno(r, "crypt_init_by_name() failed: %m");
goto finish;
}
crypt_set_log_callback(cd, log_glue, NULL);
k = crypt_deactivate(cd, argv[2]);
if (k < 0) {
log_error_errno(k, "Failed to deactivate: %m");
r = crypt_deactivate(cd, argv[2]);
if (r < 0) {
log_error_errno(r, "Failed to deactivate: %m");
goto finish;
}
@ -752,10 +753,9 @@ int main(int argc, char *argv[]) {
goto finish;
}
r = EXIT_SUCCESS;
r = 0;
finish:
if (cd)
crypt_free(cd);
@ -764,5 +764,5 @@ finish:
free(arg_header);
strv_free(arg_tcrypt_keyfiles);
return r;
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}

View File

@ -39,56 +39,53 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
assert(key);
if (streq(key, "systemd.mask")) {
char *n;
if (!value)
log_error("Missing argument for systemd.mask= kernel command line parameter.");
else {
char *n;
if (proc_cmdline_value_missing(key, value))
return 0;
r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n);
if (r < 0)
return log_error_errno(r, "Failed to glob unit name: %m");
r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n);
if (r < 0)
return log_error_errno(r, "Failed to glob unit name: %m");
r = strv_consume(&arg_mask, n);
if (r < 0)
return log_oom();
}
r = strv_consume(&arg_mask, n);
if (r < 0)
return log_oom();
} else if (streq(key, "systemd.wants")) {
char *n;
if (!value)
log_error("Missing argument for systemd.want= kernel command line parameter.");
else {
char *n;
if (proc_cmdline_value_missing(key, value))
return 0;
r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n);
if (r < 0)
return log_error_errno(r, "Failed to glob unit name: %m");
r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n);
if (r < 0)
return log_error_errno(r, "Failed to glob unit name: %m");
r = strv_consume(&arg_wants, n);
if (r < 0)
return log_oom();
}
r = strv_consume(&arg_wants, n);
if (r < 0)
return log_oom();
} else if (streq(key, "systemd.debug-shell")) {
} else if (proc_cmdline_key_streq(key, "systemd.debug_shell")) {
if (value) {
r = parse_boolean(value);
if (r < 0)
log_error("Failed to parse systemd.debug-shell= argument '%s', ignoring.", value);
log_error("Failed to parse systemd.debug_shell= argument '%s', ignoring.", value);
else
arg_debug_shell = r;
} else
arg_debug_shell = true;
} else if (streq(key, "systemd.unit")) {
if (!value)
log_error("Missing argument for systemd.unit= kernel command line parameter.");
else {
r = free_and_strdup(&arg_default_unit, value);
if (r < 0)
return log_error_errno(r, "Failed to set default unit %s: %m", value);
}
if (proc_cmdline_value_missing(key, value))
return 0;
r = free_and_strdup(&arg_default_unit, value);
if (r < 0)
return log_error_errno(r, "Failed to set default unit %s: %m", value);
} else if (!value) {
const char *target;
@ -173,7 +170,7 @@ int main(int argc, char *argv[]) {
umask(0022);
r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, false);
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");

View File

@ -35,7 +35,7 @@ static enum {
} arg_action = ACTION_DISSECT;
static const char *arg_image = NULL;
static const char *arg_path = NULL;
static DissectImageFlags arg_flags = DISSECT_IMAGE_DISCARD_ON_LOOP;
static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP;
static void *arg_root_hash = NULL;
static size_t arg_root_hash_size = 0;
@ -191,7 +191,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
r = dissect_image(d->fd, arg_root_hash, arg_root_hash_size, &m);
r = dissect_image(d->fd, arg_root_hash, arg_root_hash_size, arg_flags, &m);
if (r == -ENOPKG) {
log_error_errno(r, "Couldn't identify a suitable partition table or file system in %s.", arg_image);
goto finish;
@ -221,6 +221,9 @@ int main(int argc, char *argv[]) {
p->rw ? "writable" : "read-only",
partition_designator_to_string(i));
if (!sd_id128_is_null(p->uuid))
printf(" (UUID " SD_ID128_FORMAT_STR ")", SD_ID128_FORMAT_VAL(p->uuid));
if (p->fstype)
printf(" of type %s", p->fstype);

View File

@ -33,6 +33,7 @@
#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
#include "random-util.h"
#include "string-util.h"
#include "strv.h"
@ -825,6 +826,7 @@ static int parse_argv(int argc, char *argv[]) {
}
int main(int argc, char *argv[]) {
bool enabled;
int r;
r = parse_argv(argc, argv);
@ -837,6 +839,16 @@ int main(int argc, char *argv[]) {
umask(0022);
r = proc_cmdline_get_bool("systemd.firstboot", &enabled);
if (r < 0) {
log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring.");
goto finish;
}
if (r > 0 && !enabled) {
r = 0; /* disabled */
goto finish;
}
r = process_locale();
if (r < 0)
goto finish;

View File

@ -99,7 +99,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
assert(key);
if (streq(key, "fsck.mode") && value) {
if (streq(key, "fsck.mode")) {
if (proc_cmdline_value_missing(key, value))
return 0;
if (streq(value, "auto"))
arg_force = arg_skip = false;
@ -110,7 +113,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
else
log_warning("Invalid fsck.mode= parameter '%s'. Ignoring.", value);
} else if (streq(key, "fsck.repair") && value) {
} else if (streq(key, "fsck.repair")) {
if (proc_cmdline_value_missing(key, value))
return 0;
if (streq(value, "preen"))
arg_repair = "-a";
@ -293,7 +299,7 @@ int main(int argc, char *argv[]) {
umask(0022);
r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, true);
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");

View File

@ -42,16 +42,20 @@
#include "unit-name.h"
#include "util.h"
#include "virt.h"
#include "volatile-util.h"
static const char *arg_dest = "/tmp";
static const char *arg_dest_late = "/tmp";
static bool arg_fstab_enabled = true;
static char *arg_root_what = NULL;
static char *arg_root_fstype = NULL;
static char *arg_root_options = NULL;
static char *arg_root_hash = NULL;
static int arg_root_rw = -1;
static char *arg_usr_what = NULL;
static char *arg_usr_fstype = NULL;
static char *arg_usr_options = NULL;
static VolatileMode arg_volatile_mode = _VOLATILE_MODE_INVALID;
static int add_swap(
const char *what,
@ -235,6 +239,7 @@ static int write_requires_mounts_for(FILE *f, const char *opts) {
}
static int add_mount(
const char *dest,
const char *what,
const char *where,
const char *fstype,
@ -286,7 +291,7 @@ static int add_mount(
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
unit = strjoin(arg_dest, "/", name);
unit = strjoin(dest, "/", name);
if (!unit)
return log_oom();
@ -318,7 +323,7 @@ static int add_mount(
}
if (passno != 0) {
r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
r = generator_write_fsck_deps(f, dest, what, where, fstype);
if (r < 0)
return r;
}
@ -334,7 +339,7 @@ static int add_mount(
if (!isempty(fstype) && !streq(fstype, "auto"))
fprintf(f, "Type=%s\n", fstype);
r = generator_write_timeouts(arg_dest, what, where, opts, &filtered);
r = generator_write_timeouts(dest, what, where, opts, &filtered);
if (r < 0)
return r;
@ -350,7 +355,7 @@ static int add_mount(
return log_error_errno(r, "Failed to write unit file %s: %m", unit);
if (!noauto && !automount) {
lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", name);
lnk = strjoin(dest, "/", post, nofail ? ".wants/" : ".requires/", name);
if (!lnk)
return log_oom();
@ -364,7 +369,7 @@ static int add_mount(
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
automount_unit = strjoin(arg_dest, "/", automount_name);
automount_unit = strjoin(dest, "/", automount_name);
if (!automount_unit)
return log_oom();
@ -406,7 +411,7 @@ static int add_mount(
return log_error_errno(r, "Failed to write unit file %s: %m", automount_unit);
free(lnk);
lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name);
lnk = strjoin(dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name);
if (!lnk)
return log_oom();
@ -479,7 +484,8 @@ static int parse_fstab(bool initrd) {
else
post = SPECIAL_LOCAL_FS_TARGET;
k = add_mount(what,
k = add_mount(arg_dest,
what,
where,
me->mnt_type,
me->mnt_opts,
@ -540,7 +546,8 @@ static int add_sysroot_mount(void) {
return r;
}
return add_mount(what,
return add_mount(arg_dest,
what,
"/sysroot",
arg_root_fstype,
opts,
@ -593,7 +600,8 @@ static int add_sysroot_usr_mount(void) {
opts = arg_usr_options;
log_debug("Found entry what=%s where=/sysroot/usr type=%s", what, strna(arg_usr_fstype));
return add_mount(what,
return add_mount(arg_dest,
what,
"/sysroot/usr",
arg_usr_fstype,
opts,
@ -605,6 +613,46 @@ static int add_sysroot_usr_mount(void) {
"/proc/cmdline");
}
static int add_volatile_root(void) {
const char *from, *to;
if (arg_volatile_mode != VOLATILE_YES)
return 0;
/* Let's add in systemd-remount-volatile.service which will remount the root device to tmpfs if this is
* requested, leaving only /usr from the root mount inside. */
from = strjoina(SYSTEM_DATA_UNIT_PATH "/systemd-volatile-root.service");
to = strjoina(arg_dest, "/" SPECIAL_INITRD_ROOT_FS_TARGET, ".requires/systemd-volatile-root.service");
(void) mkdir_parents(to, 0755);
if (symlink(from, to) < 0)
return log_error_errno(errno, "Failed to hook in volatile remount service: %m");
return 0;
}
static int add_volatile_var(void) {
if (arg_volatile_mode != VOLATILE_STATE)
return 0;
/* If requested, mount /var as tmpfs, but do so only if there's nothing else defined for this. */
return add_mount(arg_dest_late,
"tmpfs",
"/var",
"tmpfs",
"mode=0755",
0,
false,
false,
false,
SPECIAL_LOCAL_FS_TARGET,
"/proc/cmdline");
}
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
int r;
@ -612,27 +660,36 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
* instance should take precedence. In the case of multiple rootflags=
* or usrflags= the arguments should be concatenated */
if (STR_IN_SET(key, "fstab", "rd.fstab") && value) {
if (STR_IN_SET(key, "fstab", "rd.fstab")) {
r = parse_boolean(value);
r = value ? parse_boolean(value) : 1;
if (r < 0)
log_warning("Failed to parse fstab switch %s. Ignoring.", value);
else
arg_fstab_enabled = r;
} else if (streq(key, "root") && value) {
} else if (streq(key, "root")) {
if (proc_cmdline_value_missing(key, value))
return 0;
if (free_and_strdup(&arg_root_what, value) < 0)
return log_oom();
} else if (streq(key, "rootfstype") && value) {
} else if (streq(key, "rootfstype")) {
if (proc_cmdline_value_missing(key, value))
return 0;
if (free_and_strdup(&arg_root_fstype, value) < 0)
return log_oom();
} else if (streq(key, "rootflags") && value) {
} else if (streq(key, "rootflags")) {
char *o;
if (proc_cmdline_value_missing(key, value))
return 0;
o = arg_root_options ?
strjoin(arg_root_options, ",", value) :
strdup(value);
@ -641,20 +698,36 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
free(arg_root_options);
arg_root_options = o;
} else if (streq(key, "roothash")) {
} else if (streq(key, "mount.usr") && value) {
if (proc_cmdline_value_missing(key, value))
return 0;
if (free_and_strdup(&arg_root_hash, value) < 0)
return log_oom();
} else if (streq(key, "mount.usr")) {
if (proc_cmdline_value_missing(key, value))
return 0;
if (free_and_strdup(&arg_usr_what, value) < 0)
return log_oom();
} else if (streq(key, "mount.usrfstype") && value) {
} else if (streq(key, "mount.usrfstype")) {
if (proc_cmdline_value_missing(key, value))
return 0;
if (free_and_strdup(&arg_usr_fstype, value) < 0)
return log_oom();
} else if (streq(key, "mount.usrflags") && value) {
} else if (streq(key, "mount.usrflags")) {
char *o;
if (proc_cmdline_value_missing(key, value))
return 0;
o = arg_usr_options ?
strjoin(arg_usr_options, ",", value) :
strdup(value);
@ -668,10 +741,40 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
arg_root_rw = true;
else if (streq(key, "ro") && !value)
arg_root_rw = false;
else if (streq(key, "systemd.volatile")) {
VolatileMode m;
if (value) {
m = volatile_mode_from_string(value);
if (m < 0)
log_warning("Failed to parse systemd.volatile= argument: %s", value);
else
arg_volatile_mode = m;
} else
arg_volatile_mode = VOLATILE_YES;
}
return 0;
}
static int determine_root(void) {
/* If we have a root hash but no root device then Verity is used, and we use the "root" DM device as root. */
if (arg_root_what)
return 0;
if (!arg_root_hash)
return 0;
arg_root_what = strdup("/dev/mapper/root");
if (!arg_root_what)
return log_oom();
log_info("Using verity root device %s.", arg_root_what);
return 1;
}
int main(int argc, char *argv[]) {
int r = 0;
@ -682,6 +785,8 @@ int main(int argc, char *argv[]) {
if (argc > 1)
arg_dest = argv[1];
if (argc > 3)
arg_dest_late = argv[3];
log_set_target(LOG_TARGET_SAFE);
log_parse_environment();
@ -689,10 +794,12 @@ int main(int argc, char *argv[]) {
umask(0022);
r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, false);
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
(void) determine_root();
/* Always honour root= and usr= in the kernel command line if we are in an initrd */
if (in_initrd()) {
int k;
@ -702,8 +809,12 @@ int main(int argc, char *argv[]) {
k = add_sysroot_usr_mount();
if (k < 0)
r = k;
k = add_volatile_root();
if (k < 0)
r = k;
} else
r = 0;
r = add_volatile_var();
/* Honour /etc/fstab only when that's enabled */
if (arg_fstab_enabled) {
@ -729,6 +840,7 @@ int main(int argc, char *argv[]) {
free(arg_root_what);
free(arg_root_fstype);
free(arg_root_options);
free(arg_root_hash);
free(arg_usr_what);
free(arg_usr_fstype);

View File

@ -29,6 +29,7 @@
#include "blkid-util.h"
#include "btrfs-util.h"
#include "dirent-util.h"
#include "dissect-image.h"
#include "efivars.h"
#include "fd-util.h"
#include "fileio.h"
@ -54,7 +55,7 @@ static bool arg_enabled = true;
static bool arg_root_enabled = true;
static bool arg_root_rw = false;
static int add_cryptsetup(const char *id, const char *what, bool rw, char **device) {
static int add_cryptsetup(const char *id, const char *what, bool rw, bool require, char **device) {
_cleanup_free_ char *e = NULL, *n = NULL, *p = NULL, *d = NULL, *to = NULL;
_cleanup_fclose_ FILE *f = NULL;
char *from, *ret;
@ -62,7 +63,6 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi
assert(id);
assert(what);
assert(device);
r = unit_name_from_path(what, ".device", &d);
if (r < 0)
@ -119,23 +119,26 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi
if (symlink(from, to) < 0)
return log_error_errno(errno, "Failed to create symlink %s: %m", to);
free(to);
to = strjoin(arg_dest, "/cryptsetup.target.requires/", n);
if (!to)
return log_oom();
if (require) {
free(to);
mkdir_parents_label(to, 0755);
if (symlink(from, to) < 0)
return log_error_errno(errno, "Failed to create symlink %s: %m", to);
to = strjoin(arg_dest, "/cryptsetup.target.requires/", n);
if (!to)
return log_oom();
free(to);
to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n);
if (!to)
return log_oom();
mkdir_parents_label(to, 0755);
if (symlink(from, to) < 0)
return log_error_errno(errno, "Failed to create symlink %s: %m", to);
mkdir_parents_label(to, 0755);
if (symlink(from, to) < 0)
return log_error_errno(errno, "Failed to create symlink %s: %m", to);
free(to);
to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n);
if (!to)
return log_oom();
mkdir_parents_label(to, 0755);
if (symlink(from, to) < 0)
return log_error_errno(errno, "Failed to create symlink %s: %m", to);
}
free(p);
p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf");
@ -155,7 +158,8 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi
if (!ret)
return log_oom();
*device = ret;
if (device)
*device = ret;
return 0;
}
@ -182,7 +186,7 @@ static int add_mount(
if (streq_ptr(fstype, "crypto_LUKS")) {
r = add_cryptsetup(id, what, rw, &crypto_what);
r = add_cryptsetup(id, what, rw, true, &crypto_what);
if (r < 0)
return r;
@ -270,61 +274,28 @@ static bool path_is_busy(const char *where) {
return false;
}
static int probe_and_add_mount(
static int add_partition_mount(
DissectedPartition *p,
const char *id,
const char *what,
const char *where,
bool rw,
const char *description,
const char *post) {
const char *description) {
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
const char *fstype = NULL;
int r;
assert(id);
assert(what);
assert(where);
assert(description);
assert(p);
if (path_is_busy(where)) {
log_debug("%s already populated, ignoring.", where);
return 0;
}
/* Let's check the partition type here, so that we know
* whether to do LUKS magic. */
errno = 0;
b = blkid_new_probe_from_filename(what);
if (!b) {
if (errno == 0)
return log_oom();
return log_error_errno(errno, "Failed to allocate prober: %m");
}
blkid_probe_enable_superblocks(b, 1);
blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
errno = 0;
r = blkid_do_safeprobe(b);
if (r == -2 || r == 1) /* no result or uncertain */
return 0;
else if (r != 0)
return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what);
/* add_mount is OK with fstype being NULL. */
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
return add_mount(
id,
what,
p->node,
where,
fstype,
rw,
p->fstype,
p->rw,
NULL,
description,
post);
SPECIAL_LOCAL_FS_TARGET);
}
static int add_swap(const char *path) {
@ -449,22 +420,17 @@ static int add_automount(
return 0;
}
static int add_boot(const char *what) {
static int add_esp(DissectedPartition *p) {
const char *esp;
int r;
assert(what);
assert(p);
if (in_initrd()) {
log_debug("In initrd, ignoring the ESP.");
return 0;
}
if (detect_container() > 0) {
log_debug("In a container, ignoring the ESP.");
return 0;
}
/* If /efi exists we'll use that. Otherwise we'll use /boot, as that's usually the better choice */
esp = access("/efi/", F_OK) >= 0 ? "/efi" : "/boot";
@ -480,9 +446,7 @@ static int add_boot(const char *what) {
}
if (is_efi_boot()) {
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
const char *fstype = NULL, *uuid_string = NULL;
sd_id128_t loader_uuid, part_uuid;
sd_id128_t loader_uuid;
/* If this is an EFI boot, be extra careful, and only mount the ESP if it was the ESP used for booting. */
@ -494,43 +458,7 @@ static int add_boot(const char *what) {
if (r < 0)
return log_error_errno(r, "Failed to read ESP partition UUID: %m");
errno = 0;
b = blkid_new_probe_from_filename(what);
if (!b) {
if (errno == 0)
return log_oom();
return log_error_errno(errno, "Failed to allocate prober: %m");
}
blkid_probe_enable_partitions(b, 1);
blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
errno = 0;
r = blkid_do_safeprobe(b);
if (r == -2 || r == 1) /* no result or uncertain */
return 0;
else if (r != 0)
return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what);
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
if (!streq_ptr(fstype, "vfat")) {
log_debug("Partition for %s is not a FAT filesystem, ignoring.", esp);
return 0;
}
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &uuid_string, NULL);
if (r != 0) {
log_debug_errno(errno, "Partition for %s does not have a UUID, ignoring.", esp);
return 0;
}
if (sd_id128_from_string(uuid_string, &part_uuid) < 0) {
log_debug("Partition for %s does not have a valid UUID, ignoring.", esp);
return 0;
}
if (!sd_id128_equal(part_uuid, loader_uuid)) {
if (!sd_id128_equal(p->uuid, loader_uuid)) {
log_debug("Partition for %s does not appear to be the partition we are booted from.", esp);
return 0;
}
@ -538,35 +466,29 @@ static int add_boot(const char *what) {
log_debug("Not an EFI boot, skipping ESP check.");
return add_automount("boot",
what,
esp,
"vfat",
true,
"umask=0077",
"EFI System Partition Automount",
120 * USEC_PER_SEC);
p->node,
esp,
p->fstype,
true,
"umask=0077",
"EFI System Partition Automount",
120 * USEC_PER_SEC);
}
#else
static int add_boot(const char *what) {
static int add_esp(const char *what) {
return 0;
}
#endif
static int enumerate_partitions(dev_t devnum) {
_cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
static int open_parent(dev_t devnum, int *ret) {
_cleanup_udev_device_unref_ struct udev_device *d = NULL;
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
_cleanup_udev_unref_ struct udev *udev = NULL;
_cleanup_free_ char *boot = NULL, *home = NULL, *srv = NULL;
struct udev_list_entry *first, *item;
struct udev_device *parent = NULL;
const char *name, *node, *pttype, *devtype;
int boot_nr = -1, home_nr = -1, srv_nr = -1;
bool home_rw = true, srv_rw = true;
blkid_partlist pl;
int r, k;
const char *name, *devtype, *node;
struct udev_device *parent;
dev_t pn;
int fd;
assert(ret);
udev = udev_new();
if (!udev)
@ -580,228 +502,94 @@ static int enumerate_partitions(dev_t devnum) {
if (!name)
name = udev_device_get_syspath(d);
if (!name) {
log_debug("Device %u:%u does not have a name, ignoring.",
major(devnum), minor(devnum));
return 0;
log_debug("Device %u:%u does not have a name, ignoring.", major(devnum), minor(devnum));
goto not_found;
}
parent = udev_device_get_parent(d);
if (!parent) {
log_debug("%s: not a partitioned device, ignoring.", name);
return 0;
goto not_found;
}
/* Does it have a devtype? */
devtype = udev_device_get_devtype(parent);
if (!devtype) {
log_debug("%s: parent doesn't have a device type, ignoring.", name);
return 0;
goto not_found;
}
/* Is this a disk or a partition? We only care for disks... */
if (!streq(devtype, "disk")) {
log_debug("%s: parent isn't a raw disk, ignoring.", name);
return 0;
goto not_found;
}
/* Does it have a device node? */
node = udev_device_get_devnode(parent);
if (!node) {
log_debug("%s: parent device does not have device node, ignoring.", name);
return 0;
goto not_found;
}
log_debug("%s: root device %s.", name, node);
pn = udev_device_get_devnum(parent);
if (major(pn) == 0)
return 0;
errno = 0;
b = blkid_new_probe_from_filename(node);
if (!b) {
if (errno == 0)
return log_oom();
return log_error_errno(errno, "%s: failed to allocate prober: %m", node);
if (major(pn) == 0) {
log_debug("%s: parent device is not a proper block device, ignoring.", name);
goto not_found;
}
blkid_probe_enable_partitions(b, 1);
blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
fd = open(node, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return log_error_errno(errno, "Failed to open %s: %m", node);
errno = 0;
r = blkid_do_safeprobe(b);
if (r == 1)
return 0; /* no results */
else if (r == -2) {
log_warning("%s: probe gave ambiguous results, ignoring.", node);
return 0;
} else if (r != 0)
return log_error_errno(errno ?: EIO, "%s: failed to probe: %m", node);
*ret = fd;
return 1;
errno = 0;
r = blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
if (r != 0) {
if (errno == 0)
return 0; /* No partition table found. */
not_found:
*ret = -1;
return 0;
}
return log_error_errno(errno, "%s: failed to determine partition table type: %m", node);
}
static int enumerate_partitions(dev_t devnum) {
/* We only do this all for GPT... */
if (!streq_ptr(pttype, "gpt")) {
log_debug("%s: not a GPT partition table, ignoring.", node);
_cleanup_close_ int fd = -1;
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
int r, k;
r = open_parent(devnum, &fd);
if (r <= 0)
return r;
r = dissect_image(fd, NULL, 0, DISSECT_IMAGE_GPT_ONLY, &m);
if (r == -ENOPKG) {
log_debug_errno(r, "No suitable partition table found, ignoring.");
return 0;
}
errno = 0;
pl = blkid_probe_get_partitions(b);
if (!pl) {
if (errno == 0)
return log_oom();
return log_error_errno(errno, "%s: failed to list partitions: %m", node);
}
e = udev_enumerate_new(udev);
if (!e)
return log_oom();
r = udev_enumerate_add_match_parent(e, parent);
if (r < 0)
return log_oom();
return log_error_errno(r, "Failed to dissect: %m");
r = udev_enumerate_add_match_subsystem(e, "block");
if (r < 0)
return log_oom();
r = udev_enumerate_scan_devices(e);
if (r < 0)
return log_error_errno(r, "%s: failed to enumerate partitions: %m", node);
first = udev_enumerate_get_list_entry(e);
udev_list_entry_foreach(item, first) {
_cleanup_udev_device_unref_ struct udev_device *q;
unsigned long long flags;
const char *stype, *subnode;
sd_id128_t type_id;
blkid_partition pp;
dev_t qn;
int nr;
q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
if (!q)
continue;
qn = udev_device_get_devnum(q);
if (major(qn) == 0)
continue;
if (qn == devnum)
continue;
if (qn == pn)
continue;
subnode = udev_device_get_devnode(q);
if (!subnode)
continue;
pp = blkid_partlist_devno_to_partition(pl, qn);
if (!pp)
continue;
nr = blkid_partition_get_partno(pp);
if (nr < 0)
continue;
stype = blkid_partition_get_type_string(pp);
if (!stype)
continue;
if (sd_id128_from_string(stype, &type_id) < 0)
continue;
flags = blkid_partition_get_flags(pp);
if (sd_id128_equal(type_id, GPT_SWAP)) {
if (flags & GPT_FLAG_NO_AUTO)
continue;
if (flags & GPT_FLAG_READ_ONLY) {
log_debug("%s marked as read-only swap partition, which is bogus. Ignoring.", subnode);
continue;
}
k = add_swap(subnode);
if (k < 0)
r = k;
} else if (sd_id128_equal(type_id, GPT_ESP)) {
/* We only care for the first /boot partition */
if (boot && nr >= boot_nr)
continue;
/* Note that we do not honour the "no-auto"
* flag for the ESP, as it is often unset, to
* hide it from Windows. */
boot_nr = nr;
r = free_and_strdup(&boot, subnode);
if (r < 0)
return log_oom();
} else if (sd_id128_equal(type_id, GPT_HOME)) {
if (flags & GPT_FLAG_NO_AUTO)
continue;
/* We only care for the first /home partition */
if (home && nr >= home_nr)
continue;
home_nr = nr;
home_rw = !(flags & GPT_FLAG_READ_ONLY),
r = free_and_strdup(&home, subnode);
if (r < 0)
return log_oom();
} else if (sd_id128_equal(type_id, GPT_SRV)) {
if (flags & GPT_FLAG_NO_AUTO)
continue;
/* We only care for the first /srv partition */
if (srv && nr >= srv_nr)
continue;
srv_nr = nr;
srv_rw = !(flags & GPT_FLAG_READ_ONLY),
r = free_and_strdup(&srv, subnode);
if (r < 0)
return log_oom();
}
}
if (boot) {
k = add_boot(boot);
if (m->partitions[PARTITION_SWAP].found) {
k = add_swap(m->partitions[PARTITION_SWAP].node);
if (k < 0)
r = k;
}
if (home) {
k = probe_and_add_mount("home", home, "/home", home_rw, "Home Partition", SPECIAL_LOCAL_FS_TARGET);
if (m->partitions[PARTITION_ESP].found) {
k = add_esp(m->partitions + PARTITION_ESP);
if (k < 0)
r = k;
}
if (srv) {
k = probe_and_add_mount("srv", srv, "/srv", srv_rw, "Server Data Partition", SPECIAL_LOCAL_FS_TARGET);
if (m->partitions[PARTITION_HOME].found) {
k = add_partition_mount(m->partitions + PARTITION_HOME, "home", "/home", "Home Partition");
if (k < 0)
r = k;
}
if (m->partitions[PARTITION_SRV].found) {
k = add_partition_mount(m->partitions + PARTITION_SRV, "srv", "/srv", "Server Data Partition");
if (k < 0)
r = k;
}
@ -876,8 +664,40 @@ static int get_block_device_harder(const char *path, dev_t *dev) {
if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
continue;
if (found) /* Don't try to support multiple backing block devices */
goto fallback;
if (found) {
_cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL;
/* We found a device backed by multiple other devices. We don't really support automatic
* discovery on such setups, with the exception of dm-verity partitions. In this case there are
* two backing devices: the data partition and the hash partition. We are fine with such
* setups, however, only if both partitions are on the same physical device. Hence, let's
* verify this. */
u = strjoin(p, "/", de->d_name, "/../dev");
if (!u)
return -ENOMEM;
v = strjoin(p, "/", found->d_name, "/../dev");
if (!v)
return -ENOMEM;
r = read_one_line_file(u, &a);
if (r < 0) {
log_debug_errno(r, "Failed to read %s: %m", u);
goto fallback;
}
r = read_one_line_file(v, &b);
if (r < 0) {
log_debug_errno(r, "Failed to read %s: %m", v);
goto fallback;
}
/* Check if the parent device is the same. If not, then the two backing devices are on
* different physical devices, and we don't support that. */
if (!streq(a, b))
goto fallback;
}
found = de;
}
@ -912,21 +732,33 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
assert(key);
if (STR_IN_SET(key, "systemd.gpt_auto", "rd.systemd.gpt_auto") && value) {
if (STR_IN_SET(key, "systemd.gpt_auto", "rd.systemd.gpt_auto")) {
r = parse_boolean(value);
r = value ? parse_boolean(value) : 1;
if (r < 0)
log_warning("Failed to parse gpt-auto switch \"%s\". Ignoring.", value);
else
arg_enabled = r;
} else if (streq(key, "root") && value) {
} else if (streq(key, "root")) {
if (proc_cmdline_value_missing(key, value))
return 0;
/* Disable root disk logic if there's a root= value
* specified (unless it happens to be "gpt-auto") */
arg_root_enabled = streq(value, "gpt-auto");
} else if (streq(key, "roothash")) {
if (proc_cmdline_value_missing(key, value))
return 0;
/* Disable root disk logic if there's roothash= defined (i.e. verity enabled) */
arg_root_enabled = false;
} else if (streq(key, "rw") && !value)
arg_root_rw = true;
else if (streq(key, "ro") && !value)
@ -935,6 +767,16 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
return 0;
}
#ifdef ENABLE_EFI
static int add_root_cryptsetup(void) {
/* If a device /dev/gpt-auto-root-luks appears, then make it pull in systemd-cryptsetup-root.service, which
* sets it up, and causes /dev/gpt-auto-root to appear which is all we are looking for. */
return add_cryptsetup("root", "/dev/gpt-auto-root-luks", true, false, NULL);
}
#endif
static int add_root_mount(void) {
#ifdef ENABLE_EFI
@ -960,6 +802,10 @@ static int add_root_mount(void) {
r = generator_write_initrd_root_device_deps(arg_dest, "/dev/gpt-auto-root");
if (r < 0)
return 0;
r = add_root_cryptsetup();
if (r < 0)
return r;
}
return add_mount(
@ -983,11 +829,11 @@ static int add_mounts(void) {
r = get_block_device_harder("/", &devno);
if (r < 0)
return log_error_errno(r, "Failed to determine block device of root file system: %m");
else if (r == 0) {
if (r == 0) {
r = get_block_device_harder("/usr", &devno);
if (r < 0)
return log_error_errno(r, "Failed to determine block device of /usr file system: %m");
else if (r == 0) {
if (r == 0) {
log_debug("Neither root nor /usr file system are on a (single) block device.");
return 0;
}
@ -997,7 +843,7 @@ static int add_mounts(void) {
}
int main(int argc, char *argv[]) {
int r = 0;
int r = 0, k;
if (argc > 1 && argc != 4) {
log_error("This program takes three or no arguments.");
@ -1018,7 +864,7 @@ int main(int argc, char *argv[]) {
return EXIT_SUCCESS;
}
r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, false);
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
@ -1031,8 +877,6 @@ int main(int argc, char *argv[]) {
r = add_root_mount();
if (!in_initrd()) {
int k;
k = add_mounts();
if (k < 0)
r = k;

View File

@ -31,15 +31,22 @@
#include "util.h"
static const char *arg_dest = "/tmp";
static char *arg_resume_dev = NULL;
static char *arg_resume_device = NULL;
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
if (streq(key, "resume") && value) {
free(arg_resume_dev);
arg_resume_dev = fstab_node_to_udev_node(value);
if (!arg_resume_dev)
if (streq(key, "resume")) {
char *s;
if (proc_cmdline_value_missing(key, value))
return 0;
s = fstab_node_to_udev_node(value);
if (!s)
return log_oom();
free(arg_resume_device);
arg_resume_device = s;
}
return 0;
@ -49,10 +56,10 @@ static int process_resume(void) {
_cleanup_free_ char *name = NULL, *lnk = NULL;
int r;
if (!arg_resume_dev)
if (!arg_resume_device)
return 0;
r = unit_name_from_path_instance("systemd-hibernate-resume", arg_resume_dev, ".service", &name);
r = unit_name_from_path_instance("systemd-hibernate-resume", arg_resume_device, ".service", &name);
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
@ -88,12 +95,12 @@ int main(int argc, char *argv[]) {
if (!in_initrd())
return EXIT_SUCCESS;
r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, false);
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
r = process_resume();
free(arg_resume_dev);
free(arg_resume_device);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}

View File

@ -218,37 +218,40 @@ int pull_make_path(const char *url, const char *etag, const char *image_root, co
return 0;
}
int pull_make_settings_job(
int pull_make_auxiliary_job(
PullJob **ret,
const char *url,
int (*strip_suffixes)(const char *name, char **ret),
const char *suffix,
CurlGlue *glue,
PullJobFinished on_finished,
void *userdata) {
_cleanup_free_ char *last_component = NULL, *ll = NULL, *settings_url = NULL;
_cleanup_free_ char *last_component = NULL, *ll = NULL, *auxiliary_url = NULL;
_cleanup_(pull_job_unrefp) PullJob *job = NULL;
const char *q;
int r;
assert(ret);
assert(url);
assert(strip_suffixes);
assert(glue);
r = import_url_last_component(url, &last_component);
if (r < 0)
return r;
r = tar_strip_suffixes(last_component, &ll);
r = strip_suffixes(last_component, &ll);
if (r < 0)
return r;
q = strjoina(ll, ".nspawn");
q = strjoina(ll, suffix);
r = import_url_change_last_component(url, q, &settings_url);
r = import_url_change_last_component(url, q, &auxiliary_url);
if (r < 0)
return r;
r = pull_job_new(&job, settings_url, glue, userdata);
r = pull_job_new(&job, auxiliary_url, glue, userdata);
if (r < 0)
return r;
@ -320,7 +323,56 @@ int pull_make_verification_jobs(
return 0;
}
static int verify_one(PullJob *checksum_job, PullJob *job) {
_cleanup_free_ char *fn = NULL;
const char *line, *p;
int r;
assert(checksum_job);
if (!job)
return 0;
assert(IN_SET(job->state, PULL_JOB_DONE, PULL_JOB_FAILED));
/* Don't verify the checksum if we didn't actually successfully download something new */
if (job->state != PULL_JOB_DONE)
return 0;
if (job->error != 0)
return 0;
if (job->etag_exists)
return 0;
assert(job->calc_checksum);
assert(job->checksum);
r = import_url_last_component(job->url, &fn);
if (r < 0)
return log_oom();
if (!filename_is_valid(fn)) {
log_error("Cannot verify checksum, could not determine server-side file name.");
return -EBADMSG;
}
line = strjoina(job->checksum, " *", fn, "\n");
p = memmem(checksum_job->payload,
checksum_job->payload_size,
line,
strlen(line));
if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) {
log_error("DOWNLOAD INVALID: Checksum of %s file did not checkout, file has been tampered with.", fn);
return -EBADMSG;
}
log_info("SHA256 checksum of %s is valid.", job->url);
return 1;
}
int pull_verify(PullJob *main_job,
PullJob *roothash_job,
PullJob *settings_job,
PullJob *checksum_job,
PullJob *signature_job) {
@ -328,7 +380,6 @@ int pull_verify(PullJob *main_job,
_cleanup_close_pair_ int gpg_pipe[2] = { -1, -1 };
_cleanup_free_ char *fn = NULL;
_cleanup_close_ int sig_file = -1;
const char *p, *line;
char sig_file_path[] = "/tmp/sigXXXXXX", gpg_home[] = "/tmp/gpghomeXXXXXX";
_cleanup_(sigkill_waitp) pid_t pid = 0;
bool gpg_home_created = false;
@ -342,6 +393,7 @@ int pull_verify(PullJob *main_job,
assert(main_job->calc_checksum);
assert(main_job->checksum);
assert(checksum_job->state == PULL_JOB_DONE);
if (!checksum_job->payload || checksum_job->payload_size <= 0) {
@ -349,64 +401,17 @@ int pull_verify(PullJob *main_job,
return -EBADMSG;
}
r = import_url_last_component(main_job->url, &fn);
r = verify_one(checksum_job, main_job);
if (r < 0)
return log_oom();
return r;
if (!filename_is_valid(fn)) {
log_error("Cannot verify checksum, could not determine valid server-side file name.");
return -EBADMSG;
}
r = verify_one(checksum_job, roothash_job);
if (r < 0)
return r;
line = strjoina(main_job->checksum, " *", fn, "\n");
p = memmem(checksum_job->payload,
checksum_job->payload_size,
line,
strlen(line));
if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) {
log_error("DOWNLOAD INVALID: Checksum did not check out, payload has been tampered with.");
return -EBADMSG;
}
log_info("SHA256 checksum of %s is valid.", main_job->url);
assert(!settings_job || IN_SET(settings_job->state, PULL_JOB_DONE, PULL_JOB_FAILED));
if (settings_job &&
settings_job->state == PULL_JOB_DONE &&
settings_job->error == 0 &&
!settings_job->etag_exists) {
_cleanup_free_ char *settings_fn = NULL;
assert(settings_job->calc_checksum);
assert(settings_job->checksum);
r = import_url_last_component(settings_job->url, &settings_fn);
if (r < 0)
return log_oom();
if (!filename_is_valid(settings_fn)) {
log_error("Cannot verify checksum, could not determine server-side settings file name.");
return -EBADMSG;
}
line = strjoina(settings_job->checksum, " *", settings_fn, "\n");
p = memmem(checksum_job->payload,
checksum_job->payload_size,
line,
strlen(line));
if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) {
log_error("DOWNLOAD INVALID: Checksum of settings file did not checkout, settings file has been tampered with.");
return -EBADMSG;
}
log_info("SHA256 checksum of %s is valid.", settings_job->url);
}
r = verify_one(checksum_job, settings_job);
if (r < 0)
return r;
if (!signature_job)
return 0;

View File

@ -30,7 +30,7 @@ int pull_find_old_etags(const char *url, const char *root, int dt, const char *p
int pull_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret);
int pull_make_settings_job(PullJob **ret, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata);
int pull_make_auxiliary_job(PullJob **ret, const char *url, int (*strip_suffixes)(const char *name, char **ret), const char *suffix, CurlGlue *glue, PullJobFinished on_finished, void *userdata);
int pull_make_verification_jobs(PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata);
int pull_verify(PullJob *main_job, PullJob *settings_job, PullJob *checksum_job, PullJob *signature_job);
int pull_verify(PullJob *main_job, PullJob *roothash_job, PullJob *settings_job, PullJob *checksum_job, PullJob *signature_job);

View File

@ -63,6 +63,7 @@ struct RawPull {
char *image_root;
PullJob *raw_job;
PullJob *roothash_job;
PullJob *settings_job;
PullJob *checksum_job;
PullJob *signature_job;
@ -74,6 +75,7 @@ struct RawPull {
bool force_local;
bool grow_machine_directory;
bool settings;
bool roothash;
char *final_path;
char *temp_path;
@ -81,6 +83,9 @@ struct RawPull {
char *settings_path;
char *settings_temp_path;
char *roothash_path;
char *roothash_temp_path;
ImportVerify verify;
};
@ -90,6 +95,7 @@ RawPull* raw_pull_unref(RawPull *i) {
pull_job_unref(i->raw_job);
pull_job_unref(i->settings_job);
pull_job_unref(i->roothash_job);
pull_job_unref(i->checksum_job);
pull_job_unref(i->signature_job);
@ -101,12 +107,18 @@ RawPull* raw_pull_unref(RawPull *i) {
free(i->temp_path);
}
if (i->roothash_temp_path) {
(void) unlink(i->roothash_temp_path);
free(i->roothash_temp_path);
}
if (i->settings_temp_path) {
(void) unlink(i->settings_temp_path);
free(i->settings_temp_path);
}
free(i->final_path);
free(i->roothash_path);
free(i->settings_path);
free(i->image_root);
free(i->local);
@ -176,6 +188,11 @@ static void raw_pull_report_progress(RawPull *i, RawProgress p) {
remain -= 5;
}
if (i->roothash_job) {
percent += i->roothash_job->progress_percent * 5 / 100;
remain -= 5;
}
if (i->checksum_job) {
percent += i->checksum_job->progress_percent * 5 / 100;
remain -= 5;
@ -262,6 +279,55 @@ static int raw_pull_maybe_convert_qcow2(RawPull *i) {
return 1;
}
static int raw_pull_determine_path(RawPull *i, const char *suffix, char **field) {
int r;
assert(i);
assert(field);
if (*field)
return 0;
assert(i->raw_job);
r = pull_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", suffix, field);
if (r < 0)
return log_oom();
return 1;
}
static int raw_pull_copy_auxiliary_file(
RawPull *i,
const char *suffix,
char **path) {
const char *local;
int r;
assert(i);
assert(suffix);
assert(path);
r = raw_pull_determine_path(i, suffix, path);
if (r < 0)
return r;
local = strjoina(i->image_root, "/", i->local, suffix);
r = copy_file_atomic(*path, local, 0644, i->force_local, 0);
if (r == -EEXIST)
log_warning_errno(r, "File %s already exists, not replacing.", local);
else if (r == -ENOENT)
log_debug_errno(r, "Skipping creation of auxiliary file, since none was found.");
else if (r < 0)
log_warning_errno(r, "Failed to copy file %s, ignoring: %m", local);
else
log_info("Created new file %s.", local);
return 0;
}
static int raw_pull_make_local_copy(RawPull *i) {
_cleanup_free_ char *tp = NULL;
_cleanup_close_ int dfd = -1;
@ -274,12 +340,6 @@ static int raw_pull_make_local_copy(RawPull *i) {
if (!i->local)
return 0;
if (!i->final_path) {
r = pull_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", ".raw", &i->final_path);
if (r < 0)
return log_oom();
}
if (i->raw_job->etag_exists) {
/* We have downloaded this one previously, reopen it */
@ -338,27 +398,16 @@ static int raw_pull_make_local_copy(RawPull *i) {
log_info("Created new local image '%s'.", i->local);
if (i->roothash) {
r = raw_pull_copy_auxiliary_file(i, ".roothash", &i->roothash_path);
if (r < 0)
return r;
}
if (i->settings) {
const char *local_settings;
assert(i->settings_job);
if (!i->settings_path) {
r = pull_make_path(i->settings_job->url, i->settings_job->etag, i->image_root, ".settings-", NULL, &i->settings_path);
if (r < 0)
return log_oom();
}
local_settings = strjoina(i->image_root, "/", i->local, ".nspawn");
r = copy_file_atomic(i->settings_path, local_settings, 0644, i->force_local, 0);
if (r == -EEXIST)
log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings);
else if (r == -ENOENT)
log_debug_errno(r, "Skipping creation of settings file, since none was found.");
else if (r < 0)
log_warning_errno(r, "Failed to copy settings files %s, ignoring: %m", local_settings);
else
log_info("Created new settings file %s.", local_settings);
r = raw_pull_copy_auxiliary_file(i, ".nspawn", &i->settings_path);
if (r < 0)
return r;
}
return 0;
@ -370,6 +419,8 @@ static bool raw_pull_is_done(RawPull *i) {
if (!PULL_JOB_IS_COMPLETE(i->raw_job))
return false;
if (i->roothash_job && !PULL_JOB_IS_COMPLETE(i->roothash_job))
return false;
if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job))
return false;
if (i->checksum_job && !PULL_JOB_IS_COMPLETE(i->checksum_job))
@ -380,6 +431,39 @@ static bool raw_pull_is_done(RawPull *i) {
return true;
}
static int raw_pull_rename_auxiliary_file(
RawPull *i,
const char *suffix,
char **temp_path,
char **path) {
int r;
assert(i);
assert(temp_path);
assert(suffix);
assert(path);
/* Regenerate final name for this auxiliary file, we might know the etag of the raw file now, and we shoud
* incorporate it in the file name if we can */
*path = mfree(*path);
r = raw_pull_determine_path(i, suffix, path);
if (r < 0)
return r;
r = import_make_read_only(*temp_path);
if (r < 0)
return r;
r = rename_noreplace(AT_FDCWD, *temp_path, AT_FDCWD, *path);
if (r < 0)
return log_error_errno(r, "Failed to rename file %s to %s: %m", *temp_path, *path);
*temp_path = mfree(*temp_path);
return 1;
}
static void raw_pull_job_on_finished(PullJob *j) {
RawPull *i;
int r;
@ -388,7 +472,10 @@ static void raw_pull_job_on_finished(PullJob *j) {
assert(j->userdata);
i = j->userdata;
if (j == i->settings_job) {
if (j == i->roothash_job) {
if (j->error != 0)
log_info_errno(j->error, "Root hash file could not be retrieved, proceeding without.");
} else if (j == i->settings_job) {
if (j->error != 0)
log_info_errno(j->error, "Settings file could not be retrieved, proceeding without.");
} else if (j->error != 0) {
@ -413,16 +500,22 @@ static void raw_pull_job_on_finished(PullJob *j) {
if (!raw_pull_is_done(i))
return;
if (i->roothash_job)
i->roothash_job->disk_fd = safe_close(i->roothash_job->disk_fd);
if (i->settings_job)
i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd);
r = raw_pull_determine_path(i, ".raw", &i->final_path);
if (r < 0)
goto finish;
if (!i->raw_job->etag_exists) {
/* This is a new download, verify it, and move it into place */
assert(i->raw_job->disk_fd >= 0);
raw_pull_report_progress(i, RAW_VERIFYING);
r = pull_verify(i->raw_job, i->settings_job, i->checksum_job, i->signature_job);
r = pull_verify(i->raw_job, i->roothash_job, i->settings_job, i->checksum_job, i->signature_job);
if (r < 0)
goto finish;
@ -446,24 +539,18 @@ static void raw_pull_job_on_finished(PullJob *j) {
i->temp_path = mfree(i->temp_path);
if (i->settings_job &&
i->settings_job->error == 0 &&
!i->settings_job->etag_exists) {
assert(i->settings_temp_path);
assert(i->settings_path);
r = import_make_read_only(i->settings_temp_path);
if (i->roothash_job &&
i->roothash_job->error == 0) {
r = raw_pull_rename_auxiliary_file(i, ".roothash", &i->roothash_temp_path, &i->roothash_path);
if (r < 0)
goto finish;
}
r = rename_noreplace(AT_FDCWD, i->settings_temp_path, AT_FDCWD, i->settings_path);
if (r < 0) {
log_error_errno(r, "Failed to rename settings file: %m");
if (i->settings_job &&
i->settings_job->error == 0) {
r = raw_pull_rename_auxiliary_file(i, ".nspawn", &i->settings_temp_path, &i->settings_path);
if (r < 0)
goto finish;
}
i->settings_temp_path = mfree(i->settings_temp_path);
}
}
@ -482,6 +569,35 @@ finish:
sd_event_exit(i->event, r);
}
static int raw_pull_job_on_open_disk_generic(
RawPull *i,
PullJob *j,
const char *extra,
char **temp_path) {
_cleanup_free_ char *p = NULL;
int r;
assert(i);
assert(j);
assert(extra);
assert(temp_path);
if (!*temp_path) {
r = tempfn_random_child(i->image_root, extra, temp_path);
if (r < 0)
return log_oom();
}
(void) mkdir_parents_label(*temp_path, 0700);
j->disk_fd = open(*temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
if (j->disk_fd < 0)
return log_error_errno(errno, "Failed to create %s: %m", *temp_path);
return 0;
}
static int raw_pull_job_on_open_disk_raw(PullJob *j) {
RawPull *i;
int r;
@ -491,57 +607,40 @@ static int raw_pull_job_on_open_disk_raw(PullJob *j) {
i = j->userdata;
assert(i->raw_job == j);
assert(!i->final_path);
assert(!i->temp_path);
r = pull_make_path(j->url, j->etag, i->image_root, ".raw-", ".raw", &i->final_path);
r = raw_pull_job_on_open_disk_generic(i, j, "raw", &i->temp_path);
if (r < 0)
return log_oom();
r = tempfn_random(i->final_path, NULL, &i->temp_path);
if (r < 0)
return log_oom();
(void) mkdir_parents_label(i->temp_path, 0700);
j->disk_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
if (j->disk_fd < 0)
return log_error_errno(errno, "Failed to create %s: %m", i->temp_path);
return r;
r = chattr_fd(j->disk_fd, FS_NOCOW_FL, FS_NOCOW_FL);
if (r < 0)
log_warning_errno(r, "Failed to set file attributes on %s: %m", i->temp_path);
log_warning_errno(r, "Failed to set file attributes on %s, ignoring: %m", i->temp_path);
return 0;
}
static int raw_pull_job_on_open_disk_roothash(PullJob *j) {
RawPull *i;
assert(j);
assert(j->userdata);
i = j->userdata;
assert(i->roothash_job == j);
return raw_pull_job_on_open_disk_generic(i, j, "roothash", &i->roothash_temp_path);
}
static int raw_pull_job_on_open_disk_settings(PullJob *j) {
RawPull *i;
int r;
assert(j);
assert(j->userdata);
i = j->userdata;
assert(i->settings_job == j);
assert(!i->settings_path);
assert(!i->settings_temp_path);
r = pull_make_path(j->url, j->etag, i->image_root, ".settings-", NULL, &i->settings_path);
if (r < 0)
return log_oom();
r = tempfn_random(i->settings_path, NULL, &i->settings_temp_path);
if (r < 0)
return log_oom();
mkdir_parents_label(i->settings_temp_path, 0700);
j->disk_fd = open(i->settings_temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
if (j->disk_fd < 0)
return log_error_errno(errno, "Failed to create %s: %m", i->settings_temp_path);
return 0;
return raw_pull_job_on_open_disk_generic(i, j, "settings", &i->settings_temp_path);
}
static void raw_pull_job_on_progress(PullJob *j) {
@ -561,7 +660,8 @@ int raw_pull_start(
const char *local,
bool force_local,
ImportVerify verify,
bool settings) {
bool settings,
bool roothash) {
int r;
@ -585,6 +685,7 @@ int raw_pull_start(
i->force_local = force_local;
i->verify = verify;
i->settings = settings;
i->roothash = roothash;
/* Queue job for the image itself */
r = pull_job_new(&i->raw_job, url, i->glue, i);
@ -601,18 +702,24 @@ int raw_pull_start(
if (r < 0)
return r;
if (roothash) {
r = pull_make_auxiliary_job(&i->roothash_job, url, raw_strip_suffixes, ".roothash", i->glue, raw_pull_job_on_finished, i);
if (r < 0)
return r;
i->roothash_job->on_open_disk = raw_pull_job_on_open_disk_roothash;
i->roothash_job->on_progress = raw_pull_job_on_progress;
i->roothash_job->calc_checksum = verify != IMPORT_VERIFY_NO;
}
if (settings) {
r = pull_make_settings_job(&i->settings_job, url, i->glue, raw_pull_job_on_finished, i);
r = pull_make_auxiliary_job(&i->settings_job, url, raw_strip_suffixes, ".nspawn", i->glue, raw_pull_job_on_finished, i);
if (r < 0)
return r;
i->settings_job->on_open_disk = raw_pull_job_on_open_disk_settings;
i->settings_job->on_progress = raw_pull_job_on_progress;
i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO;
r = pull_find_old_etags(i->settings_job->url, i->image_root, DT_REG, ".settings-", NULL, &i->settings_job->old_etags);
if (r < 0)
return r;
}
r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, raw_pull_job_on_finished, i);
@ -623,6 +730,12 @@ int raw_pull_start(
if (r < 0)
return r;
if (i->roothash_job) {
r = pull_job_begin(i->roothash_job);
if (r < 0)
return r;
}
if (i->settings_job) {
r = pull_job_begin(i->settings_job);
if (r < 0)

View File

@ -33,4 +33,4 @@ RawPull* raw_pull_unref(RawPull *pull);
DEFINE_TRIVIAL_CLEANUP_FUNC(RawPull*, raw_pull_unref);
int raw_pull_start(RawPull *pull, const char *url, const char *local, bool force_local, ImportVerify verify, bool settings);
int raw_pull_start(RawPull *pull, const char *url, const char *local, bool force_local, ImportVerify verify, bool settings, bool roothash);

View File

@ -215,6 +215,24 @@ static void tar_pull_report_progress(TarPull *i, TarProgress p) {
log_debug("Combined progress %u%%", percent);
}
static int tar_pull_determine_path(TarPull *i, const char *suffix, char **field) {
int r;
assert(i);
assert(field);
if (*field)
return 0;
assert(i->tar_job);
r = pull_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", suffix, field);
if (r < 0)
return log_oom();
return 1;
}
static int tar_pull_make_local_copy(TarPull *i) {
int r;
@ -224,12 +242,6 @@ static int tar_pull_make_local_copy(TarPull *i) {
if (!i->local)
return 0;
if (!i->final_path) {
r = pull_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", NULL, &i->final_path);
if (r < 0)
return log_oom();
}
r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
if (r < 0)
return r;
@ -238,11 +250,9 @@ static int tar_pull_make_local_copy(TarPull *i) {
const char *local_settings;
assert(i->settings_job);
if (!i->settings_path) {
r = pull_make_path(i->settings_job->url, i->settings_job->etag, i->image_root, ".settings-", NULL, &i->settings_path);
if (r < 0)
return log_oom();
}
r = tar_pull_determine_path(i, ".nspawn", &i->settings_path);
if (r < 0)
return r;
local_settings = strjoina(i->image_root, "/", i->local, ".nspawn");
@ -311,6 +321,10 @@ static void tar_pull_job_on_finished(PullJob *j) {
if (i->settings_job)
i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd);
r = tar_pull_determine_path(i, NULL, &i->final_path);
if (r < 0)
goto finish;
if (i->tar_pid > 0) {
r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
i->tar_pid = 0;
@ -327,7 +341,7 @@ static void tar_pull_job_on_finished(PullJob *j) {
tar_pull_report_progress(i, TAR_VERIFYING);
r = pull_verify(i->tar_job, i->settings_job, i->checksum_job, i->signature_job);
r = pull_verify(i->tar_job, NULL, i->settings_job, i->checksum_job, i->signature_job);
if (r < 0)
goto finish;
@ -346,16 +360,18 @@ static void tar_pull_job_on_finished(PullJob *j) {
i->temp_path = mfree(i->temp_path);
if (i->settings_job &&
i->settings_job->error == 0 &&
!i->settings_job->etag_exists) {
i->settings_job->error == 0) {
assert(i->settings_temp_path);
assert(i->settings_path);
/* Also move the settings file into place, if
* it exist. Note that we do so only if we
* also moved the tar file in place, to keep
* things strictly in sync. */
/* Also move the settings file into place, if it exist. Note that we do so only if we also
* moved the tar file in place, to keep things strictly in sync. */
i->settings_path = mfree(i->settings_path);
r = tar_pull_determine_path(i, ".nspawn", &i->settings_path);
if (r < 0)
goto finish;
r = import_make_read_only(i->settings_temp_path);
if (r < 0)
@ -395,17 +411,13 @@ static int tar_pull_job_on_open_disk_tar(PullJob *j) {
i = j->userdata;
assert(i->tar_job == j);
assert(!i->final_path);
assert(!i->temp_path);
assert(i->tar_pid <= 0);
r = pull_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path);
if (r < 0)
return log_oom();
r = tempfn_random(i->final_path, NULL, &i->temp_path);
if (r < 0)
return log_oom();
if (!i->temp_path) {
r = tempfn_random_child(i->image_root, "tar", &i->temp_path);
if (r < 0)
return log_oom();
}
mkdir_parents_label(i->temp_path, 0700);
@ -434,16 +446,12 @@ static int tar_pull_job_on_open_disk_settings(PullJob *j) {
i = j->userdata;
assert(i->settings_job == j);
assert(!i->settings_path);
assert(!i->settings_temp_path);
r = pull_make_path(j->url, j->etag, i->image_root, ".settings-", NULL, &i->settings_path);
if (r < 0)
return log_oom();
r = tempfn_random(i->settings_path, NULL, &i->settings_temp_path);
if (r < 0)
return log_oom();
if (!i->settings_temp_path) {
r = tempfn_random_child(i->image_root, "settings", &i->settings_temp_path);
if (r < 0)
return log_oom();
}
mkdir_parents_label(i->settings_temp_path, 0700);
@ -513,17 +521,13 @@ int tar_pull_start(
/* Set up download job for the settings file (.nspawn) */
if (settings) {
r = pull_make_settings_job(&i->settings_job, url, i->glue, tar_pull_job_on_finished, i);
r = pull_make_auxiliary_job(&i->settings_job, url, tar_strip_suffixes, ".nspawn", i->glue, tar_pull_job_on_finished, i);
if (r < 0)
return r;
i->settings_job->on_open_disk = tar_pull_job_on_open_disk_settings;
i->settings_job->on_progress = tar_pull_job_on_progress;
i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO;
r = pull_find_old_etags(i->settings_job->url, i->image_root, DT_REG, ".settings-", NULL, &i->settings_job->old_etags);
if (r < 0)
return r;
}
/* Set up download of checksum/signature files */

View File

@ -37,6 +37,7 @@ static bool arg_force = false;
static const char *arg_image_root = "/var/lib/machines";
static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
static bool arg_settings = true;
static bool arg_roothash = true;
static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
log_notice("Transfer aborted.");
@ -204,7 +205,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to allocate puller: %m");
r = raw_pull_start(pull, url, local, arg_force, arg_verify, arg_settings);
r = raw_pull_start(pull, url, local, arg_force, arg_verify, arg_settings, arg_roothash);
if (r < 0)
return log_error_errno(r, "Failed to pull image: %m");
@ -226,6 +227,7 @@ static int help(int argc, char *argv[], void *userdata) {
" --verify=MODE Verify downloaded image, one of: 'no',\n"
" 'checksum', 'signature'\n"
" --settings=BOOL Download settings file with image\n"
" --roothash=BOOL Download root hash file with image\n"
" --image-root=PATH Image root directory\n\n"
"Commands:\n"
" tar URL [NAME] Download a TAR image\n"
@ -243,6 +245,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_IMAGE_ROOT,
ARG_VERIFY,
ARG_SETTINGS,
ARG_ROOTHASH,
};
static const struct option options[] = {
@ -252,6 +255,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
{ "verify", required_argument, NULL, ARG_VERIFY },
{ "settings", required_argument, NULL, ARG_SETTINGS },
{ "roothash", required_argument, NULL, ARG_ROOTHASH },
{}
};
@ -295,6 +299,14 @@ static int parse_argv(int argc, char *argv[]) {
arg_settings = r;
break;
case ARG_ROOTHASH:
r = parse_boolean(optarg);
if (r < 0)
return log_error_errno(r, "Failed to parse --roothash= parameter '%s'", optarg);
arg_roothash = r;
break;
case '?':
return -EINVAL;

View File

@ -283,17 +283,16 @@ static int open_journal(
}
static bool flushed_flag_is_set(void) {
return (access("/run/systemd/journal/flushed", F_OK) >= 0);
return access("/run/systemd/journal/flushed", F_OK) >= 0;
}
static int system_journal_open(Server *s, bool flush_requested) {
bool flushed = false;
const char *fn;
int r = 0;
if (!s->system_journal &&
(s->storage == STORAGE_PERSISTENT || s->storage == STORAGE_AUTO) &&
(flush_requested || (flushed = flushed_flag_is_set()))) {
IN_SET(s->storage, STORAGE_PERSISTENT, STORAGE_AUTO) &&
(flush_requested || flushed_flag_is_set())) {
/* If in auto mode: first try to create the machine
* path, but not the prefix.
@ -326,8 +325,8 @@ static int system_journal_open(Server *s, bool flush_requested) {
* Perform an implicit flush to var, leaving the runtime
* journal closed, now that the system journal is back.
*/
if (s->runtime_journal && flushed)
(void) server_flush_to_var(s);
if (!flush_requested)
(void) server_flush_to_var(s, true);
}
if (!s->runtime_journal &&
@ -1183,7 +1182,7 @@ finish:
dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len, unit_id, priority, object_pid);
}
int server_flush_to_var(Server *s) {
int server_flush_to_var(Server *s, bool require_flag_file) {
sd_id128_t machine;
sd_journal *j = NULL;
char ts[FORMAT_TIMESPAN_MAX];
@ -1193,13 +1192,15 @@ int server_flush_to_var(Server *s) {
assert(s);
if (s->storage != STORAGE_AUTO &&
s->storage != STORAGE_PERSISTENT)
if (!IN_SET(s->storage, STORAGE_AUTO, STORAGE_PERSISTENT))
return 0;
if (!s->runtime_journal)
return 0;
if (require_flag_file && !flushed_flag_is_set())
return 0;
(void) system_journal_open(s, true);
if (!s->system_journal)
@ -1411,7 +1412,7 @@ static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *
log_info("Received request to flush runtime journal from PID " PID_FMT, si->ssi_pid);
(void) server_flush_to_var(s);
(void) server_flush_to_var(s, false);
server_sync(s);
server_vacuum(s, false);
@ -1532,60 +1533,93 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
assert(s);
if (streq(key, "systemd.journald.forward_to_syslog")) {
if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_syslog")) {
r = value ? parse_boolean(value) : true;
if (r < 0)
log_warning("Failed to parse forward to syslog switch \"%s\". Ignoring.", value);
else
s->forward_to_syslog = r;
} else if (streq(key, "systemd.journald.forward_to_kmsg")) {
} else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_kmsg")) {
r = value ? parse_boolean(value) : true;
if (r < 0)
log_warning("Failed to parse forward to kmsg switch \"%s\". Ignoring.", value);
else
s->forward_to_kmsg = r;
} else if (streq(key, "systemd.journald.forward_to_console")) {
} else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_console")) {
r = value ? parse_boolean(value) : true;
if (r < 0)
log_warning("Failed to parse forward to console switch \"%s\". Ignoring.", value);
else
s->forward_to_console = r;
} else if (streq(key, "systemd.journald.forward_to_wall")) {
} else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_wall")) {
r = value ? parse_boolean(value) : true;
if (r < 0)
log_warning("Failed to parse forward to wall switch \"%s\". Ignoring.", value);
else
s->forward_to_wall = r;
} else if (streq(key, "systemd.journald.max_level_console") && value) {
} else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_console")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = log_level_from_string(value);
if (r < 0)
log_warning("Failed to parse max level console value \"%s\". Ignoring.", value);
else
s->max_level_console = r;
} else if (streq(key, "systemd.journald.max_level_store") && value) {
} else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_store")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = log_level_from_string(value);
if (r < 0)
log_warning("Failed to parse max level store value \"%s\". Ignoring.", value);
else
s->max_level_store = r;
} else if (streq(key, "systemd.journald.max_level_syslog") && value) {
} else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_syslog")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = log_level_from_string(value);
if (r < 0)
log_warning("Failed to parse max level syslog value \"%s\". Ignoring.", value);
else
s->max_level_syslog = r;
} else if (streq(key, "systemd.journald.max_level_kmsg") && value) {
} else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_kmsg")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = log_level_from_string(value);
if (r < 0)
log_warning("Failed to parse max level kmsg value \"%s\". Ignoring.", value);
else
s->max_level_kmsg = r;
} else if (streq(key, "systemd.journald.max_level_wall") && value) {
} else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_wall")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = log_level_from_string(value);
if (r < 0)
log_warning("Failed to parse max level wall value \"%s\". Ignoring.", value);
else
s->max_level_wall = r;
} else if (startswith(key, "systemd.journald"))
log_warning("Unknown journald kernel command line option \"%s\". Ignoring.", key);
@ -1898,7 +1932,10 @@ int server_init(Server *s) {
journal_reset_metrics(&s->runtime_storage.metrics);
server_parse_config_file(s);
parse_proc_cmdline(parse_proc_cmdline_item, s, true);
r = proc_cmdline_parse(parse_proc_cmdline_item, s, PROC_CMDLINE_STRIP_RD_PREFIX);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
if (!!s->rate_limit_interval ^ !!s->rate_limit_burst) {
log_debug("Setting both rate limit interval and burst from "USEC_FMT",%u to 0,0",

View File

@ -197,7 +197,7 @@ void server_sync(Server *s);
int server_vacuum(Server *s, bool verbose);
void server_rotate(Server *s);
int server_schedule_sync(Server *s, int priority);
int server_flush_to_var(Server *s);
int server_flush_to_var(Server *s, bool require_flag_file);
void server_maybe_append_tags(Server *s);
int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userdata);
void server_space_usage_message(Server *s, JournalStorage *storage);

View File

@ -52,7 +52,7 @@ int main(int argc, char *argv[]) {
goto finish;
server_vacuum(&server, false);
server_flush_to_var(&server);
server_flush_to_var(&server, true);
server_flush_dev_kmsg(&server);
log_debug("systemd-journald running as pid "PID_FMT, getpid());

View File

@ -335,7 +335,7 @@ static int raw_image_get_os_release(Image *image, char ***ret, sd_bus_error *err
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to set up loop block device for %s: %m", image->path);
r = dissect_image(d->fd, NULL, 0, &m);
r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT, &m);
if (r == -ENOPKG)
return sd_bus_error_set_errnof(error, r, "Disk image %s not understood: %m", image->path);
if (r < 0)

View File

@ -2666,9 +2666,9 @@ static int help(int argc, char *argv[], void *userdata) {
" -o --output=STRING Change journal output mode (short,\n"
" short-monotonic, verbose, export, json,\n"
" json-pretty, json-sse, cat)\n"
" --verify=MODE Verification mode for downloaded images (no,\n"
" --verify=MODE Verification mode for downloaded images (no,\n"
" checksum, signature)\n"
" --force Download image even if already exists\n\n"
" --force Download image even if already exists\n\n"
"Machine Commands:\n"
" list List running VMs and containers\n"
" status NAME... Show VM/container details\n"

View File

@ -62,7 +62,11 @@ static int add_modules(const char *p) {
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
int r;
if (streq(key, "modules-load") && value) {
if (proc_cmdline_key_streq(key, "modules_load")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = add_modules(value);
if (r < 0)
return r;
@ -226,7 +230,7 @@ int main(int argc, char *argv[]) {
umask(0022);
r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, true);
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");

View File

@ -1349,21 +1349,3 @@ fail:
(void) rmdir(template);
return r;
}
VolatileMode volatile_mode_from_string(const char *s) {
int b;
if (isempty(s))
return _VOLATILE_MODE_INVALID;
b = parse_boolean(s);
if (b > 0)
return VOLATILE_YES;
if (b == 0)
return VOLATILE_NO;
if (streq(s, "state"))
return VOLATILE_STATE;
return _VOLATILE_MODE_INVALID;
}

View File

@ -22,6 +22,7 @@
#include <stdbool.h>
#include "cgroup-util.h"
#include "volatile-util.h"
typedef enum MountSettingsMask {
MOUNT_FATAL = 1 << 0, /* if set, a mount error is considered fatal */
@ -32,14 +33,6 @@ typedef enum MountSettingsMask {
Works only if MOUNT_APPLY_APIVFS_RO is also set. */
} MountSettingsMask;
typedef enum VolatileMode {
VOLATILE_NO,
VOLATILE_YES,
VOLATILE_STATE,
_VOLATILE_MODE_MAX,
_VOLATILE_MODE_INVALID = -1
} VolatileMode;
typedef enum CustomMountType {
CUSTOM_MOUNT_BIND,
CUSTOM_MOUNT_TMPFS,
@ -77,5 +70,3 @@ int mount_custom(const char *dest, CustomMount *mounts, unsigned n, bool userns,
int setup_volatile(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
int setup_volatile_state(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
VolatileMode volatile_mode_from_string(const char *s);

View File

@ -1288,15 +1288,18 @@ static int setup_timezone(const char *dest) {
return 0;
}
r = unlink(where);
if (r < 0 && errno != ENOENT) {
log_error_errno(errno, "Failed to remove existing timezone info %s in container: %m", where);
if (unlink(where) < 0 && errno != ENOENT) {
log_full_errno(IN_SET(errno, EROFS, EACCES, EPERM) ? LOG_DEBUG : LOG_WARNING, /* Don't complain on read-only images */
errno,
"Failed to remove existing timezone info %s in container, ignoring: %m", where);
return 0;
}
what = strjoina("../usr/share/zoneinfo/", z);
if (symlink(what, where) < 0) {
log_error_errno(errno, "Failed to correct timezone of container: %m");
log_full_errno(IN_SET(errno, EROFS, EACCES, EPERM) ? LOG_DEBUG : LOG_WARNING,
errno,
"Failed to correct timezone of container, ignoring: %m");
return 0;
}
@ -1308,31 +1311,43 @@ static int setup_timezone(const char *dest) {
}
static int setup_resolv_conf(const char *dest) {
const char *where = NULL;
int r;
_cleanup_free_ char *resolved = NULL, *etc = NULL;
const char *where;
int r, found;
assert(dest);
if (arg_private_network)
return 0;
/* Fix resolv.conf, if possible */
where = prefix_roota(dest, "/etc/resolv.conf");
r = chase_symlinks("/etc", dest, CHASE_PREFIX_ROOT, &etc);
if (r < 0) {
log_warning_errno(r, "Failed to resolve /etc path in container, ignoring: %m");
return 0;
}
where = strjoina(etc, "/resolv.conf");
found = chase_symlinks(where, dest, CHASE_NONEXISTENT, &resolved);
if (found < 0) {
log_warning_errno(found, "Failed to resolve /etc/resolv.conf path in container, ignoring: %m");
return 0;
}
if (access("/run/systemd/resolve/resolv.conf", F_OK) >= 0 &&
access("/usr/lib/systemd/resolv.conf", F_OK) >= 0) {
access("/usr/lib/systemd/resolv.conf", F_OK) >= 0) {
/* resolved is enabled on the host. In this, case bind mount its static resolv.conf file into the
* container, so that the container can use the host's resolver. Given that network namespacing is
* disabled it's only natural of the container also uses the host's resolver. It also has the big
* advantage that the container will be able to follow the host's DNS server configuration changes
* transparently. */
(void) touch(where);
if (found == 0) /* missing? */
(void) touch(resolved);
r = mount_verbose(LOG_WARNING, "/usr/lib/systemd/resolv.conf", where, NULL, MS_BIND, NULL);
r = mount_verbose(LOG_DEBUG, "/usr/lib/systemd/resolv.conf", resolved, NULL, MS_BIND, NULL);
if (r >= 0)
return mount_verbose(LOG_ERR, NULL, where, NULL,
MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL);
return mount_verbose(LOG_ERR, NULL, resolved, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL);
}
/* If that didn't work, let's copy the file */
@ -1343,7 +1358,7 @@ static int setup_resolv_conf(const char *dest) {
*
* If the disk image is read-only, there's also no point in complaining.
*/
log_full_errno(IN_SET(r, -ELOOP, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
log_full_errno(IN_SET(r, -ELOOP, -EROFS, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to copy /etc/resolv.conf to %s, ignoring: %m", where);
return 0;
}
@ -2467,20 +2482,6 @@ static int outer_child(
if (r < 0)
return r;
/* Mark everything as shared so our mounts get propagated down. This is
* required to make new bind mounts available in systemd services
* inside the containter that create a new mount namespace.
* See https://github.com/systemd/systemd/issues/3860
* Further submounts (such as /dev) done after this will inherit the
* shared propagation mode.*/
r = mount_verbose(LOG_ERR, NULL, directory, NULL, MS_SHARED|MS_REC, NULL);
if (r < 0)
return r;
r = recursive_chown(directory, arg_uid_shift, arg_uid_range);
if (r < 0)
return r;
r = setup_volatile(
directory,
arg_volatile_mode,
@ -2501,6 +2502,20 @@ static int outer_child(
if (r < 0)
return r;
/* Mark everything as shared so our mounts get propagated down. This is
* required to make new bind mounts available in systemd services
* inside the containter that create a new mount namespace.
* See https://github.com/systemd/systemd/issues/3860
* Further submounts (such as /dev) done after this will inherit the
* shared propagation mode.*/
r = mount_verbose(LOG_ERR, NULL, directory, NULL, MS_SHARED|MS_REC, NULL);
if (r < 0)
return r;
r = recursive_chown(directory, arg_uid_shift, arg_uid_range);
if (r < 0)
return r;
r = base_filesystem_create(directory, arg_uid_shift, (gid_t) arg_uid_shift);
if (r < 0)
return r;
@ -3740,7 +3755,11 @@ int main(int argc, char *argv[]) {
goto finish;
}
r = dissect_image(loop->fd, arg_root_hash, arg_root_hash_size, &dissected_image);
r = dissect_image(
loop->fd,
arg_root_hash, arg_root_hash_size,
DISSECT_IMAGE_REQUIRE_ROOT,
&dissected_image);
if (r == -ENOPKG) {
log_error_errno(r, "Could not find a suitable file system or partition table in image: %s", arg_image);

View File

@ -34,7 +34,10 @@ static bool arg_force = false;
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
if (streq(key, "quotacheck.mode") && value) {
if (streq(key, "quotacheck.mode")) {
if (proc_cmdline_value_missing(key, value))
return 0;
if (streq(value, "auto"))
arg_force = arg_skip = false;
@ -88,7 +91,7 @@ int main(int argc, char *argv[]) {
umask(0022);
r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, false);
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
@ -104,9 +107,10 @@ int main(int argc, char *argv[]) {
pid = fork();
if (pid < 0) {
log_error_errno(errno, "fork(): %m");
return EXIT_FAILURE;
} else if (pid == 0) {
r = log_error_errno(errno, "fork(): %m");
goto finish;
}
if (pid == 0) {
/* Child */
@ -120,5 +124,6 @@ int main(int argc, char *argv[]) {
r = wait_for_terminate_and_warn("quotacheck", pid, true);
finish:
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}

View File

@ -56,7 +56,7 @@ int main(int argc, char *argv[]) {
umask(0022);
f = setmntent("/etc/fstab", "r");
f = setmntent("/etc/fstab", "re");
if (!f) {
if (errno == ENOENT) {
r = 0;

View File

@ -101,7 +101,7 @@ int base_filesystem_create(const char *root, uid_t uid, gid_t gid) {
if (r < 0 && errno != EEXIST)
return log_error_errno(errno, "Failed to create symlink at %s/%s: %m", root, table[i].dir);
if (uid != UID_INVALID || gid != UID_INVALID) {
if (uid_is_valid(uid) || gid_is_valid(gid)) {
if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
return log_error_errno(errno, "Failed to chown symlink at %s/%s: %m", root, table[i].dir);
}

View File

@ -84,7 +84,7 @@ not_found:
#endif
}
int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectedImage **ret) {
int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret) {
#ifdef HAVE_BLKID
sd_id128_t root_uuid = SD_ID128_NULL, verity_uuid = SD_ID128_NULL;
@ -95,7 +95,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
_cleanup_udev_unref_ struct udev *udev = NULL;
_cleanup_free_ char *generic_node = NULL;
const char *pttype = NULL, *usage = NULL;
sd_id128_t generic_uuid = SD_ID128_NULL;
const char *pttype = NULL;
struct udev_list_entry *first, *item;
blkid_partlist pl;
int r, generic_nr;
@ -147,8 +148,12 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
return -errno;
}
blkid_probe_enable_superblocks(b, 1);
blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE);
if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) {
/* Look for file system superblocks, unless we only shall look for GPT partition tables */
blkid_probe_enable_superblocks(b, 1);
blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE);
}
blkid_probe_enable_partitions(b, 1);
blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
@ -169,40 +174,45 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
if (!m)
return -ENOMEM;
(void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
_cleanup_free_ char *t = NULL, *n = NULL;
const char *fstype = NULL;
if (!(flags & DISSECT_IMAGE_GPT_ONLY) &&
(flags & DISSECT_IMAGE_REQUIRE_ROOT)) {
const char *usage = NULL;
/* OK, we have found a file system, that's our root partition then. */
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
(void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
_cleanup_free_ char *t = NULL, *n = NULL;
const char *fstype = NULL;
if (fstype) {
t = strdup(fstype);
if (!t)
/* OK, we have found a file system, that's our root partition then. */
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
if (fstype) {
t = strdup(fstype);
if (!t)
return -ENOMEM;
}
if (asprintf(&n, "/dev/block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
return -ENOMEM;
m->partitions[PARTITION_ROOT] = (DissectedPartition) {
.found = true,
.rw = true,
.partno = -1,
.architecture = _ARCHITECTURE_INVALID,
.fstype = t,
.node = n,
};
t = n = NULL;
m->encrypted = streq(fstype, "crypto_LUKS");
*ret = m;
m = NULL;
return 0;
}
if (asprintf(&n, "/dev/block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
return -ENOMEM;
m->partitions[PARTITION_ROOT] = (DissectedPartition) {
.found = true,
.rw = true,
.partno = -1,
.architecture = _ARCHITECTURE_INVALID,
.fstype = t,
.node = n,
};
t = n = NULL;
m->encrypted = streq(fstype, "crypto_LUKS");
*ret = m;
m = NULL;
return 0;
}
(void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
@ -212,7 +222,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
is_gpt = streq_ptr(pttype, "gpt");
is_mbr = streq_ptr(pttype, "dos");
if (!is_gpt && !is_mbr)
if (!is_gpt && ((flags & DISSECT_IMAGE_GPT_ONLY) || !is_mbr))
return -ENOPKG;
errno = 0;
@ -300,7 +310,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
first = udev_enumerate_get_list_entry(e);
udev_list_entry_foreach(item, first) {
_cleanup_udev_device_unref_ struct udev_device *q;
unsigned long long flags;
unsigned long long pflags;
blkid_partition pp;
const char *node;
dev_t qn;
@ -325,7 +335,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
if (!pp)
continue;
flags = blkid_partition_get_flags(pp);
pflags = blkid_partition_get_flags(pp);
nr = blkid_partition_get_partno(pp);
if (nr < 0)
@ -337,7 +347,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
sd_id128_t type_id, id;
bool rw = true;
if (flags & GPT_FLAG_NO_AUTO)
if (pflags & GPT_FLAG_NO_AUTO)
continue;
sid = blkid_partition_get_uuid(pp);
@ -354,10 +364,10 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
if (sd_id128_equal(type_id, GPT_HOME)) {
designator = PARTITION_HOME;
rw = !(flags & GPT_FLAG_READ_ONLY);
rw = !(pflags & GPT_FLAG_READ_ONLY);
} else if (sd_id128_equal(type_id, GPT_SRV)) {
designator = PARTITION_SRV;
rw = !(flags & GPT_FLAG_READ_ONLY);
rw = !(pflags & GPT_FLAG_READ_ONLY);
} else if (sd_id128_equal(type_id, GPT_ESP)) {
designator = PARTITION_ESP;
fstype = "vfat";
@ -371,7 +381,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
designator = PARTITION_ROOT;
architecture = native_architecture();
rw = !(flags & GPT_FLAG_READ_ONLY);
rw = !(pflags & GPT_FLAG_READ_ONLY);
} else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY)) {
m->can_verity = true;
@ -395,9 +405,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
designator = PARTITION_ROOT_SECONDARY;
architecture = SECONDARY_ARCHITECTURE;
rw = !(flags & GPT_FLAG_READ_ONLY);
rw = !(pflags & GPT_FLAG_READ_ONLY);
} else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY)) {
m->can_verity = true;
/* Ignore verity unless root has is specified */
@ -419,7 +428,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
multiple_generic = true;
else {
generic_nr = nr;
generic_rw = !(flags & GPT_FLAG_READ_ONLY);
generic_rw = !(pflags & GPT_FLAG_READ_ONLY);
generic_uuid = id;
generic_node = strdup(node);
if (!generic_node)
return -ENOMEM;
@ -450,6 +460,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
.architecture = architecture,
.node = n,
.fstype = t,
.uuid = id,
};
n = t = NULL;
@ -457,7 +468,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
} else if (is_mbr) {
if (flags != 0x80) /* Bootable flag */
if (pflags != 0x80) /* Bootable flag */
continue;
if (blkid_partition_get_type(pp) != 0x83) /* Linux partition */
@ -480,7 +491,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
* either, then check if there's a single generic one, and use that. */
if (m->partitions[PARTITION_ROOT_VERITY].found)
return -ENXIO;
return -EADDRNOTAVAIL;
if (m->partitions[PARTITION_ROOT_SECONDARY].found) {
m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_SECONDARY];
@ -489,8 +500,19 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY];
zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
} else if (generic_node && !root_hash) {
} else if (flags & DISSECT_IMAGE_REQUIRE_ROOT) {
/* If the root has was set, then we won't fallback to a generic node, because the root hash
* decides */
if (root_hash)
return -EADDRNOTAVAIL;
/* If we didn't find a generic node, then we can't fix this up either */
if (!generic_node)
return -ENXIO;
/* If we didn't find a properly marked root partition, but we did find a single suitable
* generic Linux partition, then use this as root partition, if the caller asked for it. */
if (multiple_generic)
return -ENOTUNIQ;
@ -500,17 +522,15 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
.partno = generic_nr,
.architecture = _ARCHITECTURE_INVALID,
.node = generic_node,
.uuid = generic_uuid,
};
generic_node = NULL;
} else
return -ENXIO;
}
}
assert(m->partitions[PARTITION_ROOT].found);
if (root_hash) {
if (!m->partitions[PARTITION_ROOT_VERITY].found)
if (!m->partitions[PARTITION_ROOT_VERITY].found || !m->partitions[PARTITION_ROOT].found)
return -EADDRNOTAVAIL;
/* If we found the primary root with the hash, then we definitely want to suppress any secondary root

View File

@ -32,6 +32,7 @@ struct DissectedPartition {
bool rw:1;
int partno; /* -1 if there was no partition and the images contains a file system directly */
int architecture; /* Intended architecture: either native, secondary or unset (-1). */
sd_id128_t uuid; /* Partition entry UUID as reported by the GPT */
char *fstype;
char *node;
char *decrypted_node;
@ -67,6 +68,8 @@ typedef enum DissectImageFlags {
DISSECT_IMAGE_DISCARD_ANY = DISSECT_IMAGE_DISCARD_ON_LOOP |
DISSECT_IMAGE_DISCARD |
DISSECT_IMAGE_DISCARD_ON_CRYPTO,
DISSECT_IMAGE_GPT_ONLY = 16, /* Only recognize images with GPT partition tables */
DISSECT_IMAGE_REQUIRE_ROOT = 32, /* Don't accept disks without root partition */
} DissectImageFlags;
struct DissectedImage {
@ -76,7 +79,7 @@ struct DissectedImage {
DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX];
};
int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectedImage **ret);
int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret);
DissectedImage* dissected_image_unref(DissectedImage *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);

View File

@ -38,7 +38,7 @@ bool fstab_is_mount_point(const char *mount) {
_cleanup_endmntent_ FILE *f = NULL;
struct mntent *m;
f = setmntent("/etc/fstab", "r");
f = setmntent("/etc/fstab", "re");
if (!f)
return false;

View File

@ -99,6 +99,16 @@ static char **image_settings_path(Image *image) {
return ret;
}
static char *image_roothash_path(Image *image) {
const char *fn;
assert(image);
fn = strjoina(image->name, ".roothash");
return file_in_same_dir(image->path, fn);
}
static int image_new(
ImageType t,
const char *pretty,
@ -397,6 +407,7 @@ void image_hashmap_free(Hashmap *map) {
int image_remove(Image *i) {
_cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
_cleanup_strv_free_ char **settings = NULL;
_cleanup_free_ char *roothash = NULL;
char **j;
int r;
@ -409,6 +420,10 @@ int image_remove(Image *i) {
if (!settings)
return -ENOMEM;
roothash = image_roothash_path(i);
if (!roothash)
return -ENOMEM;
/* Make sure we don't interfere with a running nspawn */
r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
if (r < 0)
@ -445,14 +460,17 @@ int image_remove(Image *i) {
log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", *j);
}
if (unlink(roothash) < 0 && errno != ENOENT)
log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", roothash);
return 0;
}
static int rename_settings_file(const char *path, const char *new_name) {
static int rename_auxiliary_file(const char *path, const char *new_name, const char *suffix) {
_cleanup_free_ char *rs = NULL;
const char *fn;
fn = strjoina(new_name, ".nspawn");
fn = strjoina(new_name, suffix);
rs = file_in_same_dir(path, fn);
if (!rs)
@ -463,7 +481,7 @@ static int rename_settings_file(const char *path, const char *new_name) {
int image_rename(Image *i, const char *new_name) {
_cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT, name_lock = LOCK_FILE_INIT;
_cleanup_free_ char *new_path = NULL, *nn = NULL;
_cleanup_free_ char *new_path = NULL, *nn = NULL, *roothash = NULL;
_cleanup_strv_free_ char **settings = NULL;
unsigned file_attr = 0;
char **j;
@ -481,6 +499,10 @@ int image_rename(Image *i, const char *new_name) {
if (!settings)
return -ENOMEM;
roothash = image_roothash_path(i);
if (!roothash)
return -ENOMEM;
/* Make sure we don't interfere with a running nspawn */
r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
if (r < 0)
@ -550,19 +572,23 @@ int image_rename(Image *i, const char *new_name) {
nn = NULL;
STRV_FOREACH(j, settings) {
r = rename_settings_file(*j, new_name);
r = rename_auxiliary_file(*j, new_name, ".nspawn");
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to rename settings file %s, ignoring: %m", *j);
}
r = rename_auxiliary_file(roothash, new_name, ".roothash");
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to rename roothash file %s, ignoring: %m", roothash);
return 0;
}
static int clone_settings_file(const char *path, const char *new_name) {
static int clone_auxiliary_file(const char *path, const char *new_name, const char *suffix) {
_cleanup_free_ char *rs = NULL;
const char *fn;
fn = strjoina(new_name, ".nspawn");
fn = strjoina(new_name, suffix);
rs = file_in_same_dir(path, fn);
if (!rs)
@ -574,6 +600,7 @@ static int clone_settings_file(const char *path, const char *new_name) {
int image_clone(Image *i, const char *new_name, bool read_only) {
_cleanup_release_lock_file_ LockFile name_lock = LOCK_FILE_INIT;
_cleanup_strv_free_ char **settings = NULL;
_cleanup_free_ char *roothash = NULL;
const char *new_path;
char **j;
int r;
@ -587,6 +614,10 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
if (!settings)
return -ENOMEM;
roothash = image_roothash_path(i);
if (!roothash)
return -ENOMEM;
/* Make sure nobody takes the new name, between the time we
* checked it is currently unused in all search paths, and the
* time we take possession of it */
@ -636,11 +667,15 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
return r;
STRV_FOREACH(j, settings) {
r = clone_settings_file(*j, new_name);
r = clone_auxiliary_file(*j, new_name, ".nspawn");
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to clone settings %s, ignoring: %m", *j);
}
r = clone_auxiliary_file(roothash, new_name, ".roothash");
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to clone root hash file %s, ignoring: %m", roothash);
return 0;
}

View File

@ -28,123 +28,102 @@
#include "base-filesystem.h"
#include "fd-util.h"
#include "fs-util.h"
#include "log.h"
#include "missing.h"
#include "mkdir.h"
#include "mount-util.h"
#include "path-util.h"
#include "rm-rf.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "switch-root.h"
#include "user-util.h"
#include "util.h"
int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, unsigned long mountflags) {
/* Don't try to unmount/move the old "/", there's no way to do it. */
static const char move_mounts[] =
"/dev\0"
"/proc\0"
"/sys\0"
"/run\0";
int switch_root(const char *new_root,
const char *old_root_after, /* path below the new root, where to place the old root after the transition */
bool unmount_old_root,
unsigned long mount_flags) { /* MS_MOVE or MS_BIND */
_cleanup_free_ char *resolved_old_root_after = NULL;
_cleanup_close_ int old_root_fd = -1;
struct stat new_root_stat;
bool old_root_remove;
const char *i, *temporary_old_root;
const char *i;
int r;
assert(new_root);
assert(old_root_after);
if (path_equal(new_root, "/"))
return 0;
temporary_old_root = strjoina(new_root, oldroot);
mkdir_p_label(temporary_old_root, 0755);
/* Check if we shall remove the contents of the old root */
old_root_remove = in_initrd();
if (old_root_remove) {
old_root_fd = open("/", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
if (old_root_fd < 0)
return log_error_errno(errno, "Failed to open root directory: %m");
}
if (stat(new_root, &new_root_stat) < 0)
return log_error_errno(errno, "Failed to stat directory %s: %m", new_root);
/* Determine where we shall place the old root after the transition */
r = chase_symlinks(old_root_after, new_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved_old_root_after);
if (r < 0)
return log_error_errno(r, "Failed to resolve %s/%s: %m", new_root, old_root_after);
if (r == 0) /* Doesn't exist yet. Let's create it */
(void) mkdir_p_label(resolved_old_root_after, 0755);
/* Work-around for kernel design: the kernel refuses switching
* root if any file systems are mounted MS_SHARED. Hence
/* Work-around for kernel design: the kernel refuses MS_MOVE if any file systems are mounted MS_SHARED. Hence
* remount them MS_PRIVATE here as a work-around.
*
* https://bugzilla.redhat.com/show_bug.cgi?id=847418 */
if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0)
log_warning_errno(errno, "Failed to make \"/\" private mount: %m");
return log_error_errno(errno, "Failed to set \"/\" mount propagation to private: %m");
NULSTR_FOREACH(i, move_mounts) {
char new_mount[PATH_MAX];
struct stat sb;
size_t n;
FOREACH_STRING(i, "/sys", "/dev", "/run", "/proc") {
_cleanup_free_ char *chased = NULL;
n = snprintf(new_mount, sizeof new_mount, "%s%s", new_root, i);
if (n >= sizeof new_mount) {
bool move = mountflags & MS_MOVE;
r = chase_symlinks(i, new_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &chased);
if (r < 0)
return log_error_errno(r, "Failed to resolve %s/%s: %m", new_root, i);
if (r > 0) {
/* Already exists. Let's see if it is a mount point already. */
r = path_is_mount_point(chased, NULL, 0);
if (r < 0)
return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", chased);
if (r > 0) /* If it is already mounted, then do nothing */
continue;
} else
/* Doesn't exist yet? */
(void) mkdir_p_label(chased, 0755);
log_warning("New path is too long, %s: %s%s",
move ? "forcing unmount instead" : "ignoring",
new_root, i);
if (move)
if (umount2(i, MNT_FORCE) < 0)
log_warning_errno(errno, "Failed to unmount %s: %m", i);
continue;
}
mkdir_p_label(new_mount, 0755);
if (stat(new_mount, &sb) < 0 ||
sb.st_dev != new_root_stat.st_dev) {
/* Mount point seems to be mounted already or
* stat failed. Unmount the old mount point. */
if (umount2(i, MNT_DETACH) < 0)
log_warning_errno(errno, "Failed to unmount %s: %m", i);
continue;
}
if (mount(i, new_mount, NULL, mountflags, NULL) < 0) {
if (mountflags & MS_MOVE) {
log_error_errno(errno, "Failed to move mount %s to %s, forcing unmount: %m", i, new_mount);
if (umount2(i, MNT_FORCE) < 0)
log_warning_errno(errno, "Failed to unmount %s: %m", i);
} else if (mountflags & MS_BIND)
log_error_errno(errno, "Failed to bind mount %s to %s: %m", i, new_mount);
}
if (mount(i, chased, NULL, mount_flags, NULL) < 0)
return log_error_errno(r, "Failed to mount %s to %s: %m", i, chased);
}
/* Do not fail, if base_filesystem_create() fails. Not all
* switch roots are like base_filesystem_create() wants them
* to look like. They might even boot, if they are RO and
* don't have the FS layout. Just ignore the error and
* switch_root() nevertheless. */
/* Do not fail if base_filesystem_create() fails. Not all switch roots are like base_filesystem_create() wants
* them to look like. They might even boot, if they are RO and don't have the FS layout. Just ignore the error
* and switch_root() nevertheless. */
(void) base_filesystem_create(new_root, UID_INVALID, GID_INVALID);
if (chdir(new_root) < 0)
return log_error_errno(errno, "Failed to change directory to %s: %m", new_root);
if (old_root_remove) {
old_root_fd = open("/", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
if (old_root_fd < 0)
log_warning_errno(errno, "Failed to open root directory: %m");
}
/* We first try a pivot_root() so that we can umount the old
* root dir. In many cases (i.e. where rootfs is /), that's
* not possible however, and hence we simply overmount root */
if (pivot_root(new_root, temporary_old_root) >= 0) {
/* We first try a pivot_root() so that we can umount the old root dir. In many cases (i.e. where rootfs is /),
* that's not possible however, and hence we simply overmount root */
if (pivot_root(new_root, resolved_old_root_after) >= 0) {
/* Immediately get rid of the old root, if detach_oldroot is set.
* Since we are running off it we need to do this lazily. */
if (detach_oldroot && umount2(oldroot, MNT_DETACH) < 0)
log_error_errno(errno, "Failed to lazily umount old root dir %s, %s: %m",
oldroot,
errno == ENOENT ? "ignoring" : "leaving it around");
if (unmount_old_root) {
r = umount_recursive(old_root_after, MNT_DETACH);
if (r < 0)
log_warning_errno(r, "Failed to unmount old root directory tree, ignoring: %m");
}
} else if (mount(new_root, "/", NULL, MS_MOVE, NULL) < 0)
return log_error_errno(errno, "Failed to mount moving %s to /: %m", new_root);
return log_error_errno(errno, "Failed to move %s to /: %m", new_root);
if (chroot(".") < 0)
return log_error_errno(errno, "Failed to change root: %m");

View File

@ -0,0 +1,68 @@
/***
This file is part of systemd.
Copyright 2015 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "alloc-util.h"
#include "macro.h"
#include "parse-util.h"
#include "proc-cmdline.h"
#include "string-util.h"
#include "volatile-util.h"
VolatileMode volatile_mode_from_string(const char *s) {
int b;
if (isempty(s))
return _VOLATILE_MODE_INVALID;
b = parse_boolean(s);
if (b > 0)
return VOLATILE_YES;
if (b == 0)
return VOLATILE_NO;
if (streq(s, "state"))
return VOLATILE_STATE;
return _VOLATILE_MODE_INVALID;
}
int query_volatile_mode(VolatileMode *ret) {
_cleanup_free_ char *mode = NULL;
VolatileMode m = VOLATILE_NO;
int r;
r = proc_cmdline_get_key("systemd.volatile", PROC_CMDLINE_VALUE_OPTIONAL, &mode);
if (r < 0)
return r;
if (r == 0)
goto finish;
if (mode) {
m = volatile_mode_from_string(mode);
if (m < 0)
return -EINVAL;
} else
m = VOLATILE_YES;
r = 1;
finish:
*ret = m;
return r;
}

View File

@ -0,0 +1,32 @@
#pragma once
/***
This file is part of systemd.
Copyright 2016 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
typedef enum VolatileMode {
VOLATILE_NO,
VOLATILE_YES,
VOLATILE_STATE,
_VOLATILE_MODE_MAX,
_VOLATILE_MODE_INVALID = -1
} VolatileMode;
VolatileMode volatile_mode_from_string(const char *s);
int query_volatile_mode(VolatileMode *ret);

View File

@ -43,7 +43,7 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
r = dissect_image(d->fd, NULL, 0, &m);
r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT, &m);
if (r < 0) {
log_error_errno(r, "Failed to dissect image: %m");
return EXIT_FAILURE;

View File

@ -33,6 +33,7 @@
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
#endif
#include "stat-util.h"
#include "test-helper.h"
#include "unit.h"
#include "util.h"
@ -188,15 +189,27 @@ static void test_exec_protectkernelmodules(Manager *m) {
}
static void test_exec_readonlypaths(Manager *m) {
if (path_is_read_only_fs("/var") > 0)
return;
test(m, "exec-readonlypaths.service", 0, CLD_EXITED);
test(m, "exec-readonlypaths-mount-propagation.service", 0, CLD_EXITED);
}
static void test_exec_readwritepaths(Manager *m) {
if (path_is_read_only_fs("/") > 0)
return;
test(m, "exec-readwritepaths-mount-propagation.service", 0, CLD_EXITED);
}
static void test_exec_inaccessiblepaths(Manager *m) {
if (path_is_read_only_fs("/") > 0)
return;
test(m, "exec-inaccessiblepaths-mount-propagation.service", 0, CLD_EXITED);
}

View File

@ -87,27 +87,19 @@ static void test_undecchar(void) {
}
static void test_unhexmem(void) {
const char *hex = "efa214921";
const char *hex = "efa2149213";
const char *hex_invalid = "efa214921o";
_cleanup_free_ char *hex2 = NULL;
_cleanup_free_ void *mem = NULL;
size_t len;
assert_se(unhexmem(hex, strlen(hex), &mem, &len) == 0);
assert_se(unhexmem(hex, strlen(hex) + 1, &mem, &len) == -EINVAL);
assert_se(unhexmem(hex_invalid, strlen(hex_invalid), &mem, &len) == -EINVAL);
assert_se(unhexmem(hex, strlen(hex) + 1, &mem, &len) == -EINVAL);
assert_se(unhexmem(hex, strlen(hex) - 1, &mem, &len) == -EINVAL);
assert_se(unhexmem(hex, strlen(hex), &mem, &len) == 0);
assert_se((hex2 = hexmem(mem, len)));
free(mem);
assert_se(memcmp(hex, hex2, strlen(hex)) == 0);
free(hex2);
assert_se(unhexmem(hex, strlen(hex) - 1, &mem, &len) == 0);
assert_se((hex2 = hexmem(mem, len)));
assert_se(memcmp(hex, hex2, strlen(hex) - 1) == 0);
assert_se(streq(hex, hex2));
}
/* https://tools.ietf.org/html/rfc4648#section-10 */

View File

@ -35,8 +35,8 @@ static int parse_item(const char *key, const char *value, void *data) {
return 0;
}
static void test_parse_proc_cmdline(void) {
assert_se(parse_proc_cmdline(parse_item, &obj, true) >= 0);
static void test_proc_cmdline_parse(void) {
assert_se(proc_cmdline_parse(parse_item, &obj, true) >= 0);
}
static void test_runlevel_to_target(void) {
@ -55,11 +55,101 @@ static void test_runlevel_to_target(void) {
assert_se(streq_ptr(runlevel_to_target("rd.rescue"), SPECIAL_RESCUE_TARGET));
}
static void test_proc_cmdline_get_key(void) {
_cleanup_free_ char *value = NULL;
putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm");
assert_se(proc_cmdline_get_key("", 0, &value) == -EINVAL);
assert_se(proc_cmdline_get_key("abc", 0, NULL) == 0);
assert_se(proc_cmdline_get_key("abc", 0, &value) == 0 && value == NULL);
assert_se(proc_cmdline_get_key("abc", PROC_CMDLINE_VALUE_OPTIONAL, &value) == 0 && value == NULL);
assert_se(proc_cmdline_get_key("foo_bar", 0, &value) > 0 && streq_ptr(value, "quux"));
value = mfree(value);
assert_se(proc_cmdline_get_key("foo_bar", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "quux"));
value = mfree(value);
assert_se(proc_cmdline_get_key("foo-bar", 0, &value) > 0 && streq_ptr(value, "quux"));
value = mfree(value);
assert_se(proc_cmdline_get_key("foo-bar", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "quux"));
value = mfree(value);
assert_se(proc_cmdline_get_key("foo-bar", 0, NULL) == 0);
assert_se(proc_cmdline_get_key("foo-bar", PROC_CMDLINE_VALUE_OPTIONAL, NULL) == -EINVAL);
assert_se(proc_cmdline_get_key("wuff-piep", 0, &value) > 0 && streq_ptr(value, "tuet"));
value = mfree(value);
assert_se(proc_cmdline_get_key("wuff-piep", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "tuet"));
value = mfree(value);
assert_se(proc_cmdline_get_key("wuff_piep", 0, &value) > 0 && streq_ptr(value, "tuet"));
value = mfree(value);
assert_se(proc_cmdline_get_key("wuff_piep", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "tuet"));
value = mfree(value);
assert_se(proc_cmdline_get_key("wuff_piep", 0, NULL) == 0);
assert_se(proc_cmdline_get_key("wuff_piep", PROC_CMDLINE_VALUE_OPTIONAL, NULL) == -EINVAL);
assert_se(proc_cmdline_get_key("zumm", 0, &value) == 0 && value == NULL);
assert_se(proc_cmdline_get_key("zumm", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && value == NULL);
assert_se(proc_cmdline_get_key("zumm", 0, NULL) > 0);
}
static void test_proc_cmdline_get_bool(void) {
bool value = false;
putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar bar-waldo=1 x_y-z=0 quux=miep");
assert_se(proc_cmdline_get_bool("", &value) == -EINVAL);
assert_se(proc_cmdline_get_bool("abc", &value) == 0 && value == false);
assert_se(proc_cmdline_get_bool("foo_bar", &value) > 0 && value == true);
assert_se(proc_cmdline_get_bool("foo-bar", &value) > 0 && value == true);
assert_se(proc_cmdline_get_bool("bar-waldo", &value) > 0 && value == true);
assert_se(proc_cmdline_get_bool("bar_waldo", &value) > 0 && value == true);
assert_se(proc_cmdline_get_bool("x_y-z", &value) > 0 && value == false);
assert_se(proc_cmdline_get_bool("x-y-z", &value) > 0 && value == false);
assert_se(proc_cmdline_get_bool("x-y_z", &value) > 0 && value == false);
assert_se(proc_cmdline_get_bool("x_y_z", &value) > 0 && value == false);
assert_se(proc_cmdline_get_bool("quux", &value) == -EINVAL && value == false);
}
static void test_proc_cmdline_key_streq(void) {
assert_se(proc_cmdline_key_streq("", ""));
assert_se(proc_cmdline_key_streq("a", "a"));
assert_se(!proc_cmdline_key_streq("", "a"));
assert_se(!proc_cmdline_key_streq("a", ""));
assert_se(proc_cmdline_key_streq("a", "a"));
assert_se(!proc_cmdline_key_streq("a", "b"));
assert_se(proc_cmdline_key_streq("x-y-z", "x-y-z"));
assert_se(proc_cmdline_key_streq("x-y-z", "x_y_z"));
assert_se(proc_cmdline_key_streq("x-y-z", "x-y_z"));
assert_se(proc_cmdline_key_streq("x-y-z", "x_y-z"));
assert_se(proc_cmdline_key_streq("x_y-z", "x-y_z"));
assert_se(!proc_cmdline_key_streq("x_y-z", "x-z_z"));
}
static void test_proc_cmdline_key_startswith(void) {
assert_se(proc_cmdline_key_startswith("", ""));
assert_se(proc_cmdline_key_startswith("x", ""));
assert_se(!proc_cmdline_key_startswith("", "x"));
assert_se(proc_cmdline_key_startswith("x", "x"));
assert_se(!proc_cmdline_key_startswith("x", "y"));
assert_se(!proc_cmdline_key_startswith("foo-bar", "quux"));
assert_se(proc_cmdline_key_startswith("foo-bar", "foo"));
assert_se(proc_cmdline_key_startswith("foo-bar", "foo-bar"));
assert_se(proc_cmdline_key_startswith("foo-bar", "foo_bar"));
assert_se(proc_cmdline_key_startswith("foo-bar", "foo_"));
assert_se(!proc_cmdline_key_startswith("foo-bar", "foo_xx"));
}
int main(void) {
log_parse_environment();
log_open();
test_parse_proc_cmdline();
test_proc_cmdline_parse();
test_proc_cmdline_key_streq();
test_proc_cmdline_key_startswith();
test_proc_cmdline_get_key();
test_proc_cmdline_get_bool();
test_runlevel_to_target();
return 0;

View File

@ -18,12 +18,14 @@
***/
#include <fcntl.h>
#include <linux/magic.h>
#include <unistd.h>
#include "alloc-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "macro.h"
#include "missing.h"
#include "stat-util.h"
static void test_files_same(void) {
@ -60,9 +62,33 @@ static void test_is_symlink(void) {
unlink(name_link);
}
static void test_path_is_os_tree(void) {
assert_se(path_is_os_tree("/") > 0);
assert_se(path_is_os_tree("/etc") == 0);
assert_se(path_is_os_tree("/idontexist") == -ENOENT);
}
static void test_path_check_fstype(void) {
assert_se(path_check_fstype("/run", TMPFS_MAGIC) > 0);
assert_se(path_check_fstype("/run", BTRFS_SUPER_MAGIC) == 0);
assert_se(path_check_fstype("/proc", PROC_SUPER_MAGIC) > 0);
assert_se(path_check_fstype("/proc", BTRFS_SUPER_MAGIC) == 0);
assert_se(path_check_fstype("/proc", BTRFS_SUPER_MAGIC) == 0);
assert_se(path_check_fstype("/i-dont-exist", BTRFS_SUPER_MAGIC) == -ENOENT);
}
static void test_path_is_temporary_fs(void) {
assert_se(path_is_temporary_fs("/run") > 0);
assert_se(path_is_temporary_fs("/proc") == 0);
assert_se(path_is_temporary_fs("/i-dont-exist") == -ENOENT);
}
int main(int argc, char *argv[]) {
test_files_same();
test_is_symlink();
test_path_is_os_tree();
test_path_check_fstype();
test_path_is_temporary_fs();
return 0;
}

View File

@ -192,14 +192,9 @@ static int load_link(link_config_ctx *ctx, const char *filename) {
}
static bool enable_name_policy(void) {
_cleanup_free_ char *value = NULL;
int r;
bool b;
r = get_proc_cmdline_key("net.ifnames=", &value);
if (r > 0 && streq(value, "0"))
return false;
return true;
return proc_cmdline_get_bool("net.ifnames", &b) <= 0 || b;
}
int link_config_load(link_config_ctx *ctx) {

View File

@ -1357,10 +1357,10 @@ static int listen_fds(int *rctrl, int *rnetlink) {
/*
* read the kernel command line, in case we need to get into debug mode
* udev.log-priority=<level> syslog priority
* udev.children-max=<number of workers> events are fully serialized if set to 1
* udev.exec-delay=<number of seconds> delay execution of every executed program
* udev.event-timeout=<number of seconds> seconds to wait before terminating an event
* udev.log_priority=<level> syslog priority
* udev.children_max=<number of workers> events are fully serialized if set to 1
* udev.exec_delay=<number of seconds> delay execution of every executed program
* udev.event_timeout=<number of seconds> seconds to wait before terminating an event
*/
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
int r = 0;
@ -1370,25 +1370,46 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (!value)
return 0;
if (streq(key, "udev.log-priority") && value) {
if (proc_cmdline_key_streq(key, "udev.log_priority")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = util_log_priority(value);
if (r >= 0)
log_set_max_level(r);
} else if (streq(key, "udev.event-timeout") && value) {
} else if (proc_cmdline_key_streq(key, "udev.event_timeout")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = safe_atou64(value, &arg_event_timeout_usec);
if (r >= 0) {
arg_event_timeout_usec *= USEC_PER_SEC;
arg_event_timeout_warn_usec = (arg_event_timeout_usec / 3) ? : 1;
}
} else if (streq(key, "udev.children-max") && value)
} else if (proc_cmdline_key_streq(key, "udev.children_max")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = safe_atou(value, &arg_children_max);
else if (streq(key, "udev.exec-delay") && value)
} else if (proc_cmdline_key_streq(key, "udev.exec_delay")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = safe_atoi(value, &arg_exec_delay);
else if (startswith(key, "udev."))
} else if (startswith(key, "udev."))
log_warning("Unknown udev kernel command line option \"%s\"", key);
if (r < 0)
log_warning_errno(r, "Failed to parse \"%s=%s\", ignoring: %m", key, value);
return 0;
}
@ -1649,7 +1670,7 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto exit;
r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, true);
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
if (r < 0)
log_warning_errno(r, "failed to parse kernel command line, ignoring: %m");

1
src/veritysetup/Makefile Symbolic link
View File

@ -0,0 +1 @@
../Makefile

View File

@ -0,0 +1,253 @@
/***
This file is part of systemd.
Copyright 2016 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include "alloc-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fstab-util.h"
#include "hexdecoct.h"
#include "id128-util.h"
#include "mkdir.h"
#include "parse-util.h"
#include "proc-cmdline.h"
#include "string-util.h"
#include "unit-name.h"
static char *arg_dest = NULL;
static bool arg_enabled = true;
static char *arg_root_hash = NULL;
static char *arg_data_what = NULL;
static char *arg_hash_what = NULL;
static int create_device(void) {
_cleanup_free_ char *u = NULL, *v = NULL, *d = NULL, *e = NULL;
_cleanup_fclose_ FILE *f = NULL;
const char *p, *to;
int r;
/* If all three pieces of information are missing, then verity is turned off */
if (!arg_root_hash && !arg_data_what && !arg_hash_what)
return 0;
/* if one of them is missing however, the data is simply incomplete and this is an error */
if (!arg_root_hash)
log_error("Verity information incomplete, root hash unspecified.");
if (!arg_data_what)
log_error("Verity information incomplete, root data device unspecified.");
if (!arg_hash_what)
log_error("Verity information incomplete, root hash device unspecified.");
if (!arg_root_hash || !arg_data_what || !arg_hash_what)
return -EINVAL;
log_debug("Using root verity data device %s,\n"
" hash device %s,\n"
" and root hash %s.", arg_data_what, arg_hash_what, arg_root_hash);
p = strjoina(arg_dest, "/systemd-veritysetup@root.service");
u = fstab_node_to_udev_node(arg_data_what);
if (!u)
return log_oom();
v = fstab_node_to_udev_node(arg_hash_what);
if (!v)
return log_oom();
r = unit_name_from_path(u, ".device", &d);
if (r < 0)
return log_error_errno(r, "Failed to to generate unit name: %m");
r = unit_name_from_path(v, ".device", &e);
if (r < 0)
return log_error_errno(r, "Failed to to generate unit name: %m");
f = fopen(p, "wxe");
if (!f)
return log_error_errno(errno, "Failed to create unit file %s: %m", p);
fprintf(f,
"# Automatically generated by systemd-veritysetup-generator\n\n"
"[Unit]\n"
"Description=Integrity Protection Setup for %%I\n"
"Documentation=man:systemd-veritysetup-generator(8) man:systemd-veritysetup@.service(8)\n"
"SourcePath=/proc/cmdline\n"
"DefaultDependencies=no\n"
"Conflicts=umount.target\n"
"BindsTo=%s %s\n"
"IgnoreOnIsolate=true\n"
"After=cryptsetup-pre.target %s %s\n"
"Before=cryptsetup.target umount.target\n"
"\n[Service]\n"
"Type=oneshot\n"
"RemainAfterExit=yes\n"
"ExecStart=" ROOTLIBEXECDIR "/systemd-veritysetup attach root '%s' '%s' '%s'\n"
"ExecStop=" ROOTLIBEXECDIR "/systemd-veritysetup detach root\n",
d, e,
d, e,
u, v, arg_root_hash);
r = fflush_and_check(f);
if (r < 0)
return log_error_errno(r, "Failed to write file %s: %m", p);
to = strjoina(arg_dest, "/cryptsetup.target.requires/systemd-veritysetup@root.service");
(void) mkdir_parents(to, 0755);
if (symlink("../systemd-veritysetup@root.service", to) < 0)
return log_error_errno(errno, "Failed to create symlink %s: %m", to);
return 0;
}
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
int r;
if (streq(key, "systemd.verity")) {
r = value ? parse_boolean(value) : 1;
if (r < 0)
log_warning("Failed to parse verity= kernel command line switch %s. Ignoring.", value);
else
arg_enabled = r;
} else if (streq(key, "roothash")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = free_and_strdup(&arg_root_hash, value);
if (r < 0)
return log_oom();
} else if (streq(key, "systemd.verity_root_data")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = free_and_strdup(&arg_data_what, value);
if (r < 0)
return log_oom();
} else if (streq(key, "systemd.verity_root_hash")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = free_and_strdup(&arg_hash_what, value);
if (r < 0)
return log_oom();
}
return 0;
}
static int determine_devices(void) {
_cleanup_free_ void *m = NULL;
sd_id128_t root_uuid, verity_uuid;
char ids[37];
size_t l;
int r;
/* Try to automatically derive the root data and hash device paths from the root hash */
if (!arg_root_hash)
return 0;
if (arg_data_what && arg_hash_what)
return 0;
r = unhexmem(arg_root_hash, strlen(arg_root_hash), &m, &l);
if (r < 0)
return log_error_errno(r, "Failed to parse root hash: %s", arg_root_hash);
if (l < sizeof(sd_id128_t)) {
log_debug("Root hash is shorter than 128 bits (32 characters), ignoring for discovering verity partition.");
return 0;
}
if (!arg_data_what) {
memcpy(&root_uuid, m, sizeof(root_uuid));
arg_data_what = strjoin("/dev/disk/by-partuuid/", id128_to_uuid_string(root_uuid, ids));
if (!arg_data_what)
return log_oom();
}
if (!arg_hash_what) {
memcpy(&verity_uuid, (uint8_t*) m + l - sizeof(verity_uuid), sizeof(verity_uuid));
arg_hash_what = strjoin("/dev/disk/by-partuuid/", id128_to_uuid_string(verity_uuid, ids));
if (!arg_hash_what)
return log_oom();
}
return 1;
}
int main(int argc, char *argv[]) {
int r;
if (argc > 1 && argc != 4) {
log_error("This program takes three or no arguments.");
return EXIT_FAILURE;
}
if (argc > 1)
arg_dest = argv[1];
log_set_target(LOG_TARGET_SAFE);
log_parse_environment();
log_open();
umask(0022);
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
if (r < 0) {
log_warning_errno(r, "Failed to parse kernel command line: %m");
goto finish;
}
/* For now we only support the root device on verity. Later on we might want to add support for /etc/veritytab
* or similar to define additional mappings */
if (!arg_enabled) {
r = 0;
goto finish;
}
r = determine_devices();
if (r < 0)
goto finish;
r = create_device();
if (r < 0)
goto finish;
r = 0;
finish:
free(arg_root_hash);
free(arg_data_what);
free(arg_hash_what);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}

View File

@ -0,0 +1,154 @@
/***
This file is part of systemd.
Copyright 2016 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <libcryptsetup.h>
#include <stdio.h>
#include <sys/stat.h>
#include "log.h"
#include "hexdecoct.h"
#include "string-util.h"
#include "alloc-util.h"
static char *arg_root_hash = NULL;
static char *arg_data_what = NULL;
static char *arg_hash_what = NULL;
static int help(void) {
printf("%s attach VOLUME DATADEVICE HASHDEVICE ROOTHASH\n"
"%s detach VOLUME\n\n"
"Attaches or detaches an integrity protected block device.\n",
program_invocation_short_name,
program_invocation_short_name);
return 0;
}
static void log_glue(int level, const char *msg, void *usrptr) {
log_debug("%s", msg);
}
int main(int argc, char *argv[]) {
struct crypt_device *cd = NULL;
int r;
if (argc <= 1) {
r = help();
goto finish;
}
if (argc < 3) {
log_error("This program requires at least two arguments.");
r = -EINVAL;
goto finish;
}
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
umask(0022);
if (streq(argv[1], "attach")) {
_cleanup_free_ void *m = NULL;
crypt_status_info status;
size_t l;
if (argc < 6) {
log_error("attach requires at least two arguments.");
r = -EINVAL;
goto finish;
}
r = unhexmem(argv[5], strlen(argv[5]), &m, &l);
if (r < 0) {
log_error("Failed to parse root hash.");
goto finish;
}
r = crypt_init(&cd, argv[4]);
if (r < 0) {
log_error_errno(r, "Failed to open verity device %s: %m", argv[4]);
goto finish;
}
crypt_set_log_callback(cd, log_glue, NULL);
status = crypt_status(cd, argv[2]);
if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
log_info("Volume %s already active.", argv[2]);
r = 0;
goto finish;
}
r = crypt_load(cd, CRYPT_VERITY, NULL);
if (r < 0) {
log_error_errno(r, "Failed to load verity superblock: %m");
goto finish;
}
r = crypt_set_data_device(cd, argv[3]);
if (r < 0) {
log_error_errno(r, "Failed to configure data device: %m");
goto finish;
}
r = crypt_activate_by_volume_key(cd, argv[2], m, l, CRYPT_ACTIVATE_READONLY);
if (r < 0) {
log_error_errno(r, "Failed to set up verity device: %m");
goto finish;
}
} else if (streq(argv[1], "detach")) {
r = crypt_init_by_name(&cd, argv[2]);
if (r == -ENODEV) {
log_info("Volume %s already inactive.", argv[2]);
goto finish;
} else if (r < 0) {
log_error_errno(r, "crypt_init_by_name() failed: %m");
goto finish;
}
crypt_set_log_callback(cd, log_glue, NULL);
r = crypt_deactivate(cd, argv[2]);
if (r < 0) {
log_error_errno(r, "Failed to deactivate: %m");
goto finish;
}
} else {
log_error("Unknown verb %s.", argv[1]);
r = -EINVAL;
goto finish;
}
r = 0;
finish:
if (cd)
crypt_free(cd);
free(arg_root_hash);
free(arg_data_what);
free(arg_hash_what);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}

1
src/volatile-root/Makefile Symbolic link
View File

@ -0,0 +1 @@
../Makefile

View File

@ -0,0 +1,157 @@
/***
This file is part of systemd.
Copyright 2016 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <sys/mount.h>
#include "alloc-util.h"
#include "fs-util.h"
#include "mkdir.h"
#include "mount-util.h"
#include "stat-util.h"
#include "volatile-util.h"
#include "string-util.h"
#include "path-util.h"
static int make_volatile(const char *path) {
_cleanup_free_ char *old_usr = NULL;
int r;
r = path_is_mount_point(path, NULL, AT_SYMLINK_FOLLOW);
if (r < 0)
return log_error_errno(r, "Couldn't determine whether %s is a mount point: %m", path);
if (r == 0) {
log_error("%s is not a mount point.", path);
return -EINVAL;
}
r = path_is_temporary_fs(path);
if (r < 0)
return log_error_errno(r, "Couldn't determine whether %s is a temporary file system: %m", path);
if (r > 0) {
log_info("%s already is a temporary file system.", path);
return 0;
}
r = chase_symlinks("/usr", path, CHASE_PREFIX_ROOT, &old_usr);
if (r < 0)
return log_error_errno(r, "/usr not available in old root: %m");
r = mkdir_p("/run/systemd/volatile-sysroot", 0700);
if (r < 0)
return log_error_errno(r, "Couldn't generate volatile sysroot directory: %m");
r = mount_verbose(LOG_ERR, "tmpfs", "/run/systemd/volatile-sysroot", "tmpfs", MS_STRICTATIME, "mode=755");
if (r < 0)
goto finish_rmdir;
if (mkdir("/run/systemd/volatile-sysroot/usr", 0755) < 0) {
r = -errno;
goto finish_umount;
}
r = mount_verbose(LOG_ERR, old_usr, "/run/systemd/volatile-sysroot/usr", NULL, MS_BIND|MS_REC, NULL);
if (r < 0)
goto finish_umount;
r = bind_remount_recursive("/run/systemd/volatile-sysroot/usr", true, NULL);
if (r < 0)
goto finish_umount;
r = umount_recursive(path, 0);
if (r < 0) {
log_error_errno(r, "Failed to unmount %s: %m", path);
goto finish_umount;
}
if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
log_warning_errno(errno, "Failed to remount %s MS_SLAVE|MS_REC: %m", path);
r = mount_verbose(LOG_ERR, "/run/systemd/volatile-sysroot", path, NULL, MS_MOVE, NULL);
finish_umount:
(void) umount_recursive("/run/systemd/volatile-sysroot", 0);
finish_rmdir:
(void) rmdir("/run/systemd/volatile-sysroot");
return r;
}
int main(int argc, char *argv[]) {
VolatileMode m = _VOLATILE_MODE_INVALID;
const char *path;
int r;
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
if (argc > 3) {
log_error("Too many arguments. Expected directory and mode.");
r = -EINVAL;
goto finish;
}
r = query_volatile_mode(&m);
if (r < 0) {
log_error_errno(r, "Failed to determine volatile mode from kernel command line.");
goto finish;
}
if (r == 0 && argc >= 2) {
/* The kernel command line always wins. However if nothing was set there, the argument passed here wins instead. */
m = volatile_mode_from_string(argv[1]);
if (m < 0) {
log_error("Couldn't parse volatile mode: %s", argv[1]);
r = -EINVAL;
goto finish;
}
}
if (argc < 3)
path = "/sysroot";
else {
path = argv[2];
if (isempty(path)) {
log_error("Directory name cannot be empty.");
r = -EINVAL;
goto finish;
}
if (!path_is_absolute(path)) {
log_error("Directory must be specified as absolute path.");
r = -EINVAL;
goto finish;
}
if (path_equal(path, "/")) {
log_error("Directory cannot be the root directory.");
r = -EINVAL;
goto finish;
}
}
if (m != VOLATILE_YES) {
r = 0;
goto finish;
}
r = make_volatile(path);
finish:
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}

1
units/.gitignore vendored
View File

@ -75,5 +75,6 @@
/systemd-update-utmp.service
/systemd-user-sessions.service
/systemd-vconsole-setup.service
/systemd-volatile-root.service
/tmp.mount
/user@.service

View File

@ -15,6 +15,5 @@ AllowIsolate=yes
[Service]
Type=oneshot
# we have to use "--force" here, otherwise systemd would umount /run
ExecStart=@rootbindir@/systemctl --no-block --force switch-root /sysroot
ExecStart=@rootbindir@/systemctl --no-block switch-root /sysroot
KillMode=none

View File

@ -0,0 +1,21 @@
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Enforce Volatile Root File Systems
Documentation=man:systemd-volatile-root.service(8)
DefaultDependencies=no
Conflicts=shutdown.target
After=sysroot.mount
Before=initrd-root-fs.target shutdown.target
Conflicts=shutdown.target
AssertPathExists=/etc/initrd-release
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=@rootlibexecdir@/systemd-volatile-root yes /sysroot