dry-run: false
sanitizer: ${{ matrix.sanitizer }}
- name: Upload Crash
- uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8
+ uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb
if: failure() && steps.build.outcome == 'success'
with:
name: ${{ matrix.sanitizer }}-${{ matrix.architecture }}-artifacts
jobs:
analyze:
name: Analyze
+ if: github.repository != 'systemd/systemd-security'
runs-on: ubuntu-22.04
concurrency:
group: ${{ github.workflow }}-${{ matrix.language }}-${{ github.ref }}
jobs:
freezer:
runs-on: ubuntu-22.04
+ if: github.repository == 'systemd/systemd'
permissions:
pull-requests: write
fetch-depth: 0
- name: Differential ShellCheck
- uses: redhat-plumbers-in-action/differential-shellcheck@1b1b75e42f0694c1012228513b21617a748c866e
+ uses: redhat-plumbers-in-action/differential-shellcheck@f3cd08fcf12680861615270b29494d2b87c3e1cc
with:
token: ${{ secrets.GITHUB_TOKEN }}
template-path: .github/ISSUE_TEMPLATE/${{ matrix.template }}
- name: Set labels based on component field
- uses: redhat-plumbers-in-action/advanced-issue-labeler@fe9c43b7d77bd8bd7fbe86c2c217e74ebeea71f2
+ uses: redhat-plumbers-in-action/advanced-issue-labeler@88209aef583c66312529c515d41ea6a710a4baba
with:
issue-form: ${{ steps.issue-parser.outputs.jsonString }}
template: ${{ matrix.template }}
-meson==0.63.3 \
- --hash=sha256:519c0932e1a8b208741f0fdce90aa5c0b528dd297cf337009bf63539846ac056 \
- --hash=sha256:d677b809c4895dcbaac9bf6c43703fcb3609a4b24c6057c78f828590049cf43a
-ninja==1.10.2.4 \
- --hash=sha256:24e3bc4713667a9a1d15484ad2bb77bbaedb1e6d45254cb03f7964b8b497231a \
- --hash=sha256:251fb21cd6691accd0d95e28721ad8a50a6ec0ace97f9a8de3976f39301686f6 \
- --hash=sha256:327c319176c5a4af21908b727b776e9f5caf275680403da632821ba071fd6296 \
- --hash=sha256:3300f3f37d62dcc7bdd19284dff9eaed7d629f4ed2725019a6ce3291c655fb83 \
- --hash=sha256:34c8e44f6e2e35ff9444994bfc7bf451c8d4bf15e31ad1e3ef7b06f78647b35b \
- --hash=sha256:3fa6e69838613815c80abcaca34681c5b7cf15bf921543e518f5c918d7098bb7 \
- --hash=sha256:5b973b1ce7075e9091db290adbbf93ba9066a94f97c369d0ff631251c633e81b \
- --hash=sha256:685daebd1bc21480256351000a01dfb520636832fa65efc9f121474ff640e3df \
- --hash=sha256:b0350784b37c5080223ec1bedc507153cc714b502c17dd5a64552e930b0dca25 \
- --hash=sha256:b12cfed6382e510a597b3d08d7eec96664f7c8b8ee436eef645736c453d1c135 \
- --hash=sha256:b264085e409533aecb57040c5e90fbfb64db91a61575c7e637411780446412fa \
- --hash=sha256:b86a4e4ba2ed999d8b10f2b3f2ed56d7457ff647268f4098dd0b63dd145ede32 \
- --hash=sha256:da7a6d9b2ed2018165fbf90068e2c64da08f2568c700fdb8abea07a245dc4664 \
- --hash=sha256:ea245943a9849e5b1ebd74c1a4c1edd2c9801b62c0386165c7ac47623e353627
+meson==0.64.1 \
+ --hash=sha256:3a8e030c2334f782085f81627062cc6d4a6771edf31e055ffe374f9e6b089ab9 \
+ --hash=sha256:8cf376d3b8640d8957d335eb4b61f2e30412c4aea3fa2affe72ae4c98145d51d
+ninja==1.11.1 \
+ --hash=sha256:1c474326e11fba3f8c2582715d79216292e327d3335367c0e87e9647a002cc4a \
+ --hash=sha256:3329b4b7c1694730772522a3ba0ba40fd15c012476ed3e1c9f0fd9e76190394e \
+ --hash=sha256:34753459493543782d87267e4cad63dd4639b07f8394ffe6d4417e9eda05c8a8 \
+ --hash=sha256:3b28b595ed580752240ade7821b6cb7a5a4c6a604c865dc474bd38f06e2eb7f5 \
+ --hash=sha256:4e547bc759c570773d83d110c41fd5ca9a94c0a9a8388f5a3ea37bdf97d002b0 \
+ --hash=sha256:60179bb4f22c88279c53a5402bb5fe81c97c627a28d93c737d1fa067d892115d \
+ --hash=sha256:642cb64d859276998f14972724850e0c5b7febbc1bce3d2065b7e0cb7d3a0b79 \
+ --hash=sha256:6f6465a7efe6473a2a34edab83633594de19d59406a727316e1367ebcc528908 \
+ --hash=sha256:779f228e407c54a8b6e4cbf8f835489998dd250f67bf1b9bd7b8a8ab6bdcdc7b \
+ --hash=sha256:817e2aee2a4d28a708a67bcfba1817ae502c32c6d8ef80e50d63b0f23adf3a08 \
+ --hash=sha256:a7a564fe755ddfbdbccb07b0b758e3f8460e5f8ba1adaab40a5eaa2f8c01ce68 \
+ --hash=sha256:ba50a32424912e5f3ee40d791b506a160dc0eeda7de5ad8faebe7aa8006244dc \
+ --hash=sha256:c833a47d39b2d1eee3f9ca886fa1581efd5be6068b82734ac229961ee8748f90 \
+ --hash=sha256:df11b8afea0501883e33faeb1c43d2ef67f466d5f4bd85f9c376e9a93a43a277 \
+ --hash=sha256:edec1053e141253076b2df7ec03a246ff581e9270aa1ca9759397b21e2760e57 \
+ --hash=sha256:f48c3c6eea204062f6bbf089dfc63e1ad41a08640e1da46ef2b30fa426f7ce23
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: Upload artifact
- uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # tag=v3.0.0
+ uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # tag=v3.0.0
with:
name: SARIF file
path: results.sarif
virtualized ones, and is activated in the case that the system token
is missing from either sd-boot and sd-stub booted systems.
+ Changes in systemctl:
+
+ * systemctl reboot has dropped support for accepting a positional argument
+ as the argument to reboot(2) syscall. Please use --reboot-argument instead.
+
CHANGES WITH 252 🎃:
Announcements of Future Feature Removals:
Features:
+* systemd-dissect: maybe add "--attach" and "--detach" verbs which
+ synchronously attach a DDI to a loopback device but not actually mount them.
+
+* gpt-auto-generator: generate device names via /dev/disk/by-diskseq/ to pin
+ the medium
+
+* pam_systemd_home: add module parameter to control whether to only accept
+ only password or only pcks11/fido2 auth, and then use this to hook nicely
+ into two of the three PAM stacks gdm provides.
+ See discussion at https://github.com/authselect/authselect/pull/311
+
+* sd-boot: make boot loader spec type #1 accept http urls in "linux"
+ lines. THen, do the uefi http dance to download kernels and boot them. This
+ is then useful for network boot, by embdedding a cpio with type #1 snippets
+ in sd-boot, which reference remote kernels.
+
* fix systemd-gpt-auto-generator in case a UKI is spawned from XBOOTLDR without
sd-boot. In that case LoaderDevicePartUUID will point to the XBOOTLDR, and we
should then derive the root disk from that, and then the ESP/XBOOTLDR from
to make a parser for this reasonably robust. use same path structures as in
the ESP. Similar add one for signature key drop-ins.
+* sd-boot: also allow passing in the cpio as in the previous item via SMBIOS
+
* add a new EFI tool "sd-fetch" or so. It looks in a PE section ".url" for an
URL, then downloads the file from it using UEFI HTTP APIs, and executes it.
Usecase: provide a minimal ESP with sd-boot and a couple of these sd-fetch
encrypted/verity root fs in UKI.
* sd-stub: add ".bootcfg" section for kernel bootconfig data (as per
+ https://docs.kernel.org/admin-guide/bootconfig.html)
* tpm2: add (optional) support for generating a local signing key from PCR 15
state. use private key part to sign PCR 7+14 policies. stash signatures for
xattr set, check bpf table to find dirs with hashes for other prior DDIs and
try to use inode from there.
-* dissect too: add --with switch that will invoke a command with the image
- mounted, and as current working directory. Terminate once done.
-
* extend the verity signature partition to permit multiple signatures for the
same root hash, so that people can sign a single image with multiple keys.
ID from it securely. This would then allow us to bind secrets a specific
system securely.
-* nspawn: maybe allow TPM passthrough, backed by swtpm, and measure --image=
- hash into its PCR 11, so that nspawn instances can be TPM enabled, and
- partake in measurements/remote attestation and such. swtpm would run outside
- of control of container, and ideally would itself bind its encryption keys to
- host TPM.
-
* tree-wide: convert as much as possible over to use sd_event_set_signal_exit(), instead
of manually hooking into SIGINT/SIGTERM
* Add support for extra verity configuration options to systemd-repart (FEC,
hash type, etc)
-* chase_symlinks(): take inspiraton from path_extract_filename() and return
+* chase_symlinks(): take inspiration from path_extract_filename() and return
O_DIRECTORY if input path contains trailing slash.
* chase_symlinks(): refuse resolution if trailing slash is specified on input,
multiple versions are around of the same resource, show which ones. (in other
words: show partition labels).
-* systemd-nspawn: make boot assessment do something sensible in a
- container. i.e send an sd_notify() from payload to container manager once
- boot-up is completed successfully, and use that in nspawn for dealing with
- boot counting, implemented in the partition table labels and directory names.
-
* maybe add a generator that reads /proc/cmdline, looks for
systemd.pull-raw-portable=, systemd-pull-raw-sysext= and similar switches
that take an URL as parameter. It then generates service units for
* add linker script that implicitly adds symbol for build ID and new coredump
json package metadata, and use that when logging
-* systemd-dissect: show GPT disk UUID in output
-
* Enable RestrictFileSystems= for all our long-running services (similar:
RestrictNetworkInterfaces=)
* cryptsetup/homed: implement TOTP authentication backed by TPM2 and its
internal clock.
-* nspawn: optionally set up nftables/iptables routes that forward UDP/TCP
- traffic on port 53 to resolved stub 127.0.0.54
-
* man: rework os-release(5), and clearly separate our extension-release.d/ and
initrd-release parts, i.e. list explicitly which fields are about what.
records would be stripped of all meta info, except the basic UID/name
info. Then use this in portabled environments that do not use PrivateUsers=1.
+* portabled: when extracting unit files and copying to system.attached, if a
+ .p7s is available in the image, use it to protect the system.attached copy
+ with fs-verity, so that it cannot be tampered with
+
* logind introduce two types of sessions: "heavy" and "light". The former would
be our current sessions. But the latter would be a new type of session that
is mostly the same but does not pull in user@.service or wait for it. Then,
for /home/, and similar. Similar add --image-dissect-policy= to tools that
take --image= that take the same short string.
-* nspawn: maybe optionally insert .nspawn file as GPT partition into images, so
- that such container images are entirely stand-alone and can be updated as
- one.
-
* we probably should extend the root verity hash of the root fs into some PCR
on boot. (i.e. maybe add a veritytab option tpm2-measure=12 or so to measure
it into PCR 12); Similar: we probably should extend the LUKS volume key of
* systemd-path: add ESP and XBOOTLDR path. Add "private" runtime/state/cache dir enum,
mapping to $RUNTIME_DIRECTORY, $STATE_DIRECTORY and such
-* All tools that support --root= should also learn --image= so that they can
- operate on disk images directly. Specifically: systemctl, coredumpctl.
- (Already done: bootctl, systemd-nspawn, systemd-firstboot,
- systemd-repart, systemd-tmpfiles, systemd-sysusers, journalctl)
-
* seccomp: by default mask x32 ABI system wide on x86-64. it's on its way out
* seccomp: don't install filters for ABIs that are masked anyway for the
PID 1...
- optionally automatically add FORWARD rules to iptables whenever nspawn is
running, remove them when shut down.
-
-* nspawn: add support for sysext extensions, too. i.e. a new --extension=
- switch that takes one or more arguments, and applies the extensions already
- during startup.
-
-* when main nspawn supervisor process gets suspended due to SIGSTOP/SIGTTOU or
- so, freeze the payload too.
+ - add support for sysext extensions, too. i.e. a new --extension= switch that
+ takes one or more arguments, and applies the extensions already during
+ startup.
+ - when main nspawn supervisor process gets suspended due to SIGSTOP/SIGTTOU
+ or so, freeze the payload too.
+ - support time namespaces
+ - on cgroupsv1 issue cgroup empty handler process based on host events, so
+ that we make cgroup agent logic safe
+ - add API to invoke binary in container, then use that as fallback in
+ "machinectl shell"
+ - make nspawn suitable for shell pipelines: instead of triggering a hangup
+ when input is finished, send ^D, which synthesizes an EOF. Then wait for
+ hangup or ^D before passing on the EOF.
+ - greater control over selinux label?
+ - support that /proc, /sys/, /dev are pre-mounted
+ - maybe allow TPM passthrough, backed by swtpm, and measure --image= hash
+ into its PCR 11, so that nspawn instances can be TPM enabled, and partake
+ in measurements/remote attestation and such. swtpm would run outside of
+ control of container, and ideally would itself bind its encryption keys to
+ host TPM.
+ - make boot assessment do something sensible in a container. i.e send an
+ sd_notify() from payload to container manager once boot-up is completed
+ successfully, and use that in nspawn for dealing with boot counting,
+ implemented in the partition table labels and directory names.
+ - optionally set up nftables/iptables routes that forward UDP/TCP traffic on
+ port 53 to resolved stub 127.0.0.54
+ - maybe optionally insert .nspawn file as GPT partition into images, so that
+ such container images are entirely stand-alone and can be updated as one.
+ - The subreaper logic we currently have seems overly complex. We should
+ investigate whether creating the inner child with CLONE_PARENT isn't better.
+ - Reduce the number of sockets that are currently in use and just rely on one
+ or two sockets.
+ - Support running nspawn as an unprivileged user.
* machined: add API to acquire UID range. add API to mount/dissect loopback
file. Both protected by PK. Then make nspawn use these APIs to run
so that the client side can remain entirely unprivileged, with SUID or
anything like that.
-* nspawn: support time namespaces
-
-* nspawn: on cgroupsv1 issue cgroup empty handler process based on host events,
- so that we make cgroup agent logic safe
-
-* nspawn/machined: add API to invoke binary in container, then use that as
- fallback in "machinectl shell"
-
-* nspawn: make nspawn suitable for shell pipelines: instead of triggering a
- hangup when input is finished, send ^D, which synthesizes an EOF. Then wait
- for hangup or ^D before passing on the EOF.
-
-* nspawn: greater control over selinux label?
-
-* nspawn: support that /proc, /sys/, /dev are pre-mounted
-
* machined:
- add an API so that libvirt-lxc can inform us about network interfaces being
removed or added to an existing machine
Following these guidelines makes it easier for us to process your issue, and ensures we won't close your issue right-away for being misfiled.
### Older downstream versions
+
For older versions that are still supported by your distribution please use respective downstream tracker:
+
* **Fedora** - [bugzilla](https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora&component=systemd)
-* **RHEL/CentOS** - [bugzilla](https://bugzilla.redhat.com/) or [systemd-rhel github](https://github.com/systemd-rhel/)
+* **RHEL/CentOS stream** - [bugzilla](https://bugzilla.redhat.com/) or [systemd-rhel GitHub](https://github.com/redhat-plumbers)
* **Debian** - [bugs.debian.org](https://bugs.debian.org/cgi-bin/pkgreport.cgi?pkg=systemd)
## Security vulnerability reports
* See [filtered list of pull requests](https://github.com/systemd/systemd/pulls?q=is%3Aopen+is%3Apr+-label%3A%22reviewed%2Fneeds-rework+%F0%9F%94%A8%22+-label%3Aneeds-rebase+-label%3Agood-to-merge%2Fwith-minor-suggestions+-label%3A%22good-to-merge%2Fwaiting-for-ci+%F0%9F%91%8D%22+-label%3Apostponed+-label%3A%22needs-reporter-feedback+%E2%9D%93%22+-label%3A%22dont-merge+%F0%9F%92%A3%22+-label%3A%22ci-fails%2Fneeds-rework+%F0%9F%94%A5%22+sort%3Aupdated-desc) for requests that are ready for review.
* After performing a review, set
- * `reviewed/needs-rework` if the pull request needs significant changes
- * `ci-fails/needs-rework` if the automatic tests fail and the failure is relevant to the pull request
- * `ci-failure-appears-unrelated` if the test failures seem irrelevant
- * `needs-rebase` if the pull request needs a rebase because of conflicts
- * `good-to-merge/waiting-for-ci` if the pull request should be merged without further review
- * `good-to-merge/with-minor-suggestions` if the pull request should be merged after an update without going through another round of reviews
+ * `reviewed/needs-rework` if the pull request needs significant changes
+ * `ci-fails/needs-rework` if the automatic tests fail and the failure is relevant to the pull request
+ * `ci-failure-appears-unrelated` if the test failures seem irrelevant
+ * `needs-rebase` if the pull request needs a rebase because of conflicts
+ * `good-to-merge/waiting-for-ci` if the pull request should be merged without further review
+ * `good-to-merge/with-minor-suggestions` if the pull request should be merged after an update without going through another round of reviews
Unfortunately only members of the `systemd` organization on github can change labels.
If your pull request is mislabeled, make a comment in the pull request and somebody will fix it.
EVDEV_ABS_35=::24
EVDEV_ABS_36=::34
+# Dell Inspiron N4010
+evdev:name:SynPS/2 Synaptics TouchPad:dmi:bvn*:bvr*:bd*:svnDellInc.:pnInspironN4010:*
+ EVDEV_ABS_00=1188:5797:49
+ EVDEV_ABS_01=893:4894:80
+ EVDEV_ABS_35=1188:5797:49
+ EVDEV_ABS_36=893:4894:80
+
# Dell Inspiron N5040
evdev:name:AlpsPS/2 ALPS DualPoint TouchPad:dmi:bvn*:bvr*:bd*:svnDellInc.:pnInspironN5040:*
EVDEV_ABS_00=25:2000:22
KEYBOARD_KEY_ae=!volumedown
KEYBOARD_KEY_b0=!volumeup
+evdev:atkbd:dmi:bvn*:bvr*:svnNotebook:pnNS50_70MU:*
+evdev:atkbd:dmi:bvn*:bvr*:svnNotebook:pnNV4XMB,ME,MZ:*
+evdev:atkbd:dmi:bvn*:bvr*:svnNotebook:pnNS5x_NS7xPU:*
+evdev:atkbd:dmi:bvn*:bvr*:svnNotebook:pnNV4xPZ:*
+ KEYBOARD_KEY_f7=f21 # Touchpad Toggle
+ KEYBOARD_KEY_f8=f21 # Touchpad Toggle
+
###########################################################
# Compal
###########################################################
usb:v0FD9p0080*
ID_AV_PRODUCTION_CONTROLLER=1
+# Stream Deck Pedal
+usb:v0FD9p0086*
+ ID_AV_PRODUCTION_CONTROLLER=1
+
#############################
# Hercules (Guillemot Corp)
#############################
and use this to determine which key to send, allowing a single listening socket to serve keys for
multiple volumes. If the PKCS#11 logic is used (see above), the socket source name is picked in similar
fashion, except that the literal string <literal>/cryptsetup-pkcs11/</literal> is used. And similarly for
- FIDO2 (<literal>/cryptsetup-fido2/</literal>) and TPM2 (<literal>/cryptsetup-tpm2/</literal>). A diffent
+ FIDO2 (<literal>/cryptsetup-fido2/</literal>) and TPM2 (<literal>/cryptsetup-tpm2/</literal>). A different
path component is used so that services providing key material know that the secret key was not requested
directly, but instead an encrypted key that will be decrypted via the PKCS#11/FIDO2/TPM2 logic to acquire
the final secret key.</para>
is missing), or "Linux <replaceable>KERNEL-VERSION</replaceable>", if unset.</para>
<para>If <varname>$KERNEL_INSTALL_LAYOUT</varname> is not "bls", this plugin does nothing.</para></listitem>
+
+ <listitem><para><filename>90-uki-copy.install</filename> copies a file
+ <filename>uki.efi</filename> from <varname>$KERNEL_INSTALL_STAGING_AREA</varname> or if it does
+ not exist the <replaceable>KERNEL-IMAGE</replaceable> argument, iff it has a
+ <literal>.efi</literal> extension, to
+ <filename>$BOOT/EFI/Linux/<replaceable>ENTRY-TOKEN</replaceable>-<replaceable>KERNEL-VERSION</replaceable>.efi</filename>.</para>
+
+ <para>If <varname>$KERNEL_INSTALL_LAYOUT</varname> is not "uki", this plugin does nothing.</para></listitem>
</itemizedlist>
</listitem>
</varlistentry>
<listitem><para><filename>90-loaderentry.install</filename> removes the file
<filename>$BOOT/loader/entries/<replaceable>ENTRY-TOKEN</replaceable>-<replaceable>KERNEL-VERSION</replaceable>.conf</filename>.</para></listitem>
+
+ <listitem><para><filename>90-uki-copy.install</filename> removes the file
+ <filename>$BOOT/EFI/Linux/<replaceable>ENTRY-TOKEN</replaceable>-<replaceable>KERNEL-VERSION</replaceable>.efi</filename>.</para></listitem>
</itemizedlist>
</listitem>
</varlistentry>
(EFI System Partition) are mounted, and also conceptually referred to as <varname>$BOOT</varname>. Can
be overridden by setting <varname>$BOOT_ROOT</varname> (see below).</para>
- <para><varname>$KERNEL_INSTALL_LAYOUT=bls|other|...</varname> is set for the plugins to specify the
+ <para><varname>$KERNEL_INSTALL_LAYOUT=bls|uki|other|...</varname> is set for the plugins to specify the
installation layout. Defaults to <option>bls</option> if
<filename>$BOOT/<replaceable>ENTRY-TOKEN</replaceable></filename> exists, or <option>other</option>
otherwise. Additional layout names may be defined by convention. If a plugin uses a special layout,
<para>Implemented by <filename>90-loaderentry.install</filename>.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>uki</term>
+ <listitem>
+ <para>Standard <ulink
+ url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader
+ Specification</ulink> Type #2 layout, compatible with
+ <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>:
+ unified kernel images under <filename>$BOOT/EFI/Linux</filename> as
+ <filename>$BOOT/EFI/Linux/<replaceable>ENTRY-TOKEN</replaceable>-<replaceable>KERNEL-VERSION</replaceable>[+<replaceable>TRIES</replaceable>].efi</filename>.</para>
+ <para>Implemented by <filename>90-uki-copy.install</filename>.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term>other</term>
<listitem>
<filename>/etc/kernel/tries</filename>
</term>
<listitem>
- <para>Read by <filename>90-loaderentry.install</filename>. If this file exists a numeric value is read from
- it and the naming of the generated entry file is slightly altered to include it as
- <filename>$BOOT/loader/entries/<replaceable>MACHINE-ID</replaceable>-<replaceable>KERNEL-VERSION</replaceable>+<replaceable>TRIES</replaceable>.conf</filename>. This
+ <para>Read by <filename>90-loaderentry.install</filename> and
+ <filename>90-uki-copy.install</filename>. If this file exists a numeric value is read from it
+ and the naming of the generated entry file or UKI is slightly altered to include it as
+ <filename>$BOOT/loader/entries/<replaceable>ENTRY-TOKEN</replaceable>-<replaceable>KERNEL-VERSION</replaceable>+<replaceable>TRIES</replaceable>.conf</filename>
+ or
+ <filename>$BOOT/EFI/Linux/<replaceable>ENTRY-TOKEN</replaceable>-<replaceable>KERNEL-VERSION</replaceable>+<replaceable>TRIES</replaceable>.conf</filename>, respectively. This
is useful for boot loaders such as
- <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> which
- implement boot attempt counting with a counter embedded in the entry file name.
+ <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ which implement boot attempt counting with a counter embedded in the entry file name.
<varname>$KERNEL_INSTALL_CONF_ROOT</varname> may be used to override the path.</para>
</listitem>
</varlistentry>
DisableUnitFilesWithFlags(in as files,
in t flags,
out a(sss) changes);
+ DisableUnitFilesWithFlagsAndInstallInfo(in as files,
+ in t flags,
+ out b carries_install_info,
+ out a(sss) changes);
ReenableUnitFiles(in as files,
in b runtime,
in b force,
<variablelist class="dbus-method" generated="True" extra-ref="DisableUnitFilesWithFlags()"/>
+ <variablelist class="dbus-method" generated="True" extra-ref="DisableUnitFilesWithFlagsAndInstallInfo()"/>
+
<variablelist class="dbus-method" generated="True" extra-ref="ReenableUnitFiles()"/>
<variablelist class="dbus-method" generated="True" extra-ref="LinkUnitFiles()"/>
enabled for runtime only (true, <filename>/run/</filename>), or persistently (false,
<filename>/etc/</filename>). The second one controls whether symlinks pointing to other units shall be
replaced if necessary. This method returns one boolean and an array of the changes made. The boolean
- signals whether the unit files contained any enablement information (i.e. an [Install]) section. The
+ signals whether the unit files contained any enablement information (i.e. an [Install] section). The
changes array consists of structures with three strings: the type of the change (one of
<literal>symlink</literal> or <literal>unlink</literal>), the file name of the symlink and the
destination of the symlink. Note that most of the following calls return a changes list in the same
replaced if necessary. <varname>SD_SYSTEMD_UNIT_PORTABLE</varname> will add or remove the symlinks in
<filename>/etc/systemd/system.attached</filename> and <filename>/run/systemd/system.attached</filename>.</para>
+ <para><function>DisableUnitFilesWithFlagsAndInstallInfo()</function> is similar to
+ <function>DisableUnitFilesWithFlags()</function> and takes the same arguments, but returns
+ a boolean to indicate whether the unit files contain any enablement information, like
+ <function>EnableUnitFiles()</function>. The changes made are still returned in an array.</para>
+
<para>Similarly, <function>ReenableUnitFiles()</function> applies the changes to one or more units that
would result from disabling and enabling the unit quickly one after the other in an atomic
fashion. This is useful to apply updated [Install] information contained in unit files.</para>
readonly t RuntimeMaxUSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly t RuntimeRandomizedExtraUSec = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly s OOMPolicy = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s Slice = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
<!--property RuntimeRandomizedExtraUSec is not documented!-->
+ <!--property OOMPolicy is not documented!-->
+
<!--property Slice is not documented!-->
<!--property ControlGroupId is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="RuntimeRandomizedExtraUSec"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="OOMPolicy"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="Slice"/>
<variablelist class="dbus-property" generated="True" extra-ref="ControlGroup"/>
<para>The copy operation is executed before the file system is registered in the partition table,
thus ensuring that a file system populated this way only ever exists fully initialized.</para>
+ <para>Note that <varname>CopyFiles=</varname> will skip copying files that aren't supported by the
+ target filesystem (e.g symlinks, fifos, sockets and devices on vfat). When an unsupported file type
+ is encountered, repart will skip copying this file and write a log message about it.</para>
+
<para>This option cannot be combined with <varname>CopyBlocks=</varname>.</para>
<para>When <command>systemd-repart</command> is invoked with the <option>--image=</option> or
<listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
(the default), select domains are resolved on the local system, among them
- <literal>localhost</literal>, <literal>_gateway</literal> and <literal>_outbound</literal>, or
- entries from <filename>/etc/hosts</filename>. If false these domains are not resolved locally, and
- either fail (in case of <literal>localhost</literal>, <literal>_gateway</literal> or
- <literal>_outbound</literal> and suchlike) or go to the network via regular DNS/mDNS/LLMNR lookups
- (in case of <filename>/etc/hosts</filename> entries).</para></listitem>
+ <literal>localhost</literal>, <literal>_gateway</literal>, <literal>_outbound</literal>,
+ <literal>_localdnsstub</literal> and <literal>_localdnsproxy</literal> or entries from
+ <filename>/etc/hosts</filename>. If false these domains are not resolved locally, and either fail (in
+ case of <literal>localhost</literal>, <literal>_gateway</literal> or <literal>_outbound</literal> and
+ suchlike) or go to the network via regular DNS/mDNS/LLMNR lookups (in case of
+ <filename>/etc/hosts</filename> entries).</para></listitem>
</varlistentry>
<varlistentry>
'sd_bus_emit_object_removed',
'sd_bus_emit_properties_changed',
'sd_bus_emit_properties_changed_strv',
+ 'sd_bus_emit_signal_to',
+ 'sd_bus_emit_signal_tov',
'sd_bus_emit_signalv'],
''],
['sd_bus_enqueue_for_read', '3', [], ''],
'sd_bus_message_new_method_errnof',
'sd_bus_message_new_method_errorf'],
''],
- ['sd_bus_message_new_signal', '3', [], ''],
+ ['sd_bus_message_new_signal', '3', ['sd_bus_message_new_signal_to'], ''],
['sd_bus_message_open_container',
'3',
['sd_bus_message_close_container',
account.
</para>
+ <para>When using this operation on units without install information, a warning about it is shown.
+ <option>--no-warn</option> can be used to suppress the warning.</para>
+
<para>Enabling units should not be confused with starting (activating) units, as done by the
<command>start</command> command. Enabling and starting units is orthogonal: units may be enabled without
being started and started without being enabled. Enabling simply hooks the unit into various suggested
executed. This output may be suppressed by passing <option>--quiet</option>.
</para>
- <para>This command honors <option>--system</option>, <option>--user</option>, <option>--runtime</option>
- and <option>--global</option> in a similar way as <command>enable</command>.</para>
+ <para>This command honors <option>--system</option>, <option>--user</option>, <option>--runtime</option>,
+ <option>--global</option> and <option>--no-warn</option> in a similar way as <command>enable</command>.</para>
</listitem>
</varlistentry>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--no-warn</option></term>
+
+ <listitem>
+ <para>Don't generate the warning shown by default when using
+ <command>enable</command> or <command>disable</command> on units
+ without install information (i.e. don't have or have an empty
+ [Install] section).</para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>--no-block</option></term>
</thead>
<tbody>
<row>
- <entry valign="top" morerows="15">VM</entry>
+ <entry valign="top" morerows="16">VM</entry>
<entry><varname>qemu</varname></entry>
<entry>QEMU software virtualization, without KVM</entry>
</row>
<entry><ulink url="https://developer.apple.com/documentation/virtualization">Apple Virtualization.framework</ulink></entry>
</row>
+ <row>
+ <entry><varname>sre</varname></entry>
+ <entry><ulink url="https://www.lockheedmartin.com/en-us/products/Hardened-Security-for-Intel-Processors.html">LMHS SRE hypervisor</ulink></entry>
+ </row>
+
<row>
<entry valign="top" morerows="9">Container</entry>
<entry><varname>openvz</varname></entry>
are excluded. If <option>--exclude-partitions=</option> is used, all partitions that are specified
are excluded. Both options take a comma separated list of GPT partition type UUIDs or identifiers
(see <varname>Type=</varname> in
- <citerefentry><refentrytitle>repart.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>). All
- partitions that are excluded using these options are still taken into account when calculating the
- sizes and offsets of other partitions, but aren't actually written to the disk image. The net effect
- of this option is that if you run <command>systemd-repart</command> again without these options, the
- missing partitions will be added as if they had not been excluded the first time
+ <citerefentry><refentrytitle>repart.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--skip-partitions=</option><arg rep="repeat">PARTITION</arg></term>
+
+ <listitem><para>This option specifies which partition types <command>systemd-repart</command> should
+ skip. All partitions that are skipped using this option are still taken into account when calculating
+ the sizes and offsets of other partitions, but aren't actually written to the disk image. The net
+ effect of this option is that if you run <command>systemd-repart</command> again without these
+ options, the missing partitions will be added as if they had not been skipped the first time
<command>systemd-repart</command> was executed.</para></listitem>
</varlistentry>
local default gateway configured. This assigns a stable hostname to the local outbound IP addresses,
useful for referencing them independently of the current network configuration state.</para></listitem>
+ <listitem><para>The hostname <literal>_localdnsstub</literal> is resolved to the IP address 127.0.0.53,
+ i.e. the address the local DNS stub (see above) is listening on.</para></listitem>
+
+ <listitem><para>The hostname <literal>_localdnsproxy</literal> is resolved to the IP address 127.0.0.54,
+ i.e. the address the local DNS proxy (see above) is listening on.</para></listitem>
+
<listitem><para>The mappings defined in <filename>/etc/hosts</filename> are resolved to their
configured addresses and back, but they will not affect lookups for non-address types (like MX).
Support for <filename>/etc/hosts</filename> may be disabled with <varname>ReadEtcHosts=no</varname>,
<listitem>
<para>Takes a boolean. Specifies whether to add an address from the delegated prefixes which
are received from the WAN interface by the DHCPv6 Prefix Delegation. When true (on LAN
- interfce), the EUI-64 algorithm will be used by default to form an interface identifier from
+ interface), the EUI-64 algorithm will be used by default to form an interface identifier from
the delegated prefixes. See also <varname>Token=</varname> setting below. Defaults to yes.
</para>
</listitem>
only respect these extended attributes if the unit's cgroup is owned by root.</para>
<para>When calculating candidates to relieve memory pressure, <command>systemd-oomd</command>
- will only respect these extended attributes if the unit's cgroup owner, and the
- owner of the monitored ancestor cgroup are the same. For example, if <command>systemd-oomd</command>
- is calculating candidates for <filename>-.slice</filename>, then extended attributes set
- on descendants of <filename>/user.slice/user-1000.slice/user@1000.service/</filename>
+ will only respect these extended attributes if the unit's cgroup is owned by root, or if the
+ unit's cgroup owner, and the owner of the monitored ancestor cgroup are the same. For example,
+ if <command>systemd-oomd</command> is calculating candidates for <filename>-.slice</filename>,
+ then extended attributes set on descendants of <filename>/user.slice/user-1000.slice/user@1000.service/</filename>
will be ignored because the descendants are owned by UID 1000, and <filename>-.slice</filename>
is owned by UID 0. But, if calculating candidates for
<filename>/user.slice/user-1000.slice/user@1000.service/</filename>, then extended attributes set
<literal>bhyve</literal>,
<literal>qnx</literal>,
<literal>apple</literal>,
+ <literal>sre</literal>,
<literal>openvz</literal>,
<literal>lxc</literal>,
<literal>lxc-libvirt</literal>,
conf.set('SIZEOF_RLIM_T', cc.sizeof('rlim_t', prefix : '#include <sys/resource.h>'))
conf.set('SIZEOF_TIMEX_MEMBER', cc.sizeof('typeof(((struct timex *)0)->freq)', prefix : '#include <sys/timex.h>'))
+long_max = cc.compute_int('LONG_MAX', prefix : '#include <limits.h>')
+assert(long_max > 100000)
+conf.set_quoted('LONG_MAX_STR', '@0@'.format(long_max))
+
decl_headers = '''
#include <uchar.h>
#include <sys/mount.h>
'systemd-homework',
systemd_homework_sources,
include_directories : includes,
- link_with : [libshared],
+ link_with : [libshared,
+ libshared_fdisk],
dependencies : [threads,
libblkid,
libcrypt,
'systemd-sysupdate',
systemd_sysupdate_sources,
include_directories : includes,
- link_with : [libshared],
+ link_with : [libshared,
+ libshared_fdisk],
dependencies : [threads,
libblkid,
libfdisk,
'systemd-repart',
systemd_repart_sources,
include_directories : includes,
- link_with : [libshared],
+ link_with : [libshared,
+ libshared_fdisk],
dependencies : [threads,
libblkid,
libfdisk,
link_with : [libshared_static,
libbasic,
libbasic_gcrypt,
- libsystemd_static],
+ libsystemd_static,
+ libshared_fdisk],
dependencies : [threads,
libblkid,
libfdisk,
############################################################
+subdir('docs/sysvinit')
+subdir('docs/var-log')
+subdir('hwdb.d')
+subdir('man')
subdir('modprobe.d')
+subdir('network')
+subdir('presets')
+subdir('shell-completion/bash')
+subdir('shell-completion/zsh')
subdir('sysctl.d')
subdir('sysusers.d')
subdir('tmpfiles.d')
-subdir('hwdb.d')
subdir('units')
-subdir('presets')
-subdir('network')
-subdir('man')
-subdir('shell-completion/bash')
-subdir('shell-completion/zsh')
-subdir('docs/sysvinit')
-subdir('docs/var-log')
install_subdir('factory/etc',
install_dir : factorydir)
# This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi).
[Output]
-Format=gpt_btrfs
Bootable=yes
-HostonlyInitrd=yes
# Prevent ASAN warnings when building the image
Environment=ASAN_OPTIONS=verify_asan_link_order=false
OutputDirectory=mkosi.output
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# Indonesian translation for systemd.
-# Andika Triwidada <andika@gmail.com>, 2014, 2021.
+# Andika Triwidada <andika@gmail.com>, 2014, 2021, 2022.
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-10-20 10:35+0200\n"
-"PO-Revision-Date: 2021-09-24 11:05+0000\n"
+"PO-Revision-Date: 2022-11-25 08:19+0000\n"
"Last-Translator: Andika Triwidada <andika@gmail.com>\n"
"Language-Team: Indonesian <https://translate.fedoraproject.org/projects/"
"systemd/master/id/>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.8\n"
+"X-Generator: Weblate 4.14.2\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
#: src/hostname/org.freedesktop.hostname1.policy:61
msgid "Get hardware serial number"
-msgstr ""
+msgstr "Dapatkan nomor seri perangkat keras"
#: src/hostname/org.freedesktop.hostname1.policy:62
-#, fuzzy
msgid "Authentication is required to get hardware serial number."
-msgstr "Otentikasi diperlukan untuk menyetel waktu sistem."
+msgstr "Otentikasi diperlukan untuk mendapatkan nomor seri perangkat keras."
#: src/hostname/org.freedesktop.hostname1.policy:71
-#, fuzzy
msgid "Get system description"
-msgstr "Setel zona waktu sistem"
+msgstr "Dapatkan deskripsi sistem"
#: src/hostname/org.freedesktop.hostname1.policy:72
-#, fuzzy
msgid "Authentication is required to get system description."
-msgstr "Otentikasi diperlukan untuk menyetel zona waktu sistem."
+msgstr "Otentikasi diperlukan untuk mendapatkan deskripsi sistem."
#: src/import/org.freedesktop.import1.policy:22
msgid "Import a VM or container image"
#: src/login/org.freedesktop.login1.policy:310
msgid "Hibernate the system while other users are logged in"
-msgstr "Hibernasikan sistem ketika pengguna lain sedang log masuk."
+msgstr "Hibernasikan sistem ketika pengguna lain sedang log masuk"
#: src/login/org.freedesktop.login1.policy:311
msgid ""
#: src/login/org.freedesktop.login1.policy:321
msgid "Hibernate the system while an application is inhibiting this"
-msgstr "Hibernasikan sistem ketika sebuah aplikasi meminta untuk mencegahnya."
+msgstr "Hibernasikan sistem ketika sebuah aplikasi meminta untuk mencegahnya"
#: src/login/org.freedesktop.login1.policy:322
msgid ""
return -errno;
flags |= CHASE_AT_RESOLVE_IN_ROOT;
- } else
+ } else {
+ path = absolute;
fd = AT_FDCWD;
+ }
r = chase_symlinks_at(fd, path, flags & ~CHASE_PREFIX_ROOT, ret_path ? &p : NULL, ret_fd ? &pfd : NULL);
if (r < 0)
#define DEFAULT_EXIT_USEC (30*USEC_PER_SEC)
/* The default value for the net.unix.max_dgram_qlen sysctl */
-#define DEFAULT_UNIX_MAX_DGRAM_QLEN 512UL
+#define DEFAULT_UNIX_MAX_DGRAM_QLEN 512
#define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT
#define SIGNALS_IGNORE SIGPIPE
#include "stat-util.h"
#include "string-util.h"
-static int dirent_ensure_type(DIR *d, struct dirent *de) {
+int dirent_ensure_type(int dir_fd, struct dirent *de) {
STRUCT_STATX_DEFINE(sx);
int r;
- assert(d);
+ assert(dir_fd >= 0);
assert(de);
if (de->d_type != DT_UNKNOWN)
}
/* Let's ask only for the type, nothing else. */
- r = statx_fallback(dirfd(d), de->d_name, AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, STATX_TYPE, &sx);
+ r = statx_fallback(dir_fd, de->d_name, AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, STATX_TYPE, &sx);
if (r < 0)
return r;
if (!de)
return NULL;
- r = dirent_ensure_type(d, de);
+ r = dirent_ensure_type(dirfd(d), de);
if (r >= 0)
return de;
if (r != -ENOENT) {
bool dirent_is_file(const struct dirent *de) _pure_;
bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pure_;
+int dirent_ensure_type(int dir_fd, struct dirent *de);
struct dirent *readdir_ensure_type(DIR *d);
struct dirent *readdir_no_dot(DIR *dirp);
return new_fd;
}
+int fd_reopen_condition(
+ int fd,
+ int flags,
+ int mask,
+ int *ret_new_fd) {
+
+ int r, new_fd;
+
+ assert(fd >= 0);
+
+ /* Invokes fd_reopen(fd, flags), but only if the existing F_GETFL flags don't match the specified
+ * flags (masked by the specified mask). This is useful for converting O_PATH fds into real fds if
+ * needed, but only then. */
+
+ r = fcntl(fd, F_GETFL);
+ if (r < 0)
+ return -errno;
+
+ if ((r & mask) == (flags & mask)) {
+ *ret_new_fd = -1;
+ return fd;
+ }
+
+ new_fd = fd_reopen(fd, flags);
+ if (new_fd < 0)
+ return new_fd;
+
+ *ret_new_fd = new_fd;
+ return new_fd;
+}
+
int read_nr_open(void) {
_cleanup_free_ char *nr_open = NULL;
int r;
})
int fd_reopen(int fd, int flags);
+int fd_reopen_condition(int fd, int flags, int mask, int *ret_new_fd);
int read_nr_open(void);
int fd_get_diskseq(int fd, uint64_t *ret);
return STRCASE_IN_SET(hostname, "_outbound", "_outbound.");
}
+static inline bool is_dns_stub_hostname(const char *hostname) {
+ return STRCASE_IN_SET(hostname, "_localdnsstub", "_localdnsstub.");
+}
+
+static inline bool is_dns_proxy_stub_hostname(const char *hostname) {
+ return STRCASE_IN_SET(hostname, "_localdnsproxy", "_localdnsproxy.");
+}
+
int get_pretty_hostname(char **ret);
struct dirent *entry;
DirectoryEntries *nde;
size_t add, sz, j;
+ int r;
assert(dir_fd >= 0);
if (ignore_dirent(entry, flags))
continue;
+ if (FLAGS_SET(flags, RECURSE_DIR_ENSURE_TYPE)) {
+ r = dirent_ensure_type(dir_fd, entry);
+ if (r == -ENOENT)
+ /* dentry gone by now? no problem, let's just suppress it */
+ continue;
+ if (r < 0)
+ return r;
+ }
+
de->n_entries++;
}
if (ignore_dirent(entry, flags))
continue;
+ /* If d_type == DT_UNKNOWN that means we failed to ensure the type in the earlier loop and
+ * didn't include the dentry in de->n_entries and as such should skip it here as well. */
+ if (FLAGS_SET(flags, RECURSE_DIR_ENSURE_TYPE) && entry->d_type == DT_UNKNOWN)
+ continue;
+
de->entries[j++] = entry;
}
+ assert(j == de->n_entries);
if (FLAGS_SET(flags, RECURSE_DIR_SORT))
typesafe_qsort(de->entries, de->n_entries, sort_func);
return r;
}
- r = readdir_all(dir_fd, flags, &de);
+ /* Mask out RECURSE_DIR_ENSURE_TYPE so we can do it ourselves and avoid an extra statx() call. */
+ r = readdir_all(dir_fd, flags & ~RECURSE_DIR_ENSURE_TYPE, &de);
if (r < 0)
return r;
termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC);
termios.c_iflag |= ICRNL | IMAXBEL | IUTF8;
- termios.c_oflag |= ONLCR;
+ termios.c_oflag |= ONLCR | OPOST;
termios.c_cflag |= CREAD;
termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOPRT | ECHOKE;
{ "QNXQVMBSQG", VIRTUALIZATION_QNX },
/* https://projectacrn.org */
{ "ACRNACRNACRN", VIRTUALIZATION_ACRN },
+ /* https://www.lockheedmartin.com/en-us/products/Hardened-Security-for-Intel-Processors.html */
+ { "SRESRESRESRE", VIRTUALIZATION_SRE },
};
uint32_t eax, ebx, ecx, edx;
[VIRTUALIZATION_ACRN] = "acrn",
[VIRTUALIZATION_POWERVM] = "powervm",
[VIRTUALIZATION_APPLE] = "apple",
+ [VIRTUALIZATION_SRE] = "sre",
[VIRTUALIZATION_VM_OTHER] = "vm-other",
[VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",
VIRTUALIZATION_ACRN,
VIRTUALIZATION_POWERVM,
VIRTUALIZATION_APPLE,
+ VIRTUALIZATION_SRE,
VIRTUALIZATION_VM_OTHER,
VIRTUALIZATION_VM_LAST = VIRTUALIZATION_VM_OTHER,
continue;
}
free(config->entry_default_config);
- config->entry_default_config = xstra_to_str(value);
+ config->entry_default_config = xstr8_to_16(value);
continue;
}
while ((line = line_get_key_value(content, " \t", &pos, &key, &value))) {
if (streq8(key, "title")) {
free(entry->title);
- entry->title = xstra_to_str(value);
+ entry->title = xstr8_to_16(value);
continue;
}
if (streq8(key, "sort-key")) {
free(entry->sort_key);
- entry->sort_key = xstra_to_str(value);
+ entry->sort_key = xstr8_to_16(value);
continue;
}
if (streq8(key, "version")) {
free(entry->version);
- entry->version = xstra_to_str(value);
+ entry->version = xstr8_to_16(value);
continue;
}
if (streq8(key, "machine-id")) {
free(entry->machine_id);
- entry->machine_id = xstra_to_str(value);
+ entry->machine_id = xstr8_to_16(value);
continue;
}
if (streq8(key, "linux")) {
free(entry->loader);
entry->type = LOADER_LINUX;
- entry->loader = xstra_to_path(value);
+ entry->loader = xstr8_to_path(value);
entry->key = 'l';
continue;
}
if (streq8(key, "efi")) {
entry->type = LOADER_EFI;
free(entry->loader);
- entry->loader = xstra_to_path(value);
+ entry->loader = xstr8_to_path(value);
/* do not add an entry for ourselves */
if (strcaseeq16(entry->loader, loaded_image_path)) {
if (streq8(key, "devicetree")) {
free(entry->devicetree);
- entry->devicetree = xstra_to_path(value);
+ entry->devicetree = xstr8_to_path(value);
continue;
}
entry->initrd,
n_initrd == 0 ? 0 : (n_initrd + 1) * sizeof(uint16_t *),
(n_initrd + 2) * sizeof(uint16_t *));
- entry->initrd[n_initrd++] = xstra_to_path(value);
+ entry->initrd[n_initrd++] = xstr8_to_path(value);
entry->initrd[n_initrd] = NULL;
continue;
}
if (streq8(key, "options")) {
_cleanup_free_ char16_t *new = NULL;
- new = xstra_to_str(value);
+ new = xstr8_to_16(value);
if (entry->options) {
char16_t *s = xpool_print(L"%s %s", entry->options, new);
free(entry->options);
while ((line = line_get_key_value(content, "=", &pos, &key, &value))) {
if (streq8(key, "PRETTY_NAME")) {
free(os_pretty_name);
- os_pretty_name = xstra_to_str(value);
+ os_pretty_name = xstr8_to_16(value);
continue;
}
if (streq8(key, "IMAGE_ID")) {
free(os_image_id);
- os_image_id = xstra_to_str(value);
+ os_image_id = xstr8_to_16(value);
continue;
}
if (streq8(key, "NAME")) {
free(os_name);
- os_name = xstra_to_str(value);
+ os_name = xstr8_to_16(value);
continue;
}
if (streq8(key, "ID")) {
free(os_id);
- os_id = xstra_to_str(value);
+ os_id = xstr8_to_16(value);
continue;
}
if (streq8(key, "IMAGE_VERSION")) {
free(os_image_version);
- os_image_version = xstra_to_str(value);
+ os_image_version = xstr8_to_16(value);
continue;
}
if (streq8(key, "VERSION")) {
free(os_version);
- os_version = xstra_to_str(value);
+ os_version = xstr8_to_16(value);
continue;
}
if (streq8(key, "VERSION_ID")) {
free(os_version_id);
- os_version_id = xstra_to_str(value);
+ os_version_id = xstr8_to_16(value);
continue;
}
if (streq8(key, "BUILD_ID")) {
free(os_build_id);
- os_build_id = xstra_to_str(value);
+ os_build_id = xstr8_to_16(value);
continue;
}
}
content = mfree(content);
/* read the embedded cmdline file */
- err = file_read(linux_dir, f->FileName, offs[SECTION_CMDLINE], szs[SECTION_CMDLINE], &content, NULL);
+ size_t cmdline_len;
+ err = file_read(linux_dir, f->FileName, offs[SECTION_CMDLINE], szs[SECTION_CMDLINE], &content, &cmdline_len);
if (err == EFI_SUCCESS) {
- /* chomp the newline */
- if (content[szs[SECTION_CMDLINE] - 1] == '\n')
- content[szs[SECTION_CMDLINE] - 1] = '\0';
-
- entry->options = xstra_to_str(content);
+ entry->options = xstrn8_to_16(content, cmdline_len);
+ mangle_stub_cmdline(entry->options);
}
}
}
/* Uncomment the next line if you need to wait for debugger. */
// debug_break();
- /* The firmware may skip initializing some devices for the sake of a faster boot. This is especially
- * true for fastboot enabled firmwares. But this means that things we use like input devices or the
- * xbootldr partition may not be available yet. Reconnect all drivers should hopefully make the
- * firmware initialize everything we need. */
- (void) reconnect_all_drivers();
-
err = BS->OpenProtocol(image,
&LoadedImageProtocol,
(void **)&loaded_image,
#define VERTICAL_MAX_OK 1080
#define VIEWPORT_RATIO 10
+static EFI_STATUS console_connect(void) {
+ EFI_BOOT_MANAGER_POLICY_PROTOCOL *boot_policy;
+ EFI_STATUS err;
+
+ /* This should make console devices appear/fully initialize on fastboot firmware. */
+
+ err = BS->LocateProtocol(
+ &(EFI_GUID) EFI_BOOT_MANAGER_POLICY_PROTOCOL_GUID, NULL, (void **) &boot_policy);
+ if (err != EFI_SUCCESS)
+ return err;
+
+ return boot_policy->ConnectDeviceClass(boot_policy, &(EFI_GUID) EFI_BOOT_MANAGER_POLICY_CONSOLE_GUID);
+}
+
static inline void event_closep(EFI_EVENT *event) {
if (!*event)
return;
assert(key);
if (!checked) {
+ console_connect();
+
/* Get the *first* TextInputEx device.*/
err = BS->LocateProtocol(&SimpleTextInputExProtocol, NULL, (void **) &extraInEx);
if (err != EFI_SUCCESS || BS->CheckEvent(extraInEx->WaitForKeyEx) == EFI_INVALID_PARAMETER)
if (device_path_to_str(file_path, &file_path_str) != EFI_SUCCESS)
return NULL;
- for (char16_t *i = file_path_str, *fixed = i;; i++) {
- if (*i == '\0') {
- *fixed = '\0';
- break;
- }
-
- /* Fix device path node separator. */
- if (*i == '/')
- *i = '\\';
-
- /* Double '\' is not allowed in EFI file paths. */
- if (fixed != file_path_str && fixed[-1] == '\\' && *i == '\\')
- continue;
-
- *fixed = *i;
- fixed++;
- }
-
+ convert_efi_path(file_path_str);
return xpool_print(u"%s.extra.d", file_path_str);
}
# include "util.h"
#else
# include <stdlib.h>
-# include "macro.h"
+# include "alloc-util.h"
+# define xnew(t, n) ASSERT_SE_PTR(new(t, n))
# define xmalloc(n) ASSERT_SE_PTR(malloc(n))
#endif
DEFINE_STRNDUP(char, xstrndup8, strnlen8);
DEFINE_STRNDUP(char16_t, xstrndup16, strnlen16);
+static unsigned utf8_to_unichar(const char *utf8, size_t n, char32_t *c) {
+ char32_t unichar;
+ unsigned len;
+
+ assert(utf8);
+ assert(c);
+
+ if (!(utf8[0] & 0x80)) {
+ *c = utf8[0];
+ return 1;
+ } else if ((utf8[0] & 0xe0) == 0xc0) {
+ len = 2;
+ unichar = utf8[0] & 0x1f;
+ } else if ((utf8[0] & 0xf0) == 0xe0) {
+ len = 3;
+ unichar = utf8[0] & 0x0f;
+ } else if ((utf8[0] & 0xf8) == 0xf0) {
+ len = 4;
+ unichar = utf8[0] & 0x07;
+ } else if ((utf8[0] & 0xfc) == 0xf8) {
+ len = 5;
+ unichar = utf8[0] & 0x03;
+ } else if ((utf8[0] & 0xfe) == 0xfc) {
+ len = 6;
+ unichar = utf8[0] & 0x01;
+ } else {
+ *c = UINT32_MAX;
+ return 1;
+ }
+
+ if (len > n) {
+ *c = UINT32_MAX;
+ return len;
+ }
+
+ for (unsigned i = 1; i < len; i++) {
+ if ((utf8[i] & 0xc0) != 0x80) {
+ *c = UINT32_MAX;
+ return len;
+ }
+ unichar <<= 6;
+ unichar |= utf8[i] & 0x3f;
+ }
+
+ *c = unichar;
+ return len;
+}
+
+/* Convert UTF-8 to UCS-2, skipping any invalid or short byte sequences. */
+char16_t *xstrn8_to_16(const char *str8, size_t n) {
+ if (!str8 || n == 0)
+ return NULL;
+
+ size_t i = 0;
+ char16_t *str16 = xnew(char16_t, n + 1);
+
+ while (n > 0 && *str8 != '\0') {
+ char32_t unichar;
+
+ size_t utf8len = utf8_to_unichar(str8, n, &unichar);
+ str8 += utf8len;
+ n = LESS_BY(n, utf8len);
+
+ switch (unichar) {
+ case 0 ... 0xd7ffU:
+ case 0xe000U ... 0xffffU:
+ str16[i++] = unichar;
+ break;
+ }
+ }
+
+ str16[i] = '\0';
+ return str16;
+}
+
static bool efi_fnmatch_prefix(const char16_t *p, const char16_t *h, const char16_t **ret_p, const char16_t **ret_h) {
assert(p);
assert(h);
return xstrndup16(s, SIZE_MAX);
}
+char16_t *xstrn8_to_16(const char *str8, size_t n);
+static inline char16_t *xstr8_to_16(const char *str8) {
+ return xstrn8_to_16(str8, strlen8(str8));
+}
+
bool efi_fnmatch(const char16_t *pattern, const char16_t *haystack);
bool parse_number8(const char *s, uint64_t *ret_u, const char **ret_tail);
EFI_STATUS linux_exec(
EFI_HANDLE parent,
- const char *cmdline, UINTN cmdline_len,
- const void *linux_buffer, UINTN linux_length,
- const void *initrd_buffer, UINTN initrd_length) {
+ const char16_t *cmdline,
+ const void *linux_buffer,
+ size_t linux_length,
+ const void *initrd_buffer,
+ size_t initrd_length) {
uint32_t compat_address;
EFI_STATUS err;
assert(parent);
- assert(cmdline || cmdline_len == 0);
assert(linux_buffer && linux_length > 0);
assert(initrd_buffer || initrd_length == 0);
return linux_exec_efi_handover(
parent,
cmdline,
- cmdline_len,
linux_buffer,
linux_length,
initrd_buffer,
return log_error_status_stall(err, u"Error getting kernel loaded image protocol: %r", err);
if (cmdline) {
- loaded_image->LoadOptions = xstra_to_str(cmdline);
+ loaded_image->LoadOptions = (void *) cmdline;
loaded_image->LoadOptionsSize = strsize16(loaded_image->LoadOptions);
}
#pragma once
#include <efi.h>
+#include <uchar.h>
EFI_STATUS linux_exec(
EFI_HANDLE parent,
- const char *cmdline, UINTN cmdline_len,
- const void *linux_buffer, UINTN linux_length,
- const void *initrd_buffer, UINTN initrd_length);
+ const char16_t *cmdline,
+ const void *linux_buffer,
+ size_t linux_length,
+ const void *initrd_buffer,
+ size_t initrd_length);
EFI_STATUS linux_exec_efi_handover(
EFI_HANDLE parent,
- const char *cmdline, UINTN cmdline_len,
- const void *linux_buffer, UINTN linux_length,
- const void *initrd_buffer, UINTN initrd_length);
+ const char16_t *cmdline,
+ const void *linux_buffer,
+ size_t linux_length,
+ const void *initrd_buffer,
+ size_t initrd_length);
EFI_STATUS linux_exec_efi_handover(
EFI_HANDLE parent,
- const char *cmdline, UINTN cmdline_len,
- const void *linux_buffer, UINTN linux_length,
- const void *initrd_buffer, UINTN initrd_length) {
+ const char16_t *cmdline,
+ const void *linux_buffer,
+ size_t linux_length,
+ const void *initrd_buffer,
+ size_t initrd_length) {
assert(parent);
- assert(cmdline || cmdline_len == 0);
assert(linux_buffer);
assert(initrd_buffer || initrd_length == 0);
_cleanup_pages_ Pages cmdline_pages = {};
if (cmdline) {
+ size_t len = MIN(strlen16(cmdline), image_params->hdr.cmdline_size);
+
cmdline_pages = xmalloc_pages(
can_4g ? AllocateAnyPages : AllocateMaxAddress,
EfiLoaderData,
- EFI_SIZE_TO_PAGES(cmdline_len + 1),
+ EFI_SIZE_TO_PAGES(len + 1),
CMDLINE_PTR_MAX);
- memcpy(PHYSICAL_ADDRESS_TO_POINTER(cmdline_pages.addr), cmdline, cmdline_len);
- ((char *) PHYSICAL_ADDRESS_TO_POINTER(cmdline_pages.addr))[cmdline_len] = 0;
+ /* Convert cmdline to ASCII. */
+ char *cmdline8 = PHYSICAL_ADDRESS_TO_POINTER(cmdline_pages.addr);
+ for (size_t i = 0; i < len; i++)
+ cmdline8[i] = cmdline[i] <= 0x7E ? cmdline[i] : ' ';
+ cmdline8[len] = '\0';
+
boot_params->hdr.cmd_line_ptr = (uint32_t) cmdline_pages.addr;
boot_params->ext_cmd_line_ptr = cmdline_pages.addr >> 32;
assert(can_4g || cmdline_pages.addr <= CMDLINE_PTR_MAX);
_cleanup_free_ char16_t *c = NULL;
if (description)
- c = xstra_to_str(description);
+ c = xstr8_to_16(description);
return tpm_log_event(pcrindex, buffer, buffer_size, c, ret_measured);
}
{ 0xd719b2cb, 0x3d3a, 0x4596, {0xa3, 0xbc, 0xda, 0xd0, 0xe, 0x67, 0x65, 0x6f }}
#endif
+
+#ifndef EFI_SHELL_PARAMETERS_PROTOCOL_GUID
+# define EFI_SHELL_PARAMETERS_PROTOCOL_GUID \
+ { 0x752f3136, 0x4e16, 0x4fdc, { 0xa2, 0x2a, 0xe5, 0xf4, 0x68, 0x12, 0xf4, 0xca } }
+
+typedef struct {
+ CHAR16 **Argv;
+ UINTN Argc;
+ void *StdIn;
+ void *StdOut;
+ void *StdErr;
+} EFI_SHELL_PARAMETERS_PROTOCOL;
+#endif
+
+#ifndef EFI_BOOT_MANAGER_POLICY_PROTOCOL_GUID
+#define EFI_BOOT_MANAGER_POLICY_PROTOCOL_GUID \
+ { 0xFEDF8E0C, 0xE147, 0x11E3, { 0x99, 0x03, 0xB8, 0xE8, 0x56, 0x2C, 0xBA, 0xFA } }
+#define EFI_BOOT_MANAGER_POLICY_CONSOLE_GUID \
+ { 0xCAB0E94C, 0xE15F, 0x11E3, { 0x91, 0x8D, 0xB8, 0xE8, 0x56, 0x2C, 0xBA, 0xFA } }
+
+typedef struct EFI_BOOT_MANAGER_POLICY_PROTOCOL EFI_BOOT_MANAGER_POLICY_PROTOCOL;
+struct EFI_BOOT_MANAGER_POLICY_PROTOCOL {
+ UINT64 Revision;
+ EFI_STATUS (EFIAPI *ConnectDevicePath)(
+ EFI_BOOT_MANAGER_POLICY_PROTOCOL *This,
+ EFI_DEVICE_PATH *DevicePath,
+ BOOLEAN Recursive);
+ EFI_STATUS (EFIAPI *ConnectDeviceClass)(
+ EFI_BOOT_MANAGER_POLICY_PROTOCOL *This,
+ EFI_GUID *Class);
+};
+#endif
if (err != EFI_SUCCESS)
return err;
+ /* The drivers for other partitions on this drive may not be initialized on fastboot firmware, so we
+ * have to ask the firmware to do just that. */
+ (void) BS->ConnectController(disk_handle, NULL, NULL, true);
+
err = BS->HandleProtocol(disk_handle, &BlockIoProtocol, (void **)&block_io);
if (err != EFI_SUCCESS)
return err;
static EFI_STATUS bmp_parse_header(
const uint8_t *bmp,
- UINTN size,
+ size_t size,
struct bmp_dib **ret_dib,
struct bmp_map **ret_map,
const uint8_t **pixmap) {
- struct bmp_file *file;
- struct bmp_dib *dib;
- struct bmp_map *map;
- UINTN row_size;
-
assert(bmp);
assert(ret_dib);
assert(ret_map);
return EFI_INVALID_PARAMETER;
/* check file header */
- file = (struct bmp_file *)bmp;
+ struct bmp_file *file = (struct bmp_file *) bmp;
if (file->signature[0] != 'B' || file->signature[1] != 'M')
return EFI_INVALID_PARAMETER;
if (file->size != size)
return EFI_INVALID_PARAMETER;
/* check device-independent bitmap */
- dib = (struct bmp_dib *)(bmp + sizeof(struct bmp_file));
+ struct bmp_dib *dib = (struct bmp_dib *) (bmp + sizeof(struct bmp_file));
if (dib->size < sizeof(struct bmp_dib))
return EFI_UNSUPPORTED;
return EFI_UNSUPPORTED;
}
- row_size = ((UINTN) dib->depth * dib->x + 31) / 32 * 4;
+ size_t row_size = ((size_t) dib->depth * dib->x + 31) / 32 * 4;
if (file->size - file->offset < dib->y * row_size)
return EFI_INVALID_PARAMETER;
if (row_size * dib->y > 64 * 1024 * 1024)
return EFI_INVALID_PARAMETER;
/* check color table */
- map = (struct bmp_map *)(bmp + sizeof(struct bmp_file) + dib->size);
+ struct bmp_map *map = (struct bmp_map *) (bmp + sizeof(struct bmp_file) + dib->size);
if (file->offset < sizeof(struct bmp_file) + dib->size)
return EFI_INVALID_PARAMETER;
if (file->offset > sizeof(struct bmp_file) + dib->size) {
- uint32_t map_count;
- UINTN map_size;
+ uint32_t map_count = 0;
if (dib->colors_used)
map_count = dib->colors_used;
- else {
- switch (dib->depth) {
- case 1:
- case 4:
- case 8:
- map_count = 1 << dib->depth;
- break;
+ else if (IN_SET(dib->depth, 1, 4, 8))
+ map_count = 1 << dib->depth;
- default:
- map_count = 0;
- break;
- }
- }
-
- map_size = file->offset - (sizeof(struct bmp_file) + dib->size);
+ size_t map_size = file->offset - (sizeof(struct bmp_file) + dib->size);
if (map_size != sizeof(struct bmp_map) * map_count)
return EFI_INVALID_PARAMETER;
}
return EFI_SUCCESS;
}
-static void pixel_blend(uint32_t *dst, const uint32_t source) {
- uint32_t alpha, src, src_rb, src_g, dst_rb, dst_g, rb, g;
-
- assert(dst);
-
- alpha = (source & 0xff);
-
- /* convert src from RGBA to XRGB */
- src = source >> 8;
+enum Channels { R, G, B, A, _CHANNELS_MAX };
+static void read_channel_maks(
+ const struct bmp_dib *dib,
+ uint32_t channel_mask[static _CHANNELS_MAX],
+ uint8_t channel_shift[static _CHANNELS_MAX],
+ uint8_t channel_scale[static _CHANNELS_MAX]) {
- /* decompose into RB and G components */
- src_rb = (src & 0xff00ff);
- src_g = (src & 0x00ff00);
-
- dst_rb = (*dst & 0xff00ff);
- dst_g = (*dst & 0x00ff00);
-
- /* blend */
- rb = ((((src_rb - dst_rb) * alpha + 0x800080) >> 8) + dst_rb) & 0xff00ff;
- g = ((((src_g - dst_g) * alpha + 0x008000) >> 8) + dst_g) & 0x00ff00;
+ assert(dib);
- *dst = (rb | g);
+ if (IN_SET(dib->depth, 16, 32) && dib->size >= sizeof(*dib) + 3 * sizeof(uint32_t)) {
+ uint32_t *mask = (uint32_t *) ((uint8_t *) dib + sizeof(*dib));
+ channel_mask[R] = mask[R];
+ channel_mask[G] = mask[G];
+ channel_mask[B] = mask[B];
+ channel_shift[R] = __builtin_ctz(mask[R]);
+ channel_shift[G] = __builtin_ctz(mask[G]);
+ channel_shift[B] = __builtin_ctz(mask[B]);
+ channel_scale[R] = 0xff / ((1 << __builtin_popcount(mask[R])) - 1);
+ channel_scale[G] = 0xff / ((1 << __builtin_popcount(mask[G])) - 1);
+ channel_scale[B] = 0xff / ((1 << __builtin_popcount(mask[B])) - 1);
+
+ if (dib->size >= sizeof(*dib) + 4 * sizeof(uint32_t) && mask[A] != 0) {
+ channel_mask[A] = mask[A];
+ channel_shift[A] = __builtin_ctz(mask[A]);
+ channel_scale[A] = 0xff / ((1 << __builtin_popcount(mask[A])) - 1);
+ } else {
+ channel_mask[A] = 0;
+ channel_shift[A] = 0;
+ channel_scale[A] = 0;
+ }
+ } else {
+ bool bpp16 = dib->depth == 16;
+ channel_mask[R] = bpp16 ? 0x7C00 : 0xFF0000;
+ channel_mask[G] = bpp16 ? 0x03E0 : 0x00FF00;
+ channel_mask[B] = bpp16 ? 0x001F : 0x0000FF;
+ channel_mask[A] = bpp16 ? 0x0000 : 0x000000;
+ channel_shift[R] = bpp16 ? 0xA : 0x10;
+ channel_shift[G] = bpp16 ? 0x5 : 0x08;
+ channel_shift[B] = bpp16 ? 0x0 : 0x00;
+ channel_shift[A] = bpp16 ? 0x0 : 0x00;
+ channel_scale[R] = bpp16 ? 0x08 : 0x1;
+ channel_scale[G] = bpp16 ? 0x08 : 0x1;
+ channel_scale[B] = bpp16 ? 0x08 : 0x1;
+ channel_scale[A] = bpp16 ? 0x00 : 0x0;
+ }
}
static EFI_STATUS bmp_to_blt(
assert(map);
assert(pixmap);
+ uint32_t channel_mask[_CHANNELS_MAX];
+ uint8_t channel_shift[_CHANNELS_MAX], channel_scale[_CHANNELS_MAX];
+ read_channel_maks(dib, channel_mask, channel_shift, channel_scale);
+
/* transform and copy pixels */
in = pixmap;
- for (UINTN y = 0; y < dib->y; y++) {
- EFI_GRAPHICS_OUTPUT_BLT_PIXEL *out;
- UINTN row_size;
+ for (uint32_t y = 0; y < dib->y; y++) {
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL *out = &buf[(dib->y - y - 1) * dib->x];
- out = &buf[(dib->y - y - 1) * dib->x];
- for (UINTN x = 0; x < dib->x; x++, in++, out++) {
+ for (uint32_t x = 0; x < dib->x; x++, in++, out++) {
switch (dib->depth) {
case 1: {
- for (UINTN i = 0; i < 8 && x < dib->x; i++) {
+ for (unsigned i = 0; i < 8 && x < dib->x; i++) {
out->Red = map[((*in) >> (7 - i)) & 1].red;
out->Green = map[((*in) >> (7 - i)) & 1].green;
out->Blue = map[((*in) >> (7 - i)) & 1].blue;
}
case 4: {
- UINTN i;
-
- i = (*in) >> 4;
+ unsigned i = (*in) >> 4;
out->Red = map[i].red;
out->Green = map[i].green;
out->Blue = map[i].blue;
out->Blue = map[*in].blue;
break;
- case 16: {
- uint16_t i = *(uint16_t *) in;
-
- out->Red = (i & 0x7c00) >> 7;
- out->Green = (i & 0x3e0) >> 2;
- out->Blue = (i & 0x1f) << 3;
- in += 1;
- break;
- }
-
case 24:
out->Red = in[2];
out->Green = in[1];
in += 2;
break;
+ case 16:
case 32: {
- uint32_t i = *(uint32_t *) in;
+ uint32_t i = dib->depth == 16 ? *(uint16_t *) in : *(uint32_t *) in;
+
+ uint8_t r = ((i & channel_mask[R]) >> channel_shift[R]) * channel_scale[R],
+ g = ((i & channel_mask[G]) >> channel_shift[G]) * channel_scale[G],
+ b = ((i & channel_mask[B]) >> channel_shift[B]) * channel_scale[B],
+ a = 0xFFu;
+ if (channel_mask[A] != 0)
+ a = ((i & channel_mask[A]) >> channel_shift[A]) * channel_scale[A];
- pixel_blend((uint32_t *)out, i);
+ out->Red = (out->Red * (0xFFu - a) + r * a) >> 8;
+ out->Green = (out->Green * (0xFFu - a) + g * a) >> 8;
+ out->Blue = (out->Blue * (0xFFu - a) + b * a) >> 8;
- in += 3;
+ in += dib->depth == 16 ? 1 : 3;
break;
}
}
}
/* add row padding; new lines always start at 32 bit boundary */
- row_size = in - pixmap;
+ size_t row_size = in - pixmap;
in += ((row_size + 3) & ~3) - row_size;
}
return EFI_SUCCESS;
}
-EFI_STATUS graphics_splash(const uint8_t *content, UINTN len) {
+EFI_STATUS graphics_splash(const uint8_t *content, size_t len) {
EFI_GRAPHICS_OUTPUT_BLT_PIXEL background = {};
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;
struct bmp_dib *dib;
struct bmp_map *map;
const uint8_t *pixmap;
- _cleanup_free_ void *blt = NULL;
- UINTN x_pos = 0;
- UINTN y_pos = 0;
+ size_t x_pos = 0, y_pos = 0;
EFI_STATUS err;
if (len == 0)
if (err != EFI_SUCCESS)
return err;
- /* EFI buffer */
- blt = xnew(EFI_GRAPHICS_OUTPUT_BLT_PIXEL, dib->x * dib->y);
-
+ /* Read in current screen content to perform proper alpha blending. */
+ _cleanup_free_ EFI_GRAPHICS_OUTPUT_BLT_PIXEL *blt = xnew(
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL, dib->x * dib->y);
err = GraphicsOutput->Blt(
GraphicsOutput, blt,
EfiBltVideoToBltBuffer, x_pos, y_pos, 0, 0,
(void) efivar_set_uint64_le(LOADER_GUID, L"StubFeatures", stub_features, 0);
}
+static bool use_load_options(
+ EFI_HANDLE stub_image,
+ EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
+ bool have_cmdline,
+ char16_t **ret) {
+
+ assert(stub_image);
+ assert(loaded_image);
+ assert(ret);
+
+ /* We only allow custom command lines if we aren't in secure boot or if no cmdline was baked into
+ * the stub image. */
+ if (secure_boot_enabled() && have_cmdline)
+ return false;
+
+ /* We also do a superficial check whether first character of passed command line
+ * is printable character (for compat with some Dell systems which fill in garbage?). */
+ if (loaded_image->LoadOptionsSize < sizeof(char16_t) || ((char16_t *) loaded_image->LoadOptions)[0] <= 0x1F)
+ return false;
+
+ /* The UEFI shell registers EFI_SHELL_PARAMETERS_PROTOCOL onto images it runs. This lets us know that
+ * LoadOptions starts with the stub binary path which we want to strip off. */
+ EFI_SHELL_PARAMETERS_PROTOCOL *shell;
+ if (BS->HandleProtocol(stub_image, &(EFI_GUID) EFI_SHELL_PARAMETERS_PROTOCOL_GUID, (void **) &shell)
+ != EFI_SUCCESS) {
+ /* Not running from EFI shell, use entire LoadOptions. Note that LoadOptions is a void*, so
+ * it could be anything! */
+ *ret = xstrndup16(loaded_image->LoadOptions, loaded_image->LoadOptionsSize / sizeof(char16_t));
+ mangle_stub_cmdline(*ret);
+ return true;
+ }
+
+ if (shell->Argc < 2)
+ /* No arguments were provided? Then we fall back to built-in cmdline. */
+ return false;
+
+ /* Assemble the command line ourselves without our stub path. */
+ *ret = xstrdup16(shell->Argv[1]);
+ for (size_t i = 2; i < shell->Argc; i++) {
+ _cleanup_free_ char16_t *old = *ret;
+ *ret = xpool_print(u"%s %s", old, shell->Argv[i]);
+ }
+
+ mangle_stub_cmdline(*ret);
+ return true;
+}
+
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
_cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL;
- UINTN credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0;
- UINTN cmdline_len = 0, linux_size, initrd_size, dt_size;
+ size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0;
+ size_t linux_size, initrd_size, dt_size;
EFI_PHYSICAL_ADDRESS linux_base, initrd_base, dt_base;
_cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {};
EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
- UINTN addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {};
- char *cmdline = NULL;
- _cleanup_free_ char *cmdline_owned = NULL;
+ size_t addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {};
+ _cleanup_free_ char16_t *cmdline = NULL;
int sections_measured = -1, parameters_measured = -1;
bool sysext_measured = false, m;
uint64_t loader_features = 0;
/* Show splash screen as early as possible */
graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_SPLASH], szs[UNIFIED_SECTION_SPLASH]);
- if (szs[UNIFIED_SECTION_CMDLINE] > 0) {
- cmdline = (char *) loaded_image->ImageBase + addrs[UNIFIED_SECTION_CMDLINE];
- cmdline_len = szs[UNIFIED_SECTION_CMDLINE];
- }
-
- /* if we are not in secure boot mode, or none was provided, accept a custom command line and replace
- * the built-in one. We also do a superficial check whether first character of passed command line
- * is printable character (for compat with some Dell systems which fill in garbage?). */
- if ((!secure_boot_enabled() || cmdline_len == 0) &&
- loaded_image->LoadOptionsSize > 0 &&
- ((char16_t *) loaded_image->LoadOptions)[0] > 0x1F) {
- cmdline_len = (loaded_image->LoadOptionsSize / sizeof(char16_t)) * sizeof(char);
- cmdline = cmdline_owned = xnew(char, cmdline_len);
-
- for (UINTN i = 0; i < cmdline_len; i++) {
- char16_t c = ((char16_t *) loaded_image->LoadOptions)[i];
- cmdline[i] = c > 0x1F && c < 0x7F ? c : ' '; /* convert non-printable and non_ASCII characters to spaces. */
- }
-
+ if (use_load_options(image, loaded_image, szs[UNIFIED_SECTION_CMDLINE] > 0, &cmdline)) {
/* Let's measure the passed kernel command line into the TPM. Note that this possibly
* duplicates what we already did in the boot menu, if that was already used. However, since
* we want the boot menu to support an EFI binary, and want to this stub to be usable from
* any boot menu, let's measure things anyway. */
m = false;
- (void) tpm_log_load_options(loaded_image->LoadOptions, &m);
+ (void) tpm_log_load_options(cmdline, &m);
parameters_measured = m;
+ } else if (szs[UNIFIED_SECTION_CMDLINE] > 0) {
+ cmdline = xstrn8_to_16(
+ (char *) loaded_image->ImageBase + addrs[UNIFIED_SECTION_CMDLINE],
+ szs[UNIFIED_SECTION_CMDLINE]);
+ mangle_stub_cmdline(cmdline);
}
export_variables(loaded_image);
log_error_stall(L"Error loading embedded devicetree: %r", err);
}
- err = linux_exec(image, cmdline, cmdline_len,
+ err = linux_exec(image, cmdline,
PHYSICAL_ADDRESS_TO_POINTER(linux_base), linux_size,
PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);
graphics_mode(false);
free(s);
}
+TEST(xstrn8_to_16) {
+ char16_t *s = NULL;
+
+ assert_se(xstrn8_to_16(NULL, 1) == NULL);
+ assert_se(xstrn8_to_16("a", 0) == NULL);
+
+ assert_se(s = xstrn8_to_16("", 1));
+ assert_se(streq16(s, u""));
+ free(s);
+
+ assert_se(s = xstrn8_to_16("1", 1));
+ assert_se(streq16(s, u"1"));
+ free(s);
+
+ assert_se(s = xstr8_to_16("abcxyzABCXYZ09 .,-_#*!\"§$%&/()=?`~"));
+ assert_se(streq16(s, u"abcxyzABCXYZ09 .,-_#*!\"§$%&/()=?`~"));
+ free(s);
+
+ assert_se(s = xstr8_to_16("ÿⱿ𝇉 😺"));
+ assert_se(streq16(s, u"ÿⱿ "));
+ free(s);
+
+ assert_se(s = xstrn8_to_16("¶¶", 3));
+ assert_se(streq16(s, u"¶"));
+ free(s);
+}
+
#define TEST_FNMATCH_ONE(pattern, haystack, expect) \
({ \
assert_se(fnmatch(pattern, haystack, 0) == (expect ? 0 : FNM_NOMATCH)); \
efivar_set(vendor, name, str, 0);
}
-static int utf8_to_16(const char *stra, char16_t *c) {
- char16_t unichar;
- UINTN len;
-
- assert(stra);
- assert(c);
-
- if (!(stra[0] & 0x80))
- len = 1;
- else if ((stra[0] & 0xe0) == 0xc0)
- len = 2;
- else if ((stra[0] & 0xf0) == 0xe0)
- len = 3;
- else if ((stra[0] & 0xf8) == 0xf0)
- len = 4;
- else if ((stra[0] & 0xfc) == 0xf8)
- len = 5;
- else if ((stra[0] & 0xfe) == 0xfc)
- len = 6;
- else
- return -1;
-
- switch (len) {
- case 1:
- unichar = stra[0];
- break;
- case 2:
- unichar = stra[0] & 0x1f;
- break;
- case 3:
- unichar = stra[0] & 0x0f;
- break;
- case 4:
- unichar = stra[0] & 0x07;
- break;
- case 5:
- unichar = stra[0] & 0x03;
- break;
- case 6:
- unichar = stra[0] & 0x01;
- break;
- }
-
- for (UINTN i = 1; i < len; i++) {
- if ((stra[i] & 0xc0) != 0x80)
- return -1;
- unichar <<= 6;
- unichar |= stra[i] & 0x3f;
- }
-
- *c = unichar;
- return len;
-}
-
-char16_t *xstra_to_str(const char *stra) {
- UINTN strlen;
- UINTN len;
- UINTN i;
- char16_t *str;
+void convert_efi_path(char16_t *path) {
+ assert(path);
- assert(stra);
+ for (size_t i = 0, fixed = 0;; i++) {
+ /* Fix device path node separator. */
+ path[fixed] = (path[i] == '/') ? '\\' : path[i];
- len = strlen8(stra);
- str = xnew(char16_t, len + 1);
-
- strlen = 0;
- i = 0;
- while (i < len) {
- int utf8len;
-
- utf8len = utf8_to_16(stra + i, str + strlen);
- if (utf8len <= 0) {
- /* invalid utf8 sequence, skip the garbage */
- i++;
+ /* Double '\' is not allowed in EFI file paths. */
+ if (fixed > 0 && path[fixed - 1] == '\\' && path[fixed] == '\\')
continue;
- }
- strlen++;
- i += utf8len;
+ if (path[i] == '\0')
+ break;
+
+ fixed++;
}
- str[strlen] = '\0';
- return str;
}
-char16_t *xstra_to_path(const char *stra) {
- char16_t *str;
- UINTN strlen;
- UINTN len;
- UINTN i;
-
- assert(stra);
-
- len = strlen8(stra);
- str = xnew(char16_t, len + 2);
-
- str[0] = '\\';
- strlen = 1;
- i = 0;
- while (i < len) {
- int utf8len;
-
- utf8len = utf8_to_16(stra + i, str + strlen);
- if (utf8len <= 0) {
- /* invalid utf8 sequence, skip the garbage */
- i++;
- continue;
- }
-
- if (str[strlen] == '/')
- str[strlen] = '\\';
- if (str[strlen] == '\\' && str[strlen-1] == '\\') {
- /* skip double slashes */
- i += utf8len;
- continue;
- }
+char16_t *xstr8_to_path(const char *str8) {
+ assert(str8);
+ char16_t *path = xstr8_to_16(str8);
+ convert_efi_path(path);
+ return path;
+}
- strlen++;
- i += utf8len;
- }
- str[strlen] = '\0';
- return str;
+void mangle_stub_cmdline(char16_t *cmdline) {
+ for (; *cmdline != '\0'; cmdline++)
+ /* Convert ASCII control characters to spaces. */
+ if (*cmdline <= 0x1F)
+ *cmdline = ' ';
}
EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, UINTN off, UINTN size, char **ret, UINTN *ret_size) {
EFI_STATUS efivar_get_uint64_le(const EFI_GUID *vendor, const char16_t *name, uint64_t *ret);
EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const char16_t *name, bool *ret);
-char16_t *xstra_to_path(const char *stra);
-char16_t *xstra_to_str(const char *stra);
+void convert_efi_path(char16_t *path);
+char16_t *xstr8_to_path(const char *stra);
+void mangle_stub_cmdline(char16_t *cmdline);
EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, UINTN off, UINTN size, char **content, UINTN *content_size);
assert(ret_vmm_dev);
assert(ret_vmm_dir);
+ /* Make sure all file systems have been initialized. Only do this in VMs as this is slow
+ * on some real firmwares. */
+ (void) reconnect_all_drivers();
+
/* find all file system handles */
err = BS->LocateHandleBuffer(ByProtocol, &FileSystemProtocol, NULL, &n_handles, &handles);
if (err != EFI_SUCCESS)
uint64_t io_input_bps, io_output_bps;
} Group;
+/* Counted objects, enum order matters */
+typedef enum PidsCount {
+ COUNT_USERSPACE_PROCESSES, /* least */
+ COUNT_ALL_PROCESSES,
+ COUNT_PIDS, /* most, requires pids controller */
+} PidsCount;
+
static unsigned arg_depth = 3;
static unsigned arg_iterations = UINT_MAX;
static bool arg_batch = false;
static bool arg_recursive = true;
static bool arg_recursive_unset = false;
-static enum {
- COUNT_PIDS,
- COUNT_USERSPACE_PROCESSES,
- COUNT_ALL_PROCESSES,
-} arg_count = COUNT_PIDS;
+static PidsCount arg_count = COUNT_PIDS;
static enum {
ORDER_PATH,
usec_t last_refresh = 0;
bool quit = false, immediate_refresh = false;
_cleanup_free_ char *root = NULL;
+ PidsCount possible_count;
CGroupMask mask;
int r;
if (r < 0)
return log_error_errno(r, "Failed to determine supported controllers: %m");
- arg_count = (mask & CGROUP_MASK_PIDS) ? COUNT_PIDS : COUNT_USERSPACE_PROCESSES;
+ /* honor user selection unless pids controller is unavailable */
+ possible_count = (mask & CGROUP_MASK_PIDS) ? COUNT_PIDS : COUNT_ALL_PROCESSES;
+ arg_count = MIN(possible_count, arg_count);
if (arg_recursive_unset && arg_count == COUNT_PIDS)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
sd_bus_message *message,
Manager *m,
int (*call)(LookupScope scope, UnitFileFlags flags, const char *root_dir, char *files[], InstallChange **changes, size_t *n_changes),
+ bool carries_install_info,
sd_bus_error *error) {
_cleanup_strv_free_ char **l = NULL;
if (r < 0)
return r;
- if (sd_bus_message_is_method_call(message, NULL, "DisableUnitFilesWithFlags")) {
+ if (sd_bus_message_is_method_call(message, NULL, "DisableUnitFilesWithFlags") ||
+ sd_bus_message_is_method_call(message, NULL, "DisableUnitFilesWithFlagsAndInstallInfo")) {
uint64_t raw_flags;
r = sd_bus_message_read(message, "t", &raw_flags);
if (r < 0)
return install_error(error, r, changes, n_changes);
- return reply_install_changes_and_free(m, message, -1, changes, n_changes, error);
+ return reply_install_changes_and_free(m, message, carries_install_info ? r : -1, changes, n_changes, error);
}
static int method_disable_unit_files_with_flags(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_disable_unit_files_generic(message, userdata, unit_file_disable, error);
+ return method_disable_unit_files_generic(message, userdata, unit_file_disable, /* carries_install_info = */ false, error);
+}
+
+static int method_disable_unit_files_with_flags_and_install_info(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return method_disable_unit_files_generic(message, userdata, unit_file_disable, /* carries_install_info = */ true, error);
}
static int method_disable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_disable_unit_files_generic(message, userdata, unit_file_disable, error);
+ return method_disable_unit_files_generic(message, userdata, unit_file_disable, /* carries_install_info = */ false, error);
}
static int method_unmask_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_disable_unit_files_generic(message, userdata, unit_file_unmask, error);
+ return method_disable_unit_files_generic(message, userdata, unit_file_unmask, /* carries_install_info = */ false, error);
}
static int method_revert_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
SD_BUS_RESULT("a(sss)", changes),
method_disable_unit_files_with_flags,
SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("DisableUnitFilesWithFlagsAndInstallInfo",
+ SD_BUS_ARGS("as", files, "t", flags),
+ SD_BUS_RESULT("b", carries_install_info, "a(sss)", changes),
+ method_disable_unit_files_with_flags_and_install_info,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("ReenableUnitFiles",
SD_BUS_ARGS("as", files, "b", runtime, "b", force),
SD_BUS_RESULT("b", carries_install_info, "a(sss)", changes),
#include "bus-get-properties.h"
#include "dbus-cgroup.h"
#include "dbus-kill.h"
+#include "dbus-manager.h"
#include "dbus-scope.h"
#include "dbus-unit.h"
#include "dbus-util.h"
}
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, scope_result, ScopeResult);
+static BUS_DEFINE_SET_TRANSIENT_PARSE(oom_policy, OOMPolicy, oom_policy_from_string);
const sd_bus_vtable bus_scope_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Scope, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Scope, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RuntimeRandomizedExtraUSec", "t", bus_property_get_usec, offsetof(Scope, runtime_rand_extra_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("OOMPolicy", "s", bus_property_get_oom_policy, offsetof(Scope, oom_policy), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_SIGNAL("RequestStop", NULL, 0),
SD_BUS_METHOD("Abandon", NULL, NULL, bus_scope_method_abandon, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
if (streq(name, "RuntimeRandomizedExtraUSec"))
return bus_set_transient_usec(u, name, &s->runtime_rand_extra_usec, message, flags, error);
+ if (streq(name, "OOMPolicy"))
+ return bus_set_transient_oom_policy(u, name, &s->oom_policy, message, flags, error);
+
if (streq(name, "PIDs")) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
unsigned n = 0;
assert(d);
device_unset_sysfs(d);
+ d->deserialized_sysfs = mfree(d->deserialized_sysfs);
d->wants_property = strv_free(d->wants_property);
d->path = mfree(d->path);
}
* 1. MANAGER_IS_RUNNING() == false
* 2. enumerate devices: manager_enumerate() -> device_enumerate()
* Device.enumerated_found is set.
- * 3. deserialize devices: manager_deserialize() -> device_deserialize()
+ * 3. deserialize devices: manager_deserialize() -> device_deserialize_item()
* Device.deserialize_state and Device.deserialized_found are set.
* 4. coldplug devices: manager_coldplug() -> device_coldplug()
* deserialized properties are copied to the main properties.
*
* - On switch-root, the udev database may be cleared, except for devices with sticky bit, i.e.
* OPTIONS="db_persist". Hence, almost no devices are enumerated in the step 2. However, in
- * general, we have several serialized devices. So, DEVICE_FOUND_UDEV bit in the deserialized_found
- * must be ignored, as udev rules in initrd and the main system are often different. If the
- * deserialized state is DEVICE_PLUGGED, we need to downgrade it to DEVICE_TENTATIVE. Unlike the
- * other starting mode, MANAGER_IS_SWITCHING_ROOT() is true when device_coldplug() and
- * device_catchup() are called. Hence, let's conditionalize the operations by using the
- * flag. After switch-root, systemd-udevd will (re-)process all devices, and the Device.found and
- * Device.state will be adjusted.
+ * general, we have several serialized devices. So, DEVICE_FOUND_UDEV bit in the
+ * Device.deserialized_found must be ignored, as udev rules in initrd and the main system are often
+ * different. If the deserialized state is DEVICE_PLUGGED, we need to downgrade it to
+ * DEVICE_TENTATIVE. Unlike the other starting mode, MANAGER_IS_SWITCHING_ROOT() is true when
+ * device_coldplug() and device_catchup() are called. Hence, let's conditionalize the operations by
+ * using the flag. After switch-root, systemd-udevd will (re-)process all devices, and the
+ * Device.found and Device.state will be adjusted.
*
- * - On reload or reexecute, we can trust enumerated_found, deserialized_found, and deserialized_state.
- * Of course, deserialized parameters may be outdated, but the unit state can be adjusted later by
- * device_catchup() or uevents. */
+ * - On reload or reexecute, we can trust Device.enumerated_found, Device.deserialized_found, and
+ * Device.deserialized_state. Of course, deserialized parameters may be outdated, but the unit
+ * state can be adjusted later by device_catchup() or uevents. */
if (MANAGER_IS_SWITCHING_ROOT(m) &&
!FLAGS_SET(d->enumerated_found, DEVICE_FOUND_UDEV)) {
- found &= ~DEVICE_FOUND_UDEV; /* ignore DEVICE_FOUND_UDEV bit */
+
+ /* The device has not been enumerated. On switching-root, such situation is natural. See the
+ * above comment. To prevent problematic state transition active → dead → active, let's
+ * drop the DEVICE_FOUND_UDEV flag and downgrade state to DEVICE_TENTATIVE(activating). See
+ * issue #12953 and #23208. */
+ found &= ~DEVICE_FOUND_UDEV;
if (state == DEVICE_PLUGGED)
- state = DEVICE_TENTATIVE; /* downgrade state */
+ state = DEVICE_TENTATIVE;
+
+ /* Also check the validity of the device syspath. Without this check, if the device was
+ * removed while switching root, it would never go to inactive state, as both Device.found
+ * and Device.enumerated_found do not have the DEVICE_FOUND_UDEV flag, so device_catchup() in
+ * device_update_found_one() does nothing in most cases. See issue #25106. Note that the
+ * syspath field is only serialized when systemd is sufficiently new and the device has been
+ * already processed by udevd. */
+ if (d->deserialized_sysfs) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+
+ if (sd_device_new_from_syspath(&dev, d->deserialized_sysfs) < 0)
+ state = DEVICE_DEAD;
+ }
}
if (d->found == found && d->state == state)
assert(f);
assert(fds);
+ if (d->sysfs)
+ (void) serialize_item(f, "sysfs", d->sysfs);
+
if (d->path)
(void) serialize_item(f, "path", d->path);
assert(value);
assert(fds);
- if (streq(key, "path")) {
+ if (streq(key, "sysfs")) {
+ if (!d->deserialized_sysfs) {
+ d->deserialized_sysfs = strdup(value);
+ if (!d->deserialized_sysfs)
+ log_oom_debug();
+ }
+
+ } else if (streq(key, "path")) {
if (!d->path) {
d->path = strdup(value);
if (!d->path)
struct Device {
Unit meta;
- char *sysfs;
+ char *sysfs, *deserialized_sysfs;
char *path; /* syspath, device node, alias, or devlink */
/* In order to be able to distinguish dependencies on different device nodes we might end up creating multiple
#if BUMP_PROC_SYS_FS_FILE_MAX
/* The maximum the kernel allows for this since 5.2 is LONG_MAX, use that. (Previously things were
* different, but the operation would fail silently.) */
- r = sysctl_writef("fs/file-max", "%li\n", LONG_MAX);
+ r = sysctl_write("fs/file-max", LONG_MAX_STR);
if (r < 0)
- log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to bump fs.file-max, ignoring: %m");
+ log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING,
+ r, "Failed to bump fs.file-max, ignoring: %m");
#endif
#if BUMP_PROC_SYS_FS_NR_OPEN
break;
}
- r = sysctl_writef("fs/nr_open", "%i\n", v);
+ r = sysctl_writef("fs/nr_open", "%i", v);
if (r == -EINVAL) {
log_debug("Couldn't write fs.nr_open as %i, halving it.", v);
v /= 2;
if (v >= DEFAULT_UNIX_MAX_DGRAM_QLEN)
return 0;
- r = write_string_filef("/proc/sys/net/unix/max_dgram_qlen", WRITE_STRING_FILE_DISABLE_BUFFER,
- "%lu", DEFAULT_UNIX_MAX_DGRAM_QLEN);
+ r = sysctl_write("net/unix/max_dgram_qlen", STRINGIFY(DEFAULT_UNIX_MAX_DGRAM_QLEN));
if (r < 0)
return log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to bump AF_UNIX datagram queue length, ignoring: %m");
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_FSCK |
DISSECT_IMAGE_USR_NO_ROOT |
- DISSECT_IMAGE_GROWFS;
+ DISSECT_IMAGE_GROWFS |
+ DISSECT_IMAGE_ADD_PARTITION_DEVICES |
+ DISSECT_IMAGE_PIN_PARTITION_DEVICES;
size_t n_mounts;
int r;
s->timeout_stop_usec = u->manager->default_timeout_stop_usec;
u->ignore_on_isolate = true;
s->user = s->group = NULL;
+ s->oom_policy = _OOM_POLICY_INVALID;
}
static void scope_done(Unit *u) {
if (r < 0)
return r;
+ if (s->oom_policy < 0)
+ s->oom_policy = s->cgroup_context.delegate ? OOM_CONTINUE : UNIT(s)->manager->default_oom_policy;
+
+ s->cgroup_context.memory_oom_group = s->oom_policy == OOM_KILL;
+
return scope_add_default_dependencies(s);
}
"%sScope State: %s\n"
"%sResult: %s\n"
"%sRuntimeMaxSec: %s\n"
- "%sRuntimeRandomizedExtraSec: %s\n",
+ "%sRuntimeRandomizedExtraSec: %s\n"
+ "%sOOMPolicy: %s\n",
prefix, scope_state_to_string(s->state),
prefix, scope_result_to_string(s->result),
prefix, FORMAT_TIMESPAN(s->runtime_max_usec, USEC_PER_SEC),
- prefix, FORMAT_TIMESPAN(s->runtime_rand_extra_usec, USEC_PER_SEC));
+ prefix, FORMAT_TIMESPAN(s->runtime_rand_extra_usec, USEC_PER_SEC),
+ prefix, oom_policy_to_string(s->oom_policy));
cgroup_context_dump(UNIT(s), f, prefix);
kill_context_dump(&s->kill_context, f, prefix);
else
log_unit_debug(u, "Process of control group was killed by the OOM killer.");
- /* This will probably need to be modified when scope units get an oom-policy */
+ if (s->oom_policy == OOM_CONTINUE)
+ return;
+
switch (s->state) {
case SCOPE_START_CHOWN:
case SCOPE_RUNNING:
+ scope_enter_signal(s, SCOPE_STOP_SIGTERM, SCOPE_FAILURE_OOM_KILL);
+ break;
+
case SCOPE_STOP_SIGTERM:
scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_FAILURE_OOM_KILL);
break;
char *user;
char *group;
+
+ OOMPolicy oom_policy;
};
extern const UnitVTable scope_vtable;
r = service_spawn(s,
s->control_command,
s->timeout_start_usec,
- EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_CONTROL_CGROUP|EXEC_WRITE_CREDENTIALS,
+ EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_CONTROL_CGROUP,
&s->control_pid);
if (r < 0)
goto fail;
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_FSCK |
DISSECT_IMAGE_USR_NO_ROOT |
- DISSECT_IMAGE_GROWFS;
+ DISSECT_IMAGE_GROWFS |
+ DISSECT_IMAGE_PIN_PARTITION_DEVICES |
+ DISSECT_IMAGE_ADD_PARTITION_DEVICES;
static VeritySettings arg_verity_settings = VERITY_SETTINGS_DEFAULT;
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
static PagerFlags arg_pager_flags = 0;
static int action_umount(const char *path) {
_cleanup_close_ int fd = -1;
- _cleanup_free_ char *canonical = NULL, *devname = NULL;
+ _cleanup_free_ char *canonical = NULL;
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
- dev_t devno;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
int r;
fd = chase_symlinks_and_open(path, NULL, 0, O_DIRECTORY, &canonical);
if (r < 0)
return log_error_errno(r, "Failed to determine whether '%s' is a mount point: %m", canonical);
- r = fd_get_whole_disk(fd, /*backing=*/ true, &devno);
- if (r < 0)
- return log_error_errno(r, "Failed to find backing block device for '%s': %m", canonical);
+ r = block_device_new_from_fd(fd, BLOCK_DEVICE_LOOKUP_WHOLE_DISK | BLOCK_DEVICE_LOOKUP_BACKING, &dev);
+ if (r < 0) {
+ _cleanup_close_ int usr_fd = -1;
+
+ /* The command `systemd-dissect --mount` expects that the image at least has the root or /usr
+ * partition. If it does not have the root partition, then we mount the /usr partition on a
+ * tmpfs. Hence, let's try to find the backing block device through the /usr partition. */
- r = devname_from_devnum(S_IFBLK, devno, &devname);
+ usr_fd = openat(fd, "usr", O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW);
+ if (usr_fd < 0)
+ return log_error_errno(errno, "Failed to open '%s/usr': %m", canonical);
+
+ r = block_device_new_from_fd(usr_fd, BLOCK_DEVICE_LOOKUP_WHOLE_DISK | BLOCK_DEVICE_LOOKUP_BACKING, &dev);
+ }
if (r < 0)
- return log_error_errno(r, "Failed to get devname of block device " DEVNUM_FORMAT_STR ": %m",
- DEVNUM_FORMAT_VAL(devno));
+ return log_error_errno(r, "Failed to find backing block device for '%s': %m", canonical);
- r = loop_device_open_from_path(devname, 0, LOCK_EX, &d);
+ r = loop_device_open(dev, 0, LOCK_EX, &d);
if (r < 0)
- return log_error_errno(r, "Failed to open loop device '%s': %m", devname);
+ return log_device_error_errno(dev, r, "Failed to open loopback block device: %m");
/* We've locked the loop device, now we're ready to unmount. To allow the unmount to succeed, we have
* to close the O_PATH fd we opened earlier. */
_cleanup_free_ char *unit = NULL, *p = NULL;
_cleanup_fclose_ FILE *f = NULL;
- const char *opt = "noauto";
int r;
assert(id);
assert(where);
assert(description);
- if (options)
- opt = strjoina(options, ",", opt);
-
r = add_mount(id,
what,
where,
fstype,
rw,
growfs,
- opt,
+ options,
description,
NULL);
if (r < 0)
NULL, NULL,
DISSECT_IMAGE_GPT_ONLY|
DISSECT_IMAGE_USR_NO_ROOT,
+ /* NB! Unlike most other places where we dissect block devices we do not use
+ * DISSECT_IMAGE_ADD_PARTITION_DEVICES here: we want that the kernel finds the
+ * devices, and udev probes them before we mount them via .mount units much later
+ * on. And thus we also don't set DISSECT_IMAGE_PIN_PARTITION_DEVICES here, because
+ * we don't actually mount anything immediately. */
&m);
if (r == -ENOPKG) {
log_debug_errno(r, "No suitable partition table found, ignoring.");
char *userdb_service;
EVP_PKEY *private_key; /* actually a pair of private and public key */
- Hashmap *public_keys; /* key name [char*] → publick key [EVP_PKEY*] */
+ Hashmap *public_keys; /* key name [char*] → public key [EVP_PKEY*] */
RebalanceState rebalance_state;
usec_t rebalance_interval_usec;
errno = 0;
r = blkid_probe_set_device(b, fd, 0, 0);
if (r != 0)
- return errno > 0 ? -errno : -ENOMEM;
+ return errno_or_else(ENOMEM);
(void) blkid_probe_enable_superblocks(b, 1);
(void) blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_UUID);
errno = 0;
r = blkid_do_safeprobe(b);
- if (IN_SET(r, -2, 1)) /* nothing found or ambiguous result */
+ if (r == _BLKID_SAFEPROBE_ERROR)
+ return errno_or_else(EIO);
+ if (IN_SET(r, _BLKID_SAFEPROBE_AMBIGUOUS, _BLKID_SAFEPROBE_NOT_FOUND))
return -ENOPKG;
- if (r != 0)
- return errno > 0 ? -errno : -EIO;
+
+ assert(r == _BLKID_SAFEPROBE_FOUND);
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
if (!fstype)
errno = 0;
r = blkid_probe_set_device(b, fd, 0, 0);
if (r != 0)
- return errno > 0 ? -errno : -ENOMEM;
+ return errno_or_else(ENOMEM);
(void) blkid_probe_enable_superblocks(b, 1);
(void) blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
errno = 0;
r = blkid_do_safeprobe(b);
- if (IN_SET(r, -2, 1)) /* nothing found or ambiguous result */
+ if (r == _BLKID_SAFEPROBE_ERROR)
+ return errno_or_else(EIO);
+ if (IN_SET(r, _BLKID_SAFEPROBE_AMBIGUOUS, _BLKID_SAFEPROBE_NOT_FOUND))
return -ENOPKG;
- if (r != 0)
- return errno > 0 ? -errno : -EIO;
+
+ assert(r == _BLKID_SAFEPROBE_FOUND);
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
if (streq_ptr(fstype, "crypto_LUKS")) {
errno = 0;
pl = blkid_probe_get_partitions(b);
if (!pl)
- return errno > 0 ? -errno : -ENOMEM;
+ return errno_or_else(ENOMEM);
errno = 0;
n = blkid_partlist_numof_partitions(pl);
if (n < 0)
- return errno > 0 ? -errno : -EIO;
+ return errno_or_else(EIO);
for (int i = 0; i < n; i++) {
- blkid_partition pp;
sd_id128_t id = SD_ID128_NULL;
- const char *sid;
+ blkid_partition pp;
errno = 0;
pp = blkid_partlist_get_partition(pl, i);
if (!pp)
- return errno > 0 ? -errno : -EIO;
+ return errno_or_else(EIO);
if (sd_id128_string_equal(blkid_partition_get_type_string(pp), SD_GPT_USER_HOME) <= 0)
continue;
if (!streq_ptr(blkid_partition_get_name(pp), label))
continue;
- sid = blkid_partition_get_uuid(pp);
- if (sid) {
- r = sd_id128_from_string(sid, &id);
- if (r < 0)
- log_debug_errno(r, "Couldn't parse partition UUID %s, weird: %m", sid);
- if (!sd_id128_is_null(partition_uuid) && !sd_id128_equal(id, partition_uuid))
- continue;
- }
+ r = blkid_partition_get_uuid_id128(pp, &id);
+ if (r < 0)
+ log_debug_errno(r, "Failed to read partition UUID, ignoring: %m");
+ else if (!sd_id128_is_null(partition_uuid) && !sd_id128_equal(id, partition_uuid))
+ continue;
if (found)
return -ENOPKG;
_cleanup_(fdisk_unref_partitionp) struct fdisk_partition *p = NULL, *q = NULL;
_cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *t = NULL;
_cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
- _cleanup_free_ char *path = NULL, *disk_uuid_as_string = NULL;
+ _cleanup_free_ char *disk_uuid_as_string = NULL;
uint64_t offset, size, first_lba, start, last_lba, end;
sd_id128_t disk_uuid;
int r;
if (r < 0)
return log_error_errno(r, "Failed to initialize partition type: %m");
- c = fdisk_new_context();
- if (!c)
- return log_oom();
-
- if (asprintf(&path, "/proc/self/fd/%i", fd) < 0)
- return log_oom();
-
- r = fdisk_assign_device(c, path, 0);
+ r = fdisk_new_context_fd(fd, /* read_only= */ false, &c);
if (r < 0)
return log_error_errno(r, "Failed to open device: %m");
_cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
_cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL;
- _cleanup_free_ char *path = NULL, *disk_uuid_as_string = NULL;
+ _cleanup_free_ char *disk_uuid_as_string = NULL;
struct fdisk_partition *found = NULL;
sd_id128_t disk_uuid;
size_t n_partitions;
return 0;
}
- c = fdisk_new_context();
- if (!c)
- return log_oom();
-
- if (asprintf(&path, "/proc/self/fd/%i", fd) < 0)
- return log_oom();
-
- r = fdisk_assign_device(c, path, 0);
+ r = fdisk_new_context_fd(fd, /* read_only= */ false, &c);
if (r < 0)
return log_error_errno(r, "Failed to open device: %m");
_cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
_cleanup_free_ void *two_zero_lbas = NULL;
- _cleanup_free_ char *path = NULL;
ssize_t n;
int r;
if (n != 1024)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write while wiping partition table.");
- c = fdisk_new_context();
- if (!c)
- return log_oom();
-
- if (asprintf(&path, "/proc/self/fd/%i", fd) < 0)
- return log_oom();
-
- r = fdisk_assign_device(c, path, 0);
+ r = fdisk_new_context_fd(fd, /* read_only= */ false, &c);
if (r < 0)
return log_error_errno(r, "Failed to open device: %m");
#include "fd-util.h"
#include "hexdecoct.h"
#include "io-util.h"
+#include "journal-internal.h"
#include "journald-audit.h"
#include "missing_audit.h"
#include "string-util.h"
}
if (!NLMSG_OK(nl, buffer_size)) {
- log_ratelimit_error(JOURNALD_LOG_RATELIMIT, "Audit netlink message truncated.");
+ log_ratelimit_error(JOURNAL_LOG_RATELIMIT, "Audit netlink message truncated.");
return;
}
#include "fileio.h"
#include "fs-util.h"
#include "io-util.h"
+#include "journal-internal.h"
#include "journal-util.h"
#include "journald-context.h"
#include "parse-util.h"
r = client_context_acquire(s, ucred.pid, &ucred, NULL, 0, NULL, &s->my_context);
if (r < 0)
- log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to acquire our own context, ignoring: %m");
}
r = client_context_acquire(s, 1, NULL, NULL, 0, NULL, &s->pid1_context);
if (r < 0)
- log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to acquire PID1's context, ignoring: %m");
}
#include "format-util.h"
#include "fs-util.h"
#include "io-util.h"
+#include "journal-internal.h"
#include "journald-kmsg.h"
#include "journald-server.h"
#include "journald-syslog.h"
if (l < 0) {
/* Old kernels who don't allow reading from /dev/kmsg
* return EINVAL when we try. So handle this cleanly,
- * but don' try to ever read from it again. */
+ * but don't try to ever read from it again. */
if (errno == EINVAL) {
s->dev_kmsg_event_source = sd_event_source_unref(s->dev_kmsg_event_source);
return 0;
if (ERRNO_IS_TRANSIENT(errno) || errno == EPIPE)
return 0;
- return log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT, "Failed to read from /dev/kmsg: %m");
+ return log_ratelimit_error_errno(errno, JOURNAL_LOG_RATELIMIT, "Failed to read from /dev/kmsg: %m");
}
dev_kmsg_record(s, buffer, l);
assert(fd == s->dev_kmsg_fd);
if (revents & EPOLLERR)
- log_ratelimit_warning(JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning(JOURNAL_LOG_RATELIMIT,
"/dev/kmsg buffer overrun, some messages lost.");
if (!(revents & EPOLLIN))
#include "fs-util.h"
#include "io-util.h"
#include "journal-importer.h"
+#include "journal-internal.h"
#include "journal-util.h"
#include "journald-console.h"
#include "journald-kmsg.h"
if (ucred && pid_is_valid(ucred->pid)) {
r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context);
if (r < 0)
- log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m",
ucred->pid);
}
r = fd_get_path(fd, &k);
if (r < 0) {
- log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT,
"readlink(/proc/self/fd/%i) failed: %m", fd);
return;
}
e = PATH_STARTSWITH_SET(k, "/dev/shm/", "/tmp/", "/var/tmp/");
if (!e) {
- log_ratelimit_error(JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_error(JOURNAL_LOG_RATELIMIT,
"Received file outside of allowed directories. Refusing.");
return;
}
if (!filename_is_valid(e)) {
- log_ratelimit_error(JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_error(JOURNAL_LOG_RATELIMIT,
"Received file in subdirectory of allowed directories. Refusing.");
return;
}
}
if (fstat(fd, &st) < 0) {
- log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_error_errno(errno, JOURNAL_LOG_RATELIMIT,
"Failed to stat passed file, ignoring: %m");
return;
}
if (!S_ISREG(st.st_mode)) {
- log_ratelimit_error(JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_error(JOURNAL_LOG_RATELIMIT,
"File passed is not regular. Ignoring.");
return;
}
/* When !sealed, set a lower memory limit. We have to read the file,
* effectively doubling memory use. */
if (st.st_size > ENTRY_SIZE_MAX / (sealed ? 1 : 2)) {
- log_ratelimit_error(JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_error(JOURNAL_LOG_RATELIMIT,
"File passed too large (%"PRIu64" bytes). Ignoring.",
(uint64_t) st.st_size);
return;
ps = PAGE_ALIGN(st.st_size);
p = mmap(NULL, ps, PROT_READ, MAP_PRIVATE, fd, 0);
if (p == MAP_FAILED) {
- log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_error_errno(errno, JOURNAL_LOG_RATELIMIT,
"Failed to map memfd, ignoring: %m");
return;
}
ssize_t n;
if (fstatvfs(fd, &vfs) < 0) {
- log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_error_errno(errno, JOURNAL_LOG_RATELIMIT,
"Failed to stat file system of passed file, not processing it: %m");
return;
}
* https://github.com/systemd/systemd/issues/1822
*/
if (vfs.f_flag & ST_MANDLOCK) {
- log_ratelimit_error(JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_error(JOURNAL_LOG_RATELIMIT,
"Received file descriptor from file system with mandatory locking enabled, not processing it.");
return;
}
* and so is SMB. */
r = fd_nonblock(fd, true);
if (r < 0) {
- log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to make fd non-blocking, not processing it: %m");
return;
}
n = pread(fd, p, st.st_size, 0);
if (n < 0)
- log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_error_errno(errno, JOURNAL_LOG_RATELIMIT,
"Failed to read file, ignoring: %m");
else if (n > 0)
server_process_native_message(s, p, n, ucred, tv, label, label_len);
d = opendir(path);
if (!d)
return log_ratelimit_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
- errno, JOURNALD_LOG_RATELIMIT, "Failed to open %s: %m", path);
+ errno, JOURNAL_LOG_RATELIMIT, "Failed to open %s: %m", path);
if (fstatvfs(dirfd(d), &ss) < 0)
- return log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT,
+ return log_ratelimit_error_errno(errno, JOURNAL_LOG_RATELIMIT,
"Failed to fstatvfs(%s): %m", path);
*ret_free = ss.f_bsize * ss.f_bavail;
r = fd_add_uid_acl_permission(f->file->fd, uid, ACL_READ);
if (r < 0)
- log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to set ACL on %s, ignoring: %m", f->file->path);
#endif
}
patch_min_use(&s->system_storage);
} else {
if (!IN_SET(r, -ENOENT, -EROFS))
- log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to open system journal: %m");
r = 0;
r = open_journal(s, false, fn, O_RDWR, false, &s->runtime_storage.metrics, &s->runtime_journal);
if (r < 0) {
if (r != -ENOENT)
- log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to open runtime journal: %m");
r = 0;
r = open_journal(s, true, fn, O_RDWR|O_CREAT, false, &s->runtime_storage.metrics, &s->runtime_journal);
if (r < 0)
- return log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ return log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to open runtime journal: %m");
}
r = managed_journal_file_rotate(f, s->mmap, file_flags, s->compress.threshold_bytes, s->deferred_closes);
if (r < 0) {
if (*f)
- return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+ return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to rotate %s: %m", (*f)->file->path);
else
- return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+ return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to create new %s journal: %m", name);
}
if (errno == ENOENT)
return 0;
- return log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT,
+ return log_ratelimit_error_errno(errno, JOURNAL_LOG_RATELIMIT,
"Failed to open %s: %m", s->system_storage.path);
}
de = readdir_no_dot(d);
if (!de) {
if (errno != 0)
- log_ratelimit_warning_errno(errno, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning_errno(errno, JOURNAL_LOG_RATELIMIT,
"Failed to enumerate %s, ignoring: %m",
s->system_storage.path);
fd = openat(dirfd(d), de->d_name, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
if (fd < 0) {
log_ratelimit_full_errno(IN_SET(errno, ELOOP, ENOENT) ? LOG_DEBUG : LOG_WARNING,
- errno, JOURNALD_LOG_RATELIMIT,
+ errno, JOURNAL_LOG_RATELIMIT,
"Failed to open journal file '%s' for rotation: %m", full);
continue;
}
NULL,
&f);
if (r < 0) {
- log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to read journal file %s for rotation, trying to move it out of the way: %m",
full);
r = journal_file_dispose(dirfd(d), de->d_name);
if (r < 0)
- log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to move %s out of the way, ignoring: %m",
full);
else
if (s->system_journal) {
r = managed_journal_file_set_offline(s->system_journal, false);
if (r < 0)
- log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to sync system journal, ignoring: %m");
}
ORDERED_HASHMAP_FOREACH(f, s->user_journals) {
r = managed_journal_file_set_offline(f, false);
if (r < 0)
- log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to sync user journal, ignoring: %m");
}
if (s->sync_event_source) {
r = sd_event_source_set_enabled(s->sync_event_source, SD_EVENT_OFF);
if (r < 0)
- log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to disable sync timer source: %m");
}
storage->metrics.n_max_files, s->max_retention_usec,
&s->oldest_file_usec, verbose);
if (r < 0 && r != -ENOENT)
- log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to vacuum %s, ignoring: %m", storage->path);
cache_space_invalidate(&storage->space);
log_debug("%s: Allocation limit reached, rotating.", f->path);
return true;
+ case -EROFS: /* Read-only file system */
+ /* When appending an entry fails if shall_try_append_again returns true, the journal is
+ * rotated. If the FS is read-only, rotation will fail and s->system_journal will be set to
+ * NULL. After that, when find_journal will try to open the journal since s->system_journal
+ * will be NULL, it will open the runtime journal. */
+ log_ratelimit_warning(JOURNALD_LOG_RATELIMIT, "%s: Read-only file system, rotating.", f->path);
+ return true;
+
case -EIO: /* I/O error of some kind (mmap) */
- log_ratelimit_warning(JOURNALD_LOG_RATELIMIT, "%s: IO error, rotating.", f->path);
+ log_ratelimit_warning(JOURNAL_LOG_RATELIMIT, "%s: IO error, rotating.", f->path);
return true;
case -EHOSTDOWN: /* Other machine */
- log_ratelimit_info(JOURNALD_LOG_RATELIMIT, "%s: Journal file from other machine, rotating.", f->path);
+ log_ratelimit_info(JOURNAL_LOG_RATELIMIT, "%s: Journal file from other machine, rotating.", f->path);
return true;
case -EBUSY: /* Unclean shutdown */
- log_ratelimit_info(JOURNALD_LOG_RATELIMIT, "%s: Unclean shutdown, rotating.", f->path);
+ log_ratelimit_info(JOURNAL_LOG_RATELIMIT, "%s: Unclean shutdown, rotating.", f->path);
return true;
case -EPROTONOSUPPORT: /* Unsupported feature */
- log_ratelimit_info(JOURNALD_LOG_RATELIMIT, "%s: Unsupported feature, rotating.", f->path);
+ log_ratelimit_info(JOURNAL_LOG_RATELIMIT, "%s: Unsupported feature, rotating.", f->path);
return true;
case -EBADMSG: /* Corrupted */
case -ENODATA: /* Truncated */
case -ESHUTDOWN: /* Already archived */
- log_ratelimit_warning(JOURNALD_LOG_RATELIMIT, "%s: Journal file corrupted, rotating.", f->path);
+ log_ratelimit_warning(JOURNAL_LOG_RATELIMIT, "%s: Journal file corrupted, rotating.", f->path);
return true;
case -EIDRM: /* Journal file has been deleted */
- log_ratelimit_warning(JOURNALD_LOG_RATELIMIT, "%s: Journal file has been deleted, rotating.", f->path);
+ log_ratelimit_warning(JOURNAL_LOG_RATELIMIT, "%s: Journal file has been deleted, rotating.", f->path);
return true;
case -ETXTBSY: /* Journal file is from the future */
- log_ratelimit_warning(JOURNALD_LOG_RATELIMIT, "%s: Journal file is from the future, rotating.", f->path);
+ log_ratelimit_warning(JOURNAL_LOG_RATELIMIT, "%s: Journal file is from the future, rotating.", f->path);
return true;
case -EAFNOSUPPORT:
- log_ratelimit_warning(JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning(JOURNAL_LOG_RATELIMIT,
"%s: underlying file system does not support memory mapping or another required file system feature.",
f->path);
return false;
* to ensure that the entries in the journal files are strictly ordered by time, in order to ensure
* bisection works correctly. */
- log_ratelimit_info(JOURNALD_LOG_RATELIMIT, "Time jumped backwards, rotating.");
+ log_ratelimit_info(JOURNAL_LOG_RATELIMIT, "Time jumped backwards, rotating.");
rotate = true;
} else {
return;
if (journal_file_rotate_suggested(f->file, s->max_file_usec, LOG_INFO)) {
- log_ratelimit_info(JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_info(JOURNAL_LOG_RATELIMIT,
"%s: Journal header limits reached or header out-of-date, rotating.",
f->file->path);
rotate = true;
r = sd_journal_open(&j, SD_JOURNAL_RUNTIME_ONLY);
if (r < 0)
- return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+ return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to read runtime journal: %m");
sd_journal_set_data_threshold(j, 0);
r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
if (r < 0) {
- log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Can't read entry: %m");
+ log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT, "Can't read entry: %m");
goto finish;
}
continue;
if (!shall_try_append_again(s->system_journal->file, r)) {
- log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Can't write entry: %m");
+ log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT, "Can't write entry: %m");
goto finish;
}
- log_ratelimit_info(JOURNALD_LOG_RATELIMIT, "Rotating system journal.");
+ log_ratelimit_info(JOURNAL_LOG_RATELIMIT, "Rotating system journal.");
server_rotate(s);
server_vacuum(s, false);
if (!s->system_journal) {
- log_ratelimit_notice(JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_notice(JOURNAL_LOG_RATELIMIT,
"Didn't flush runtime journal since rotation of system journal wasn't successful.");
r = -EIO;
goto finish;
log_debug("Retrying write.");
r = journal_file_copy_entry(f, s->system_journal->file, o, f->current_offset);
if (r < 0) {
- log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Can't write entry: %m");
+ log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT, "Can't write entry: %m");
goto finish;
}
}
fn = strjoina(s->runtime_directory, "/flushed");
k = touch(fn);
if (k < 0)
- log_ratelimit_warning_errno(k, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning_errno(k, JOURNAL_LOG_RATELIMIT,
"Failed to touch %s, ignoring: %m", fn);
server_refresh_idle_timer(s);
fn = strjoina(s->runtime_directory, "/flushed");
if (unlink(fn) < 0 && errno != ENOENT)
- log_ratelimit_warning_errno(errno, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning_errno(errno, JOURNAL_LOG_RATELIMIT,
"Failed to unlink %s, ignoring: %m", fn);
server_refresh_idle_timer(s);
if (ERRNO_IS_TRANSIENT(n))
return 0;
if (n == -EXFULL) {
- log_ratelimit_warning(JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning(JOURNAL_LOG_RATELIMIT,
"Got message with truncated control data (too many fds sent?), ignoring.");
return 0;
}
- return log_ratelimit_error_errno(n, JOURNALD_LOG_RATELIMIT, "recvmsg() failed: %m");
+ return log_ratelimit_error_errno(n, JOURNAL_LOG_RATELIMIT, "recvmsg() failed: %m");
}
CMSG_FOREACH(cmsg, &msghdr)
if (n > 0 && n_fds == 0)
server_process_syslog_message(s, s->buffer, n, ucred, tv, label, label_len);
else if (n_fds > 0)
- log_ratelimit_warning(JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning(JOURNAL_LOG_RATELIMIT,
"Got file descriptors via syslog socket. Ignoring.");
} else if (fd == s->native_fd) {
else if (n == 0 && n_fds == 1)
server_process_native_file(s, fds[0], ucred, tv, label, label_len);
else if (n_fds > 0)
- log_ratelimit_warning(JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning(JOURNAL_LOG_RATELIMIT,
"Got too many file descriptors via native socket. Ignoring.");
} else {
if (n > 0 && n_fds == 0)
server_process_audit_message(s, s->buffer, n, ucred, &sa, msghdr.msg_namelen);
else if (n_fds > 0)
- log_ratelimit_warning(JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning(JOURNAL_LOG_RATELIMIT,
"Got file descriptors via audit socket. Ignoring.");
}
fn = strjoina(s->runtime_directory, "/rotated");
r = write_timestamp_file_atomic(fn, now(CLOCK_MONOTONIC));
if (r < 0)
- log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to write %s, ignoring: %m", fn);
}
fn = strjoina(s->runtime_directory, "/synced");
r = write_timestamp_file_atomic(fn, now(CLOCK_MONOTONIC));
if (r < 0)
- log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to write %s, ignoring: %m", fn);
return;
#include "time-util.h"
#include "varlink.h"
-#define JOURNALD_LOG_RATELIMIT ((const RateLimit) { .interval = 60 * USEC_PER_SEC, .burst = 3 })
-
typedef enum Storage {
STORAGE_AUTO,
STORAGE_VOLATILE,
#include "fileio.h"
#include "fs-util.h"
#include "io-util.h"
+#include "journal-internal.h"
#include "journald-console.h"
#include "journald-context.h"
#include "journald-kmsg.h"
r = fstat(s->fd, &st);
if (r < 0)
- return log_ratelimit_warning_errno(errno, JOURNALD_LOG_RATELIMIT,
+ return log_ratelimit_warning_errno(errno, JOURNAL_LOG_RATELIMIT,
"Failed to stat connected stream: %m");
/* We use device and inode numbers as identifier for the stream */
if (s->server->notify_event_source) {
r = sd_event_source_set_enabled(s->server->notify_event_source, SD_EVENT_ON);
if (r < 0)
- log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT, "Failed to enable notify event source: %m");
+ log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT, "Failed to enable notify event source: %m");
}
}
fail:
(void) unlink(s->state_file);
- return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT,
+ return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to save stream data %s: %m", s->state_file);
}
else if (pid_is_valid(s->ucred.pid)) {
r = client_context_acquire(s->server, s->ucred.pid, &s->ucred, s->label, strlen_ptr(s->label), s->unit_id, &s->context);
if (r < 0)
- log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to acquire client context, ignoring: %m");
}
/* line breaks by NUL, line max length or EOF are not permissible during the negotiation part of the protocol */
if (line_break != LINE_BREAK_NEWLINE && s->state != STDOUT_STREAM_RUNNING)
- return log_ratelimit_warning_errno(SYNTHETIC_ERRNO(EINVAL), JOURNALD_LOG_RATELIMIT,
+ return log_ratelimit_warning_errno(SYNTHETIC_ERRNO(EINVAL), JOURNAL_LOG_RATELIMIT,
"Control protocol line not properly terminated.");
switch (s->state) {
priority = syslog_parse_priority_and_facility(p);
if (priority < 0)
- return log_ratelimit_warning_errno(priority, JOURNALD_LOG_RATELIMIT,
+ return log_ratelimit_warning_errno(priority, JOURNAL_LOG_RATELIMIT,
"Failed to parse log priority line: %m");
s->priority = priority;
case STDOUT_STREAM_LEVEL_PREFIX:
r = parse_boolean(p);
if (r < 0)
- return log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ return log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to parse level prefix line: %m");
s->level_prefix = r;
case STDOUT_STREAM_FORWARD_TO_SYSLOG:
r = parse_boolean(p);
if (r < 0)
- return log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ return log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to parse forward to syslog line: %m");
s->forward_to_syslog = r;
case STDOUT_STREAM_FORWARD_TO_KMSG:
r = parse_boolean(p);
if (r < 0)
- return log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ return log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to parse copy to kmsg line: %m");
s->forward_to_kmsg = r;
case STDOUT_STREAM_FORWARD_TO_CONSOLE:
r = parse_boolean(p);
if (r < 0)
- return log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ return log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to parse copy to console line.");
s->forward_to_console = r;
if (ERRNO_IS_TRANSIENT(errno))
return 0;
- log_ratelimit_warning_errno(errno, JOURNALD_LOG_RATELIMIT, "Failed to read from stream: %m");
+ log_ratelimit_warning_errno(errno, JOURNAL_LOG_RATELIMIT, "Failed to read from stream: %m");
goto terminate;
}
cmsg_close_all(&msghdr);
r = sd_id128_randomize(&id);
if (r < 0)
- return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Failed to generate stream ID: %m");
+ return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT, "Failed to generate stream ID: %m");
stream = new(StdoutStream, 1);
if (!stream)
r = getpeercred(fd, &stream->ucred);
if (r < 0)
- return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Failed to determine peer credentials: %m");
+ return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT, "Failed to determine peer credentials: %m");
r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
if (r < 0)
if (mac_selinux_use()) {
r = getpeersec(fd, &stream->label);
if (r < 0 && r != -EOPNOTSUPP)
- (void) log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT, "Failed to determine peer security context: %m");
+ (void) log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT, "Failed to determine peer security context: %m");
}
(void) shutdown(fd, SHUT_WR);
r = sd_event_add_io(s->event, &stream->event_source, fd, EPOLLIN, stdout_stream_process, stream);
if (r < 0)
- return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Failed to add stream to event loop: %m");
+ return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT, "Failed to add stream to event loop: %m");
r = sd_event_source_set_priority(stream->event_source, SD_EVENT_PRIORITY_NORMAL+5);
if (r < 0)
- return log_ratelimit_error_errno(r, JOURNALD_LOG_RATELIMIT, "Failed to adjust stdout event source priority: %m");
+ return log_ratelimit_error_errno(r, JOURNAL_LOG_RATELIMIT, "Failed to adjust stdout event source priority: %m");
stream->fd = fd;
if (ERRNO_IS_ACCEPT_AGAIN(errno))
return 0;
- return log_ratelimit_error_errno(errno, JOURNALD_LOG_RATELIMIT, "Failed to accept stdout connection: %m");
+ return log_ratelimit_error_errno(errno, JOURNAL_LOG_RATELIMIT, "Failed to accept stdout connection: %m");
}
if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) {
#include "fd-util.h"
#include "format-util.h"
#include "io-util.h"
+#include "journal-internal.h"
#include "journald-console.h"
#include "journald-kmsg.h"
#include "journald-server.h"
if (ucred && pid_is_valid(ucred->pid)) {
r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context);
if (r < 0)
- log_ratelimit_warning_errno(r, JOURNALD_LOG_RATELIMIT,
+ log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m",
ucred->pid);
}
--- /dev/null
+#!/bin/sh
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# 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.
+#
+# 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
+# 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 <https://www.gnu.org/licenses/>.
+
+set -e
+
+COMMAND="${1:?}"
+KERNEL_VERSION="${2:?}"
+# shellcheck disable=SC2034
+ENTRY_DIR_ABS="$3"
+KERNEL_IMAGE="$4"
+
+[ "$KERNEL_INSTALL_LAYOUT" = "uki" ] || exit 0
+
+ENTRY_TOKEN="$KERNEL_INSTALL_ENTRY_TOKEN"
+BOOT_ROOT="$KERNEL_INSTALL_BOOT_ROOT"
+
+UKI_DIR="$BOOT_ROOT/EFI/Linux"
+
+case "$COMMAND" in
+ remove)
+ [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \
+ echo "Removing $UKI_DIR/$ENTRY_TOKEN-$KERNEL_VERSION*.efi"
+ exec rm -f \
+ "$UKI_DIR/$ENTRY_TOKEN-$KERNEL_VERSION.efi" \
+ "$UKI_DIR/$ENTRY_TOKEN-$KERNEL_VERSION+"*".efi"
+ ;;
+ add)
+ ;;
+ *)
+ exit 0
+ ;;
+esac
+
+if ! [ -d "$UKI_DIR" ]; then
+ echo "Error: entry directory '$UKI_DIR' does not exist" >&2
+ exit 1
+fi
+
+TRIES_FILE="${KERNEL_INSTALL_CONF_ROOT:-/etc/kernel}/tries"
+
+if [ -f "$TRIES_FILE" ]; then
+ read -r TRIES <"$TRIES_FILE"
+ if ! echo "$TRIES" | grep -q '^[0-9][0-9]*$'; then
+ echo "$TRIES_FILE does not contain an integer." >&2
+ exit 1
+ fi
+ UKI_FILE="$UKI_DIR/$ENTRY_TOKEN-$KERNEL_VERSION+$TRIES.efi"
+else
+ UKI_FILE="$UKI_DIR/$ENTRY_TOKEN-$KERNEL_VERSION.efi"
+fi
+
+# If there is a UKI named uki.efi on the staging area use that, if not use what
+# was passed in as $KERNEL_IMAGE but insist it has a .efi extension
+if [ -f "$KERNEL_INSTALL_STAGING_AREA/uki.efi" ]; then
+ [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "Installing $KERNEL_INSTALL_STAGING_AREA/uki.efi"
+ install -m 0644 "$KERNEL_INSTALL_STAGING_AREA/uki.efi" "$UKI_FILE" || {
+ echo "Error: could not copy '$KERNEL_INSTALL_STAGING_AREA/uki.efi' to '$UKI_FILE'." >&2
+ exit 1
+ }
+elif [ -n "$KERNEL_IMAGE" ]; then
+ [ -f "$KERNEL_IMAGE" ] || {
+ echo "Error: UKI '$KERNEL_IMAGE' not a file." >&2
+ exit 1
+ }
+ [ "$KERNEL_IMAGE" != "${KERNEL_IMAGE%*.efi}.efi" ] && {
+ echo "Error: $KERNEL_IMAGE is missing .efi suffix." >&2
+ exit 1
+ }
+ [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "Installing $KERNEL_IMAGE"
+ install -m 0644 "$KERNEL_IMAGE" "$UKI_FILE" || {
+ echo "Error: could not copy '$KERNEL_IMAGE' to '$UKI_FILE'." >&2
+ exit 1
+ }
+else
+ [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "No UKI available. Nothing to do."
+ exit 0
+fi
+chown root:root "$UKI_FILE" || :
+
+exit 0
install_mode : 'rwxr-xr-x',
install_dir : kernelinstalldir)
+uki_copy_install = files('90-uki-copy.install')
+
if want_kernel_install
install_data('50-depmod.install',
install_mode : 'rwxr-xr-x',
assert_se(d->fd >= 0);
if (t == 0) {
- /* We don' want to disarm here, just mean some time looooong ago. */
+ /* We don't want to disarm here, just mean some time looooong ago. */
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = 1;
} else
#include "journal-authenticate.h"
#include "journal-def.h"
#include "journal-file.h"
+#include "journal-internal.h"
#include "lookup3.h"
#include "memory-util.h"
#include "path-util.h"
r = fd_is_fs_type(f->fd, BTRFS_SUPER_MAGIC);
if (r < 0)
- return log_warning_errno(r, "Failed to determine if journal is on btrfs: %m");
+ return log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT, "Failed to determine if journal is on btrfs: %m");
if (!r)
return 0;
r = read_attr_fd(f->fd, &attrs);
if (r < 0)
- return log_warning_errno(r, "Failed to read file attributes: %m");
+ return log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT, "Failed to read file attributes: %m");
if (attrs & FS_NOCOW_FL) {
log_debug("Detected btrfs file system with copy-on-write disabled, all is good.");
return 0;
}
- log_notice("Creating journal file %s on a btrfs file system, and copy-on-write is enabled. "
- "This is likely to slow down journal access substantially, please consider turning "
- "off the copy-on-write file attribute on the journal directory, using chattr +C.", f->path);
+ log_ratelimit_notice(JOURNAL_LOG_RATELIMIT,
+ "Creating journal file %s on a btrfs file system, and copy-on-write is enabled. "
+ "This is likely to slow down journal access substantially, please consider turning "
+ "off the copy-on-write file attribute on the journal directory, using chattr +C.",
+ f->path);
return 1;
}
return 1;
}
-/* Ideally this would be a function parameter but initializers for static fields have to be compile
- * time constants so we hardcode the interval instead. */
-#define LOG_RATELIMIT ((const RateLimit) { .interval = 60 * USEC_PER_SEC, .burst = 3 })
-
bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec, int log_level) {
assert(f);
assert(f->header);
/* If we gained new header fields we gained new features,
* hence suggest a rotation */
if (le64toh(f->header->header_size) < sizeof(Header)) {
- log_full(log_level, "%s uses an outdated header, suggesting rotation.", f->path);
+ log_ratelimit_full(log_level, JOURNAL_LOG_RATELIMIT,
+ "%s uses an outdated header, suggesting rotation.", f->path);
return true;
}
if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
if (le64toh(f->header->n_data) * 4ULL > (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)) * 3ULL) {
log_ratelimit_full(
- log_level, LOG_RATELIMIT,
+ log_level, JOURNAL_LOG_RATELIMIT,
"Data hash table of %s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items, %llu file size, %"PRIu64" bytes per hash table item), suggesting rotation.",
f->path,
100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))),
if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
if (le64toh(f->header->n_fields) * 4ULL > (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)) * 3ULL) {
log_ratelimit_full(
- log_level, LOG_RATELIMIT,
+ log_level, JOURNAL_LOG_RATELIMIT,
"Field hash table of %s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items), suggesting rotation.",
f->path,
100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))),
if (JOURNAL_HEADER_CONTAINS(f->header, data_hash_chain_depth) &&
le64toh(f->header->data_hash_chain_depth) > HASH_CHAIN_DEPTH_MAX) {
log_ratelimit_full(
- log_level, LOG_RATELIMIT,
+ log_level, JOURNAL_LOG_RATELIMIT,
"Data hash table of %s has deepest hash chain of length %" PRIu64 ", suggesting rotation.",
f->path, le64toh(f->header->data_hash_chain_depth));
return true;
if (JOURNAL_HEADER_CONTAINS(f->header, field_hash_chain_depth) &&
le64toh(f->header->field_hash_chain_depth) > HASH_CHAIN_DEPTH_MAX) {
log_ratelimit_full(
- log_level, LOG_RATELIMIT,
+ log_level, JOURNAL_LOG_RATELIMIT,
"Field hash table of %s has deepest hash chain of length at %" PRIu64 ", suggesting rotation.",
f->path, le64toh(f->header->field_hash_chain_depth));
return true;
le64toh(f->header->n_data) > 0 &&
le64toh(f->header->n_fields) == 0) {
log_ratelimit_full(
- log_level, LOG_RATELIMIT,
+ log_level, JOURNAL_LOG_RATELIMIT,
"Data objects of %s are not indexed by field objects, suggesting rotation.",
f->path);
return true;
if (h > 0 && t > h + max_file_usec) {
log_ratelimit_full(
- log_level, LOG_RATELIMIT,
+ log_level, JOURNAL_LOG_RATELIMIT,
"Oldest entry in %s is older than the configured file retention duration (%s), suggesting rotation.",
f->path, FORMAT_TIMESPAN(max_file_usec, USEC_PER_SEC));
return true;
#include "list.h"
#include "set.h"
+#define JOURNAL_LOG_RATELIMIT ((const RateLimit) { .interval = 60 * USEC_PER_SEC, .burst = 3 })
+
typedef struct Match Match;
typedef struct Location Location;
typedef struct Directory Directory;
#include "fs-util.h"
#include "journal-def.h"
#include "journal-file.h"
+#include "journal-internal.h"
#include "journal-vacuum.h"
#include "sort-util.h"
#include "string-util.h"
freed += size;
} else if (r != -ENOENT)
- log_warning_errno(r, "Failed to delete empty archived journal %s/%s: %m", directory, p);
+ log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
+ "Failed to delete empty archived journal %s/%s: %m",
+ directory, p);
continue;
}
sum = 0;
} else if (r != -ENOENT)
- log_warning_errno(r, "Failed to delete archived journal %s/%s: %m", directory, list[i].filename);
+ log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
+ "Failed to delete archived journal %s/%s: %m",
+ directory, list[i].filename);
}
if (oldest_usec && i < n_list && (*oldest_usec == 0 || list[i].realtime < *oldest_usec))
#include "list.h"
#include "netlink-types.h"
+#include "ordered-set.h"
#include "prioq.h"
#include "time-util.h"
Hashmap *broadcast_group_refs;
bool broadcast_group_dont_leave:1; /* until we can rely on 4.2 */
- sd_netlink_message **rqueue;
- unsigned rqueue_size;
-
- sd_netlink_message **rqueue_partial;
- unsigned rqueue_partial_size;
+ OrderedSet *rqueue;
+ Hashmap *rqueue_by_serial;
+ Hashmap *rqueue_partial_by_serial;
struct nlmsghdr *rbuffer;
int netlink_open_family(sd_netlink **ret, int family);
bool netlink_pid_changed(sd_netlink *nl);
-int netlink_rqueue_make_room(sd_netlink *nl);
-int netlink_rqueue_partial_make_room(sd_netlink *nl);
int socket_bind(sd_netlink *nl);
int socket_broadcast_group_ref(sd_netlink *nl, unsigned group);
return k;
}
-static int socket_recv_message(int fd, struct iovec *iov, uint32_t *ret_mcast_group, bool peek) {
+static int socket_recv_message(int fd, void *buf, size_t buf_size, uint32_t *ret_mcast_group, bool peek) {
+ struct iovec iov = IOVEC_MAKE(buf, buf_size);
union sockaddr_union sender;
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct nl_pktinfo))) control;
struct msghdr msg = {
- .msg_iov = iov,
+ .msg_iov = &iov,
.msg_iovlen = 1,
.msg_name = &sender,
.msg_namelen = sizeof(sender),
ssize_t n;
assert(fd >= 0);
- assert(iov);
+ assert(peek || (buf && buf_size > 0));
n = recvmsg_safe(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
if (n < 0) {
if (n == -ENOBUFS)
return log_debug_errno(n, "sd-netlink: kernel receive buffer overrun");
- if (ERRNO_IS_TRANSIENT(n))
+ if (ERRNO_IS_TRANSIENT(n)) {
+ if (ret_mcast_group)
+ *ret_mcast_group = 0;
return 0;
+ }
return (int) n;
}
return (int) n;
}
+ if (ret_mcast_group)
+ *ret_mcast_group = 0;
return 0;
}
+ if (!peek && (size_t) n > buf_size) /* message did not fit in read buffer */
+ return -EIO;
+
if (ret_mcast_group) {
struct nl_pktinfo *pi;
return (int) n;
}
-/* On success, the number of bytes received is returned and *ret points to the received message
- * which has a valid header and the correct size.
- * If nothing useful was received 0 is returned.
- * On failure, a negative error code is returned.
- */
-int socket_read_message(sd_netlink *nl) {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *first = NULL;
- bool multi_part = false, done = false;
- size_t len, allocated;
- struct iovec iov = {};
- uint32_t group = 0;
- unsigned i = 0;
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ netlink_message_hash_ops,
+ void, trivial_hash_func, trivial_compare_func,
+ sd_netlink_message, sd_netlink_message_unref);
+
+static int netlink_queue_received_message(sd_netlink *nl, sd_netlink_message *m) {
+ uint32_t serial;
int r;
assert(nl);
- assert(nl->rbuffer);
+ assert(m);
- /* read nothing, just get the pending message size */
- r = socket_recv_message(nl->fd, &iov, NULL, true);
- if (r <= 0)
+ if (ordered_set_size(nl->rqueue) >= NETLINK_RQUEUE_MAX)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS),
+ "sd-netlink: exhausted the read queue size (%d)", NETLINK_RQUEUE_MAX);
+
+ r = ordered_set_ensure_put(&nl->rqueue, &netlink_message_hash_ops, m);
+ if (r < 0)
return r;
- else
- len = (size_t) r;
- /* make room for the pending message */
- if (!greedy_realloc((void**) &nl->rbuffer, len, sizeof(uint8_t)))
- return -ENOMEM;
+ sd_netlink_message_ref(m);
- allocated = MALLOC_SIZEOF_SAFE(nl->rbuffer);
- iov = IOVEC_MAKE(nl->rbuffer, allocated);
+ if (sd_netlink_message_is_broadcast(m))
+ return 0;
- /* read the pending message */
- r = socket_recv_message(nl->fd, &iov, &group, false);
- if (r <= 0)
- return r;
- else
- len = (size_t) r;
+ serial = message_get_serial(m);
+ if (serial == 0)
+ return 0;
- if (len > allocated)
- /* message did not fit in read buffer */
- return -EIO;
+ if (sd_netlink_message_get_errno(m) < 0) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *old = NULL;
- if (NLMSG_OK(nl->rbuffer, len) && nl->rbuffer->nlmsg_flags & NLM_F_MULTI) {
- multi_part = true;
+ old = hashmap_remove(nl->rqueue_by_serial, UINT32_TO_PTR(serial));
+ if (old)
+ log_debug("sd-netlink: received error message with serial %"PRIu32", but another message with "
+ "the same serial is already stored in the read queue, replacing.", serial);
+ }
- for (i = 0; i < nl->rqueue_partial_size; i++)
- if (message_get_serial(nl->rqueue_partial[i]) ==
- nl->rbuffer->nlmsg_seq) {
- first = nl->rqueue_partial[i];
- break;
- }
+ r = hashmap_ensure_put(&nl->rqueue_by_serial, &netlink_message_hash_ops, UINT32_TO_PTR(serial), m);
+ if (r == -EEXIST) {
+ if (!sd_netlink_message_is_error(m))
+ log_debug("sd-netlink: received message with serial %"PRIu32", but another message with "
+ "the same serial is already stored in the read queue, ignoring.", serial);
+ return 0;
+ }
+ if (r < 0) {
+ sd_netlink_message_unref(ordered_set_remove(nl->rqueue, m));
+ return r;
}
- for (struct nlmsghdr *new_msg = nl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- size_t size;
+ sd_netlink_message_ref(m);
+ return 0;
+}
- if (group == 0 && new_msg->nlmsg_pid != nl->sockaddr.nl.nl_pid)
- /* not broadcast and not for us */
- continue;
+static int netlink_queue_partially_received_message(sd_netlink *nl, sd_netlink_message *m) {
+ uint32_t serial;
+ int r;
- if (new_msg->nlmsg_type == NLMSG_NOOP)
- /* silently drop noop messages */
- continue;
+ assert(nl);
+ assert(m);
+ assert(m->hdr->nlmsg_flags & NLM_F_MULTI);
- if (new_msg->nlmsg_type == NLMSG_DONE) {
- /* finished reading multi-part message */
- done = true;
+ if (hashmap_size(nl->rqueue_partial_by_serial) >= NETLINK_RQUEUE_MAX)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS),
+ "sd-netlink: exhausted the partial read queue size (%d)", NETLINK_RQUEUE_MAX);
- /* if first is not defined, put NLMSG_DONE into the receive queue. */
- if (first)
- continue;
- }
+ serial = message_get_serial(m);
+ r = hashmap_ensure_put(&nl->rqueue_partial_by_serial, &netlink_message_hash_ops, UINT32_TO_PTR(serial), m);
+ if (r < 0)
+ return r;
- /* check that we support this message type */
- r = netlink_get_policy_set_and_header_size(nl, new_msg->nlmsg_type, NULL, &size);
- if (r < 0) {
- if (r == -EOPNOTSUPP)
- log_debug("sd-netlink: ignored message with unknown type: %i",
- new_msg->nlmsg_type);
+ sd_netlink_message_ref(m);
+ return 0;
+}
- continue;
- }
+static int parse_message_one(sd_netlink *nl, uint32_t group, const struct nlmsghdr *hdr, sd_netlink_message **ret) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ size_t size;
+ int r;
- /* check that the size matches the message type */
- if (new_msg->nlmsg_len < NLMSG_LENGTH(size)) {
- log_debug("sd-netlink: message is shorter than expected, dropping");
- continue;
- }
+ assert(nl);
+ assert(hdr);
+ assert(ret);
+
+ /* not broadcast and not for us */
+ if (group == 0 && hdr->nlmsg_pid != nl->sockaddr.nl.nl_pid)
+ goto finalize;
+
+ /* silently drop noop messages */
+ if (hdr->nlmsg_type == NLMSG_NOOP)
+ goto finalize;
+
+ /* check that we support this message type */
+ r = netlink_get_policy_set_and_header_size(nl, hdr->nlmsg_type, NULL, &size);
+ if (r == -EOPNOTSUPP) {
+ log_debug("sd-netlink: ignored message with unknown type: %i", hdr->nlmsg_type);
+ goto finalize;
+ }
+ if (r < 0)
+ return r;
- r = message_new_empty(nl, &m);
- if (r < 0)
- return r;
+ /* check that the size matches the message type */
+ if (hdr->nlmsg_len < NLMSG_LENGTH(size)) {
+ log_debug("sd-netlink: message is shorter than expected, dropping.");
+ goto finalize;
+ }
- m->multicast_group = group;
- m->hdr = memdup(new_msg, new_msg->nlmsg_len);
- if (!m->hdr)
- return -ENOMEM;
+ r = message_new_empty(nl, &m);
+ if (r < 0)
+ return r;
- /* seal and parse the top-level message */
- r = sd_netlink_message_rewind(m, nl);
- if (r < 0)
- return r;
+ m->multicast_group = group;
+ m->hdr = memdup(hdr, hdr->nlmsg_len);
+ if (!m->hdr)
+ return -ENOMEM;
- /* push the message onto the multi-part message stack */
- if (first)
- m->next = first;
- first = TAKE_PTR(m);
- }
+ /* seal and parse the top-level message */
+ r = sd_netlink_message_rewind(m, nl);
+ if (r < 0)
+ return r;
- if (len > 0)
- log_debug("sd-netlink: discarding %zu bytes of incoming message", len);
+ *ret = TAKE_PTR(m);
+ return 1;
- if (!first)
+finalize:
+ *ret = NULL;
+ return 0;
+}
+
+/* On success, the number of bytes received is returned and *ret points to the received message
+ * which has a valid header and the correct size.
+ * If nothing useful was received 0 is returned.
+ * On failure, a negative error code is returned.
+ */
+int socket_read_message(sd_netlink *nl) {
+ bool done = false;
+ uint32_t group;
+ size_t len;
+ int r;
+
+ assert(nl);
+
+ /* read nothing, just get the pending message size */
+ r = socket_recv_message(nl->fd, NULL, 0, NULL, true);
+ if (r <= 0)
+ return r;
+ len = (size_t) r;
+
+ /* make room for the pending message */
+ if (!greedy_realloc((void**) &nl->rbuffer, len, sizeof(uint8_t)))
+ return -ENOMEM;
+
+ /* read the pending message */
+ r = socket_recv_message(nl->fd, nl->rbuffer, MALLOC_SIZEOF_SAFE(nl->rbuffer), &group, false);
+ if (r <= 0)
+ return r;
+ len = (size_t) r;
+
+ if (!NLMSG_OK(nl->rbuffer, len)) {
+ log_debug("sd-netlink: received invalid message, discarding %zu bytes of incoming message", len);
return 0;
+ }
+
+ for (struct nlmsghdr *hdr = nl->rbuffer; NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- if (!multi_part || done) {
- /* we got a complete message, push it on the read queue */
- r = netlink_rqueue_make_room(nl);
+ r = parse_message_one(nl, group, hdr, &m);
if (r < 0)
return r;
+ if (r == 0)
+ continue;
- nl->rqueue[nl->rqueue_size++] = TAKE_PTR(first);
+ if (hdr->nlmsg_flags & NLM_F_MULTI) {
+ if (hdr->nlmsg_type == NLMSG_DONE) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *existing = NULL;
- if (multi_part && (i < nl->rqueue_partial_size)) {
- /* remove the message form the partial read queue */
- memmove(nl->rqueue_partial + i, nl->rqueue_partial + i + 1,
- sizeof(sd_netlink_message*) * (nl->rqueue_partial_size - i - 1));
- nl->rqueue_partial_size--;
- }
+ /* finished reading multi-part message */
+ existing = hashmap_remove(nl->rqueue_partial_by_serial, UINT32_TO_PTR(hdr->nlmsg_seq));
+
+ /* if we receive only NLMSG_DONE, put it into the receive queue. */
+ r = netlink_queue_received_message(nl, existing ?: m);
+ if (r < 0)
+ return r;
+
+ done = true;
+ } else {
+ sd_netlink_message *existing;
+
+ existing = hashmap_get(nl->rqueue_partial_by_serial, UINT32_TO_PTR(hdr->nlmsg_seq));
+ if (existing) {
+ /* This is the continuation of the previously read messages.
+ * Let's append this message at the end. */
+ while (existing->next)
+ existing = existing->next;
+ existing->next = TAKE_PTR(m);
+ } else {
+ /* This is the first message. Put it into the queue for partially
+ * received messages. */
+ r = netlink_queue_partially_received_message(nl, m);
+ if (r < 0)
+ return r;
+ }
+ }
- return 1;
- } else {
- /* we only got a partial multi-part message, push it on the
- partial read queue */
- if (i < nl->rqueue_partial_size)
- nl->rqueue_partial[i] = TAKE_PTR(first);
- else {
- r = netlink_rqueue_partial_make_room(nl);
+ } else {
+ r = netlink_queue_received_message(nl, m);
if (r < 0)
return r;
- nl->rqueue_partial[nl->rqueue_partial_size++] = TAKE_PTR(first);
+ done = true;
}
-
- return 0;
}
+
+ if (len > 0)
+ log_debug("sd-netlink: discarding trailing %zu bytes of incoming message", len);
+
+ return done;
}
return 0;
}
+static bool serial_used(sd_netlink *nl, uint32_t serial) {
+ assert(nl);
+
+ return
+ hashmap_contains(nl->reply_callbacks, UINT32_TO_PTR(serial)) ||
+ hashmap_contains(nl->rqueue_by_serial, UINT32_TO_PTR(serial)) ||
+ hashmap_contains(nl->rqueue_partial_by_serial, UINT32_TO_PTR(serial));
+}
+
void netlink_seal_message(sd_netlink *nl, sd_netlink_message *m) {
uint32_t picked;
such messages */
nl->serial = nl->serial == UINT32_MAX ? 1 : nl->serial + 1;
- } while (hashmap_contains(nl->reply_callbacks, UINT32_TO_PTR(picked)));
+ } while (serial_used(nl, picked));
m->hdr->nlmsg_seq = picked;
message_seal(m);
.serial = (uint32_t) (now(CLOCK_MONOTONIC) % UINT32_MAX) + 1,
};
- /* We guarantee that the read buffer has at least space for a message header */
- if (!greedy_realloc((void**) &nl->rbuffer, sizeof(struct nlmsghdr), sizeof(uint8_t)))
- return -ENOMEM;
-
*ret = TAKE_PTR(nl);
return 0;
}
static sd_netlink *netlink_free(sd_netlink *nl) {
sd_netlink_slot *s;
- unsigned i;
assert(nl);
- for (i = 0; i < nl->rqueue_size; i++)
- sd_netlink_message_unref(nl->rqueue[i]);
- free(nl->rqueue);
-
- for (i = 0; i < nl->rqueue_partial_size; i++)
- sd_netlink_message_unref(nl->rqueue_partial[i]);
- free(nl->rqueue_partial);
-
+ ordered_set_free(nl->rqueue);
+ hashmap_free(nl->rqueue_by_serial);
+ hashmap_free(nl->rqueue_partial_by_serial);
free(nl->rbuffer);
while ((s = nl->slots)) {
return 1;
}
-int netlink_rqueue_make_room(sd_netlink *nl) {
- assert(nl);
-
- if (nl->rqueue_size >= NETLINK_RQUEUE_MAX)
- return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS),
- "sd-netlink: exhausted the read queue size (%d)",
- NETLINK_RQUEUE_MAX);
-
- if (!GREEDY_REALLOC(nl->rqueue, nl->rqueue_size + 1))
- return -ENOMEM;
-
- return 0;
-}
-
-int netlink_rqueue_partial_make_room(sd_netlink *nl) {
- assert(nl);
-
- if (nl->rqueue_partial_size >= NETLINK_RQUEUE_MAX)
- return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS),
- "sd-netlink: exhausted the partial read queue size (%d)",
- NETLINK_RQUEUE_MAX);
-
- if (!GREEDY_REALLOC(nl->rqueue_partial, nl->rqueue_partial_size + 1))
- return -ENOMEM;
-
- return 0;
-}
-
-static int dispatch_rqueue(sd_netlink *nl, sd_netlink_message **message) {
+static int dispatch_rqueue(sd_netlink *nl, sd_netlink_message **ret) {
+ sd_netlink_message *m;
int r;
assert(nl);
- assert(message);
+ assert(ret);
- if (nl->rqueue_size <= 0) {
+ if (ordered_set_size(nl->rqueue) <= 0) {
/* Try to read a new message */
r = socket_read_message(nl);
- if (r == -ENOBUFS) { /* FIXME: ignore buffer overruns for now */
+ if (r == -ENOBUFS) /* FIXME: ignore buffer overruns for now */
log_debug_errno(r, "sd-netlink: Got ENOBUFS from netlink socket, ignoring.");
- return 1;
- }
- if (r <= 0)
+ else if (r < 0)
return r;
}
/* Dispatch a queued message */
- *message = nl->rqueue[0];
- nl->rqueue_size--;
- memmove(nl->rqueue, nl->rqueue + 1, sizeof(sd_netlink_message*) * nl->rqueue_size);
-
- return 1;
+ m = ordered_set_steal_first(nl->rqueue);
+ if (m)
+ sd_netlink_message_unref(hashmap_remove_value(nl->rqueue_by_serial, UINT32_TO_PTR(message_get_serial(m)), m));
+ *ret = m;
+ return !!m;
}
static int process_timeout(sd_netlink *nl) {
assert_return(nl, -EINVAL);
assert_return(!netlink_pid_changed(nl), -ECHILD);
- if (nl->rqueue_size > 0)
+ if (ordered_set_size(nl->rqueue) > 0)
return 0;
r = netlink_poll(nl, false, timeout_usec);
timeout = calc_elapse(usec);
for (;;) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
usec_t left;
- for (unsigned i = 0; i < nl->rqueue_size; i++) {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *incoming = NULL;
- uint32_t received_serial;
+ m = hashmap_remove(nl->rqueue_by_serial, UINT32_TO_PTR(serial));
+ if (m) {
uint16_t type;
- received_serial = message_get_serial(nl->rqueue[i]);
- if (received_serial != serial)
- continue;
-
- incoming = nl->rqueue[i];
-
/* found a match, remove from rqueue and return it */
- memmove(nl->rqueue + i, nl->rqueue + i + 1,
- sizeof(sd_netlink_message*) * (nl->rqueue_size - i - 1));
- nl->rqueue_size--;
+ sd_netlink_message_unref(ordered_set_remove(nl->rqueue, m));
- r = sd_netlink_message_get_errno(incoming);
+ r = sd_netlink_message_get_errno(m);
if (r < 0)
return r;
- r = sd_netlink_message_get_type(incoming, &type);
+ r = sd_netlink_message_get_type(m, &type);
if (r < 0)
return r;
if (type == NLMSG_DONE) {
- *ret = NULL;
+ if (ret)
+ *ret = NULL;
return 0;
}
if (ret)
- *ret = TAKE_PTR(incoming);
+ *ret = TAKE_PTR(m);
return 1;
}
assert_return(nl, -EINVAL);
assert_return(!netlink_pid_changed(nl), -ECHILD);
- return nl->rqueue_size == 0 ? POLLIN : 0;
+ return ordered_set_size(nl->rqueue) == 0 ? POLLIN : 0;
}
int sd_netlink_get_timeout(sd_netlink *nl, uint64_t *timeout_usec) {
assert_return(timeout_usec, -EINVAL);
assert_return(!netlink_pid_changed(nl), -ECHILD);
- if (nl->rqueue_size > 0) {
+ if (ordered_set_size(nl->rqueue) > 0) {
*timeout_usec = 0;
return 1;
}
}
*timeout_usec = c->timeout;
-
return 1;
}
if (r < 0)
return r;
+ /* For login session scopes, if a process is OOM killed by the kernel, *don't* terminate the rest of
+ the scope */
+ r = sd_bus_message_append(m, "(sv)", "OOMPolicy", "s", "continue");
+ if (r < 0)
+ return r;
+
/* disable TasksMax= for the session scope, rely on the slice setting for it */
r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", UINT64_MAX);
if (r < 0)
#include "daemon-util.h"
#include "device-util.h"
#include "dirent-util.h"
+#include "escape.h"
#include "fd-util.h"
#include "format-util.h"
#include "fs-util.h"
FOREACH_DIRENT(de, d, return -errno) {
int k;
+ _cleanup_free_ char *n = NULL;
if (!dirent_is_file(de))
continue;
-
- k = manager_add_user_by_name(m, de->d_name, NULL);
+ k = cunescape(de->d_name, 0, &n);
+ if (k < 0) {
+ r = log_warning_errno(k, "Failed to unescape username '%s', ignoring: %m", de->d_name);
+ continue;
+ }
+ k = manager_add_user_by_name(m, n, NULL);
if (k < 0)
r = log_warning_errno(k, "Couldn't add lingering user %s, ignoring: %m", de->d_name);
}
return n;
}
-static int find_loop_device(const char *backing_file, char **loop_dev) {
- _cleanup_closedir_ DIR *d = NULL;
- _cleanup_free_ char *l = NULL;
+static int find_loop_device(const char *backing_file, sd_device **ret) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *dev;
+ int r;
assert(backing_file);
- assert(loop_dev);
+ assert(ret);
- d = opendir("/sys/devices/virtual/block");
- if (!d)
- return -errno;
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return log_oom();
- FOREACH_DIRENT(de, d, return -errno) {
- _cleanup_free_ char *sys = NULL, *fname = NULL;
- int r;
+ r = sd_device_enumerator_add_match_subsystem(e, "block", /* match = */ true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add subsystem match: %m");
- if (de->d_type != DT_DIR)
- continue;
+ r = sd_device_enumerator_add_match_property(e, "ID_FS_USAGE", "filesystem");
+ if (r < 0)
+ return log_error_errno(r, "Failed to add property match: %m");
- if (!startswith(de->d_name, "loop"))
- continue;
+ r = sd_device_enumerator_add_match_sysname(e, "loop*");
+ if (r < 0)
+ return log_error_errno(r, "Failed to add sysname match: %m");
- sys = path_join("/sys/devices/virtual/block", de->d_name, "loop/backing_file");
- if (!sys)
- return -ENOMEM;
+ r = sd_device_enumerator_add_match_sysattr(e, "loop/backing_file", /* value = */ NULL, /* match = */ true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add sysattr match: %m");
+
+ FOREACH_DEVICE(e, dev) {
+ const char *s;
- r = read_one_line_file(sys, &fname);
+ r = sd_device_get_sysattr_value(dev, "loop/backing_file", &s);
if (r < 0) {
- log_debug_errno(r, "Failed to read %s, ignoring: %m", sys);
+ log_device_debug_errno(dev, r, "Failed to read \"loop/backing_file\" sysattr, ignoring: %m");
continue;
}
- if (files_same(fname, backing_file, 0) <= 0)
+ if (files_same(s, backing_file, 0) <= 0)
continue;
- l = path_join("/dev", de->d_name);
- if (!l)
- return -ENOMEM;
-
- break;
+ *ret = sd_device_ref(dev);
+ return 0;
}
- if (!l)
- return -ENXIO;
-
- *loop_dev = TAKE_PTR(l);
-
- return 0;
+ return -ENXIO;
}
static int stop_mount(
return 0;
}
-static int umount_by_device(sd_bus *bus, const char *what) {
+static int umount_by_device(sd_bus *bus, sd_device *dev) {
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
_cleanup_strv_free_ char **list = NULL;
- struct stat st;
const char *v;
- char **l;
- int r, r2 = 0;
-
- assert(what);
+ int r, ret = 0;
- if (stat(what, &st) < 0)
- return log_error_errno(errno, "Can't stat %s: %m", what);
+ assert(bus);
+ assert(dev);
- if (!S_ISBLK(st.st_mode))
- return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK),
- "Not a block device: %s", what);
-
- r = sd_device_new_from_stat_rdev(&d, &st);
- if (r < 0)
- return log_error_errno(r, "Failed to get device from device number: %m");
+ if (sd_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE", &v) >= 0)
+ ret = stop_mounts(bus, v);
- r = sd_device_get_property_value(d, "ID_FS_USAGE", &v);
+ r = sd_device_get_devname(dev, &v);
if (r < 0)
- return log_device_error_errno(d, r, "Failed to get device property: %m");
-
- if (!streq(v, "filesystem"))
- return log_device_error_errno(d, SYNTHETIC_ERRNO(EINVAL),
- "%s does not contain a known file system.", what);
-
- if (sd_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE", &v) >= 0)
- r2 = stop_mounts(bus, v);
+ return r;
- r = find_mount_points(what, &list);
+ r = find_mount_points(v, &list);
if (r < 0)
return r;
- for (l = list; *l; l++) {
+ STRV_FOREACH(l, list) {
r = stop_mounts(bus, *l);
if (r < 0)
- r2 = r;
+ ret = r;
}
- return r2;
+ return ret;
+}
+
+static int umount_by_device_node(sd_bus *bus, const char *node) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ const char *v;
+ int r;
+
+ assert(bus);
+ assert(node);
+
+ r = sd_device_new_from_devname(&dev, node);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get device from %s: %m", node);
+
+ r = sd_device_get_property_value(dev, "ID_FS_USAGE", &v);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to get \"ID_FS_USAGE\" device property: %m");
+
+ if (!streq(v, "filesystem"))
+ return log_device_error_errno(dev, SYNTHETIC_ERRNO(EINVAL),
+ "%s does not contain a known file system.", node);
+
+ return umount_by_device(bus, dev);
}
static int umount_loop(sd_bus *bus, const char *backing_file) {
- _cleanup_free_ char *loop_dev = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
int r;
assert(backing_file);
- r = find_loop_device(backing_file, &loop_dev);
+ r = find_loop_device(backing_file, &dev);
if (r < 0)
return log_error_errno(r, r == -ENXIO ? "File %s is not mounted." : "Can't get loop device for %s: %m", backing_file);
- return umount_by_device(bus, loop_dev);
+ return umount_by_device(bus, dev);
}
static int action_umount(
return log_error_errno(errno, "Can't stat %s (from %s): %m", p, argv[i]);
if (S_ISBLK(st.st_mode))
- r = umount_by_device(bus, p);
+ r = umount_by_device_node(bus, p);
else if (S_ISREG(st.st_mode))
r = umount_loop(bus, p);
else if (S_ISDIR(st.st_mode))
return 1;
}
-static int acquire_mount_where_for_loop_dev(const char *loop_dev) {
+static int acquire_mount_where_for_loop_dev(sd_device *dev) {
_cleanup_strv_free_ char **list = NULL;
+ const char *node;
int r;
+ assert(dev);
+
if (arg_mount_where)
return 0;
- r = find_mount_points(loop_dev, &list);
+ r = sd_device_get_devname(dev, &node);
if (r < 0)
return r;
- else if (r == 0)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Can't find mount point of %s. It is expected that %s is already mounted on a place.",
- loop_dev, loop_dev);
- else if (r >= 2)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "%s is mounted on %d places. It is expected that %s is mounted on a place.",
- loop_dev, r, loop_dev);
+
+ r = find_mount_points(node, &list);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return log_device_error_errno(dev, SYNTHETIC_ERRNO(EINVAL),
+ "Can't find mount point of %s. It is expected that %s is already mounted on a place.",
+ node, node);
+ if (r >= 2)
+ return log_device_error_errno(dev, SYNTHETIC_ERRNO(EINVAL),
+ "%s is mounted on %d places. It is expected that %s is mounted on a place.",
+ node, r, node);
arg_mount_where = strdup(list[0]);
if (!arg_mount_where)
static int discover_loop_backing_file(void) {
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
- _cleanup_free_ char *loop_dev = NULL;
- struct stat st;
- const char *v;
int r;
- r = find_loop_device(arg_mount_what, &loop_dev);
+ r = find_loop_device(arg_mount_what, &d);
if (r < 0 && r != -ENXIO)
return log_error_errno(errno, "Can't get loop device for %s: %m", arg_mount_what);
return 0;
}
- if (stat(loop_dev, &st) < 0)
- return log_error_errno(errno, "Can't stat %s: %m", loop_dev);
-
- if (!S_ISBLK(st.st_mode))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Invalid file type: %s", loop_dev);
-
- r = sd_device_new_from_stat_rdev(&d, &st);
- if (r < 0)
- return log_error_errno(r, "Failed to get device from device number: %m");
-
- if (sd_device_get_property_value(d, "ID_FS_USAGE", &v) < 0 || !streq(v, "filesystem"))
- return log_device_error_errno(d, SYNTHETIC_ERRNO(EINVAL),
- "%s does not contain a known file system.", arg_mount_what);
-
r = acquire_mount_type(d);
if (r < 0)
return r;
if (r < 0)
return r;
- r = acquire_mount_where_for_loop_dev(loop_dev);
+ r = acquire_mount_where_for_loop_dev(d);
if (r < 0)
return r;
}
static int address_update(Address *address) {
- Link *link;
+ Link *link = ASSERT_PTR(ASSERT_PTR(address)->link);
int r;
- assert(address);
- assert(address->link);
-
- link = address->link;
-
if (address_is_ready(address) &&
address->family == AF_INET6 &&
in6_addr_is_link_local(&address->in_addr.in6) &&
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 0;
- r = address_set_masquerade(address, true);
+ r = address_set_masquerade(address, /* add = */ true);
if (r < 0)
return log_link_warning_errno(link, r, "Could not enable IP masquerading: %m");
return r;
}
- link_update_operstate(link, true);
+ link_update_operstate(link, /* also_update_master = */ true);
link_check_ready(link);
return 0;
}
static int address_drop(Address *address) {
- Link *link;
- bool ready;
+ Link *link = ASSERT_PTR(ASSERT_PTR(address)->link);
int r;
- assert(address);
- assert(address->link);
-
- ready = address_is_ready(address);
- link = address->link;
-
- r = address_set_masquerade(address, false);
+ r = address_set_masquerade(address, /* add = */ false);
if (r < 0)
log_link_warning_errno(link, r, "Failed to disable IP masquerading, ignoring: %m");
if (address->state == 0)
address_free(address);
- link_update_operstate(link, true);
-
- if (link && !ready)
- link_check_ready(link);
-
+ link_update_operstate(link, /* also_update_master = */ true);
+ link_check_ready(link);
return 0;
}
static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
_cleanup_(route_freep) Route *route = in;
struct in6_addr router;
+ bool is_new;
int r;
assert(route);
if (!route->protocol_set)
route->protocol = RTPROT_RA;
- if (route_get(NULL, link, route, NULL) < 0)
+ is_new = route_get(NULL, link, route, NULL) < 0;
+
+ r = link_request_route(link, TAKE_PTR(route), true, &link->ndisc_messages,
+ ndisc_route_handler, NULL);
+ if (r < 0)
+ return r;
+ if (r > 0 && is_new)
link->ndisc_configured = false;
- return link_request_route(link, TAKE_PTR(route), true, &link->ndisc_messages,
- ndisc_route_handler, NULL);
+ return 0;
}
static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) {
static int ndisc_request_address(Address *in, Link *link, sd_ndisc_router *rt) {
_cleanup_(address_freep) Address *address = in;
struct in6_addr router;
+ bool is_new;
int r;
assert(address);
if (r < 0)
return r;
- if (address_get(link, address, NULL) < 0)
- link->ndisc_configured = false;
+ is_new = address_get(link, address, NULL) < 0;
- return link_request_address(link, TAKE_PTR(address), true, &link->ndisc_messages,
+ r = link_request_address(link, TAKE_PTR(address), true, &link->ndisc_messages,
ndisc_address_handler, NULL);
+ if (r < 0)
+ return r;
+ if (r > 0 && is_new)
+ link->ndisc_configured = false;
+
+ return 0;
}
static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
return log_oom();
route->family = AF_INET6;
- route->flags = RTM_F_PREFIX;
route->dst.in6 = prefix;
route->dst_prefixlen = prefixlen;
route->lifetime_usec = sec_to_usec(lifetime_sec, timestamp_usec);
0); \
} \
static inline bool name##_is_requesting(type *t) { \
+ assert(t); \
return FLAGS_SET(t->state, NETWORK_CONFIG_STATE_REQUESTING); \
} \
static inline void name##_enter_configuring(type *t) { \
arg_uid_range = UINT32_C(0x10000);
} else if (streq(optarg, "identity")) {
- /* identitiy: User namespaces on, UID range is map the 0…0xFFFF range to
+ /* identity: User namespaces on, UID range is map the 0…0xFFFF range to
* itself, i.e. we don't actually map anything, but do take benefit of
* isolation of capability sets. */
arg_userns_mode = USER_NAMESPACE_FIXED;
* place, so that we can make changes to its mount structure (for example, to implement
* --volatile=) without this interfering with our ability to access files such as
* /etc/localtime to copy into the container. Note that we use a fixed place for this
- * (instead of a temporary directory, since we are living in our own mount namspace here
+ * (instead of a temporary directory, since we are living in our own mount namespace here
* already, and thus don't need to be afraid of colliding with anyone else's mounts). */
(void) mkdir_p("/run/systemd/nspawn-root", 0755);
DISSECT_IMAGE_GENERIC_ROOT |
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_RELAX_VAR_CHECK |
- DISSECT_IMAGE_USR_NO_ROOT;
+ DISSECT_IMAGE_USR_NO_ROOT |
+ DISSECT_IMAGE_ADD_PARTITION_DEVICES |
+ DISSECT_IMAGE_PIN_PARTITION_DEVICES;
assert(arg_image);
assert(!arg_template);
arg_quiet = true;
if (!arg_quiet)
- log_info("Spawning container %s on %s.\nPress ^] three times within 1s to kill container.",
+ log_info("Spawning container %s on %s.\nPress Ctrl-] three times within 1s to kill container.",
arg_machine, arg_image ?: arg_directory);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
#include "local-addresses.h"
#include "macro.h"
#include "nss-util.h"
+#include "resolve-util.h"
#include "signal-util.h"
#include "socket-util.h"
#include "string-util.h"
* IPv6 we use ::1 which unfortunately will not translate back to the
* hostname but instead something like "localhost" or so. */
-#define LOCALADDRESS_IPV4 (htobe32(0x7F000002))
+#define LOCALADDRESS_IPV4 (htobe32(INADDR_LOCALADDRESS))
#define LOCALADDRESS_IPV6 &in6addr_loopback
NSS_GETHOSTBYNAME_PROTOTYPES(myhostname);
}
int oomd_fetch_cgroup_oom_preference(OomdCGroupContext *ctx, const char *prefix) {
- uid_t uid, prefix_uid;
+ uid_t uid;
int r;
assert(ctx);
if (r < 0)
return log_debug_errno(r, "Failed to get owner/group from %s: %m", ctx->path);
- r = cg_get_owner(SYSTEMD_CGROUP_CONTROLLER, prefix, &prefix_uid);
- if (r < 0)
- return log_debug_errno(r, "Failed to get owner/group from %s: %m", ctx->path);
+ if (uid != 0) {
+ uid_t prefix_uid;
- if (uid == prefix_uid) {
- /* Ignore most errors when reading the xattr since it is usually unset and cgroup xattrs are only used
- * as an optional feature of systemd-oomd (and the system might not even support them). */
- r = cg_get_xattr_bool(SYSTEMD_CGROUP_CONTROLLER, ctx->path, "user.oomd_avoid");
- if (r == -ENOMEM)
- return log_oom_debug();
- if (r < 0 && !ERRNO_IS_XATTR_ABSENT(r))
- log_debug_errno(r, "Failed to get xattr user.oomd_avoid, ignoring: %m");
- ctx->preference = r > 0 ? MANAGED_OOM_PREFERENCE_AVOID : ctx->preference;
+ r = cg_get_owner(SYSTEMD_CGROUP_CONTROLLER, prefix, &prefix_uid);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to get owner/group from %s: %m", prefix);
- r = cg_get_xattr_bool(SYSTEMD_CGROUP_CONTROLLER, ctx->path, "user.oomd_omit");
- if (r == -ENOMEM)
- return log_oom_debug();
- if (r < 0 && !ERRNO_IS_XATTR_ABSENT(r))
- log_debug_errno(r, "Failed to get xattr user.oomd_omit, ignoring: %m");
- ctx->preference = r > 0 ? MANAGED_OOM_PREFERENCE_OMIT : ctx->preference;
- } else
- ctx->preference = MANAGED_OOM_PREFERENCE_NONE;
+ if (uid != prefix_uid) {
+ ctx->preference = MANAGED_OOM_PREFERENCE_NONE;
+ return 0;
+ }
+ }
+
+ /* Ignore most errors when reading the xattr since it is usually unset and cgroup xattrs are only used
+ * as an optional feature of systemd-oomd (and the system might not even support them). */
+ r = cg_get_xattr_bool(SYSTEMD_CGROUP_CONTROLLER, ctx->path, "user.oomd_avoid");
+ if (r == -ENOMEM)
+ return log_oom_debug();
+ if (r < 0 && !ERRNO_IS_XATTR_ABSENT(r))
+ log_debug_errno(r, "Failed to get xattr user.oomd_avoid, ignoring: %m");
+ ctx->preference = r > 0 ? MANAGED_OOM_PREFERENCE_AVOID : ctx->preference;
+
+ r = cg_get_xattr_bool(SYSTEMD_CGROUP_CONTROLLER, ctx->path, "user.oomd_omit");
+ if (r == -ENOMEM)
+ return log_oom_debug();
+ if (r < 0 && !ERRNO_IS_XATTR_ABSENT(r))
+ log_debug_errno(r, "Failed to get xattr user.oomd_omit, ignoring: %m");
+ ctx->preference = r > 0 ? MANAGED_OOM_PREFERENCE_OMIT : ctx->preference;
return 0;
}
* Returns the number of sorted items; negative on error. */
int oomd_sort_cgroup_contexts(Hashmap *h, oomd_compare_t compare_func, const char *prefix, OomdCGroupContext ***ret);
-/* If the cgroups represented by `ctx` and `prefix` are owned by the same user,
- * then set `ctx->preference` using the `user.oomd_avoid` and `user.oomd_omit`
- * xattrs. Otherwise, set `ctx->preference` to MANAGED_OOM_PREFERENCE_NONE.
+/* If the the cgroup is owned by root, or the cgroups represented by `ctx` and
+ * `prefix` are owned by the same user, then set `ctx->preference` using the
+ * `user.oomd_avoid` and `user.oomd_omit` xattrs. Otherwise, set
+ * `ctx->preference` to MANAGED_OOM_PREFERENCE_NONE.
*
* If `prefix` is NULL or the empty string, it is treated as root. If `prefix`
* does not specify an ancestor cgroup of `ctx`, -EINVAL is returned. Returns
/* Assert that avoid/omit are not set if the cgroup and prefix are not
* owned by the same user.*/
- if (test_xattrs && !empty_or_root(ctx->path)) {
+ if (test_xattrs && !empty_or_root(cgroup)) {
ctx = oomd_cgroup_context_free(ctx);
- assert_se(cg_set_access(SYSTEMD_CGROUP_CONTROLLER, cgroup, 65534, 0) >= 0);
+ assert_se(cg_set_access(SYSTEMD_CGROUP_CONTROLLER, cgroup, 61183, 0) >= 0);
assert_se(oomd_cgroup_context_acquire(cgroup, &ctx) == 0);
assert_se(oomd_fetch_cgroup_oom_preference(ctx, NULL) == 0);
static uint32_t arg_tpm2_public_key_pcr_mask = UINT32_MAX;
static bool arg_split = false;
static sd_id128_t *arg_filter_partitions = NULL;
-static size_t arg_filter_partitions_size = 0;
+static size_t arg_n_filter_partitions = 0;
static FilterPartitionsType arg_filter_partitions_type = FILTER_PARTITIONS_NONE;
+static sd_id128_t *arg_skip_partitions = NULL;
+static size_t arg_n_skip_partitions = 0;
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
p->verity = VERITY_OFF;
}
-static bool partition_skip(const Partition *p) {
+static bool partition_exclude(const Partition *p) {
assert(p);
if (arg_filter_partitions_type == FILTER_PARTITIONS_NONE)
return false;
- for (size_t i = 0; i < arg_filter_partitions_size; i++)
+ for (size_t i = 0; i < arg_n_filter_partitions; i++)
if (sd_id128_equal(p->type.uuid, arg_filter_partitions[i]))
return arg_filter_partitions_type == FILTER_PARTITIONS_EXCLUDE;
return arg_filter_partitions_type == FILTER_PARTITIONS_INCLUDE;
}
+static bool partition_skip(const Partition *p) {
+ assert(p);
+
+ for (size_t i = 0; i < arg_n_skip_partitions; i++)
+ if (sd_id128_equal(p->type.uuid, arg_skip_partitions[i]))
+ return true;
+
+ return false;
+}
+
static Partition* partition_unlink_and_free(Context *context, Partition *p) {
if (!p)
return NULL;
if (r < 0)
return r;
+ if (partition_exclude(p))
+ return 0;
+
if (p->size_min != UINT64_MAX && p->size_max != UINT64_MAX && p->size_min > p->size_max)
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"SizeMinBytes= larger than SizeMaxBytes=, refusing.");
} else if (streq(p->split_name_format, "-"))
p->split_name_format = mfree(p->split_name_format);
- return 0;
+ return 1;
}
static int find_verity_sibling(Context *context, Partition *p, VerityMode mode, Partition **ret) {
r = partition_read_definition(p, *f, dirs);
if (r < 0)
return r;
+ if (r == 0)
+ continue;
LIST_INSERT_AFTER(partitions, context->partitions, last, p);
last = TAKE_PTR(p);
assert(context->end == UINT64_MAX);
assert(context->total == UINT64_MAX);
- c = fdisk_new_context();
- if (!c)
- return log_oom();
-
/* libfdisk doesn't have an API to operate on arbitrary fds, hence reopen the fd going via the
* /proc/self/fd/ magic path if we have an existing fd. Open the original file otherwise. */
- if (*backing_fd < 0)
+ if (*backing_fd < 0) {
+ c = fdisk_new_context();
+ if (!c)
+ return log_oom();
+
r = fdisk_assign_device(c, node, arg_dry_run);
- else
- r = fdisk_assign_device(c, FORMAT_PROC_FD_PATH(*backing_fd), arg_dry_run);
+ } else
+ r = fdisk_new_context_fd(*backing_fd, arg_dry_run, &c);
+
if (r == -EINVAL && arg_size_auto) {
struct stat st;
_cleanup_free_ char *label_copy = NULL;
Partition *last = NULL;
struct fdisk_partition *p;
- struct fdisk_parttype *pt;
- const char *pts, *ids, *label;
+ const char *label;
uint64_t sz, start;
bool found = false;
sd_id128_t ptid, id;
fdisk_partition_has_partno(p) <= 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found a partition without a position, size or number.");
- pt = fdisk_partition_get_type(p);
- if (!pt)
- return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire type of partition: %m");
-
- pts = fdisk_parttype_get_string(pt);
- if (!pts)
- return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire type of partition as string: %m");
-
- r = sd_id128_from_string(pts, &ptid);
+ r = fdisk_partition_get_type_as_id128(p, &ptid);
if (r < 0)
- return log_error_errno(r, "Failed to parse partition type UUID %s: %m", pts);
+ return log_error_errno(r, "Failed to query partition type UUID: %m");
- ids = fdisk_partition_get_uuid(p);
- if (!ids)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found a partition without a UUID.");
-
- r = sd_id128_from_string(ids, &id);
+ r = fdisk_partition_get_uuid_as_id128(p, &id);
if (r < 0)
- return log_error_errno(r, "Failed to parse partition UUID %s: %m", ids);
+ return log_error_errno(r, "Failed to query partition UUID: %m");
label = fdisk_partition_get_name(p);
if (!isempty(label)) {
DEFINE_TRIVIAL_CLEANUP_FUNC(PartitionTarget*, partition_target_free);
+static int prepare_temporary_file(PartitionTarget *t, uint64_t size) {
+ _cleanup_(unlink_and_freep) char *temp = NULL;
+ _cleanup_close_ int fd = -1;
+ const char *vt;
+ int r;
+
+ assert(t);
+
+ r = var_tmp_dir(&vt);
+ if (r < 0)
+ return log_error_errno(r, "Could not determine temporary directory: %m");
+
+ temp = path_join(vt, "repart-XXXXXX");
+ if (!temp)
+ return log_oom();
+
+ fd = mkostemp_safe(temp);
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to create temporary file: %m");
+
+ if (ftruncate(fd, size) < 0)
+ return log_error_errno(errno, "Failed to truncate temporary file to %s: %m",
+ FORMAT_BYTES(size));
+
+ t->fd = TAKE_FD(fd);
+ t->path = TAKE_PTR(temp);
+
+ return 0;
+}
+
static int partition_target_prepare(
Context *context,
Partition *p,
PartitionTarget **ret) {
_cleanup_(partition_target_freep) PartitionTarget *t = NULL;
- struct stat st;
- int whole_fd;
- int r;
+ _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
+ int whole_fd, r;
assert(context);
assert(p);
assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
- if (fstat(whole_fd, &st) < 0)
- return -errno;
-
- /* If we're operating on a block device, we definitely need privileges to access block devices so we
- * can just use loop devices as our target. Otherwise, we're operating on a regular file, in that
- * case, let's write to regular files and copy those into the final image so we can run without root
- * privileges. On filesystems with reflinking support, we can take advantage of this and just reflink
- * the result into the image.
- */
-
t = new(PartitionTarget, 1);
if (!t)
return log_oom();
.whole_fd = -1,
};
- if (S_ISBLK(st.st_mode) || (p->format && !mkfs_supports_root_option(p->format))) {
- _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
+ if (!need_path) {
+ if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1)
+ return log_error_errno(errno, "Failed to seek to partition offset: %m");
- /* Loopback block devices are not only useful to turn regular files into block devices, but
- * also to cut out sections of block devices into new block devices. */
+ t->whole_fd = whole_fd;
+ *ret = TAKE_PTR(t);
+ return 0;
+ }
- r = loop_device_make(whole_fd, O_RDWR, p->offset, size, 0, 0, LOCK_EX, &d);
- if (r < 0)
- return log_error_errno(r, "Failed to make loopback device of future partition %" PRIu64 ": %m", p->partno);
+ /* Loopback block devices are not only useful to turn regular files into block devices, but
+ * also to cut out sections of block devices into new block devices. */
+ r = loop_device_make(whole_fd, O_RDWR, p->offset, size, 0, 0, LOCK_EX, &d);
+ if (r < 0 && r != -ENOENT && !ERRNO_IS_PRIVILEGE(r))
+ return log_error_errno(r, "Failed to make loopback device of future partition %" PRIu64 ": %m", p->partno);
+ if (r >= 0) {
t->loop = TAKE_PTR(d);
- } else if (need_path) {
- _cleanup_(unlink_and_freep) char *temp = NULL;
- _cleanup_close_ int fd = -1;
- const char *vt;
-
- r = var_tmp_dir(&vt);
- if (r < 0)
- return log_error_errno(r, "Could not determine temporary directory: %m");
-
- temp = path_join(vt, "repart-XXXXXX");
- if (!temp)
- return log_oom();
-
- fd = mkostemp_safe(temp);
- if (fd < 0)
- return log_error_errno(fd, "Failed to create temporary file: %m");
+ *ret = TAKE_PTR(t);
+ return 0;
+ }
- if (ftruncate(fd, size) < 0)
- return log_error_errno(errno, "Failed to truncate temporary file to %s: %m",
- FORMAT_BYTES(size));
+ /* If we can't allocate a loop device, let's write to a regular file that we copy into the final
+ * image so we can run in containers and without needing root privileges. On filesystems with
+ * reflinking support, we can take advantage of this and just reflink the result into the image.
+ */
- t->fd = TAKE_FD(fd);
- t->path = TAKE_PTR(temp);
- } else {
- if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1)
- return log_error_errno(errno, "Failed to seek to partition offset: %m");
+ log_debug_errno(r, "No access to loop devices, falling back to a regular file");
- t->whole_fd = whole_fd;
- }
+ r = prepare_temporary_file(t, size);
+ if (r < 0)
+ return r;
*ret = TAKE_PTR(t);
return 0;
}
-static int do_copy_files(Partition *p, const char *root, const Set *denylist) {
+static int do_copy_files(
+ Partition *p,
+ const char *root,
+ uid_t override_uid,
+ gid_t override_gid,
+ const Set *denylist) {
int r;
if (pfd < 0)
return log_error_errno(pfd, "Failed to open parent directory of target: %m");
+ /* Make sure everything is owned by the user running repart so that
+ * make_filesystem() can map the user running repart to "root" in a user
+ * namespace to have the files owned by root in the final image. */
+
r = copy_tree_at(
sfd, ".",
pfd, fn,
- getuid(), getgid(),
- COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS,
+ override_uid, override_gid,
+ COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN,
denylist);
} else
r = copy_tree_at(
sfd, ".",
tfd, ".",
- getuid(), getgid(),
- COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS,
+ override_uid, override_gid,
+ COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN,
denylist);
if (r < 0)
return log_error_errno(r, "Failed to copy '%s%s' to '%s%s': %m",
if (r < 0)
return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target);
+ if (fchown(tfd, override_uid, override_gid) < 0)
+ return log_error_errno(r, "Failed to change ownership of %s", *target);
+
(void) copy_xattr(sfd, tfd, COPY_ALL_XATTRS);
(void) copy_access(sfd, tfd);
(void) copy_times(sfd, tfd, 0);
return 0;
}
-static int do_make_directories(Partition *p, const char *root) {
+static int do_make_directories(Partition *p, uid_t override_uid, gid_t override_gid, const char *root) {
int r;
assert(p);
STRV_FOREACH(d, p->make_directories) {
- r = mkdir_p_root(root, *d, getuid(), getgid(), 0755);
+ r = mkdir_p_root(root, *d, override_uid, override_gid, 0755);
if (r < 0)
return log_error_errno(r, "Failed to create directory '%s' in file system: %m", *d);
}
return 0;
}
+static bool partition_needs_populate(Partition *p) {
+ assert(p);
+ return !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories);
+}
+
static int partition_populate_directory(Partition *p, const Set *denylist, char **ret) {
_cleanup_(rm_rf_physical_and_freep) char *root = NULL;
_cleanup_close_ int rfd = -1;
assert(ret);
- if (strv_isempty(p->copy_files) && strv_isempty(p->make_directories)) {
- *ret = NULL;
- return 0;
- }
-
rfd = mkdtemp_open("/var/tmp/repart-XXXXXX", 0, &root);
if (rfd < 0)
return log_error_errno(rfd, "Failed to create temporary directory: %m");
if (fchmod(rfd, 0755) < 0)
return log_error_errno(errno, "Failed to change mode of temporary directory: %m");
- /* Make sure everything is owned by the user running repart so that make_filesystem() can map the
- * user running repart to "root" in a user namespace to have the files owned by root in the final
- * image. */
-
- r = do_copy_files(p, root, denylist);
+ r = do_copy_files(p, root, getuid(), getgid(), denylist);
if (r < 0)
return r;
- r = do_make_directories(p, root);
+ r = do_make_directories(p, getuid(), getgid(), root);
if (r < 0)
return r;
}
static int partition_populate_filesystem(Partition *p, const char *node, const Set *denylist) {
- _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
- struct stat st;
int r;
assert(p);
assert(node);
- if (strv_isempty(p->copy_files) && strv_isempty(p->make_directories))
- return 0;
-
- if (stat(node, &st) < 0)
- return log_error_errno(errno, "Failed to stat %s: %m", node);
-
- if (!S_ISBLK(st.st_mode)) {
- r = loop_device_make_by_path(node, O_RDWR, 0, LOCK_EX, &d);
- if (r < 0)
- return log_error_errno(r, "Failed to make loopback device of %s: %m", node);
-
- node = d->node;
- }
-
log_info("Populating %s filesystem with files.", p->format);
/* We copy in a child process, since we have to mount the fs for that, and we don't want that fs to
if (mount_nofollow_verbose(LOG_ERR, node, fs, p->format, MS_NOATIME|MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL) < 0)
_exit(EXIT_FAILURE);
- if (do_copy_files(p, fs, denylist) < 0)
+ if (do_copy_files(p, fs, 0, 0, denylist) < 0)
_exit(EXIT_FAILURE);
- if (do_make_directories(p, fs) < 0)
+ if (do_make_directories(p, 0, 0, fs) < 0)
_exit(EXIT_FAILURE);
r = syncfs_path(AT_FDCWD, fs);
log_info("Formatting future partition %" PRIu64 ".", p->partno);
- /* We prefer (or are required in the case of read-only filesystems) to populate filesystems
- * directly via the corresponding mkfs binary if it supports a --rootdir (or equivalent)
- * option. To do that, we need to setup the final directory tree beforehand. */
+ /* If we're not writing to a loop device or if we're populating a read-only filesystem, we
+ * have to populate using the filesystem's mkfs's --root (or equivalent) option. To do that,
+ * we need to set up the final directory tree beforehand. */
+
+ if (partition_needs_populate(p) && (!t->loop || fstype_is_ro(p->format))) {
+ if (!mkfs_supports_root_option(p->format))
+ return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
+ "Loop device access is required to populate %s filesystems.",
+ p->format);
- if (mkfs_supports_root_option(p->format)) {
r = partition_populate_directory(p, denylist, &root);
if (r < 0)
return r;
log_info("Successfully formatted future partition %" PRIu64 ".", p->partno);
- /* Now, we can populate all the other filesystems that we couldn't populate earlier. */
- if (!mkfs_supports_root_option(p->format)) {
- r = partition_populate_filesystem(p, partition_target_path(t), denylist);
+ /* If we're writing to a loop device, we can now mount the empty filesystem and populate it. */
+ if (partition_needs_populate(p) && !root) {
+ assert(t->loop);
+
+ r = partition_populate_filesystem(p, t->loop->node, denylist);
if (r < 0)
return r;
}
errno = 0;
r = blkid_do_safeprobe(b);
- if (IN_SET(r, -2, 1)) { /* nothing found or ambiguous result */
+ if (r == _BLKID_SAFEPROBE_ERROR)
+ return log_error_errno(errno_or_else(EIO), "Unable to probe for partition table of '%s': %m", p);
+ if (IN_SET(r, _BLKID_SAFEPROBE_AMBIGUOUS, _BLKID_SAFEPROBE_NOT_FOUND)) {
log_debug("Didn't find partition table on block device '%s'.", p);
return false;
}
- if (r != 0)
- return log_error_errno(errno_or_else(EIO), "Unable to probe for partition table of '%s': %m", p);
+
+ assert(r == _BLKID_SAFEPROBE_FOUND);
(void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
if (!streq_ptr(pttype, "gpt")) {
pl = blkid_probe_get_partitions(b);
if (!pl)
return log_error_errno(errno_or_else(EIO), "Unable read partition table of '%s': %m", p);
- errno = 0;
pp = blkid_partlist_devno_to_partition(pl, partition_devno);
if (!pp) {
return false;
}
- t = blkid_partition_get_uuid(pp);
- if (isempty(t)) {
- log_debug("Partition %u:%u has no UUID.",
- major(partition_devno), minor(partition_devno));
+ r = blkid_partition_get_uuid_id128(pp, &u);
+ if (r == -ENXIO) {
+ log_debug_errno(r, "Partition " DEVNUM_FORMAT_STR " has no UUID.", DEVNUM_FORMAT_VAL(partition_devno));
return false;
}
-
- r = sd_id128_from_string(t, &u);
if (r < 0) {
- log_debug_errno(r, "Failed to parse partition UUID \"%s\": %m", t);
+ log_debug_errno(r, "Failed to read partition UUID of " DEVNUM_FORMAT_STR ": %m", DEVNUM_FORMAT_VAL(partition_devno));
return false;
}
- log_debug("Automatically found partition %u:%u of right type " SD_ID128_FORMAT_STR ".",
- major(partition_devno), minor(partition_devno),
+ log_debug("Automatically found partition " DEVNUM_FORMAT_STR " of right type " SD_ID128_FORMAT_STR ".",
+ DEVNUM_FORMAT_VAL(partition_devno),
SD_ID128_FORMAT_VAL(pt_parsed));
if (ret_uuid)
LIST_FOREACH(partitions, p, context->partitions) {
_cleanup_(rm_rf_physical_and_freep) char *root = NULL;
_cleanup_(unlink_and_freep) char *temp = NULL;
+ _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
_cleanup_close_ int fd = -1;
sd_id128_t fs_uuid;
uint64_t fsz;
if (!p->minimize)
continue;
+ if (!partition_needs_populate(p))
+ continue;
+
assert(!p->copy_blocks_path);
r = tempfn_random_child(vt, "repart", &temp);
return log_error_errno(errno, "Failed to open temporary file %s: %m", temp);
/* This may seem huge but it will be created sparse so it doesn't take up any space
- * on disk until written to. */
+ * on disk until written to. */
if (ftruncate(fd, 1024ULL * 1024ULL * 1024ULL * 1024ULL) < 0)
return log_error_errno(errno, "Failed to truncate temporary file to %s: %m",
FORMAT_BYTES(1024ULL * 1024ULL * 1024ULL * 1024ULL));
+ r = loop_device_make(fd, O_RDWR, 0, UINT64_MAX, 0, 0, LOCK_EX, &d);
+ if (r < 0 && r != -ENOENT && !ERRNO_IS_PRIVILEGE(r))
+ return log_error_errno(r, "Failed to make loopback device of %s: %m", temp);
+
/* We're going to populate this filesystem twice so use a random UUID the first time
* to avoid UUID conflicts. */
r = sd_id128_randomize(&fs_uuid);
return r;
}
- if (mkfs_supports_root_option(p->format)) {
+ if (!d || fstype_is_ro(p->format)) {
+ if (!mkfs_supports_root_option(p->format))
+ return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
+ "Loop device access is required to populate %s filesystems",
+ p->format);
+
r = partition_populate_directory(p, denylist, &root);
if (r < 0)
return r;
}
- r = make_filesystem(temp, p->format, strempty(p->new_label), root, fs_uuid, arg_discard);
+ r = make_filesystem(d ? d->node : temp, p->format, strempty(p->new_label), root, fs_uuid, arg_discard);
if (r < 0)
return r;
continue;
}
- if (!mkfs_supports_root_option(p->format)) {
- r = partition_populate_filesystem(p, temp, denylist);
+ if (!root) {
+ assert(d);
+
+ r = partition_populate_filesystem(p, d->node, denylist);
if (r < 0)
return r;
}
if (minimal_size_by_fs_name(p->format) != UINT64_MAX)
fsz = MAX(minimal_size_by_fs_name(p->format), fsz);
+ d = loop_device_unref(d);
+
/* Erase the previous filesystem first. */
if (ftruncate(fd, 0))
return log_error_errno(errno, "Failed to erase temporary file: %m");
if (ftruncate(fd, fsz))
return log_error_errno(errno, "Failed to truncate temporary file to %s: %m", FORMAT_BYTES(fsz));
- r = make_filesystem(temp, p->format, strempty(p->new_label), root, p->fs_uuid, arg_discard);
+ r = loop_device_make(fd, O_RDWR, 0, UINT64_MAX, 0, 0, LOCK_EX, &d);
+ if (r < 0 && r != -ENOENT && !ERRNO_IS_PRIVILEGE(r))
+ return log_error_errno(r, "Failed to make loopback device of %s: %m", temp);
+
+ r = make_filesystem(d ? d->node : temp, p->format, strempty(p->new_label), root, p->fs_uuid, arg_discard);
if (r < 0)
return r;
- if (!mkfs_supports_root_option(p->format)) {
- r = partition_populate_filesystem(p, temp, denylist);
+ if (!root) {
+ assert(d);
+
+ r = partition_populate_filesystem(p, d->node, denylist);
if (r < 0)
return r;
}
return 0;
}
-static int parse_filter_partitions(const char *p) {
+static int parse_partition_types(const char *p, sd_id128_t **partitions, size_t *n_partitions) {
int r;
+ assert(partitions);
+ assert(n_partitions);
+
for (;;) {
_cleanup_free_ char *name = NULL;
GptPartitionType type;
if (r < 0)
return log_error_errno(r, "'%s' is not a valid partition type identifier or GUID", name);
- if (!GREEDY_REALLOC(arg_filter_partitions, arg_filter_partitions_size + 1))
+ if (!GREEDY_REALLOC(*partitions, *n_partitions + 1))
return log_oom();
- arg_filter_partitions[arg_filter_partitions_size++] = type.uuid;
+ (*partitions)[(*n_partitions)++] = type.uuid;
}
return 0;
" Generate JSON output\n"
" --split=BOOL Whether to generate split artifacts\n"
" --include-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
- " Only operate on partitions of the specified types\n"
+ " Ignore partitions not of the specified types\n"
" --exclude-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
- " Don't operate on partitions of the specified types\n"
+ " Ignore partitions of the specified types\n"
+ " --skip-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
+ " Take partitions of the specified types into account\n"
+ " but don't populate them yet\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
ARG_SPLIT,
ARG_INCLUDE_PARTITIONS,
ARG_EXCLUDE_PARTITIONS,
+ ARG_SKIP_PARTITIONS,
};
static const struct option options[] = {
{ "split", required_argument, NULL, ARG_SPLIT },
{ "include-partitions", required_argument, NULL, ARG_INCLUDE_PARTITIONS },
{ "exclude-partitions", required_argument, NULL, ARG_EXCLUDE_PARTITIONS },
+ { "skip-partitions", required_argument, NULL, ARG_SKIP_PARTITIONS },
{}
};
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Combination of --include-partitions= and --exclude-partitions= is invalid.");
- r = parse_filter_partitions(optarg);
+ r = parse_partition_types(optarg, &arg_filter_partitions, &arg_n_filter_partitions);
if (r < 0)
return r;
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Combination of --include-partitions= and --exclude-partitions= is invalid.");
- r = parse_filter_partitions(optarg);
+ r = parse_partition_types(optarg, &arg_filter_partitions, &arg_n_filter_partitions);
if (r < 0)
return r;
break;
+ case ARG_SKIP_PARTITIONS:
+ r = parse_partition_types(optarg, &arg_skip_partitions, &arg_n_skip_partitions);
+ if (r < 0)
+ return r;
+
+ break;
+
case '?':
return -EINVAL;
* possession of the enlarged backing file. For this it suffices to open the device with libfdisk and
* immediately write it again, with no changes. */
- c = fdisk_new_context();
- if (!c)
- return log_oom();
-
- r = fdisk_assign_device(c, FORMAT_PROC_FD_PATH(fd), 0);
+ r = fdisk_new_context_fd(fd, /* read_only= */ false, &c);
if (r < 0)
return log_error_errno(r, "Failed to open device '%s': %m", FORMAT_PROC_FD_PATH(fd));
if (r < 0)
return r;
- if (context->n_partitions <= 0 && arg_empty == EMPTY_REFUSE) {
- log_info("Didn't find any partition definition files, nothing to do.");
- return 0;
- }
-
r = find_root(&node, &backing_fd);
if (r < 0)
return r;
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_DISCARD_ON_LOOP |
DISSECT_IMAGE_RELAX_VAR_CHECK |
- DISSECT_IMAGE_USR_NO_ROOT,
+ DISSECT_IMAGE_USR_NO_ROOT |
+ DISSECT_IMAGE_ADD_PARTITION_DEVICES |
+ DISSECT_IMAGE_PIN_PARTITION_DEVICES,
&m);
if (r == -ENOPKG)
sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Couldn't identify a suitable partition table or file system in '%s'.", path);
return strcmp(a->dirent.d_name, b->dirent.d_name);
}
-static int move_file(PStoreEntry *pe, const char *subdir) {
+static int move_file(PStoreEntry *pe, const char *subdir1, const char *subdir2) {
_cleanup_free_ char *ifd_path = NULL, *ofd_path = NULL;
_cleanup_free_ void *field = NULL;
const char *suffix, *message;
if (!ifd_path)
return log_oom();
- ofd_path = path_join(arg_archivedir, subdir, pe->dirent.d_name);
+ ofd_path = path_join(arg_archivedir, subdir1, subdir2, pe->dirent.d_name);
if (!ofd_path)
return log_oom();
return 0;
}
-static int write_dmesg(const char *dmesg, size_t size, const char *id) {
- _cleanup_(unlink_and_freep) char *tmp_path = NULL;
+static int append_dmesg(PStoreEntry *pe, const char *subdir1, const char *subdir2) {
+ /* Append dmesg chunk to end, create if needed */
_cleanup_free_ char *ofd_path = NULL;
_cleanup_close_ int ofd = -1;
ssize_t wr;
- int r;
- if (size == 0)
- return 0;
+ assert(pe);
- assert(dmesg);
+ if (pe->content_size == 0)
+ return 0;
- ofd_path = path_join(arg_archivedir, id, "dmesg.txt");
+ ofd_path = path_join(arg_archivedir, subdir1, subdir2, "dmesg.txt");
if (!ofd_path)
return log_oom();
- ofd = open_tmpfile_linkable(ofd_path, O_CLOEXEC|O_CREAT|O_TRUNC|O_WRONLY, &tmp_path);
+ ofd = open(ofd_path, O_CREAT|O_NOFOLLOW|O_NOCTTY|O_CLOEXEC|O_APPEND|O_WRONLY, 0640);
if (ofd < 0)
- return log_error_errno(ofd, "Failed to open temporary file %s: %m", ofd_path);
- wr = write(ofd, dmesg, size);
+ return log_error_errno(ofd, "Failed to open file %s: %m", ofd_path);
+ wr = write(ofd, pe->content, pe->content_size);
if (wr < 0)
return log_error_errno(errno, "Failed to store dmesg to %s: %m", ofd_path);
- if (wr != (ssize_t)size)
- return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to store dmesg to %s. %zu bytes are lost.", ofd_path, size - wr);
- r = link_tmpfile(ofd, tmp_path, ofd_path);
- if (r < 0)
- return log_error_errno(r, "Failed to write temporary file %s: %m", ofd_path);
- tmp_path = mfree(tmp_path);
+ if ((size_t)wr != pe->content_size)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to store dmesg to %s. %zu bytes are lost.", ofd_path, pe->content_size - wr);
return 0;
}
-static void process_dmesg_files(PStoreList *list) {
+static int process_dmesg_files(PStoreList *list) {
/* Move files, reconstruct dmesg.txt */
- _cleanup_free_ char *dmesg = NULL, *dmesg_id = NULL;
- size_t dmesg_size = 0;
- bool dmesg_bad = false;
- PStoreEntry *pe;
+ _cleanup_free_ char *erst_subdir = NULL;
+ uint64_t last_record_id = 0;
+
+ /* When dmesg is written into pstore, it is done so in small chunks, whatever the exchange buffer
+ * size is with the underlying pstore backend (ie. EFI may be ~2KiB), which means an example
+ * pstore with approximately 64KB of storage may have up to roughly 32 dmesg files, some likely
+ * related.
+ *
+ * Here we look at the dmesg filename and try to discern if files are part of a related group,
+ * meaning the same original dmesg.
+ *
+ * The dmesg- filename contains the backend-type and the Common Platform Error Record, CPER,
+ * record id, a 64-bit number.
+ *
+ * Files are processed in reverse lexigraphical order so as to properly reconstruct original dmesg.*/
- /* Handle each dmesg file: files processed in reverse
- * order so as to properly reconstruct original dmesg */
for (size_t n = list->n_entries; n > 0; n--) {
- bool move_file_and_continue = false;
- _cleanup_free_ char *pe_id = NULL;
+ PStoreEntry *pe;
char *p;
- size_t plen;
pe = &list->entries[n-1];
if (pe->handled)
continue;
- if (!startswith(pe->dirent.d_name, "dmesg-"))
- continue;
-
if (endswith(pe->dirent.d_name, ".enc.z")) /* indicates a problem */
- move_file_and_continue = true;
- p = strrchr(pe->dirent.d_name, '-');
- if (!p)
- move_file_and_continue = true;
-
- if (move_file_and_continue) {
- /* A dmesg file on which we do NO additional processing */
- (void) move_file(pe, NULL);
- continue;
- }
-
- /* See if this file is one of a related group of files
- * in order to reconstruct dmesg */
-
- /* When dmesg is written into pstore, it is done so in
- * small chunks, whatever the exchange buffer size is
- * with the underlying pstore backend (ie. EFI may be
- * ~2KiB), which means an example pstore with approximately
- * 64KB of storage may have up to roughly 32 dmesg files
- * that could be related, depending upon the size of the
- * original dmesg.
- *
- * Here we look at the dmesg filename and try to discern
- * if files are part of a related group, meaning the same
- * original dmesg.
- *
- * The two known pstore backends are EFI and ERST. These
- * backends store data in the Common Platform Error
- * Record, CPER, format. The dmesg- filename contains the
- * CPER record id, a 64bit number (in decimal notation).
- * In Linux, the record id is encoded with two digits for
- * the dmesg part (chunk) number and 3 digits for the
- * count number. So allowing an additional digit to
- * compensate for advancing time, this code ignores the
- * last six digits of the filename in determining the
- * record id.
- *
- * For the EFI backend, the record id encodes an id in the
- * upper 32 bits, and a timestamp in the lower 32-bits.
- * So ignoring the least significant 6 digits has proven
- * to generally identify related dmesg entries. */
-#define PSTORE_FILENAME_IGNORE 6
-
- /* determine common portion of record id */
- ++p; /* move beyond dmesg- */
- plen = strlen(p);
- if (plen > PSTORE_FILENAME_IGNORE) {
- pe_id = memdup_suffix0(p, plen - PSTORE_FILENAME_IGNORE);
- if (!pe_id) {
- log_oom();
- return;
- }
- } else
- pe_id = mfree(pe_id);
-
- /* Now move file from pstore to archive storage */
- move_file(pe, pe_id);
-
- if (dmesg_bad)
continue;
-
- /* If the current record id is NOT the same as the
- * previous record id, then start a new dmesg.txt file */
- if (!streq_ptr(pe_id, dmesg_id)) {
- /* Encountered a new dmesg group, close out old one, open new one */
- (void) write_dmesg(dmesg, dmesg_size, dmesg_id);
- dmesg_size = 0;
-
- /* now point dmesg_id to storage of pe_id */
- free_and_replace(dmesg_id, pe_id);
- }
-
- /* Reconstruction of dmesg is done as a useful courtesy: do not fail, but don't write garbled
- * output either. */
- size_t needed = strlen(pe->dirent.d_name) + strlen(":\n") + pe->content_size + 1;
- if (!GREEDY_REALLOC(dmesg, dmesg_size + needed)) {
- log_oom();
- dmesg_bad = true;
+ if (!startswith(pe->dirent.d_name, "dmesg-"))
continue;
- }
-
- dmesg_size += sprintf(dmesg + dmesg_size, "%s:\n", pe->dirent.d_name);
- if (pe->content) {
- memcpy(dmesg + dmesg_size, pe->content, pe->content_size);
- dmesg_size += pe->content_size;
- }
- pe_id = mfree(pe_id);
+ if ((p = startswith(pe->dirent.d_name, "dmesg-efi-"))) {
+ /* For the EFI backend, the 3 least significant digits of record id encodes a
+ * "count" number, the next 2 least significant digits for the dmesg part
+ * (chunk) number, and the remaining digits as the timestamp. See
+ * linux/drivers/firmware/efi/efi-pstore.c in efi_pstore_write(). */
+ _cleanup_free_ char *subdir1 = NULL, *subdir2 = NULL;
+ size_t plen = strlen(p);
+
+ if (plen < 6)
+ continue;
+
+ /* Extract base record id */
+ subdir1 = strndup(p, plen - 5);
+ if (!subdir1)
+ return log_oom();
+ /* Extract "count" field */
+ subdir2 = strndup(p + plen - 3, 3);
+ if (!subdir2)
+ return log_oom();
+
+ /* Now move file from pstore to archive storage */
+ (void) move_file(pe, subdir1, subdir2);
+
+ /* Append to the dmesg */
+ (void) append_dmesg(pe, subdir1, subdir2);
+ } else if ((p = startswith(pe->dirent.d_name, "dmesg-erst-"))) {
+ /* For the ERST backend, the record is a monotonically increasing number, seeded as
+ * a timestamp. See linux/drivers/acpi/apei/erst.c in erst_writer(). */
+ uint64_t record_id;
+
+ if (safe_atou64(p, &record_id) < 0)
+ continue;
+ if (last_record_id - 1 != record_id)
+ /* A discontinuity in the number has been detected, this current record id
+ * will become the directory name for all pieces of the dmesg in this
+ * series. */
+ if (free_and_strdup(&erst_subdir, p) < 0)
+ return log_oom();
+
+ /* Now move file from pstore to archive storage */
+ (void) move_file(pe, erst_subdir, NULL);
+
+ /* Append to the dmesg */
+ (void) append_dmesg(pe, erst_subdir, NULL);
+
+ /* Update, but keep erst_subdir for next file */
+ last_record_id = record_id;
+ } else
+ log_debug("Unknown backend, ignoring \"%s\".", pe->dirent.d_name);
}
-
- if (!dmesg_bad)
- (void) write_dmesg(dmesg, dmesg_size, dmesg_id);
+ return 0;
}
static int list_files(PStoreList *list, const char *sourcepath) {
typesafe_qsort(list.entries, list.n_entries, compare_pstore_entries);
/* Process known file types */
- process_dmesg_files(&list);
+ (void) process_dmesg_files(&list);
/* Move left over files out of pstore */
for (size_t n = 0; n < list.n_entries; n++)
- move_file(&list.entries[n], NULL);
+ (void) move_file(&list.entries[n], NULL, NULL);
return 0;
}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "fd-util.h"
+#include "fuzz.h"
+#include "memory-util.h"
+#include "resolved-dns-packet.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_free_ char *out = NULL; /* out should be freed after f */
+ size_t out_size;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL, *copy = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+ if (outside_size_range(size, 0, DNS_PACKET_SIZE_MAX))
+ return 0;
+
+ if (dns_resource_record_new_from_raw(&rr, data, size) < 0)
+ return 0;
+
+ assert_se(copy = dns_resource_record_copy(rr));
+ assert_se(dns_resource_record_equal(copy, rr) > 0);
+
+ assert_se(f = open_memstream_unlocked(&out, &out_size));
+ (void) fprintf(f, "%s", strna(dns_resource_record_to_string(rr)));
+
+ if (dns_resource_record_to_json(rr, &v) < 0)
+ return 0;
+
+ (void) json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR|JSON_FORMAT_SOURCE, f, NULL);
+ (void) dns_resource_record_to_wire_format(rr, false);
+ (void) dns_resource_record_to_wire_format(rr, true);
+
+ return 0;
+}
libshared],
[lib_openssl_or_gcrypt,
libm]],
+ [files('fuzz-resource-record.c'),
+ [libsystemd_resolve_core,
+ libshared],
+ [lib_openssl_or_gcrypt,
+ libm]],
]
systemd_resolved_sources += files('resolved.c')
if (!dns_name_is_single_label(name))
return false;
- if (is_localhost(name) || is_gateway_hostname(name))
+ if (is_localhost(name) ||
+ is_gateway_hostname(name) ||
+ is_outbound_hostname(name) ||
+ is_dns_stub_hostname(name) ||
+ is_dns_proxy_stub_hostname(name))
return false;
r = resolve_system_hostname(NULL, &first_label);
break;
default:
- t = hexmem(rr->generic.data, rr->generic.data_size);
- if (!t)
- return NULL;
-
/* Format as documented in RFC 3597, Section 5 */
- r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.data_size, t);
+ if (rr->generic.data_size == 0)
+ r = asprintf(&s, "%s \\# 0", k);
+ else {
+ t = hexmem(rr->generic.data, rr->generic.data_size);
+ if (!t)
+ return NULL;
+ r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.data_size, t);
+ }
if (r < 0)
return NULL;
break;
unsigned t;
int r;
- assert(b);
assert(ret);
BITMAP_FOREACH(t, b) {
if (dns_name_dont_resolve(domain))
return DNS_SCOPE_NO;
- /* Never go to network for the _gateway or _outbound domain — they're something special, synthesized locally. */
- if (is_gateway_hostname(domain) || is_outbound_hostname(domain))
+ /* Never go to network for the _gateway, _outbound, _localdnsstub, _localdnsproxy domain — they're something special, synthesized locally. */
+ if (is_gateway_hostname(domain) ||
+ is_outbound_hostname(domain) ||
+ is_dns_stub_hostname(domain) ||
+ is_dns_proxy_stub_hostname(domain))
return DNS_SCOPE_NO;
switch (s->protocol) {
}
/* If there's a true search domain defined for this scope, and the query is single-label,
- * then let's resolve things here, prefereably. Note that LLMNR considers itself
+ * then let's resolve things here, preferably. Note that LLMNR considers itself
* authoritative for single-label names too, at the same preference, see below. */
if (has_search_domains && dns_name_is_single_label(domain))
return DNS_SCOPE_YES_BASE + 1;
return DNS_SCOPE_MAYBE;
if ((dns_name_is_single_label(domain) && /* only resolve single label names via LLMNR */
- !is_gateway_hostname(domain) && /* don't resolve "_gateway" with LLMNR, let local synthesizing logic handle that */
- !is_outbound_hostname(domain) && /* similar for "_outbound" */
dns_name_equal(domain, "local") == 0 && /* don't resolve "local" with LLMNR, it's the top-level domain of mDNS after all, see above */
manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via LLMNR */
return DNS_SCOPE_YES_BASE + 1; /* Return +1, as we consider ourselves authoritative
!(t->query_flags & SD_RESOLVED_NO_CACHE))
continue;
- /* If we are asked to clamp ttls an the existing transaction doesn't do it, we can't
+ /* If we are asked to clamp ttls and the existing transaction doesn't do it, we can't
* reuse */
if ((query_flags & SD_RESOLVED_CLAMP_TTL) &&
!(t->query_flags & SD_RESOLVED_CLAMP_TTL))
#include "missing_network.h"
#include "resolved-dns-synthesize.h"
-int dns_synthesize_ifindex(int ifindex) {
-
- /* When the caller asked for resolving on a specific
- * interface, we synthesize the answer for that
- * interface. However, if nothing specific was claimed and we
- * only return localhost RRs, we synthesize the answer for
- * localhost. */
-
- if (ifindex > 0)
- return ifindex;
-
- return LOOPBACK_IFINDEX;
-}
-
int dns_synthesize_family(uint64_t flags) {
/* Picks an address family depending on set flags. This is
return DNS_PROTOCOL_DNS;
}
-static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
+static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, DnsAnswer **answer) {
int r;
assert(m);
rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK);
- r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED, NULL);
+ r = dns_answer_add(*answer, rr, LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED, NULL);
if (r < 0)
return r;
}
rr->aaaa.in6_addr = in6addr_loopback;
- r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED, NULL);
+ r = dns_answer_add(*answer, rr, LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED, NULL);
if (r < 0)
return r;
}
return dns_answer_add(*answer, rr, ifindex, flags, NULL);
}
-static int synthesize_localhost_ptr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
+static int synthesize_localhost_ptr(Manager *m, const DnsResourceKey *key, DnsAnswer **answer) {
int r;
assert(m);
if (r < 0)
return r;
- r = answer_add_ptr(answer, dns_resource_key_name(key), "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ r = answer_add_ptr(answer, dns_resource_key_name(key), "localhost", LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
}
if (n == 0) {
struct local_address buffer[2];
- /* If we have no local addresses then use ::1
- * and 127.0.0.2 as local ones. */
+ /* If we have no local addresses then use ::1 and 127.0.0.2 as local ones. */
if (IN_SET(af, AF_INET, AF_UNSPEC))
buffer[n++] = (struct local_address) {
.family = AF_INET,
- .ifindex = dns_synthesize_ifindex(ifindex),
- .address.in.s_addr = htobe32(0x7F000002),
+ .ifindex = LOOPBACK_IFINDEX,
+ .address.in.s_addr = htobe32(INADDR_LOCALADDRESS),
};
if (IN_SET(af, AF_INET6, AF_UNSPEC) && socket_ipv6_is_enabled())
buffer[n++] = (struct local_address) {
.family = AF_INET6,
- .ifindex = dns_synthesize_ifindex(ifindex),
+ .ifindex = LOOPBACK_IFINDEX,
.address.in6 = in6addr_loopback,
};
assert(address);
assert(answer);
- if (af == AF_INET && address->in.s_addr == htobe32(0x7F000002)) {
+ if (af == AF_INET && address->in.s_addr == htobe32(INADDR_LOCALADDRESS)) {
/* Always map the IPv4 address 127.0.0.2 to the local hostname, in addition to "localhost": */
if (r < 0)
return r;
- r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->full_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->full_hostname, LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
- r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->llmnr_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->llmnr_hostname, LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
- r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->mdns_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->mdns_hostname, LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
- r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
return 1; /* > 0 means: we have some gateway */
}
-static int synthesize_gateway_ptr(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) {
+static int synthesize_dns_stub_rr(
+ Manager *m,
+ const DnsResourceKey *key,
+ in_addr_t addr,
+ DnsAnswer **answer) {
+
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+ int r;
+
+ assert(m);
+ assert(key);
+ assert(answer);
+
+ if (!IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_ANY))
+ return 1; /* we still consider ourselves the owner of this name */
+
+ r = dns_answer_reserve(answer, 1);
+ if (r < 0)
+ return r;
+
+ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, dns_resource_key_name(key));
+ if (!rr)
+ return -ENOMEM;
+
+ rr->a.in_addr.s_addr = htobe32(addr);
+
+ r = dns_answer_add(*answer, rr, LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int synthesize_dns_stub_ptr(
+ Manager *m,
+ int af,
+ const union in_addr_union *address,
+ DnsAnswer **answer) {
+
+ int r;
+
+ assert(m);
+ assert(address);
+ assert(answer);
+
+ if (af != AF_INET)
+ return 0;
+
+ if (address->in.s_addr == htobe32(INADDR_DNS_STUB)) {
+
+ r = dns_answer_reserve(answer, 1);
+ if (r < 0)
+ return r;
+
+ r = answer_add_ptr(answer, "53.0.0.127.in-addr.arpa", "_localdnsstub", LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+
+ return 1;
+ }
+
+ if (address->in.s_addr == htobe32(INADDR_DNS_PROXY_STUB)) {
+
+ r = dns_answer_reserve(answer, 1);
+ if (r < 0)
+ return r;
+
+ r = answer_add_ptr(answer, "54.0.0.127.in-addr.arpa", "_localdnsproxy", LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int synthesize_gateway_ptr(
+ Manager *m,
+ int af,
+ const union in_addr_union *address,
+ int ifindex,
+ DnsAnswer **answer) {
+
_cleanup_free_ struct local_address *addresses = NULL;
int n;
} else if (is_localhost(name)) {
- r = synthesize_localhost_rr(m, key, ifindex, &answer);
+ r = synthesize_localhost_rr(m, key, &answer);
if (r < 0)
return log_error_errno(r, "Failed to synthesize localhost RRs: %m");
continue;
}
- } else if ((dns_name_endswith(name, "127.in-addr.arpa") > 0 && dns_name_equal(name, "2.0.0.127.in-addr.arpa") == 0) ||
+ } else if (is_dns_stub_hostname(name)) {
+
+ r = synthesize_dns_stub_rr(m, key, INADDR_DNS_STUB, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize local DNS stub RRs: %m");
+
+ } else if (is_dns_proxy_stub_hostname(name)) {
+
+ r = synthesize_dns_stub_rr(m, key, INADDR_DNS_PROXY_STUB, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize local DNS stub RRs: %m");
+
+ } else if ((dns_name_endswith(name, "127.in-addr.arpa") > 0 &&
+ dns_name_equal(name, "2.0.0.127.in-addr.arpa") == 0 &&
+ dns_name_equal(name, "53.0.0.127.in-addr.arpa") == 0 &&
+ dns_name_equal(name, "54.0.0.127.in-addr.arpa") == 0) ||
dns_name_equal(name, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) {
- r = synthesize_localhost_ptr(m, key, ifindex, &answer);
+ r = synthesize_localhost_ptr(m, key, &answer);
if (r < 0)
return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m");
} else if (dns_name_address(name, &af, &address) > 0) {
- int v, w;
+ int v, w, u;
if (getenv_bool("SYSTEMD_RESOLVED_SYNTHESIZE_HOSTNAME") == 0)
continue;
if (w < 0)
return log_error_errno(w, "Failed to synthesize gateway hostname PTR RR: %m");
- if (v == 0 && w == 0) /* This IP address is neither a local one nor a gateway */
+ u = synthesize_dns_stub_ptr(m, af, &address, &answer);
+ if (u < 0)
+ return log_error_errno(u, "Failed to synthesize local stub hostname PTR PR: %m");
+
+ if (v == 0 && w == 0 && u == 0) /* This IP address is neither a local one, nor a gateway, nor a stub address */
continue;
/* Note that we never synthesize reverse PTR for _outbound, since those are local
#include "resolved-dns-question.h"
#include "resolved-manager.h"
-int dns_synthesize_ifindex(int ifindex);
int dns_synthesize_family(uint64_t flags);
DnsProtocol dns_synthesize_protocol(uint64_t flags);
remove-system-units)
if [ -d /run/systemd/system ]; then
- systemctl --no-reload disable --now "$@"
+ systemctl --no-reload disable --now --no-warn "$@"
else
- systemctl --no-reload disable "$@"
+ systemctl --no-reload disable --no-warn "$@"
fi
;;
remove-user-units)
- systemctl --global disable "$@"
+ systemctl --global disable --no-warn "$@"
[ -d /run/systemd/system ] || exit 0
users=$(systemctl list-units 'user@*' --legend=no | sed -n -r 's/.*user@([0-9]+).service.*/\1/p')
for user in $users; do
SYSTEMD_BUS_TIMEOUT={{UPDATE_HELPER_USER_TIMEOUT}} \
- systemctl --user -M "$user@" disable --now "$@" &
+ systemctl --user -M "$user@" disable --now --no-warn "$@" &
done
wait
;;
_cleanup_(acl_freep) acl_t basic = NULL;
assert(acl_p);
+ assert(path);
for (r = acl_get_entry(*acl_p, ACL_FIRST_ENTRY, &i);
r > 0;
return ret;
}
-int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask) {
+int parse_acl(const char *text, acl_t *ret_acl_access, acl_t *ret_acl_default, bool want_mask) {
_cleanup_free_ char **a = NULL, **d = NULL; /* strings are not freed */
_cleanup_strv_free_ char **split = NULL;
int r = -EINVAL;
_cleanup_(acl_freep) acl_t a_acl = NULL, d_acl = NULL;
+ assert(text);
+ assert(ret_acl_access);
+ assert(ret_acl_default);
+
split = strv_split(text, ",");
if (!split)
return -ENOMEM;
}
}
- *acl_access = TAKE_PTR(a_acl);
- *acl_default = TAKE_PTR(d_acl);
+ *ret_acl_access = TAKE_PTR(a_acl);
+ *ret_acl_default = TAKE_PTR(d_acl);
return 0;
}
}
}
-static int find_acl_entry(acl_t acl, acl_entry_t entry, acl_entry_t *out) {
+static int find_acl_entry(acl_t acl, acl_entry_t entry, acl_entry_t *ret) {
acl_entry_t i;
int r;
if (r < 0)
return r;
if (r > 0) {
- *out = i;
- return 1;
+ if (ret)
+ *ret = i;
+ return 0;
}
}
if (r < 0)
return -errno;
- return 0;
+
+ return -ENOENT;
}
-int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl) {
- _cleanup_(acl_freep) acl_t old;
+int acls_for_file(const char *path, acl_type_t type, acl_t acl, acl_t *ret) {
+ _cleanup_(acl_freep) acl_t applied = NULL;
acl_entry_t i;
int r;
- old = acl_get_file(path, type);
- if (!old)
+ assert(path);
+
+ applied = acl_get_file(path, type);
+ if (!applied)
return -errno;
- for (r = acl_get_entry(new, ACL_FIRST_ENTRY, &i);
+ for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
r > 0;
- r = acl_get_entry(new, ACL_NEXT_ENTRY, &i)) {
+ r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
acl_entry_t j;
- r = find_acl_entry(old, i, &j);
- if (r < 0)
- return r;
- if (r == 0)
- if (acl_create_entry(&old, &j) < 0)
+ r = find_acl_entry(applied, i, &j);
+ if (r == -ENOENT) {
+ if (acl_create_entry(&applied, &j) < 0)
return -errno;
+ } else if (r < 0)
+ return r;
if (acl_copy_entry(j, i) < 0)
return -errno;
if (r < 0)
return -errno;
- *acl = TAKE_PTR(old);
+ if (ret)
+ *ret = TAKE_PTR(applied);
return 0;
}
int calc_acl_mask_if_needed(acl_t *acl_p);
int add_base_acls_if_needed(acl_t *acl_p, const char *path);
int acl_search_groups(const char* path, char ***ret_groups);
-int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask);
-int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl);
+int parse_acl(const char *text, acl_t *ret_acl_access, acl_t *ret_acl_default, bool want_mask);
+int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *ret);
int fd_add_uid_acl_permission(int fd, uid_t uid, unsigned mask);
/* acl_free takes multiple argument types.
#if HAVE_BLKID
# include <blkid.h>
+# include "sd-id128.h"
+
# include "macro.h"
+# include "string-util.h"
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(blkid_probe, blkid_free_probe, NULL);
+
+static inline int blkid_partition_get_uuid_id128(blkid_partition p, sd_id128_t *ret) {
+ const char *s;
+
+ assert(p);
+
+ s = blkid_partition_get_uuid(p);
+ if (isempty(s))
+ return -ENXIO;
+
+ return sd_id128_from_string(s, ret);
+}
+
+static inline int blkid_partition_get_type_id128(blkid_partition p, sd_id128_t *ret) {
+ const char *s;
+
+ assert(p);
+
+ s = blkid_partition_get_type_string(p);
+ if (isempty(s))
+ return -ENXIO;
+
+ return sd_id128_from_string(s, ret);
+}
+
+/* Define symbolic names for blkid_do_safeprobe() return values, since blkid only uses literal numbers. We
+ * prefix these symbolic definitions with underscores, to not invade libblkid's namespace needlessly. */
+enum {
+ _BLKID_SAFEPROBE_FOUND = 0,
+ _BLKID_SAFEPROBE_NOT_FOUND = 1,
+ _BLKID_SAFEPROBE_AMBIGUOUS = -2,
+ _BLKID_SAFEPROBE_ERROR = -1,
+};
+
#endif
/* If major(st.st_dev) is zero, this might mean we are backed by btrfs, which needs special
* handing, to get the backing device node. */
- r = fcntl(fd, F_GETFL);
- if (r < 0)
- return -errno;
-
- if (FLAGS_SET(r, O_PATH)) {
- _cleanup_close_ int regfd = -1;
-
- /* The fstat() above we can execute on an O_PATH fd. But the btrfs ioctl we cannot.
- * Hence acquire a "real" fd first, without the O_PATH flag. */
-
- regfd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
- if (regfd < 0)
- return regfd;
-
- r = btrfs_get_block_device_fd(regfd, &devnum);
- } else
- r = btrfs_get_block_device_fd(fd, &devnum);
+ r = btrfs_get_block_device_fd(fd, &devnum);
if (r == -ENOTTY) /* not btrfs */
return -ENOTBLK;
if (r < 0)
return 1;
}
- r = fcntl(fd, F_GETFL);
- if (r < 0)
- return -errno;
- if (FLAGS_SET(r, O_PATH) && (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))) {
- _cleanup_close_ int real_fd = -1;
-
- /* The fstat() above we can execute on an O_PATH fd. But the btrfs ioctl we cannot. Hence
- * acquire a "real" fd first, without the O_PATH flag. */
-
- real_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC);
- if (real_fd < 0)
- return real_fd;
- r = btrfs_get_block_device_fd(real_fd, ret);
- } else
- r = btrfs_get_block_device_fd(fd, ret);
+ r = btrfs_get_block_device_fd(fd, ret);
if (r > 0)
return 1;
if (r != -ENOTTY) /* not btrfs */
if (id[0] == '@') {
if (!strcaseeq(id, "@saved"))
return -1;
+ if (!config->entry_selected)
+ return -1;
id = config->entry_selected;
}
int status = chase_symlinks_and_access(p, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, F_OK, NULL, NULL);
/* Note that this shows two '/' between the root and the file. This is intentional to highlight (in
- * the abscence of color support) to the user that the boot loader is only interested in the second
+ * the absence of color support) to the user that the boot loader is only interested in the second
* part of the file. */
printf("%13s%s %s%s/%s", strempty(field), field ? ":" : " ", ansi_grey(), root, ansi_normal());
if (r < 0)
return r;
- r = fcntl(fd, F_GETFL);
- if (r < 0)
- return -errno;
- if (FLAGS_SET(r, O_PATH)) {
- /* An O_PATH fd was specified, let's convert here to a proper one, as btrfs ioctl's can't deal with
- * O_PATH. */
-
- real_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
- if (real_fd < 0)
- return real_fd;
-
- fd = real_fd;
- }
+ /* If an O_PATH fd was specified, let's convert here to a proper one, as btrfs ioctl's can't deal
+ * with O_PATH. */
+ fd = fd_reopen_condition(fd, O_RDONLY|O_CLOEXEC|O_DIRECTORY, O_PATH|O_DIRECTORY, &real_fd);
+ if (fd < 0)
+ return fd;
strncpy(args.name, subvolume, sizeof(args.name)-1);
int btrfs_get_block_device_fd(int fd, dev_t *dev) {
struct btrfs_ioctl_fs_info_args fsi = {};
+ _cleanup_close_ int regfd = -1;
uint64_t id;
int r;
assert(fd >= 0);
assert(dev);
+ fd = fd_reopen_condition(fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY, O_PATH, ®fd);
+ if (fd < 0)
+ return fd;
+
r = fd_is_fs_type(fd, BTRFS_SUPER_MAGIC);
if (r < 0)
return r;
int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool insert_intermediary_qgroup) {
_cleanup_free_ uint64_t *qgroups = NULL;
+ _cleanup_close_ int real_fd = -1;
uint64_t parent_subvol;
bool changed = false;
int n = 0, r;
* qgroup that then includes all its own child subvolumes.
*/
+ /* Turn this into a proper fd, if it is currently O_PATH */
+ fd = fd_reopen_condition(fd, O_RDONLY|O_CLOEXEC, O_PATH, &real_fd);
+ if (fd < 0)
+ return fd;
+
if (subvol_id == 0) {
r = btrfs_is_subvol_fd(fd);
if (r < 0)
if (STR_IN_SET(field, "User", "Group"))
return bus_append_string(m, field, eq);
+ if (streq(field, "OOMPolicy"))
+ return bus_append_string(m, field, eq);
+
return 0;
}
if (r < 0)
return r;
}
- r = symlinkat(target, dt, to);
+ r = RET_NERRNO(symlinkat(target, dt, to));
if (copy_flags & COPY_MAC_CREATE)
mac_selinux_create_file_clear();
- if (r < 0)
- return -errno;
+ if (r < 0) {
+ if (FLAGS_SET(copy_flags, COPY_GRACEFUL_WARN) && (ERRNO_IS_PRIVILEGE(r) || ERRNO_IS_NOT_SUPPORTED(r))) {
+ log_notice_errno(r, "Failed to copy symlink '%s', ignoring: %m", from);
+ return 0;
+ }
+
+ return r;
+ }
if (fchownat(dt, to,
uid_is_valid(override_uid) ? override_uid : st->st_uid,
if (r < 0)
return r;
}
- r = mkfifoat(dt, to, st->st_mode & 07777);
+ r = RET_NERRNO(mkfifoat(dt, to, st->st_mode & 07777));
if (copy_flags & COPY_MAC_CREATE)
mac_selinux_create_file_clear();
- if (r < 0)
- return -errno;
+ if (r < 0) {
+ if (FLAGS_SET(copy_flags, COPY_GRACEFUL_WARN) && (ERRNO_IS_PRIVILEGE(r) || ERRNO_IS_NOT_SUPPORTED(r))) {
+ log_notice_errno(r, "Failed to copy fifo '%s', ignoring: %m", from);
+ return 0;
+ }
+
+ return r;
+ }
if (fchownat(dt, to,
uid_is_valid(override_uid) ? override_uid : st->st_uid,
if (r < 0)
return r;
}
- r = mknodat(dt, to, st->st_mode, st->st_rdev);
+ r = RET_NERRNO(mknodat(dt, to, st->st_mode, st->st_rdev));
if (copy_flags & COPY_MAC_CREATE)
mac_selinux_create_file_clear();
- if (r < 0)
- return -errno;
+ if (r < 0) {
+ if (FLAGS_SET(copy_flags, COPY_GRACEFUL_WARN) && (ERRNO_IS_PRIVILEGE(r) || ERRNO_IS_NOT_SUPPORTED(r))) {
+ log_notice_errno(r, "Failed to copy node '%s', ignoring: %m", from);
+ return 0;
+ }
+
+ return r;
+ }
if (fchownat(dt, to,
uid_is_valid(override_uid) ? override_uid : st->st_uid,
#include "set.h"
typedef enum CopyFlags {
- COPY_REFLINK = 1 << 0, /* Try to reflink */
- COPY_MERGE = 1 << 1, /* Merge existing trees with our new one to copy */
- COPY_REPLACE = 1 << 2, /* Replace an existing file if there's one */
- COPY_SAME_MOUNT = 1 << 3, /* Don't descend recursively into other file systems, across mount point boundaries */
- COPY_MERGE_EMPTY = 1 << 4, /* Merge an existing, empty directory with our new tree to copy */
- COPY_CRTIME = 1 << 5, /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */
- COPY_SIGINT = 1 << 6, /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */
- COPY_SIGTERM = 1 << 7, /* ditto, but for SIGTERM */
- COPY_MAC_CREATE = 1 << 8, /* Create files with the correct MAC label (currently SELinux only) */
- COPY_HARDLINKS = 1 << 9, /* Try to reproduce hard links */
- COPY_FSYNC = 1 << 10, /* fsync() after we are done */
- COPY_FSYNC_FULL = 1 << 11, /* fsync_full() after we are done */
- COPY_SYNCFS = 1 << 12, /* syncfs() the *top-level* dir after we are done */
- COPY_ALL_XATTRS = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */
- COPY_HOLES = 1 << 14, /* Copy holes */
+ COPY_REFLINK = 1 << 0, /* Try to reflink */
+ COPY_MERGE = 1 << 1, /* Merge existing trees with our new one to copy */
+ COPY_REPLACE = 1 << 2, /* Replace an existing file if there's one */
+ COPY_SAME_MOUNT = 1 << 3, /* Don't descend recursively into other file systems, across mount point boundaries */
+ COPY_MERGE_EMPTY = 1 << 4, /* Merge an existing, empty directory with our new tree to copy */
+ COPY_CRTIME = 1 << 5, /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */
+ COPY_SIGINT = 1 << 6, /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */
+ COPY_SIGTERM = 1 << 7, /* ditto, but for SIGTERM */
+ COPY_MAC_CREATE = 1 << 8, /* Create files with the correct MAC label (currently SELinux only) */
+ COPY_HARDLINKS = 1 << 9, /* Try to reproduce hard links */
+ COPY_FSYNC = 1 << 10, /* fsync() after we are done */
+ COPY_FSYNC_FULL = 1 << 11, /* fsync_full() after we are done */
+ COPY_SYNCFS = 1 << 12, /* syncfs() the *top-level* dir after we are done */
+ COPY_ALL_XATTRS = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */
+ COPY_HOLES = 1 << 14, /* Copy holes */
+ COPY_GRACEFUL_WARN = 1 << 15, /* Skip copying file types that aren't supported by the target filesystem */
} CopyFlags;
typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata);
/* libcryptsetup added crypt_reencrypt() in 2.2.0, and marked it obsolete in 2.4.0, replacing it with
* crypt_reencrypt_run(), which takes one extra argument but is otherwise identical. The old call is
* still available though, and given we want to support 2.2.0 for a while longer, we'll stick to the
- * old symbol. Howerver, the old symbols now has a GCC deprecation decorator, hence let's turn off
+ * old symbol. However, the old symbols now has a GCC deprecation decorator, hence let's turn off
* warnings about this for now. */
DISABLE_WARNING_DEPRECATED_DECLARATIONS;
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_READ_ONLY |
- DISSECT_IMAGE_USR_NO_ROOT,
+ DISSECT_IMAGE_USR_NO_ROOT |
+ DISSECT_IMAGE_ADD_PARTITION_DEVICES |
+ DISSECT_IMAGE_PIN_PARTITION_DEVICES,
&m);
if (r < 0)
return r;
/* how many times to wait for the device nodes to appear */
#define N_DEVICE_NODE_LIST_ATTEMPTS 10
-int probe_filesystem_full(int fd, const char *path, char **ret_fstype) {
+int probe_filesystem_full(
+ int fd,
+ const char *path,
+ uint64_t offset,
+ uint64_t size,
+ char **ret_fstype) {
+
/* Try to find device content type and return it in *ret_fstype. If nothing is found,
- * 0/NULL will be returned. -EUCLEAN will be returned for ambiguous results, and an
+ * 0/NULL will be returned. -EUCLEAN will be returned for ambiguous results, and a
* different error otherwise. */
#if HAVE_BLKID
path = path_by_fd;
}
+ if (size == 0) /* empty size? nothing found! */
+ goto not_found;
+
b = blkid_new_probe();
if (!b)
return -ENOMEM;
errno = 0;
- r = blkid_probe_set_device(b, fd, 0, 0);
+ r = blkid_probe_set_device(
+ b,
+ fd,
+ offset,
+ size == UINT64_MAX ? 0 : size); /* when blkid sees size=0 it understands "everything". We prefer using UINT64_MAX for that */
if (r != 0)
return errno_or_else(ENOMEM);
errno = 0;
r = blkid_do_safeprobe(b);
- if (r == 1)
+ if (r == _BLKID_SAFEPROBE_NOT_FOUND)
goto not_found;
- if (r == -2)
+ if (r == _BLKID_SAFEPROBE_AMBIGUOUS)
return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN),
"Results ambiguous for partition %s", path);
- if (r != 0)
+ if (r == _BLKID_SAFEPROBE_ERROR)
return log_debug_errno(errno_or_else(EIO), "Failed to probe partition %s: %m", path);
+ assert(r == _BLKID_SAFEPROBE_FOUND);
+
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
if (fstype) {
}
#if HAVE_BLKID
-static int dissected_image_probe_filesystem(DissectedImage *m) {
+static int dissected_image_probe_filesystems(DissectedImage *m, int fd) {
int r;
assert(m);
if (!p->found)
continue;
- if (!p->fstype && p->mount_node_fd >= 0 && !p->decrypted_node) {
- r = probe_filesystem_full(p->mount_node_fd, p->node, &p->fstype);
- if (r < 0 && r != -EUCLEAN)
+ if (!p->fstype) {
+ /* If we have an fd referring to the partition block device, use that. Otherwise go
+ * via the whole block device or backing regular file, and read via offset. */
+ if (p->mount_node_fd >= 0)
+ r = probe_filesystem_full(p->mount_node_fd, p->node, 0, UINT64_MAX, &p->fstype);
+ else
+ r = probe_filesystem_full(fd, p->node, p->offset, p->size, &p->fstype);
+ if (r < 0)
return r;
}
errno = 0;
r = blkid_do_safeprobe(b);
- if (IN_SET(r, -2, 1))
- return log_debug_errno(SYNTHETIC_ERRNO(ENOPKG), "Failed to identify any partition table.");
- if (r != 0)
+ if (r == _BLKID_SAFEPROBE_ERROR)
return errno_or_else(EIO);
+ if (IN_SET(r, _BLKID_SAFEPROBE_AMBIGUOUS, _BLKID_SAFEPROBE_NOT_FOUND))
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOPKG), "Failed to identify any partition table.");
+
+ assert(r == _BLKID_SAFEPROBE_FOUND);
if ((!(flags & DISSECT_IMAGE_GPT_ONLY) &&
(flags & DISSECT_IMAGE_GENERIC_ROOT)) ||
_cleanup_close_ int mount_node_fd = -1;
sd_id128_t uuid = SD_ID128_NULL;
- if (FLAGS_SET(flags, DISSECT_IMAGE_OPEN_PARTITION_DEVICES)) {
+ if (FLAGS_SET(flags, DISSECT_IMAGE_PIN_PARTITION_DEVICES)) {
mount_node_fd = open_partition(devname, /* is_partition = */ false, m->loop);
if (mount_node_fd < 0)
return mount_node_fd;
m->encrypted = streq_ptr(fstype, "crypto_LUKS");
m->has_verity = verity && verity->data_path;
- m->verity_ready = m->has_verity &&
- verity->root_hash &&
- (verity->designator < 0 || verity->designator == PARTITION_ROOT);
+ m->verity_ready = verity_settings_data_covers(verity, PARTITION_ROOT);
m->has_verity_sig = false; /* signature not embedded, must be specified */
- m->verity_sig_ready = m->verity_ready &&
- verity->root_hash_sig;
+ m->verity_sig_ready = m->verity_ready && verity->root_hash_sig;
m->image_uuid = uuid;
if (verity && verity->data_path)
return -EBADR;
- if (FLAGS_SET(flags, DISSECT_IMAGE_MANAGE_PARTITION_DEVICES)) {
+ if (FLAGS_SET(flags, DISSECT_IMAGE_ADD_PARTITION_DEVICES)) {
/* Safety check: refuse block devices that carry a partition table but for which the kernel doesn't
* do partition scanning. */
r = blockdev_partscan_enabled(fd);
* Kernel returns EBUSY if there's already a partition by that number or an overlapping
* partition already existent. */
- if (FLAGS_SET(flags, DISSECT_IMAGE_MANAGE_PARTITION_DEVICES)) {
+ if (FLAGS_SET(flags, DISSECT_IMAGE_ADD_PARTITION_DEVICES)) {
r = block_device_add_partition(fd, node, nr, (uint64_t) start * 512, (uint64_t) size * 512);
if (r < 0) {
if (r != -EBUSY)
}
if (is_gpt) {
- const char *stype, *sid, *fstype = NULL, *label;
+ const char *fstype = NULL, *label;
sd_id128_t type_id, id;
GptPartitionType type;
bool rw = true, growfs = false;
- sid = blkid_partition_get_uuid(pp);
- if (!sid)
- continue;
- if (sd_id128_from_string(sid, &id) < 0)
+ r = blkid_partition_get_uuid_id128(pp, &id);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to read partition UUID, ignoring: %m");
continue;
+ }
- stype = blkid_partition_get_type_string(pp);
- if (!stype)
- continue;
- if (sd_id128_from_string(stype, &type_id) < 0)
+ r = blkid_partition_get_type_id128(pp, &type_id);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to read partition type UUID, ignoring: %m");
continue;
+ }
type = gpt_partition_type_from_uuid(type_id);
label = blkid_partition_get_name(pp); /* libblkid returns NULL here if empty */
- if (type.designator == PARTITION_HOME) {
-
- check_partition_flags(node, pflags,
- SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
-
- if (pflags & SD_GPT_FLAG_NO_AUTO)
- continue;
-
- rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
- growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
-
- } else if (type.designator == PARTITION_SRV) {
+ if (IN_SET(type.designator,
+ PARTITION_HOME,
+ PARTITION_SRV,
+ PARTITION_XBOOTLDR,
+ PARTITION_TMP)) {
check_partition_flags(node, pflags,
SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
fstype = "vfat";
- } else if (type.designator == PARTITION_XBOOTLDR) {
-
- check_partition_flags(node, pflags,
- SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
-
- if (pflags & SD_GPT_FLAG_NO_AUTO)
- continue;
-
- rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
- growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
-
} else if (type.designator == PARTITION_ROOT) {
check_partition_flags(node, pflags,
if (pflags & SD_GPT_FLAG_NO_AUTO)
continue;
+ fstype = "swap";
+
/* We don't have a designator for SD_GPT_LINUX_GENERIC so check the UUID instead. */
} else if (sd_id128_equal(type.uuid, SD_GPT_LINUX_GENERIC)) {
return -ENOMEM;
}
- } else if (type.designator == PARTITION_TMP) {
-
- check_partition_flags(node, pflags,
- SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
-
- if (pflags & SD_GPT_FLAG_NO_AUTO)
- continue;
-
- rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
- growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
-
} else if (type.designator == PARTITION_VAR) {
check_partition_flags(node, pflags,
return r;
if (!sd_id128_equal(var_uuid, id)) {
- log_debug("Found a /var/ partition, but its UUID didn't match our expectations, ignoring.");
+ log_debug("Found a /var/ partition, but its UUID didn't match our expectations "
+ "(found: " SD_ID128_UUID_FORMAT_STR ", expected: " SD_ID128_UUID_FORMAT_STR "), ignoring.",
+ SD_ID128_FORMAT_VAL(id), SD_ID128_FORMAT_VAL(var_uuid));
continue;
}
}
dissected_partition_done(m->partitions + type.designator);
}
- if (FLAGS_SET(flags, DISSECT_IMAGE_OPEN_PARTITION_DEVICES)) {
+ if (FLAGS_SET(flags, DISSECT_IMAGE_PIN_PARTITION_DEVICES) &&
+ type.designator != PARTITION_SWAP) {
mount_node_fd = open_partition(node, /* is_partition = */ true, m->loop);
if (mount_node_fd < 0)
return mount_node_fd;
.mount_node_fd = TAKE_FD(mount_node_fd),
.offset = (uint64_t) start * 512,
.size = (uint64_t) size * 512,
+ .gpt_flags = pflags,
};
}
_cleanup_close_ int mount_node_fd = -1;
_cleanup_free_ char *o = NULL;
sd_id128_t id = SD_ID128_NULL;
- const char *sid, *options = NULL;
+ const char *options = NULL;
/* First one wins */
if (m->partitions[PARTITION_XBOOTLDR].found)
continue;
- if (FLAGS_SET(flags, DISSECT_IMAGE_OPEN_PARTITION_DEVICES)) {
+ if (FLAGS_SET(flags, DISSECT_IMAGE_PIN_PARTITION_DEVICES)) {
mount_node_fd = open_partition(node, /* is_partition = */ true, m->loop);
if (mount_node_fd < 0)
return mount_node_fd;
}
- sid = blkid_partition_get_uuid(pp);
- if (sid)
- (void) sd_id128_from_string(sid, &id);
+ (void) blkid_partition_get_uuid_id128(pp, &id);
options = mount_options_from_designator(mount_options, PARTITION_XBOOTLDR);
if (options) {
_cleanup_free_ char *o = NULL;
const char *options;
- if (FLAGS_SET(flags, DISSECT_IMAGE_OPEN_PARTITION_DEVICES)) {
+ if (FLAGS_SET(flags, DISSECT_IMAGE_PIN_PARTITION_DEVICES)) {
mount_node_fd = open_partition(generic_node, /* is_partition = */ true, m->loop);
if (mount_node_fd < 0)
return mount_node_fd;
}
}
+ r = dissected_image_probe_filesystems(m, fd);
+ if (r < 0)
+ return r;
+
return 0;
}
#endif
int r;
assert(path);
- assert((flags & DISSECT_IMAGE_BLOCK_DEVICE) == 0);
assert(ret);
fd = open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
}
if (!p->decrypted_fstype && p->mount_node_fd >= 0 && p->decrypted_node) {
- r = probe_filesystem_full(p->mount_node_fd, p->decrypted_node, &p->decrypted_fstype);
+ r = probe_filesystem_full(p->mount_node_fd, p->decrypted_node, 0, UINT64_MAX, &p->decrypted_fstype);
if (r < 0 && r != -EUCLEAN)
return r;
}
m->loop = loop_device_ref(loop);
- r = dissect_image(m, loop->fd, loop->node, verity, mount_options, flags | DISSECT_IMAGE_BLOCK_DEVICE);
- if (r < 0)
- return r;
-
- r = dissected_image_probe_filesystem(m);
+ r = dissect_image(m, loop->fd, loop->node, verity, mount_options, flags);
if (r < 0)
return r;
assert(ret_directory);
assert(ret_loop_device);
+ /* We intend to mount this right-away, hence add the partitions if needed and pin them*/
+ flags |= DISSECT_IMAGE_ADD_PARTITION_DEVICES |
+ DISSECT_IMAGE_PIN_PARTITION_DEVICES;
+
r = verity_settings_load(&verity, image, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to load root hash data: %m");
return log_debug_errno(r, "Failed to load root hash: %m");
dissect_image_flags = (verity.data_path ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0) |
- (relax_extension_release_check ? DISSECT_IMAGE_RELAX_SYSEXT_CHECK : 0);
+ (relax_extension_release_check ? DISSECT_IMAGE_RELAX_SYSEXT_CHECK : 0) |
+ DISSECT_IMAGE_ADD_PARTITION_DEVICES |
+ DISSECT_IMAGE_PIN_PARTITION_DEVICES;
/* Note that we don't use loop_device_make here, as the FD is most likely O_PATH which would not be
* accepted by LOOP_CONFIGURE, so just let loop_device_make_by_path reopen it as a regular FD. */
int mount_node_fd;
uint64_t size;
uint64_t offset;
+ uint64_t gpt_flags;
};
#define DISSECTED_PARTITION_NULL \
DISSECT_IMAGE_MOUNT_READ_ONLY,
DISSECT_IMAGE_GROWFS = 1 << 18, /* Grow file systems in partitions marked for that to the size of the partitions after mount */
DISSECT_IMAGE_MOUNT_IDMAPPED = 1 << 19, /* Mount mounts with kernel 5.12-style userns ID mapping, if file system type doesn't support uid=/gid= */
- DISSECT_IMAGE_MANAGE_PARTITION_DEVICES = 1 << 20, /* Manage partition devices, e.g. probe each partition in more detail */
- DISSECT_IMAGE_OPEN_PARTITION_DEVICES = 1 << 21, /* Open dissected partitions and decrypted partitions */
- DISSECT_IMAGE_BLOCK_DEVICE = DISSECT_IMAGE_MANAGE_PARTITION_DEVICES |
- DISSECT_IMAGE_OPEN_PARTITION_DEVICES,
+ DISSECT_IMAGE_ADD_PARTITION_DEVICES = 1 << 20, /* Create partition devices via BLKPG_ADD_PARTITION */
+ DISSECT_IMAGE_PIN_PARTITION_DEVICES = 1 << 21, /* Open dissected partitions and decrypted partitions and pin them by fd */
DISSECT_IMAGE_RELAX_SYSEXT_CHECK = 1 << 22, /* Don't insist that the extension-release file name matches the image name */
} DissectImageFlags;
DEFINE_TRIVIAL_CLEANUP_FUNC(MountOptions*, mount_options_free_all);
const char* mount_options_from_designator(const MountOptions *options, PartitionDesignator designator);
-int probe_filesystem_full(int fd, const char *path, char **ret_fstype);
+int probe_filesystem_full(int fd, const char *path, uint64_t offset, uint64_t size, char **ret_fstype);
static inline int probe_filesystem(const char *path, char **ret_fstype) {
- return probe_filesystem_full(-1, path, ret_fstype);
+ return probe_filesystem_full(-1, path, 0, UINT64_MAX, ret_fstype);
}
int dissect_image_file(
const char *path,
int verity_settings_load(VeritySettings *verity, const char *image, const char *root_hash_path, const char *root_hash_sig_path);
void verity_settings_done(VeritySettings *verity);
+static inline bool verity_settings_data_covers(const VeritySettings *verity, PartitionDesignator d) {
+ /* Returns true if the verity settings contain sufficient information to cover the specified partition */
+ return verity &&
+ ((d >= 0 && verity->designator == d) || (d == PARTITION_ROOT && verity->designator < 0)) &&
+ verity->root_hash &&
+ verity->data_path;
+}
+
int dissected_image_load_verity_sig_partition(DissectedImage *m, int fd, VeritySettings *verity);
bool dissected_image_verity_candidate(const DissectedImage *image, PartitionDesignator d);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "fd-util.h"
+#include "fdisk-util.h"
+
+#if HAVE_LIBFDISK
+
+int fdisk_new_context_fd(int fd, bool read_only, struct fdisk_context **ret) {
+ _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
+ int r;
+
+ assert(ret);
+
+ if (fd < 0)
+ return -EBADF;
+
+ c = fdisk_new_context();
+ if (!c)
+ return -ENOMEM;
+
+ r = fdisk_assign_device(c, FORMAT_PROC_FD_PATH(fd), read_only);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(c);
+ return 0;
+}
+
+int fdisk_partition_get_uuid_as_id128(struct fdisk_partition *p, sd_id128_t *ret) {
+ const char *ids;
+
+ assert(p);
+ assert(ret);
+
+ ids = fdisk_partition_get_uuid(p);
+ if (!ids)
+ return -ENXIO;
+
+ return sd_id128_from_string(ids, ret);
+}
+
+int fdisk_partition_get_type_as_id128(struct fdisk_partition *p, sd_id128_t *ret) {
+ struct fdisk_parttype *pt;
+ const char *pts;
+
+ assert(p);
+ assert(ret);
+
+ pt = fdisk_partition_get_type(p);
+ if (!pt)
+ return -ENXIO;
+
+ pts = fdisk_parttype_get_string(pt);
+ if (!pts)
+ return -ENXIO;
+
+ return sd_id128_from_string(pts, ret);
+}
+
+#endif
#include <libfdisk.h>
+#include "sd-id128.h"
+
#include "macro.h"
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct fdisk_context*, fdisk_unref_context, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct fdisk_parttype*, fdisk_unref_parttype, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct fdisk_table*, fdisk_unref_table, NULL);
+int fdisk_new_context_fd(int fd, bool read_only, struct fdisk_context **ret);
+
+int fdisk_partition_get_uuid_as_id128(struct fdisk_partition *p, sd_id128_t *ret);
+int fdisk_partition_get_type_as_id128(struct fdisk_partition *p, sd_id128_t *ret);
+
#endif
errno = 0;
b = blkid_new_probe_from_filename(node);
if (!b)
- return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "%s: Failed to create blkid probe: %m", node);
+ return log_error_errno(errno_or_else(ENOMEM), "%s: Failed to create blkid probe: %m", node);
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)
+ if (r == _BLKID_SAFEPROBE_AMBIGUOUS)
return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "%s: File system is ambiguous.", node);
- else if (r == 1)
+ if (r == _BLKID_SAFEPROBE_NOT_FOUND)
return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "%s: File system does not contain a label.", node);
- else if (r != 0)
- return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "%s: Failed to probe file system: %m", node);
+ if (r == _BLKID_SAFEPROBE_ERROR)
+ return log_error_errno(errno_or_else(EIO), "%s: Failed to probe file system: %m", node);
+
+ assert(r == _BLKID_SAFEPROBE_FOUND);
r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &type, NULL);
if (r != 0)
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
if (r != 0)
- return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "%s: Failed to probe PART_ENTRY_TYPE: %m", node);
+ return log_error_errno(errno_or_else(EIO), "%s: Failed to probe PART_ENTRY_TYPE: %m", node);
if (sd_id128_string_equal(v, SD_GPT_XBOOTLDR) <= 0)
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
if (r != 0)
- return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "%s: Failed to probe PART_ENTRY_UUID: %m", node);
+ return log_error_errno(errno_or_else(EIO), "%s: Failed to probe PART_ENTRY_UUID: %m", node);
r = sd_id128_from_string(v, &uuid);
if (r < 0)
return log_error_errno(r, "%s: Partition has invalid UUID PART_ENTRY_TYPE=%s: %m", node, v);
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
if (r != 0)
- return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "%s: Failed to probe PART_ENTRY_TYPE: %m", node);
+ return log_error_errno(errno_or_else(EIO), "%s: Failed to probe PART_ENTRY_TYPE: %m", node);
if (!streq(v, "0xea"))
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
_cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
_cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
+ InstallInfo *info;
+ bool has_install_info = false;
int r;
STRV_FOREACH(name, names) {
if (!unit_name_is_valid(*name, UNIT_NAME_ANY))
return install_changes_add(changes, n_changes, -EUCLEAN, *name, NULL);
- r = install_info_add(&ctx, *name, NULL, lp->root_dir, /* auxiliary= */ false, NULL);
+ r = install_info_add(&ctx, *name, NULL, lp->root_dir, /* auxiliary= */ false, &info);
+ if (r >= 0)
+ r = install_info_traverse(&ctx, lp, info, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
+
if (r < 0)
- return r;
+ return install_changes_add(changes, n_changes, r, *name, NULL);
+
+ /* If we enable multiple units, some with install info and others without,
+ * the "empty [Install] section" warning is not shown. Let's make the behavior
+ * of disable align with that. */
+ has_install_info = has_install_info || install_info_has_rules(info) || install_info_has_also(info);
}
r = install_context_mark_for_removal(&ctx, lp, &remove_symlinks_to, config_path, changes, n_changes);
+ if (r >= 0)
+ r = remove_marked_symlinks(remove_symlinks_to, config_path, lp, flags & UNIT_FILE_DRY_RUN, changes, n_changes);
+
if (r < 0)
return r;
- return remove_marked_symlinks(remove_symlinks_to, config_path, lp, flags & UNIT_FILE_DRY_RUN, changes, n_changes);
+ /* The warning is shown only if it's a no-op */
+ return install_changes_have_modification(*changes, *n_changes) || has_install_info;
}
-
int unit_file_disable(
LookupScope scope,
UnitFileFlags flags,
'exit-status.h',
'extension-release.c',
'extension-release.h',
- 'fdisk-util.h',
'fdset.c',
'fdset.h',
'fileio-label.c',
dependencies : libshared_deps,
install : true,
install_dir : rootpkglibdir)
+
+shared_fdisk_sources = files(
+ 'fdisk-util.h',
+ 'fdisk-util.c',
+)
+
+if get_option('fdisk') != 'false'
+ libshared_fdisk = static_library(
+ 'shared-fdisk',
+ shared_fdisk_sources,
+ include_directories : includes,
+ dependencies : [libfdisk],
+ c_args : ['-fvisibility=default'],
+ build_by_default : false)
+endif
}
static int do_mcopy(const char *node, const char *root) {
+ _cleanup_free_ char *mcopy = NULL;
_cleanup_strv_free_ char **argv = NULL;
- _cleanup_closedir_ DIR *rootdir = NULL;
+ _cleanup_close_ int rfd = -1;
+ _cleanup_free_ DirectoryEntries *de = NULL;
struct stat st;
int r;
if (dir_is_empty(root, /*ignore_hidden_or_backup=*/ false))
return 0;
- argv = strv_new("mcopy", "-b", "-s", "-p", "-Q", "-n", "-m", "-i", node);
+ r = find_executable("mcopy", &mcopy);
+ if (r == -ENOENT)
+ return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "Could not find mcopy binary.");
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether mcopy binary exists: %m");
+
+ argv = strv_new(mcopy, "-s", "-p", "-Q", "-m", "-i", node);
if (!argv)
return log_oom();
/* mcopy copies the top level directory instead of everything in it so we have to pass all
* the subdirectories to mcopy instead to end up with the correct directory structure. */
- rootdir = opendir(root);
- if (!rootdir)
- return log_error_errno(errno, "Failed to open directory '%s'", root);
+ rfd = open(root, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
+ if (rfd < 0)
+ return log_error_errno(errno, "Failed to open directory '%s': %m", root);
+
+ r = readdir_all(rfd, RECURSE_DIR_SORT|RECURSE_DIR_ENSURE_TYPE, &de);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read '%s' contents: %m", root);
- FOREACH_DIRENT(de, rootdir, return -errno) {
- char *p = path_join(root, de->d_name);
+ for (size_t i = 0; i < de->n_entries; i++) {
+ _cleanup_free_ char *p = NULL;
+
+ p = path_join(root, de->entries[i]->d_name);
if (!p)
return log_oom();
+ if (!IN_SET(de->entries[i]->d_type, DT_REG, DT_DIR)) {
+ log_debug("%s is not a file/directory which are the only file types supported by vfat, ignoring", p);
+ continue;
+ }
+
r = strv_consume(&argv, TAKE_PTR(p));
if (r < 0)
return log_oom();
if (r < 0)
return log_oom();
- if (fstat(dirfd(rootdir), &st) < 0)
+ if (fstat(rfd, &st) < 0)
return log_error_errno(errno, "Failed to stat '%s': %m", root);
r = safe_fork("(mcopy)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR|FORK_NEW_USERNS|FORK_CLOSE_ALL_FDS, NULL);
/* Avoid failures caused by mismatch in expectations between mkfs.vfat and mcopy by disabling
* the stricter mcopy checks using MTOOLS_SKIP_CHECK. */
- execvpe("mcopy", argv, STRV_MAKE("MTOOLS_SKIP_CHECK=1"));
+ execve(mcopy, argv, STRV_MAKE("MTOOLS_SKIP_CHECK=1"));
log_error_errno(errno, "Failed to execute mcopy: %m");
"0 0\n"
"d--755 0 0\n", f);
- r = recurse_dir_at(AT_FDCWD, root, STATX_TYPE|STATX_MODE, UINT_MAX, 0, protofile_print_item, f);
+ r = recurse_dir_at(AT_FDCWD, root, STATX_TYPE|STATX_MODE, UINT_MAX, RECURSE_DIR_SORT, protofile_print_item, f);
if (r < 0)
return log_error_errno(r, "Failed to recurse through %s: %m", root);
/* 127.0.0.54 in native endian (The IP address we listen on we only implement "proxy" mode) */
#define INADDR_DNS_PROXY_STUB ((in_addr_t) 0x7f000036U)
+/* 127.0.0.2 is an address we always map to the local hostname. This is different from 127.0.0.1 which maps to "localhost" */
+#define INADDR_LOCALADDRESS ((in_addr_t) 0x7f000002U)
+
typedef enum DnsCacheMode DnsCacheMode;
enum DnsCacheMode {
DISSECT_IMAGE_GENERIC_ROOT |
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_MOUNT_ROOT_ONLY |
- DISSECT_IMAGE_USR_NO_ROOT;
+ DISSECT_IMAGE_USR_NO_ROOT |
+ DISSECT_IMAGE_ADD_PARTITION_DEVICES |
+ DISSECT_IMAGE_PIN_PARTITION_DEVICES;
r = verity_settings_load(&verity_settings, img->path, NULL, NULL);
if (r < 0)
_cleanup_close_ int orig_stdout_fd = -1;
int r;
+ if (size > 4*1024*1024)
+ return 0; /* See the comment below about the limit for strv_length(). */
+
/* We don't want to fill the logs with messages about parse errors.
* Disable most logging if not running standalone */
if (!getenv("SYSTEMD_LOG_LEVEL"))
InstallChange *changes = NULL;
size_t n_changes = 0;
int carries_install_info = -1;
- bool ignore_carries_install_info = arg_quiet;
+ bool ignore_carries_install_info = arg_quiet || arg_no_warn;
int r;
if (!argv[1])
if (streq(verb, "enable")) {
r = unit_file_enable(arg_scope, flags, arg_root, names, &changes, &n_changes);
carries_install_info = r;
- } else if (streq(verb, "disable"))
+ } else if (streq(verb, "disable")) {
r = unit_file_disable(arg_scope, flags, arg_root, names, &changes, &n_changes);
- else if (streq(verb, "reenable")) {
+ carries_install_info = r;
+ } else if (streq(verb, "reenable")) {
r = unit_file_reenable(arg_scope, flags, arg_root, names, &changes, &n_changes);
carries_install_info = r;
} else if (streq(verb, "link"))
method = "EnableUnitFiles";
expect_carries_install_info = true;
} else if (streq(verb, "disable")) {
- method = "DisableUnitFiles";
+ method = "DisableUnitFilesWithFlagsAndInstallInfo";
+ expect_carries_install_info = true;
send_force = false;
} else if (streq(verb, "reenable")) {
method = "ReenableUnitFiles";
}
if (send_runtime) {
- r = sd_bus_message_append(m, "b", arg_runtime);
+ if (streq(method, "DisableUnitFilesWithFlagsAndInstallInfo"))
+ r = sd_bus_message_append(m, "t", arg_runtime ? UNIT_FILE_RUNTIME : 0);
+ else
+ r = sd_bus_message_append(m, "b", arg_runtime);
if (r < 0)
return bus_log_create_error(r);
}
if (carries_install_info == 0 && !ignore_carries_install_info)
log_notice("The unit files have no installation config (WantedBy=, RequiredBy=, Also=,\n"
"Alias= settings in the [Install] section, and DefaultInstance= for template\n"
- "units). This means they are not meant to be enabled using systemctl.\n"
+ "units). This means they are not meant to be enabled or disabled using systemctl.\n"
" \n" /* trick: the space is needed so that the line does not get stripped from output */
"Possible reasons for having this kind of units are:\n"
"%1$s A unit may be statically enabled by being symlinked from another unit's\n"
return r;
if (a == ACTION_REBOOT) {
- const char *arg = NULL;
-
- if (argc > 1) {
- if (arg_reboot_argument)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Both --reboot-argument= and positional argument passed to reboot command, refusing.");
-
- log_notice("Positional argument to reboot command is deprecated, please use --reboot-argument= instead. Accepting anyway.");
- arg = argv[1];
- } else
- arg = arg_reboot_argument;
-
- if (arg) {
- r = update_reboot_parameter_and_warn(arg, false);
+ if (arg_reboot_argument) {
+ r = update_reboot_parameter_and_warn(arg_reboot_argument, false);
if (r < 0)
return r;
}
int arg_check_inhibitors = -1;
bool arg_dry_run = false;
bool arg_quiet = false;
+bool arg_no_warn = false;
bool arg_full = false;
bool arg_recursive = false;
bool arg_with_dependencies = false;
" kexec, suspend, hibernate, suspend-then-hibernate,\n"
" hybrid-sleep, default, rescue, emergency, and exit.\n"
" -q --quiet Suppress output\n"
+ " --no-warn Don't generate warning when trying to enable/disable\n"
+ " units without install information\n"
" --wait For (re)start, wait until service stopped again\n"
" For is-system-running, wait until startup is completed\n"
" --no-block Do not wait until operation finished\n"
ARG_READ_ONLY,
ARG_MKDIR,
ARG_MARKED,
+ ARG_NO_WARN,
};
static const struct option options[] = {
{ "no-wall", no_argument, NULL, ARG_NO_WALL },
{ "dry-run", no_argument, NULL, ARG_DRY_RUN },
{ "quiet", no_argument, NULL, 'q' },
+ { "no-warn", no_argument, NULL, ARG_NO_WARN },
{ "root", required_argument, NULL, ARG_ROOT },
{ "image", required_argument, NULL, ARG_IMAGE },
{ "force", no_argument, NULL, 'f' },
arg_marked = true;
break;
+ case ARG_NO_WARN:
+ arg_no_warn = true;
+ break;
+
case '.':
/* Output an error mimicking getopt, and print a hint afterwards */
log_error("%s: invalid option -- '.'", program_invocation_name);
{ "import-environment", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_import_environment },
{ "halt", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "poweroff", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
- { "reboot", VERB_ANY, 2, VERB_ONLINE_ONLY, verb_start_system_special },
+ { "reboot", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "kexec", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "suspend", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
{ "hibernate", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
extern int arg_check_inhibitors;
extern bool arg_dry_run;
extern bool arg_quiet;
+extern bool arg_no_warn;
extern bool arg_full;
extern bool arg_recursive;
extern bool arg_with_dependencies;
PartitionInfo *ret) {
_cleanup_free_ char *label_copy = NULL, *device = NULL;
- const char *pts, *ids, *label;
+ const char *label;
struct fdisk_partition *p;
- struct fdisk_parttype *pt;
uint64_t start, size, flags;
sd_id128_t ptid, id;
GptPartitionType type;
if (!label)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found a partition without a label.");
- pt = fdisk_partition_get_type(p);
- if (!pt)
- return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire type of partition: %m");
-
- pts = fdisk_parttype_get_string(pt);
- if (!pts)
- return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire type of partition as string: %m");
-
- r = sd_id128_from_string(pts, &ptid);
+ r = fdisk_partition_get_type_as_id128(p, &ptid);
if (r < 0)
- return log_error_errno(r, "Failed to parse partition type UUID %s: %m", pts);
-
- ids = fdisk_partition_get_uuid(p);
- if (!ids)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found a partition without a UUID.");
+ return log_error_errno(r, "Failed to read partition type UUID: %m");
- r = sd_id128_from_string(ids, &id);
+ r = fdisk_partition_get_uuid_as_id128(p, &id);
if (r < 0)
- return log_error_errno(r, "Failed to parse partition UUID %s: %m", ids);
+ return log_error_errno(r, "Failed to read partition UUID: %m");
r = fdisk_partition_get_attrs_as_uint64(p, &flags);
if (r < 0)
fd1 = -1;
}
+TEST(fd_reopen_condition) {
+ _cleanup_close_ int fd1 = -1, fd3 = -1;
+ int fd2, fl;
+
+ /* Open without O_PATH */
+ fd1 = open("/usr/", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
+ assert_se(fd1 >= 0);
+
+ fl = fcntl(fd1, F_GETFL);
+ assert_se(FLAGS_SET(fl, O_DIRECTORY));
+ assert_se(!FLAGS_SET(fl, O_PATH));
+
+ fd2 = fd_reopen_condition(fd1, O_DIRECTORY, O_DIRECTORY|O_PATH, &fd3);
+ assert_se(fd2 == fd1);
+ assert_se(fd3 < 0);
+
+ /* Switch on O_PATH */
+ fd2 = fd_reopen_condition(fd1, O_DIRECTORY|O_PATH, O_DIRECTORY|O_PATH, &fd3);
+ assert_se(fd2 != fd1);
+ assert_se(fd3 == fd2);
+
+ fl = fcntl(fd2, F_GETFL);
+ assert_se(FLAGS_SET(fl, O_DIRECTORY));
+ assert_se(FLAGS_SET(fl, O_PATH));
+
+ close_and_replace(fd1, fd3);
+
+ fd2 = fd_reopen_condition(fd1, O_DIRECTORY|O_PATH, O_DIRECTORY|O_PATH, &fd3);
+ assert_se(fd2 == fd1);
+ assert_se(fd3 < 0);
+
+ /* Switch off O_PATH again */
+ fd2 = fd_reopen_condition(fd1, O_DIRECTORY, O_DIRECTORY|O_PATH, &fd3);
+ assert_se(fd2 != fd1);
+ assert_se(fd3 == fd2);
+
+ fl = fcntl(fd2, F_GETFL);
+ assert_se(FLAGS_SET(fl, O_DIRECTORY));
+ assert_se(!FLAGS_SET(fl, O_PATH));
+
+ close_and_replace(fd1, fd3);
+
+ fd2 = fd_reopen_condition(fd1, O_DIRECTORY, O_DIRECTORY|O_PATH, &fd3);
+ assert_se(fd2 == fd1);
+ assert_se(fd3 < 0);
+}
+
TEST(take_fd) {
_cleanup_close_ int fd1 = -1, fd2 = -1;
int array[2] = { -1, -1 }, i = 0;
#include <linux/loop.h>
#include <pthread.h>
#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
#include "alloc-util.h"
#include "capability-util.h"
assert_se(dissected->partitions[PARTITION_HOME].node);
}
+static void verify_dissected_image_harder(DissectedImage *dissected) {
+ verify_dissected_image(dissected);
+
+ assert_se(streq(dissected->partitions[PARTITION_ESP].fstype, "vfat"));
+ assert_se(streq(dissected->partitions[PARTITION_XBOOTLDR].fstype, "vfat"));
+ assert_se(streq(dissected->partitions[PARTITION_ROOT].fstype, "ext4"));
+ assert_se(streq(dissected->partitions[PARTITION_HOME].fstype, "ext4"));
+}
+
static void* thread_func(void *ptr) {
int fd = PTR_TO_FD(ptr);
int r;
log_notice("Acquired loop device %s, will mount on %s", loop->node, mounted);
- r = dissect_loop_device(loop, NULL, NULL, DISSECT_IMAGE_READ_ONLY, &dissected);
+ r = dissect_loop_device(loop, NULL, NULL, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_ADD_PARTITION_DEVICES|DISSECT_IMAGE_PIN_PARTITION_DEVICES, &dissected);
if (r < 0)
log_error_errno(r, "Failed dissect loopback device %s: %m", loop->node);
assert_se(r >= 0);
assert_se(loop_device_make(fd, O_RDWR, 0, UINT64_MAX, 0, LO_FLAGS_PARTSCAN, LOCK_EX, &loop) >= 0);
#if HAVE_BLKID
- assert_se(dissect_loop_device(loop, NULL, NULL, 0, &dissected) >= 0);
+ assert_se(dissect_loop_device(loop, NULL, NULL, DISSECT_IMAGE_ADD_PARTITION_DEVICES|DISSECT_IMAGE_PIN_PARTITION_DEVICES, &dissected) >= 0);
verify_dissected_image(dissected);
FOREACH_STRING(fs, "vfat", "ext4") {
assert_se(make_filesystem(dissected->partitions[PARTITION_HOME].node, "ext4", "home", NULL, id, true) >= 0);
dissected = dissected_image_unref(dissected);
+
+ /* We created the file systems now via the per-partition block devices. But the dissection code might
+ * probe them via the whole block device. These block devices have separate buffer caches though,
+ * hence what was written via the partition device might not appear on the whole block device
+ * yet. Let's hence explicitly flush the whole block device, so that the read-back definitely
+ * works. */
+ assert_se(ioctl(loop->fd, BLKFLSBUF, 0) >= 0);
+
+ /* Try to read once, without pinning or adding partitions, i.e. by only accessing the whole block
+ * device. */
assert_se(dissect_loop_device(loop, NULL, NULL, 0, &dissected) >= 0);
- verify_dissected_image(dissected);
+ verify_dissected_image_harder(dissected);
+ dissected = dissected_image_unref(dissected);
+
+ /* Now go via the loopback device after all, but this time add/pin, because now we want to mount it. */
+ assert_se(dissect_loop_device(loop, NULL, NULL, DISSECT_IMAGE_ADD_PARTITION_DEVICES|DISSECT_IMAGE_PIN_PARTITION_DEVICES, &dissected) >= 0);
+ verify_dissected_image_harder(dissected);
assert_se(mkdtemp_malloc(NULL, &mounted) >= 0);
creation = r >= 0 ? CREATION_NORMAL : CREATION_EXISTING;
- /* Open the inode via O_PATH, regardless if we managed to create it or not. Maybe it is is already the FIFO we want */
+ /* Open the inode via O_PATH, regardless if we managed to create it or not. Maybe it is already the FIFO we want */
fd = openat(pfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
if (fd < 0) {
if (r < 0)
#if defined(SD_GPT_ROOT_NATIVE) && ENABLE_EFI
- _cleanup_free_ char *root_id = NULL, *root_label = NULL;
+ _cleanup_free_ char *root_label = NULL;
bool found_esp_or_xbootldr = false;
+ sd_id128_t root_id = SD_ID128_NULL;
int r;
assert(pr);
int nvals = blkid_partlist_numof_partitions(pl);
for (int i = 0; i < nvals; i++) {
blkid_partition pp;
- const char *stype, *sid, *label;
- sd_id128_t type;
+ const char *label;
+ sd_id128_t type, id;
pp = blkid_partlist_get_partition(pl, i);
if (!pp)
continue;
- sid = blkid_partition_get_uuid(pp);
- if (!sid)
+ r = blkid_partition_get_uuid_id128(pp, &id);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to get partition UUID, ignoring: %m");
continue;
+ }
- label = blkid_partition_get_name(pp); /* returns NULL if empty */
-
- stype = blkid_partition_get_type_string(pp);
- if (!stype)
+ r = blkid_partition_get_type_id128(pp, &type);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to get partition type UUID, ignoring: %m");
continue;
+ }
- if (sd_id128_from_string(stype, &type) < 0)
- continue;
+ label = blkid_partition_get_name(pp); /* returns NULL if empty */
if (sd_id128_in_set(type, SD_GPT_ESP, SD_GPT_XBOOTLDR)) {
- sd_id128_t id, esp_or_xbootldr;
+ sd_id128_t esp_or_xbootldr;
/* We found an ESP or XBOOTLDR, let's see if it matches the ESP/XBOOTLDR we booted from. */
- if (sd_id128_from_string(sid, &id) < 0)
- continue;
-
r = efi_loader_get_device_part_uuid(&esp_or_xbootldr);
if (r < 0)
return r;
/* We found a suitable root partition, let's remember the first one, or the one with
* the newest version, as determined by comparing the partition labels. */
- if (!root_id || strverscmp_improved(label, root_label) > 0) {
- r = free_and_strdup(&root_id, sid);
- if (r < 0)
- return r;
+ if (sd_id128_is_null(root_id) || strverscmp_improved(label, root_label) > 0) {
+ root_id = id;
r = free_and_strdup(&root_label, label);
if (r < 0)
/* We found the ESP/XBOOTLDR on this disk, and also found a root partition, nice! Let's export its
* UUID */
- if (found_esp_or_xbootldr && root_id)
- udev_builtin_add_property(dev, test, "ID_PART_GPT_AUTO_ROOT_UUID", root_id);
+ if (found_esp_or_xbootldr && !sd_id128_is_null(root_id))
+ udev_builtin_add_property(dev, test, "ID_PART_GPT_AUTO_ROOT_UUID", SD_ID128_TO_UUID_STRING(root_id));
#endif
return 0;
--- /dev/null
+{"config":"default @saved","loader":[""]}
\ No newline at end of file
#!/usr/bin/python3
# SPDX-License-Identifier: LGPL-2.1-or-later
-#
+# pylint: disable=line-too-long,invalid-name,missing-module-docstring,missing-function-docstring,too-many-statements,broad-except
import argparse
import logging
import sys
+import time
import pexpect
console.sendcontrol('a')
console.send('0')
logger.info("verify broadcast message")
- console.expect('Broadcast message from root@H on %s' % pty, 2)
- console.expect('The system will reboot at %s' % date, 2)
+ console.expect(f'Broadcast message from root@H on {pty}', 2)
+ console.expect(f'The system will reboot at {date}', 2)
logger.info("check show output")
console.sendline('shutdown --show')
- console.expect("Reboot scheduled for %s, use 'shutdown -c' to cancel" % date, 2)
+ console.expect(f"Reboot scheduled for {date}, use 'shutdown -c' to cancel", 2)
logger.info("cancel shutdown")
console.sendline('shutdown -c')
except Exception as e:
logger.error(e)
logger.info("killing child pid %d", console.pid)
- console.terminate(force=True)
+ # We can't use console.terminate(force=True) right away, since
+ # the internal delay between sending a signal and checking the process
+ # is just 0.1s [0], which means we'd get SIGKILLed pretty quickly.
+ # Let's send SIGHUP/SIGINT first, wait a bit, and then follow-up with
+ # SIGHUP/SIGINT/SIGKILL if the process is still alive.
+ # [0] https://github.com/pexpect/pexpect/blob/acb017a97332c19a9295660fe87316926a8adc55/pexpect/spawnbase.py#L71
+ console.terminate()
+ for _ in range(10):
+ if not console.isalive():
+ break
+
+ time.sleep(1)
+ else:
+ # We haven't exited the loop early, so check if the process is
+ # still alive - if so, force-kill it.
+ if console.isalive():
+ console.terminate(force=True)
return ret
unit: sectors
first-lba: 2048
last-lba: 2097118
-$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
-$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
+$imgs/zzz1 : start= 2048, size= 1775576, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
+$imgs/zzz2 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
runas testuser systemd-repart --definitions="$defs" \
--dry-run=no \
--seed="$seed" \
- --exclude-partitions=root \
+ --empty=force \
+ --skip-partitions=home,root \
"$imgs/zzz"
output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
unit: sectors
first-lba: 2048
last-lba: 2097118
-$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
runas testuser systemd-repart --definitions="$defs" \
echo "${FUNCNAME[0]}: test failover"
local device expected link mpoint part
local -a devices
+ mkdir -p /mnt
mpoint="$(mktemp -d /mnt/mpathXXX)"
wwid="deaddeadbeef0000"
path="/dev/disk/by-id/wwn-0x$wwid"
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out "/tmp/pcrsign-private.pem"
openssl rsa -pubout -in "/tmp/pcrsign-private.pem" -out "/tmp/pcrsign-public.pem"
+ MEASURE_BANKS=("--bank=sha256")
+ # Check if SHA1 signatures are supported
+ #
+ # Some distros have started phasing out SHA1, so make sure the SHA1
+ # signatures are supported before trying to use them.
+ if echo hello | openssl dgst -sign /tmp/pcrsign-private.pem -sha1 >/dev/null; then
+ MEASURE_BANKS+=("--bank=sha1")
+ fi
+
# Sign current PCR state with it
- /usr/lib/systemd/systemd-measure sign --current --bank=sha1 --bank=sha256 --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: | tee "/tmp/pcrsign.sig"
+ /usr/lib/systemd/systemd-measure sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: | tee "/tmp/pcrsign.sig"
dd if=/dev/urandom of=/tmp/pcrtestdata bs=1024 count=64
systemd-creds encrypt /tmp/pcrtestdata /tmp/pcrtestdata.encrypted --with-key=host+tpm2-with-public-key --tpm2-public-key="/tmp/pcrsign-public.pem"
systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" | cmp - /tmp/pcrtestdata
systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" > /dev/null && { echo 'unexpected success'; exit 1; }
# Sign new PCR state, decrypting should work now.
- /usr/lib/systemd/systemd-measure sign --current --bank=sha1 --bank=sha256 --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: > "/tmp/pcrsign.sig2"
+ /usr/lib/systemd/systemd-measure sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: > "/tmp/pcrsign.sig2"
systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig2" | cmp - /tmp/pcrtestdata
# Now, do the same, but with a cryptsetup binding
SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 /usr/lib/systemd/systemd-cryptsetup attach test-volume2 $img - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1 && { echo 'unexpected success'; exit 1; }
# But once we sign the current PCRs, we should be able to unlock again
- /usr/lib/systemd/systemd-measure sign --current --bank=sha1 --bank=sha256 --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: > "/tmp/pcrsign.sig3"
+ /usr/lib/systemd/systemd-measure sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: > "/tmp/pcrsign.sig3"
SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 /usr/lib/systemd/systemd-cryptsetup attach test-volume2 $img - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1
/usr/lib/systemd/systemd-cryptsetup detach test-volume2
SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 /usr/lib/systemd/systemd-cryptsetup attach test-volume2 $img - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1
systemd-cgtop --cpu=time
systemd-cgtop -P
systemd-cgtop -k
-# FIXME: https://github.com/systemd/systemd/issues/25248
-#systemd-cgtop --recursive=no
+systemd-cgtop --recursive=no -P
+systemd-cgtop --recursive=no -k
systemd-cgtop --depth=0
systemd-cgtop --depth=100
(! systemd-cgtop --order=foo)
(! systemd-cgtop --depth=-1)
(! systemd-cgtop --recursive=foo)
+(! systemd-cgtop --recursive=no)
(! systemd-cgtop --delay=1foo)
# shellcheck disable=SC2016
ROOT_HASHED_PASSWORD2='$6$foobarsalt$q.P2932zYMLbKnjFwIxPI8y3iuxeuJ2BgE372LcZMMnj3Gcg/9mJg2LPKUl.ha0TG/.fRNNnRQcLfzM0SNot3.'
+# Debian and Ubuntu use /etc/default/locale instead of /etc/locale.conf. Make
+# sure we use the appropriate path for locale configuration.
+LOCALE_PATH="/etc/locale.conf"
+[ -e "$LOCALE_PATH" ] || LOCALE_PATH="/etc/default/locale"
+[ -e "$LOCALE_PATH" ] || systemd-firstboot --locale=C.UTF-8
+
# Create a minimal root so we don't modify the testbed
ROOT=test-root
mkdir -p "$ROOT/bin"
touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell"
systemd-firstboot --root="$ROOT" --locale=foo
-grep -q "LANG=foo" "$ROOT/etc/locale.conf"
-rm -fv "$ROOT/etc/locale.conf"
+grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
+rm -fv "$ROOT$LOCALE_PATH"
systemd-firstboot --root="$ROOT" --locale-messages=foo
-grep -q "LC_MESSAGES=foo" "$ROOT/etc/locale.conf"
-rm -fv "$ROOT/etc/locale.conf"
+grep -q "LC_MESSAGES=foo" "$ROOT$LOCALE_PATH"
+rm -fv "$ROOT$LOCALE_PATH"
systemd-firstboot --root="$ROOT" --locale=foo --locale-messages=bar
-grep -q "LANG=foo" "$ROOT/etc/locale.conf"
-grep -q "LC_MESSAGES=bar" "$ROOT/etc/locale.conf"
+grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
+grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
systemd-firstboot --root="$ROOT" --keymap=foo
grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
--root-password-hashed="$ROOT_HASHED_PASSWORD2" \
--root-shell=/bin/barshell \
--kernel-command-line="hello.world=0"
-grep -q "LANG=foo" "$ROOT/etc/locale.conf"
-grep -q "LC_MESSAGES=bar" "$ROOT/etc/locale.conf"
+grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
+grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$"
grep -q "foobar" "$ROOT/etc/hostname"
--root-password-hashed="$ROOT_HASHED_PASSWORD2" \
--root-shell=/bin/barshell \
--kernel-command-line="hello.world=0"
-grep -q "LANG=locale-overwrite" "$ROOT/etc/locale.conf"
-grep -q "LC_MESSAGES=messages-overwrite" "$ROOT/etc/locale.conf"
+grep -q "LANG=locale-overwrite" "$ROOT$LOCALE_PATH"
+grep -q "LC_MESSAGES=messages-overwrite" "$ROOT$LOCALE_PATH"
grep -q "KEYMAP=keymap-overwrite" "$ROOT/etc/vconsole.conf"
readlink "$ROOT/etc/localtime" | grep -q "/CET$"
grep -q "hostname-overwrite" "$ROOT/etc/hostname"
mkdir "$ROOT"
# Copy everything at once (--copy)
systemd-firstboot --root="$ROOT" --copy
-diff /etc/locale.conf "$ROOT/etc/locale.conf"
+diff $LOCALE_PATH "$ROOT$LOCALE_PATH"
diff <(awk -F: '/^root/ { print $7; }' /etc/passwd) <(awk -F: '/^root/ { print $7; }' "$ROOT/etc/passwd")
diff <(awk -F: '/^root/ { print $2; }' /etc/shadow) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow")
[[ -e /etc/vconsole.conf ]] && diff /etc/vconsole.conf "$ROOT/etc/vconsole.conf"
mkdir "$ROOT"
# Copy everything at once, but now by using separate switches
systemd-firstboot --root="$ROOT" --copy-locale --copy-keymap --copy-timezone --copy-root-password --copy-root-shell
-diff /etc/locale.conf "$ROOT/etc/locale.conf"
+diff $LOCALE_PATH "$ROOT$LOCALE_PATH"
diff <(awk -F: '/^root/ { print $7; }' /etc/passwd) <(awk -F: '/^root/ { print $7; }' "$ROOT/etc/passwd")
diff <(awk -F: '/^root/ { print $2; }' /etc/shadow) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow")
[[ -e /etc/vconsole.conf ]] && diff /etc/vconsole.conf "$ROOT/etc/vconsole.conf"
# We can do only limited testing here, since it's all an interactive stuff,
# so --prompt and --prompt-root-password are skipped on purpose
echo -ne "\nfoo\nbar\n" | systemd-firstboot --root="$ROOT" --prompt-locale
-grep -q "LANG=foo" "$ROOT/etc/locale.conf"
-grep -q "LC_MESSAGES=bar" "$ROOT/etc/locale.conf"
+grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
+grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
echo -ne "\nfoo\n" | systemd-firstboot --root="$ROOT" --prompt-keymap
grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
echo -ne "\nEurope/Berlin\n" | systemd-firstboot --root="$ROOT" --prompt-timezone
assert_in '10.0.3.1 10.0.3.2' "$(resolvectl dns hoge)"
assert_in '10.0.3.3 10.0.3.4' "$(resolvectl dns hoge.foo)"
+# Tests for _localdnsstub and _localdnsproxy
+assert_in '127.0.0.53' "$(resolvectl query _localdnsstub)"
+assert_in '_localdnsstub' "$(resolvectl query 127.0.0.53)"
+assert_in '127.0.0.54' "$(resolvectl query _localdnsproxy)"
+assert_in '_localdnsproxy' "$(resolvectl query 127.0.0.54)"
+
+assert_in '127.0.0.53' "$(dig @127.0.0.53 _localdnsstub)"
+assert_in '_localdnsstub' "$(dig @127.0.0.53 -x 127.0.0.53)"
+assert_in '127.0.0.54' "$(dig @127.0.0.53 _localdnsproxy)"
+assert_in '_localdnsproxy' "$(dig @127.0.0.53 -x 127.0.0.54)"
+
# Tests for mDNS and LLMNR settings
mkdir -p /run/systemd/resolved.conf.d
{
Before=shutdown.target initrd-switch-root.target
# Only run this if the boot loader can support random seed initialization.
-ConditionPathExists|=/sys/firmware/efi/efivars/LoaderFeatures-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
-ConditionPathExists|=/sys/firmware/efi/efivars/StubFeatures-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
+ConditionPathExists=|/sys/firmware/efi/efivars/LoaderFeatures-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
+ConditionPathExists=|/sys/firmware/efi/efivars/StubFeatures-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
# Only run this if there is no system token defined yet
ConditionPathExists=!/sys/firmware/efi/efivars/LoaderSystemToken-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
Documentation=man:systemd-networkd-wait-online.service(8)
DefaultDependencies=no
Conflicts=shutdown.target
-Requires=systemd-networkd.service
+BindsTo=systemd-networkd.service
After=systemd-networkd.service
Before=network-online.target shutdown.target
Documentation=man:systemd-networkd-wait-online.service(8)
DefaultDependencies=no
Conflicts=shutdown.target
-Requires=systemd-networkd.service
+BindsTo=systemd-networkd.service
After=systemd-networkd.service
Before=network-online.target shutdown.target