python3-evdev
python3-jinja2
python3-lxml
+ python3-pefile
python3-pip
python3-pyparsing
python3-setuptools
name: "Pull Request Labeler"
on:
-- pull_request_target
+ pull_request_target:
+ types: [opened, synchronize, reopened, ready_for_review]
+ issue_comment:
+ types: [created]
permissions:
contents: read
jobs:
triage:
- if: github.event.repository.name != 'systemd-security'
+ if: github.repository == 'systemd/systemd'
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: actions/labeler@e54e5b338fbd6e6cdb5d60f51c22335fc57c401e
+ if: github.event_name == 'pull_request_target'
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
configuration-path: .github/labeler.yml
sync-labels: "" # This is a workaround for issue 18671
+
+ - uses: actions/github-script@d556feaca394842dc55e4734bf3bb9f685482fa0
+ if: github.event_name == 'pull_request_target' && !github.event.pull_request.draft
+ with:
+ script: |
+ response = await github.rest.issues.listLabelsOnIssue({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ });
+
+ good_to_merge = [
+ "good-to-merge/waiting-for-ci 👍",
+ "good-to-merge/after-next-release",
+ "good-to-merge/with-minor-suggestions",
+ "good-to-merge/waiting-for-reporter-feedback 👍",
+ ];
+
+ if (response.data.every(l => !good_to_merge.includes(l.name))) {
+ await github.rest.issues.addLabels({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ labels: ["please-review"]
+ });
+ }
+
+ for (const label of ["reviewed/needs-rework 🔨",
+ "ci-fails/needs-rework 🔥",
+ "ci-failure-appears-unrelated",
+ "needs-rebase"]) {
+ try {
+ await github.rest.issues.removeLabel({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ name: label,
+ });
+ } catch (err) {
+ if (err.status != 404) {
+ throw err;
+ }
+ }
+ }
+
+ - uses: actions/github-script@d556feaca394842dc55e4734bf3bb9f685482fa0
+ if: github.event_name == 'issue_comment' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/please-review')
+ with:
+ script: |
+ await github.rest.issues.addLabels({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ labels: ["please-review"]
+ })
libzstd-dev
perl
python3-libevdev
+ python3-pefile
python3-pyparsing
rpm
zstd
# [0] https://github.com/mesonbuild/meson/issues/7360
# [1] https://github.com/systemd/systemd/pull/18908#issuecomment-792250110
- 'sed -i "/^CONFIGURE_OPTS=(/a--werror" .packit_rpm/systemd.spec'
+ # Ignore unpackages standalone binaries
+ - "sed -i 's/assert False,.*/pass/' .packit_rpm/split-files.py"
jobs:
- job: copr_build
CHANGES WITH 253 in spe:
+ Deprecations and incompatible changes
+
+ * systemctl will now warn when invoked without /proc mounted (e.g. when
+ invoked after chroot into an image without the API mount points like
+ /proc being set up.) Operation in such an environment is not fully
+ supported.
+
+ * 'udevadm hwdb' subcommand is deprecated and will emit a warning.
+ systemd-hwdb (added in 2014) should be used instead.
+
+ * 'bootctl --json' now outputs well-formed JSON, instead of a stream
+ of newline-separated JSON objects.
+
+ * Udev rules in 60-evdev.rules have been changed to load hwdb properties
+ for all modalias patterns. Previously only the first matching pattern
+ was used. This could change what properties are assigned if the user
+ has more and less specific patterns that could match the same device,
+ but it is expected that the change will have no effect for most users.
+
+ New components:
+
+ * A tool to build, measure, and sign Unified Kernel Images (UKIs) has
+ been added. This replaces functionality provided by 'dracut --uefi'
+ and extends it with automatic calculation of offsets, insertion of
+ signed PCR policies generated by systemd-measure, support for initrd
+ concatenation, signing of the embedded Linux image and the combined
+ image with sbsign, and heuristics to autodetect the kernel uname and
+ verify the splash image.
+
+ Changes in systemd:
+
+ * Initrd environments which are not on a temporary file system (for
+ example an overlayfs combination) are now supported. Systemd will only
+ skip removal of the files in the initrd if it doesn't detect a
+ temporary file system.
+
+ * New MemoryZSwapMax= option has been added to configure
+ memory.zswap.max cgroup properties (the maximum amount of zswap used).
+
+ * Scope units now support OOMPolicy=. Login session scopes default to
+ OOMPolicy=continue, allowing login scopes to survive the oom killer
+ terminating some processes in the scope.
+
+ * systemd-fstab-generator now supports x-systemd.makefs option for
+ /sysroot (in the initrd).
+
+ Changes in udev:
+
+ * The new net naming scheme "v253" has been introduced. In the new
+ scheme, ID_NET_NAME_PATH is also set for USB devices not connected via
+ a PCI bus. This extends the converage of predictable interface names
+ in some embedded systems.
+
+ The "amba" bus path is now included in ID_NET_NAME_PATH, resulting in
+ a more informative path on some embedded systems.
+
Changes in sd-boot, bootctl, and the Boot Loader Specification:
* systemd-boot now passes its random seed directly to the kernel's RNG
protocol or a prior seed in LINUX_EFI_RANDOM_SEED_TABLE_GUID from a
preceding bootloader.
- * The random seed stored in ESP is now refreshed whenever
+ * The random seed stored in the ESP is now refreshed whenever
systemd-random-seed.service is run.
* systemd-boot handles various seed inputs using a domain- and
virtualized ones, and is activated in the case that the system token
is missing from either sd-boot and sd-stub booted systems.
+ * systemd-boot now supports being loaded not from the ESP, for example
+ for direct kernel boot under QEMU or when embedded into the firmware.
+
+ Changes in kernel-install:
+
+ * A new "installation layout" can be configured as layout=uki. With this
+ setting, a Boot Loader Specification Type#1 entry will not be created.
+ Instead, a new kernel-install plugin 90-uki-copy.install will copy any
+ .efi files from the staging area into the boot partition. A plugin to
+ generate the UKI .efi file must be provided separately.
+
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.
+ * 'systemctl reboot' has dropped support for accepting a positional
+ argument as the argument to the reboot(2) syscall. Please use the
+ --reboot-argument option instead.
+
+ * 'systemctl disable' will now warn when called on units without install
+ information. A new --no-warn option has been added that silences this
+ warning.
+
+ * 'systemctl kexec' now supports XEN.
+
+ Changes in systemd-networkd and related tools:
+
+ * The RouteMetric= option (for DHCPv4, DHCPv6, and IPv6 advertised
+ routes) now accepts three values, for high, medium, and low preference
+ of the router (which can be set with the RouterPreference=) setting.
+
+ * systemd-networkd-wait-online now supports alternative interface names.
+
+ Changes in systemd-dissect:
+
+ * systemd-dissect gained a new option --list, to print the paths fo the
+ files and directories in the image.
+
+ * systemd-dissect gained a new option --mtree, to generate output
+ compatible with BSD mtree(5).
+
+ * systemd-dissect gained a new option --with, to execute a command in
+ the image temporarily mounted.
+
+ * systemd-dissect gained a new option --discover, to search for
+ Discoverable Disk Images (DDIs) in well-known directories. This will
+ list machine, portable service and system extension disk images.
+
+ * systemd-dissect now understands 2nd stage initrd images stored as a
+ Discoverable Disk Image (DDI).
+
+ Changes in systemd-repart:
+
+ * systemd-repart gained new options --include-partitions and
+ --exclude-partitions to filter operation on partitions by type UUID.
+ This allows systemd-repart to be used to build images in which the
+ type of one partition is set based on the contents of another
+ partition (for example when the boot partition shall include a verity
+ hash of the root partition).
+
+ * systemd-repart now supports erofs (a read-only file system similar to
+ squashfs).
+
+ Changes in systemd-homed:
+
+ * systemd-homed gained support for luksPbkdfForceIterations (the
+ intended number of iterations for the PBKDF operation on LUKS).
+
+ Changes in systemd-homenamed:
+
+ * systemd-homed now exports the contents of
+ /sys/class/dmi/id/bios_vendor and /sys/class/dmi/id/bios_date via two
+ new D-Bus properties: FirmwareVendor and FirmwareDate. This allows
+ unprivileged code to access those values.
+
+ Changes in libsystemd and shared code:
+
+ * sd-bus gained new convenience functions sd_bus_emit_signal_to(),
+ sd_bus_emit_signal_tov(), and sd_bus_message_new_signal_to().
+
+ * Detection of chroot environments now works if /proc/ is not mounted.
+ This affects systemd-detect-virt --chroot, but also means that systemd
+ tools will silently skip various operations in such an environment.
+
+ * "Lockheed Matrin Hardened Security for Intel Processors" (HS SRE)
+ virtualization is now detected.
+
+ Changes in the build system:
+
+ * Standalone variant of systemd-repart is built (if -Dstandalone=true).
+
+ * systemd-ac-power has been moved to /usr/bin/, to, for example, allow
+ scripts to conditionalize execution on AC power supply.
+
+ Changes in the documentation:
+
+ * Specifications that are not closely tied to systemd have moved to
+ https://uapi-group.org/specifications/: the Boot Loader Specification
+ and the Discoverable Partitions Specification.
+
CHANGES WITH 252 🎃:
docbook-xsl (optional, required for documentation)
xsltproc (optional, required for documentation)
python-jinja2
+ python-pefile
python-lxml (optional, required to build the indices)
python >= 3.5
meson >= 0.53.2
4.3 new baseline). Then drop support for "!!" modifier for ExecStart= which
is only supported for such old kernels.
+* drop support for kernels lacking memfd_create() (i.e. make 3.17 new
+ baseline), then drop all pipe() based fallbacks.
+
* drop support for getrandom()-less kernels. (GRND_INSECURE means once kernel
5.6 becomes our baseline). See
https://github.com/systemd/systemd/pull/24101#issuecomment-1193966468 for
Features:
+* in journald, write out a recognizable log record whenever the system clock is
+ changed ("stepped"), and in timesyncd whenever we acquire an NTP fix
+ ("slewing"). Then, in journalctl for each boot time we come across, find
+ these records, and use the structured info they include to display
+ "corrected" wallclock time, as calculted from the monotonic timestamp in the
+ log record, adjusted by the delta declared in the structured log record.
+
+* in journald: whenever we start a new journal file because the boot ID
+ changed, let's generate a recognizable log record containing info about old
+ and new new ID. Then, when displaying log stream in journalctl look for these
+ records, to be able to order them.
+
+* timesyncd: when saving/restoring clock try to take boot time into account.
+ Specifically, along with the saved clock, store the current boot ID. When
+ starting, check if the boot id matches. If so, don't do anything (we are on
+ the same boot and clock just kept running anyway). If not, then read
+ CLOCK_BOOTTIME (which started at boot), and add it to the saved clock
+ timestamp, to compensate for the time we spent booting. If EFI timestamps are
+ available, also include that in the calculation. With this we'll then only
+ miss the time spent during shutdown after timesync stopped and before the
+ system actually reset.
+
+* systemd-stub: maybe store a "boot counter" in the ESP, and pass it down to
+ userspace to allow ordering boots (for example in journalctl). The counter
+ would be monotonically increased on every boot.
+
+* systemd-sysext: for sysext DDIs picked up via EFI stub, set much stricter
+ image policy by default
+
* systemd-dissect: maybe add "--attach" and "--detach" verbs which
synchronously attach a DDI to a loopback device but not actually mount them.
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,
// src/basic/umask-util.h
#define _cleanup_umask_
-#define RUN_WITH_UMASK(mask) \
+#define WITH_UMASK(mask) \
for (_cleanup_umask_ mode_t _saved_umask_ = umask(mask) | S_IFMT; \
FLAGS_SET(_saved_umask_, S_IFMT); \
_saved_umask_ &= 0777)
manipulating block devices in all tools that change file system block devices
(`mkfs`, `fsck`, …) or partition tables (`fdisk`, `parted`, …), right after
opening the node.
+
+# Example of Locking The Whole Disk
+
+The following is an example to leverage `libsystemd` infrastructure to get the whole disk of a block device and take a BSD lock on it.
+
+## Compile and Execute
+**Note that this example requires `libsystemd` version 251 or newer.**
+
+Place the code in a source file, e.g. `take_BSD_lock.c` and run the following commands:
+```
+$ gcc -o take_BSD_lock -lsystemd take_BSD_lock.c
+
+$ ./take_BSD_lock /dev/sda1
+Successfully took a BSD lock: /dev/sda
+
+$ flock -x /dev/sda ./take_BSD_lock /dev/sda1
+Failed to take a BSD lock on /dev/sda: Resource temporarily unavailable
+```
+
+## Code
+```c
+/* SPDX-License-Identifier: MIT-0 */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <systemd/sd-device.h>
+#include <unistd.h>
+
+static inline void closep(int *fd) {
+ if (*fd >= 0)
+ close(*fd);
+}
+
+/**
+ * lock_whole_disk_from_devname
+ * @devname: devname of a block device, e.g., /dev/sda or /dev/sda1
+ * @open_flags: the flags to open the device, e.g., O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY
+ * @flock_operation: the operation to call flock, e.g., LOCK_EX|LOCK_NB
+ *
+ * given the devname of a block device, take a BSD lock of the whole disk
+ *
+ * Returns: negative errno value on error, or non-negative fd if the lock was taken successfully.
+ **/
+int lock_whole_disk_from_devname(const char *devname, int open_flags, int flock_operation) {
+ __attribute__((cleanup(sd_device_unrefp))) sd_device *dev = NULL;
+ sd_device *whole_dev;
+ const char *whole_disk_devname, *subsystem, *devtype;
+ int r;
+
+ // create a sd_device instance from devname
+ r = sd_device_new_from_devname(&dev, devname);
+ if (r < 0) {
+ errno = -r;
+ fprintf(stderr, "Failed to create sd_device: %m\n");
+ return r;
+ }
+
+ // if the subsystem of dev is block, but its devtype is not disk, find its parent
+ r = sd_device_get_subsystem(dev, &subsystem);
+ if (r < 0) {
+ errno = -r;
+ fprintf(stderr, "Failed to get the subsystem: %m\n");
+ return r;
+ }
+ if (strcmp(subsystem, "block") != 0) {
+ fprintf(stderr, "%s is not a block device, refusing.\n", devname);
+ return -EINVAL;
+ }
+
+ r = sd_device_get_devtype(dev, &devtype);
+ if (r < 0) {
+ errno = -r;
+ fprintf(stderr, "Failed to get the devtype: %m\n");
+ return r;
+ }
+ if (strcmp(devtype, "disk") == 0)
+ whole_dev = dev;
+ else {
+ r = sd_device_get_parent_with_subsystem_devtype(dev, "block", "disk", &whole_dev);
+ if (r < 0) {
+ errno = -r;
+ fprintf(stderr, "Failed to get the parent device: %m\n");
+ return r;
+ }
+ }
+
+ // open the whole disk device node
+ __attribute__((cleanup(closep))) int fd = sd_device_open(whole_dev, open_flags);
+ if (fd < 0) {
+ errno = -fd;
+ fprintf(stderr, "Failed to open the device: %m\n");
+ return fd;
+ }
+
+ // get the whole disk devname
+ r = sd_device_get_devname(whole_dev, &whole_disk_devname);
+ if (r < 0) {
+ errno = -r;
+ fprintf(stderr, "Failed to get the whole disk name: %m\n");
+ return r;
+ }
+
+ // take a BSD lock of the whole disk device node
+ if (flock(fd, flock_operation) < 0) {
+ r = -errno;
+ fprintf(stderr, "Failed to take a BSD lock on %s: %m\n", whole_disk_devname);
+ return r;
+ }
+
+ printf("Successfully took a BSD lock: %s\n", whole_disk_devname);
+
+ // take the fd to avoid automatic cleanup
+ int ret_fd = fd;
+ fd = -1;
+ return ret_fd;
+}
+
+int main(int argc, char **argv) {
+ if (argc != 2) {
+ fprintf(stderr, "Invalid number of parameters.\n");
+ return EXIT_FAILURE;
+ }
+
+ // try to take an exclusive and nonblocking BSD lock
+ __attribute__((cleanup(closep))) int fd =
+ lock_whole_disk_from_devname(
+ argv[1],
+ O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY,
+ LOCK_EX|LOCK_NB);
+
+ if (fd < 0)
+ return EXIT_FAILURE;
+
+ /**
+ * The device is now locked until the return below.
+ * Now you can safely manipulate the block device.
+ **/
+
+ return EXIT_SUCCESS;
+}
+```
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
* After you have pushed a new version, add a comment explaining the latest changes. If you are a member of the systemd project on GitHub, remove the `reviewed/needs-rework`/`ci-fails/needs-rework`/`needs-rebase` labels.
* If you are copying existing code from another source (eg: a compat header), please make sure the license is compatible with `LGPL-2.1-or-later`. If the license is not `LGPL-2.1-or-later`, please add a note to [`LICENSES/README.md`](https://github.com/systemd/systemd/blob/main/LICENSES/README.md).
* If the pull request stalls without review, post a ping in a comment after some time has passed. We are always short on reviewer time, and pull requests which haven't seen any recent activity can be easily forgotten.
+* Github will automatically add the please-review label when a pull request is opened or updated. If you need
+more information after a review, you can comment `/please-review` on the pull request to have Github add the
+please-review to the pull request.
## Reviewing Pull Requests
* 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.
type as unsupported may not prevent loading some units of that type if they
are referenced by other units of another supported type.
+* `$SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST` — can be set to override the mount
+ units burst rate limit for parsing `/proc/self/mountinfo`. On a system with
+ few resources but many mounts the rate limit may be hit, which will cause the
+ processing of mount units to stall. The burst limit may be adjusted when the
+ default is not appropriate for a given system. Defaults to `5`, accepts
+ positive integers.
+
`systemd-remount-fs`:
* `$SYSTEMD_REMOUNT_ROOT_RW=1` — if set and no entry for the root directory
* The initrd should mount `/run/` as a tmpfs and pass it pre-mounted when
jumping into the main system when executing systemd. The mount options should
- be `mode=755,nodev,nosuid,strictatime`.
+ be `mode=0755,nodev,nosuid,strictatime`.
* It's highly recommended that the initrd also mounts `/usr/` (if split off) as
appropriate and passes it pre-mounted to the main system, to avoid the
If the image is writable, and some of the files or directories that are
overmounted from the host do not exist yet they will be automatically created.
-On read-only, immutable images (e.g. squashfs images) all files and directories
-to over-mount must exist already.
+On read-only, immutable images (e.g. `erofs` or `squashfs` images) all files
+and directories to over-mount must exist already.
Note that as no new image format or metadata is defined, it's very
straightforward to define images than can be made use of in a number of
`luksPbkdfType` → A string, indicating the PBKDF type to use for the LUKS storage mechanism.
+`luksPbkdfForceIterations` → An unsigned 64bit integer, indicating the intended
+number of iterations for the PBKDF operation, when LUKS storage is used.
+
`luksPbkdfTimeCostUSec` → An unsigned 64bit integer, indicating the intended
time cost for the PBKDF operation, when the LUKS storage mechanism is used, in
-µs.
+µs. Ignored when `luksPbkdfForceIterations` is set.
`luksPbkdfMemoryCost` → An unsigned 64bit integer, indicating the intended
memory cost for the PBKDF operation, when LUKS storage is used, in bytes.
`gid`, `memberOf`, `fileSystemType`, `partitionUuid`, `luksUuid`,
`fileSystemUuid`, `luksDiscard`, `luksOfflineDiscard`, `luksCipher`,
`luksCipherMode`, `luksVolumeKeySize`, `luksPbkdfHashAlgorithm`,
-`luksPbkdfType`, `luksPbkdfTimeCostUSec`, `luksPbkdfMemoryCost`,
+`luksPbkdfType`, `luksPbkdfForceIterations`, `luksPbkdfTimeCostUSec`, `luksPbkdfMemoryCost`,
`luksPbkdfParallelThreads`, `luksSectorSize`, `autoResizeMode`, `rebalanceWeight`,
`rateLimitIntervalUSec`, `rateLimitBurst`, `enforcePasswordPolicy`,
`autoLogin`, `stopDelayUSec`, `killProcesses`, `passwordChangeMinUSec`,
# This file is part of systemd.
#
+# ########################### MATCHING #######################################
+#
# The lookup keys are composed in:
# 60-evdev.rules
#
-# Match string formats:
-# evdev:<modalias>
-# evdev:name:<device name>:dmi:<dmi string>
+# Supported hardware matches are:
+# - Generic input devices match:
+# evdev:input:bZZZZvYYYYpXXXXeWWWW-VVVV
+# This matches on the kernel modalias of the input-device, mainly:
+# ZZZZ is the bus-id (see /usr/include/linux/input.h BUS_*), YYYY, XXXX and
+# WWWW are the 4-digit hex uppercase vendor, product and version ID and VVVV
+# is a variable-length input-modalias describing the device capabilities.
+# The vendor, product and version ID for a device node "eventX" is listed
+# in /sys/class/input/eventX/device/id.
+#
+# - Input driver device name and DMI data match:
+# evdev:name:<input device name>:dmi:bvn*:bvr*:bd*:svn<vendor>:pn*
+# <input device name> is the name device specified by the
+# driver, <vendor> is the firmware-provided string exported
+# by the kernel DMI modalias, see /sys/class/dmi/id/modalias.
+#
+# - Extended input driver device name, properties and DMI data match:
+# evdev:name:<input device name>:phys:<phys>:ev:<ev>:dmi:bvn*:bvr*:bd*:svn<vendor>:pn*
+# <input device name> is the name device specified by the
+# driver, <phys> is the physical-device-path, "cat
+# /sys/class/input/input?/phys", <ev> is the event bitmask, "cat
+# /sys/class/input/input?/capabilities/ev" and <vendor> is the
+# firmware-provided string exported by the kernel DMI modalias,
+# see /sys/class/dmi/id/modalias.
#
# To add local entries, create a new file
# /etc/udev/hwdb.d/61-evdev-local.hwdb
EVDEV_ABS_00=::152
EVDEV_ABS_01=::244
+#########################################
+# Packard Bell
+#########################################
+
+# EASYNOTE_TS11HR-200GE
+evdev:name:ETPS/2 Elantech Touchpad:dmi:bvnPackardBell:bvr*:br*:svnPackardBell:pnEasyNoteTS11HR:*
+ EVDEV_ABS_00=0:2472:31
+ EVDEV_ABS_01=-524:528:31
+ EVDEV_ABS_35=0:2472:31
+ EVDEV_ABS_36=-524:528:31
+
###########################################################
# Pine64
###########################################################
# This matches on the kernel modalias of the input-device, mainly:
# ZZZZ is the bus-id (see /usr/include/linux/input.h BUS_*), YYYY, XXXX and
# WWWW are the 4-digit hex uppercase vendor, product and version ID and VVVV
-# is an arbitrary length input-modalias describing the device capabilities.
+# is a variable-length input-modalias describing the device capabilities.
# The vendor, product and version ID for a device node "eventX" is listed
# in /sys/class/input/eventX/device/id.
#
# - AT keyboard DMI data matches:
# evdev:atkbd:dmi:bvn*:bvr*:bd*:svn<vendor>:pn<product>:pvr*
# <vendor> and <product> are the firmware-provided strings
-# exported by the kernel DMI modalias, see /sys/class/dmi/id/modalias
+# exported by the kernel DMI modalias, see /sys/class/dmi/id/modalias.
#
# - Input driver device name and DMI data match:
# evdev:name:<input device name>:dmi:bvn*:bvr*:bd*:svn<vendor>:pn*
# /sys/class/input/input?/phys", <ev> is the event bitmask, "cat
# /sys/class/input/input?/capabilities/ev" and <vendor> is the
# firmware-provided string exported by the kernel DMI modalias,
-# see /sys/class/dmi/id/modalias
+# see /sys/class/dmi/id/modalias.
+#
+# To add local entries, create a new file
+# /etc/udev/hwdb.d/61-keyboard-local.hwdb
+# and add your rules there. To load the new rules execute (as root):
+# systemd-hwdb update
+# udevadm trigger /dev/input/eventXX
+# where /dev/input/eventXX is the keyboard in question. If in doubt, simply use
+# /dev/input/event* to reload all input rules.
+#
+# If your changes are generally applicable, preferably send them as a pull
+# request to
+# https://github.com/systemd/systemd
+# or create a bug report on https://github.com/systemd/systemd/issues and
+# include your new rules, a description of the device, and the output of
+# udevadm info /dev/input/eventXX.
# ######################### KEY MAPPING ######################################
#
# Examples of such devices: Chromebooks where the top row is used for both
# media and F1-F10 keys.
-# To update this file, create a new file
-# /etc/udev/hwdb.d/70-keyboard.hwdb
-# and add your rules there. To load the new rules execute (as root):
-# systemd-hwdb update
-# udevadm trigger /dev/input/eventXX
-# where /dev/input/eventXX is the keyboard in question. If in
-# doubt, simply reload all input rules
-# udevadm trigger --verbose --sysname-match="event*"
-#
-# If your changes are generally applicable, preferably send them as a pull
-# request to
-# https://github.com/systemd/systemd
-# or create a bug report on https://github.com/systemd/systemd/issues and
-# include your new rules, a description of the device, and the output of
-# udevadm info /dev/input/eventXX.
-
##########################################
# Acer
##########################################
###########################################################
# Positivo-Vaio
###########################################################
+# Vaio Pro (VJPW11F11X)
+evdev:name:AT Translated Set 2 keyboard:dmi:bvn*:bvr*:bd*:svnPositivoBahia-VAIO:pnVJPW11F11X*:pvr*:*
# Vaio FE14 (VJFE41F11X, VJE42F11X, VJFE44F11X, VJFE54F11X)
evdev:name:AT Translated Set 2 keyboard:dmi:bvn*:bvr*:bd*:svnPositivoBahia-VAIO:pnVJFE*:pvr*:*
KEYBOARD_KEY_76=f21 # Fn+F1 toggle touchpad
# Positivo
###########################################################
# Positivo DUO (k116)
-evdev:name:AT Translated Set 2 keyboard:dmi:bvn*:svnPositivoTecnologiaSA:pn*:pvr*:rvnPositivoTecnologiaSA:rnK116*
+evdev:name:AT Translated Set 2 keyboard:dmi:bvn*:svnPositivoTecnologiaSA:pn*:pvr*:rvnPositivoTecnologiaSA:rnK116*
KEYBOARD_KEY_76=f21 # Fn+F1 toggle touchpad
# Positivo Motion (N14DP6, N14DP7, N14DP7-V2, N14DP9, N14JP6, N14KP6)
sensor:modalias:acpi:KIOX000A*:dmi:*:svnConnect:pnTablet9:*
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
+#########################################
+# CSL Computer
+#########################################
+# CSL Panther Tab HD
+sensor:modalias:acpi:KIOX000A*:dmi:*:svnCSL*Computer*:pnCSL*Panther*Tab*HD:*
+ ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+
#########################################
# Cube
#########################################
sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0B0B:*
sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0B0D:*
sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0B11:*
+sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0C00:*
+sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0C02:*
ACCEL_LOCATION=base
# Dell Venue 8 Pro 3845
sensor:modalias:acpi:SMO8500*:dmi:*:svnUMAX:pnVisionBook10WiPlus:*
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
+sensor:modalias:acpi:MXC6655*:dmi:*:svnUMAX:pnVisionbook12WrTab:*
+ ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
+
#########################################
# Voyo
#########################################
# This file is part of systemd.
#
# Pointingstick const-accel configuration, to make different brand / model
-# laptop pointingsticks have the same speed / feel, and per model adjustment
-# of the IBM TrackPoint driver's sensitivity setting
+# laptop pointingsticks have the same speed / feel, and per model adjustment of
+# the IBM TrackPoint and Dell DualPoint Stick driver's sensitivity setting.
+#
+# ########################### MATCHING #######################################
#
# The lookup keys are composed in:
# 60-evdev.rules
# This matches on the kernel modalias of the input-device, mainly:
# ZZZZ is the bus-id (see /usr/include/linux/input.h BUS_*), YYYY, XXXX and
# WWW are the 4-digit hex uppercase vendor, product and version ID and VVVV
-# is an arbitrary length input-modalias describing the device capabilities.
+# is a variable-length input-modalias describing the device capabilities.
# The vendor, product and version ID for a device node "eventX" is listed
# in /sys/class/input/eventX/device/id.
#
# evdev:name:<input device name>:dmi:bvn*:bvr*:bd*:svn<vendor>:pn*:*
# <input device name> is the name device specified by the driver,
# <vendor> is the firmware-provided string from the kernel DMI modalias,
-# see /sys/class/dmi/id/modalias
+# see /sys/class/dmi/id/modalias.
+#
+# - Extended input driver device name, properties and DMI data match:
+# evdev:name:<input device name>:phys:<phys>:ev:<ev>:dmi:bvn*:bvr*:bd*:svn<vendor>:pn*
+# <input device name> is the name device specified by the
+# driver, <phys> is the physical-device-path, "cat
+# /sys/class/input/input?/phys", <ev> is the event bitmask, "cat
+# /sys/class/input/input?/capabilities/ev" and <vendor> is the
+# firmware-provided string exported by the kernel DMI modalias,
+# see /sys/class/dmi/id/modalias.
#
# To add local entries, create a new file
# /etc/udev/hwdb.d/71-pointingstick-local.hwdb
# and add your rules there. To load the new rules execute (as root):
# systemd-hwdb update
# udevadm trigger /dev/input/eventXX
-# where /dev/input/eventXX is the pointingstick in question. If in
-# doubt, simply use /dev/input/event* to reload all input rules.
+# where /dev/input/eventXX is the pointingstick in question. If in doubt, simply
+# use /dev/input/event* to reload all input rules.
#
# If your changes are generally applicable, preferably send them as a pull
# request to
# Generic
##########################################
evdev:name:*[tT]rack[pP]oint*:*
+evdev:name:*[dD]ual[pP]oint [sS]tick*:*
ID_INPUT_POINTINGSTICK=1
#########################################
<term><option>--luks-volume-key-size=</option><replaceable>BYTES</replaceable></term>
<term><option>--luks-pbkdf-type=</option><replaceable>TYPE</replaceable></term>
<term><option>--luks-pbkdf-hash-algorithm=</option><replaceable>ALGORITHM</replaceable></term>
+ <term><option>--luks-pbkdf-force-iterations=</option><replaceable>ITERATIONS</replaceable></term>
<term><option>--luks-pbkdf-time-cost=</option><replaceable>SECONDS</replaceable></term>
<term><option>--luks-pbkdf-memory-cost=</option><replaceable>BYTES</replaceable></term>
<term><option>--luks-pbkdf-parallel-threads=</option><replaceable>THREADS</replaceable></term>
<term><varname>systemd.machine_id=</varname></term>
<term><varname>systemd.set_credential=</varname></term>
<term><varname>systemd.import_credentials=</varname></term>
+ <term><varname>systemd.reload_limit_interval_sec=</varname></term>
+ <term><varname>systemd.reload_limit_burst=</varname></term>
<listitem>
<para>Parameters understood by the system and service
manager to control system behavior. For details, see
readonly s HardwareModel = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s FirmwareVersion = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly s FirmwareVendor = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly s FirmwareDate = '...';
};
interface org.freedesktop.DBus.Peer { ... };
interface org.freedesktop.DBus.Introspectable { ... };
<!--property FirmwareVersion is not documented!-->
+ <!--property FirmwareVendor is not documented!-->
+
+ <!--property FirmwareDate is not documented!-->
+
<!--Autogenerated cross-references for systemd.directives, do not edit-->
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.hostname1"/>
<variablelist class="dbus-property" generated="True" extra-ref="FirmwareVersion"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="FirmwareVendor"/>
+
+ <variablelist class="dbus-property" generated="True" extra-ref="FirmwareDate"/>
+
<!--End of Autogenerated section-->
<para>Whenever the hostname or other metadata is changed via the daemon,
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>
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
-<refentry id="halt"
+<refentry id="poweroff"
xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
- <title>halt</title>
+ <title>poweroff</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
- <refentrytitle>halt</refentrytitle>
+ <refentrytitle>poweroff</refentrytitle>
<manvolnum>8</manvolnum>
</refmeta>
<refnamediv>
- <refname>halt</refname>
<refname>poweroff</refname>
<refname>reboot</refname>
- <refpurpose>Halt, power-off or reboot the machine</refpurpose>
+ <refname>halt</refname>
+ <refpurpose>Power off, reboot, or halt the machine</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
- <command>halt</command>
+ <command>poweroff</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
</cmdsynopsis>
<cmdsynopsis>
- <command>poweroff</command>
+ <command>reboot</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
</cmdsynopsis>
<cmdsynopsis>
- <command>reboot</command>
+ <command>halt</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
- <para><command>halt</command>, <command>poweroff</command>, <command>reboot</command> may be used to
- halt, power-off, or reboot the machine. All three commands take the same options.</para>
+ <para><command>poweroff</command>, <command>reboot</command>, and <command>halt</command> may be used to
+ power off, reboot, or halt the machine. All three commands take the same options.</para>
</refsect1>
<term><option>-p</option></term>
<term><option>--poweroff</option></term>
- <listitem><para>Power-off the machine, when either <command>halt</command>
+ <listitem><para>Power off the machine, when either <command>halt</command>
or <command>poweroff</command> is invoked. This option is ignored when
<command>reboot</command> is invoked.</para></listitem>
</varlistentry>
<term><option>--force</option></term>
<listitem>
- <para>Force immediate halt, power-off, reboot. If specified, the command does not contact the init
- system. In most cases, filesystems are not properly unmounted before shutdown. For example, the
- command <command>reboot -f</command> is mostly equivalent to <command>systemctl reboot -ff</command>,
- instead of <command>systemctl reboot -f</command>.</para>
+ <para>Force immediate power-off, halt, or reboot. If specified, the command does not contact the
+ init system. In most cases, filesystems are not properly unmounted before shutdown. For example,
+ the command <command>reboot -f</command> is mostly equivalent to
+ <command>systemctl reboot -ff</command>, instead of <command>systemctl reboot -f</command>.
+ </para>
</listitem>
</varlistentry>
<term><option>-w</option></term>
<term><option>--wtmp-only</option></term>
- <listitem><para>Only write wtmp shutdown entry, do not
- actually halt, power-off, reboot.</para></listitem>
+ <listitem><para>Only write wtmp shutdown entry, do not actually power off, reboot, or halt.
+ </para></listitem>
</varlistentry>
<varlistentry>
<term><option>-d</option></term>
<term><option>--no-wtmp</option></term>
- <listitem><para>Do not write wtmp shutdown
- entry.</para></listitem>
+ <listitem><para>Do not write wtmp shutdown entry.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-n</option></term>
<term><option>--no-sync</option></term>
- <listitem><para>Don't sync hard disks/storage media before
- halt, power-off, reboot.</para></listitem>
+ <listitem><para>Don't sync hard disks/storage media before power-off, reboot, or halt.
+ </para></listitem>
</varlistentry>
<varlistentry>
<term><option>--no-wall</option></term>
- <listitem><para>Do not send wall message before halt,
- power-off, reboot.</para></listitem>
+ <listitem><para>Do not send wall message before power-off, reboot, or halt.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Exit status</title>
- <para>On success, 0 is returned, a non-zero failure code
- otherwise.</para>
+ <para>On success, 0 is returned, a non-zero failure code otherwise.</para>
</refsect1>
<refsect1>
<title>Notes</title>
<para>These commands are implemented in a way that preserves basic compatibility with the original SysV
- commands. <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- verbs <command>halt</command>, <command>poweroff</command>, <command>reboot</command> provide the same
+ commands. <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ verbs <command>poweroff</command>, <command>reboot</command>, <command>halt</command> provide the same
functionality with some additional features.</para>
<para>Note that on many SysV systems <command>halt</command> used to be synonymous to
<term><varname>Format=</varname></term>
<listitem><para>Takes a file system name, such as <literal>ext4</literal>, <literal>btrfs</literal>,
- <literal>xfs</literal>, <literal>vfat</literal>, <literal>squashfs</literal>, or the special value
- <literal>swap</literal>. If specified and the partition is newly created it is formatted with the
- specified file system (or as swap device). The file system UUID and label are automatically derived
- from the partition UUID and label. If this option is used, the size allocation algorithm is slightly
- altered: the partition is created as least as big as required for the minimal file system of the
- specified type (or 4KiB if the minimal size is not known).</para>
+ <literal>xfs</literal>, <literal>vfat</literal>, <literal>erofs</literal>,
+ <literal>squashfs</literal> or the special value <literal>swap</literal>. If specified and the partition
+ is newly created it is formatted with the specified file system (or as swap device). The file system
+ UUID and label are automatically derived from the partition UUID and label. If this option is used,
+ the size allocation algorithm is slightly altered: the partition is created as least as big as
+ required for the minimal file system of the specified type (or 4KiB if the minimal size is not
+ known).</para>
<para>This option has no effect if the partition already exists.</para>
<varlistentry>
<term><varname>Minimize=</varname></term>
- <listitem><para>Takes a boolean. Disabled by default. If enabled, the partition is created at least
- as big as required for the minimal file system of the type specified by <varname>Format=</varname>,
- taking into account the sources configured with <varname>CopyFiles=</varname>. Note that unless the
- filesystem is a read-only filesystem, <command>systemd-repart</command> will have to populate the
- filesystem twice, so enabling this option might slow down repart when populating large partitions.
+ <listitem><para>Takes one of <literal>off</literal>, <literal>best</literal>, and
+ <literal>guess</literal> (alternatively, also accepts a boolean value, which is mapped to
+ <literal>off</literal> when false, and <literal>best</literal> when true). Defaults to
+ <literal>off</literal>. If set to <literal>best</literal>, the partition will have the minimal size
+ required to store the sources configured with <varname>CopyFiles=</varname>. <literal>best</literal>
+ is currently only supported for read-only filesystems. If set to <literal>guess</literal>, the
+ partition is created at least as big as required to store the sources configured with
+ <varname>CopyFiles=</varname>. Note that unless the filesystem is a read-only filesystem,
+ <command>systemd-repart</command> will have to populate the filesystem twice to guess the minimal
+ required size, so enabling this option might slow down repart when populating large partitions.
</para></listitem>
</varlistentry>
</variablelist>
'ENABLE_RESOLVE'],
['environment.d', '5', [], 'ENABLE_ENVIRONMENT_D'],
['file-hierarchy', '7', [], ''],
- ['halt', '8', ['poweroff', 'reboot'], ''],
['homectl', '1', [], 'ENABLE_HOMED'],
['homed.conf', '5', ['homed.conf.d'], 'ENABLE_HOMED'],
['hostname', '5', [], ''],
['pam_systemd', '8', [], 'HAVE_PAM'],
['pam_systemd_home', '8', [], 'ENABLE_PAM_HOME'],
['portablectl', '1', [], 'ENABLE_PORTABLED'],
+ ['poweroff', '8', ['halt', 'reboot'], ''],
['pstore.conf', '5', ['pstore.conf.d'], 'ENABLE_PSTORE'],
['repart.d', '5', [], 'ENABLE_REPART'],
['resolvectl', '1', ['resolvconf'], 'ENABLE_RESOLVE'],
['systemd-fstab-generator', '8', [], ''],
['systemd-getty-generator', '8', [], ''],
['systemd-gpt-auto-generator', '8', [], 'HAVE_BLKID'],
- ['systemd-halt.service',
- '8',
- ['systemd-kexec.service',
- 'systemd-poweroff.service',
- 'systemd-reboot.service',
- 'systemd-shutdown'],
- ''],
['systemd-hibernate-resume-generator', '8', [], 'ENABLE_HIBERNATE'],
['systemd-hibernate-resume@.service',
'8',
'systemd-pcrphase-sysinit.service'],
'HAVE_GNU_EFI'],
['systemd-portabled.service', '8', ['systemd-portabled'], 'ENABLE_PORTABLED'],
+ ['systemd-poweroff.service',
+ '8',
+ ['systemd-halt.service',
+ 'systemd-kexec.service',
+ 'systemd-reboot.service',
+ 'systemd-shutdown'],
+ ''],
['systemd-pstore.service', '8', ['systemd-pstore'], 'ENABLE_PSTORE'],
['systemd-quotacheck.service',
'8',
''],
['udev_new', '3', ['udev_ref', 'udev_unref'], ''],
['udevadm', '8', [], ''],
+ ['ukify', '1', [], 'HAVE_GNU_EFI'],
['user@.service',
'5',
['systemd-user-runtime-dir', 'user-runtime-dir@.service'],
has properties similar to the machine ID during that time.</para>
<para><function>sd_id128_get_invocation()</function> returns the invocation ID of the currently executed
- service. In its current implementation, this reads and parses the <varname>$INVOCATION_ID</varname> environment
- variable that the service manager sets when activating a service, see
- <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details. The
- ID is cached internally. In future a different mechanism to determine the invocation ID may be added.</para>
+ service. In its current implementation, this tries to read and parse the following:
+ <itemizedlist>
+ <listitem>
+ <para>The <varname>$INVOCATION_ID</varname> environment variable that the service manager sets when
+ activating a service.</para>
+ </listitem>
+ <listitem>
+ <para>An entry in the kernel keyring that the system service manager sets when activating a service.
+ </para>
+ </listitem>
+ </itemizedlist>
+ See <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for details. The ID is cached internally. In future a different mechanism to determine the invocation ID
+ may be added.</para>
<para>Note that <function>sd_id128_get_machine_app_specific()</function>,
<function>sd_id128_get_boot()</function>, <function>sd_id128_get_boot_app_specific()</function>, and
<varlistentry>
<term><constant>-ENOENT</constant></term>
- <listitem><para>Returned by <function>sd_id128_get_machine()</function>,
- <function>sd_id128_get_machine_app_specific()</function>, and
- <function>sd_id128_get_boot_app_specific()</function> when <filename>/etc/machine-id</filename> is
- missing.</para></listitem>
+ <listitem><para>Returned by <function>sd_id128_get_machine()</function> and
+ <function>sd_id128_get_machine_app_specific()</function> when <filename>/etc/machine-id</filename>
+ is missing.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-ENOMEDIUM</constant></term>
- <listitem><para>Returned by <function>sd_id128_get_machine()</function>,
- <function>sd_id128_get_machine_app_specific()</function>, and
- <function>sd_id128_get_boot_app_specific()</function> when <filename>/etc/machine-id</filename> is
- empty or all zeros.</para></listitem>
+ <listitem><para>Returned by <function>sd_id128_get_machine()</function> and
+ <function>sd_id128_get_machine_app_specific()</function> when <filename>/etc/machine-id</filename>
+ is empty or all zeros. Also returned by <function>sd_id128_get_invocation()</function> when the
+ invocation ID is all zeros.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOPKG</constant></term>
+
+ <listitem><para>Returned by <function>sd_id128_get_machine()</function> and
+ <function>sd_id128_get_machine_app_specific()</function> when the content of
+ <filename>/etc/machine-id</filename> is <literal>uninitialized</literal>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOSYS</constant></term>
+
+ <listitem><para>Returned by <function>sd_id128_get_boot()</function> and
+ <function>sd_id128_get_boot_app_specific()</function> when <filename>/proc/</filename> is not
+ mounted.</para></listitem>
</varlistentry>
<varlistentry>
</varlistentry>
<varlistentry>
- <term><constant>-EIO</constant></term>
+ <term><constant>-EUCLEAN</constant></term>
<listitem><para>Returned by any of the functions described here when the configured value has
invalid format.</para></listitem>
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>
<entry>The unit file is invalid or another error occurred. Note that <command>is-enabled</command> will not actually return this state, but print an error message instead. However the unit file listing printed by <command>list-unit-files</command> might show it.</entry>
<entry>> 0</entry>
</row>
+ <row>
+ <entry><literal>not-found</literal></entry>
+ <entry>The unit file doesn't exist.</entry>
+ <entry>4</entry>
+ </row>
</tbody>
</tgroup>
</table>
</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>
operation begins.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--discover</option></term>
+
+ <listitem><para>Show a list of DDIs in well-known directories. This will show machine, portable
+ service and system extension disk images in the usual directories
+ <filename>/usr/lib/machines/</filename>, <filename>/usr/lib/portables/</filename>,
+ <filename>/usr/lib/extensions/</filename>, <filename>/var/lib/machines/</filename>,
+ <filename>/var/lib/portables/</filename>, <filename>/var/lib/extensions/</filename> and so
+ on.</para></listitem>
+ </varlistentry>
+
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
systems. If <literal>all</literal> discarding is unconditionally enabled.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--in-memory</option></term>
+
+ <listitem><para>If specified an in-memory copy of the specified disk image is used. This may be used
+ to operate with write-access on a (possibly read-only) image, without actually modifying the original
+ file. This may also be used in order to operate on a disk image without keeping the originating file
+ system busy, in order to allow it to be unmounted.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>--root-hash=</option></term>
<term><option>--root-hash-sig=</option></term>
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
-<refentry id="systemd-halt.service">
+<refentry id="systemd-poweroff.service">
<refentryinfo>
- <title>systemd-halt.service</title>
+ <title>systemd-poweroff.service</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
- <refentrytitle>systemd-halt.service</refentrytitle>
+ <refentrytitle>systemd-poweroff.service</refentrytitle>
<manvolnum>8</manvolnum>
</refmeta>
<refnamediv>
- <refname>systemd-halt.service</refname>
<refname>systemd-poweroff.service</refname>
+ <refname>systemd-halt.service</refname>
<refname>systemd-reboot.service</refname>
<refname>systemd-kexec.service</refname>
<refname>systemd-shutdown</refname>
</refnamediv>
<refsynopsisdiv>
- <para><filename>systemd-halt.service</filename></para>
<para><filename>systemd-poweroff.service</filename></para>
+ <para><filename>systemd-halt.service</filename></para>
<para><filename>systemd-reboot.service</filename></para>
<para><filename>systemd-kexec.service</filename></para>
<para><filename>/usr/lib/systemd/systemd-shutdown</filename></para>
<refsect1>
<title>Description</title>
- <para><filename>systemd-halt.service</filename> is a system
- service that is pulled in by <filename>halt.target</filename> and
- is responsible for the actual system halt. Similarly,
- <filename>systemd-poweroff.service</filename> is pulled in by
- <filename>poweroff.target</filename>,
+ <para><filename>systemd-poweroff.service</filename> is a system
+ service that is pulled in by <filename>poweroff.target</filename> and
+ is responsible for the actual system power-off operation. Similarly,
+ <filename>systemd-halt.service</filename> is pulled in by
+ <filename>halt.target</filename>,
<filename>systemd-reboot.service</filename> by
<filename>reboot.target</filename> and
<filename>systemd-kexec.service</filename> by
cannot be re-mounted read-only.</para>
<para>Immediately before executing the actual system
- halt/poweroff/reboot/kexec <filename>systemd-shutdown</filename>
+ power-off/halt/reboot/kexec <filename>systemd-shutdown</filename>
will run all executables in
<filename>/usr/lib/systemd/system-shutdown/</filename> and pass
- one arguments to them: either <literal>halt</literal>,
- <literal>poweroff</literal>, <literal>reboot</literal> or
+ one arguments to them: either <literal>poweroff</literal>,
+ <literal>halt</literal>, <literal>reboot</literal>, or
<literal>kexec</literal>, depending on the chosen action. All
executables in this directory are executed in parallel, and
execution of the action is not continued before all executables
finished.</para>
- <para>Note that <filename>systemd-halt.service</filename> (and the
- related units) should never be executed directly. Instead, trigger
- system shutdown with a command such as <literal>systemctl
- halt</literal> or suchlike.</para>
+ <para>Note that <filename>systemd-poweroff.service</filename> (and the related units) should never be
+ executed directly. Instead, trigger system shutdown with a command such as <literal>systemctl
+ poweroff</literal>.</para>
</refsect1>
<refsect1>
</varlistentry>
<varlistentry>
- <term><option>--skip-partitions=</option><arg rep="repeat">PARTITION</arg></term>
+ <term><option>--defer-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
<listitem><para>Plain directories or btrfs subvolumes containing the OS tree</para></listitem>
<listitem><para>Disk images with a GPT disk label, following the <ulink
url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">Discoverable Partitions Specification</ulink></para></listitem>
- <listitem><para>Disk images lacking a partition table, with a naked Linux file system (e.g. squashfs or ext4)</para></listitem>
+ <listitem><para>Disk images lacking a partition table, with a naked Linux file system (e.g. erofs,
+ squashfs or ext4)</para></listitem>
</orderedlist>
<para>These image formats are the same ones that
<para>If the value is <literal>/</literal>, only labels specified with <varname>SmackProcessLabel=</varname>
are assigned and the compile-time default is ignored.</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>ReloadLimitIntervalSec=</varname></term>
+ <term><varname>ReloadLimitBurst=</varname></term>
+
+ <listitem><para>Rate limiting for daemon-reload requests. Default to unset, and any number of daemon-reload
+ operations can be requested at any time. <varname>ReloadLimitIntervalSec=</varname> takes a value in seconds
+ to configure the rate limit window, and <varname>ReloadLimitBurst=</varname> takes a positive integer to
+ configure the maximum allowed number of reloads within the configured time window.</para></listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<varlistentry>
<term><varname>DefaultRouteOnDevice=</varname></term>
<listitem>
- <para>Takes a boolean. If set to true, sets up the default route bound to the interface.
+ <para>Takes a boolean. If set to true, sets up the IPv4 default route bound to the interface.
Defaults to false. This is useful when creating routes on point-to-point interfaces. This is
equivalent to e.g. the following,
<programlisting>ip route add default dev veth99</programlisting>
following instead:
<programlisting>[Route]
Gateway=0.0.0.0
+Table=1234</programlisting></para>
+ <para>If you'd like to create an IPv6 default route bound to the interface, please use the
+ following:
+ <programlisting>[Route]
+Gateway=::
Table=1234</programlisting></para>
</listitem>
</varlistentry>
of scope units are the following:</para>
<variablelist class='unit-directives'>
+ <xi:include href="systemd.service.xml" xpointer="oom-policy" />
+
<varlistentry>
<term><varname>RuntimeMaxSec=</varname></term>
above.</para></listitem>
</varlistentry>
- <varlistentry>
+ <varlistentry id='oom-policy'>
<term><varname>OOMPolicy=</varname></term>
- <listitem><para>Configure the out-of-memory (OOM) kernel killer policy. Note that the userspace OOM
+ <listitem><para>Configure the out-of-memory (OOM) killing policy for the kernel and the userspace OOM
killer
- <citerefentry><refentrytitle>systemd-oomd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- is a more flexible solution that aims to prevent out-of-memory situations for the userspace, not just
- the kernel.</para>
-
- <para>On Linux, when memory becomes scarce to the point that the kernel has trouble allocating memory
- for itself, it might decide to kill a running process in order to free up memory and reduce memory
- pressure. This setting takes one of <constant>continue</constant>, <constant>stop</constant> or
- <constant>kill</constant>. If set to <constant>continue</constant> and a process of the service is
- killed by the kernel's OOM killer this is logged but the service continues running. If set to
- <constant>stop</constant> the event is logged but the service is terminated cleanly by the service
- manager. If set to <constant>kill</constant> and one of the service's processes is killed by the OOM
- killer the kernel is instructed to kill all remaining processes of the service too, by setting the
+ <citerefentry><refentrytitle>systemd-oomd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ On Linux, when memory becomes scarce to the point that the kernel has trouble allocating memory for
+ itself, it might decide to kill a running process in order to free up memory and reduce memory
+ pressure. Note that <filename>systemd-oomd.service</filename> is a more flexible solution that aims
+ to prevent out-of-memory situations for the userspace too, not just the kernel, by attempting to
+ terminate services earlier, before the kernel would have to act.</para>
+
+ <para>This setting takes one of <constant>continue</constant>, <constant>stop</constant> or
+ <constant>kill</constant>. If set to <constant>continue</constant> and a process in the unit is
+ killed by the OOM killer, this is logged but the unit continues running. If set to
+ <constant>stop</constant> the event is logged but the unit is terminated cleanly by the service
+ manager. If set to <constant>kill</constant> and one of the unit's processes is killed by the OOM
+ killer the kernel is instructed to kill all remaining processes of the unit too, by setting the
<filename>memory.oom.group</filename> attribute to <constant>1</constant>; also see <ulink
- url="https://docs.kernel.org/admin-guide/cgroup-v2.html">kernel documentation</ulink>.
- </para>
+ url="https://docs.kernel.org/admin-guide/cgroup-v2.html">kernel documentation</ulink>.</para>
<para>Defaults to the setting <varname>DefaultOOMPolicy=</varname> in
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- is set to, except for services where <varname>Delegate=</varname> is turned on, where it defaults to
+ is set to, except for units where <varname>Delegate=</varname> is turned on, where it defaults to
<constant>continue</constant>.</para>
<para>Use the <varname>OOMScoreAdjust=</varname> setting to configure whether processes of the unit
details.</para>
<para>This setting also applies to <command>systemd-oomd</command>. Similarly to the kernel OOM
- kills, this setting determines the state of the service after <command>systemd-oomd</command> kills a
- cgroup associated with the service.</para></listitem>
+ kills, this setting determines the state of the unit after <command>systemd-oomd</command> kills a
+ cgroup associated with it.</para></listitem>
</varlistentry>
-
</variablelist>
<para id='shared-unit-options'>Check
--- /dev/null
+<?xml version="1.0"?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+<refentry id="ukify" xmlns:xi="http://www.w3.org/2001/XInclude" conditional='HAVE_GNU_EFI'>
+
+ <refentryinfo>
+ <title>ukify</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>ukify</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>ukify</refname>
+ <refpurpose>Combine kernel and initrd into a signed Unified Kernel Image</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>/usr/lib/systemd/ukify</command>
+ <arg choice="plain"><replaceable>LINUX</replaceable></arg>
+ <arg choice="plain" rep="repeat"><replaceable>INITRD</replaceable></arg>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>Note: this command is experimental for now. While it is intended to become a regular component of
+ systemd, it might still change in behaviour and interface.</para>
+
+ <para><command>ukify</command> is a tool that combines a kernel and an initrd with
+ a UEFI boot stub to create a
+ <ulink url="https://uapi-group.org/specifications/specs/unified_kernel_image/">Unified Kernel Image (UKI)</ulink>
+ — a PE binary that can be executed by the firmware to start the embedded linux kernel.
+ See <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ for details about the stub.</para>
+
+ <para>Additional sections will be inserted into the UKI, either automatically or only if a specific
+ option is provided. See the discussions of
+ <option>--cmdline=</option>,
+ <option>--os-release=</option>,
+ <option>--devicetree=</option>,
+ <option>--splash=</option>,
+ <option>--pcrpkey=</option>,
+ <option>--uname=</option>,
+ and <option>--section=</option>
+ below.</para>
+
+ <para>If PCR signing keys are provided via the <option>--pcr-public-key=</option> and
+ <option>--pcr-private-key=</option> options, PCR values that will be seen after booting with the given
+ kernel, initrd, and other sections, will be calculated, signed, and embedded in the UKI.
+ <citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry> is
+ used to perform this calculation and signing.</para>
+
+ <para>The calculation of PCR values is done for specific boot phase paths. Those can be specified with
+ <option>--phases=</option> option. If not specified, the default provided by
+ <command>systemd-measure</command> is used. It is also possible to specify the
+ <option>--pcr-private-key=</option>, <option>--pcr-public-key=</option>, and <option>--phases=</option>
+ arguments more than once. Signatures will be then performed with each of the specified keys. When both
+ <option>--phases=</option> and <option>--pcr-private-key=</option> are used, they must be specified the
+ same number of times, and then the n-th boot phase path set will be signed by the n-th key. This can be
+ used to build different trust policies for different phases of the boot.</para>
+
+ <para>If a SecureBoot signing key is provided via the <option>--secureboot-private-key=</option> option,
+ the resulting PE binary will be signed as a whole, allowing the resulting UKI to be trusted by
+ SecureBoot. Also see the discussion of automatic enrollment in
+ <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Options</title>
+
+ <para>Note that the <replaceable>LINUX</replaceable> positional argument is mandatory. The
+ <replaceable>INITRD</replaceable> positional arguments are optional. If more than one is specified, they
+ will all be combined into a single PE section. This is useful to for example prepend microcode before the
+ actual initrd.</para>
+
+ <para>The following options are understood:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><option>--cmdline=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></option></term>
+
+ <listitem><para>Specify the kernel command line (the <literal>.cmdline</literal> section). The
+ argument may be a literal string, or <literal>@</literal> followed by a path name. If not specified,
+ no command line will be embedded.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--os-release=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></option></term>
+
+ <listitem><para>Specify the os-release description (the <literal>.osrel</literal> section). The
+ argument may be a literal string, or <literal>@</literal> followed by a path name. If not specified,
+ the <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ file will be picked up from the host system.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--devicetree=<replaceable>PATH</replaceable></option></term>
+
+ <listitem><para>Specify the devicetree description (the <literal>.dtb</literal> section). The
+ argument is a path to a compiled binary DeviceTree file. If not specified, the section will not be
+ present.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--splash=<replaceable>PATH</replaceable></option></term>
+
+ <listitem><para>Specify a picture to display during boot (the <literal>.splash</literal> section).
+ The argument is a path to a BMP file. If not specified, the section will not be present.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--pcrpkey=<replaceable>PATH</replaceable></option></term>
+
+ <listitem><para>Specify a path to a public key to embed in the <literal>.pcrpkey</literal> section.
+ If not specified, and there's exactly one <option>--pcr-public-key=</option> argument, that key will
+ be used. Otherwise, the section will not be present.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--uname=<replaceable>VERSION</replaceable></option></term>
+
+ <listitem><para>Specify the kernel version (as in <command>uname -r</command>, the
+ <literal>.uname</literal> section). If not specified, an attempt will be made to extract the version
+ string from the kernel image. It is recommended to pass this explicitly if known, because the
+ extraction is based on heuristics and not very reliable. If not specified and extraction fails, the
+ section will not be present.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--section=<replaceable>NAME</replaceable>:<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></option></term>
+
+ <listitem><para>Specify an arbitrary additional section
+ <literal><replaceable>NAME</replaceable></literal>. Note that the name is used as-is, and if the
+ section name should start with a dot, it must be included in <replaceable>NAME</replaceable>. The
+ argument may be a literal string, or <literal>@</literal> followed by a path name. This option may be
+ specified more than once. Any sections specified in this fashion will be inserted (in order) before
+ the <literal>.linux</literal> section which is always last.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--pcr-private-key=<replaceable>PATH</replaceable></option></term>
+
+ <listitem><para>Specify a private key to use for signing PCR policies. This option may be specified
+ more than once, in which case multiple signatures will be made.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--pcr-public-key=<replaceable>PATH</replaceable></option></term>
+
+ <listitem><para>Specify a public key to use for signing PCR policies. This option may be specified
+ more than once, similarly to the <option>--pcr-private-key=</option> option. If not present, the
+ public keys will be extracted from the private keys. If present, the this option must be specified
+ the same number of times as the <option>--pcr-private-key=</option> option.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--phases=<replaceable>LIST</replaceable></option></term>
+
+ <listitem><para>A comma or space-separated list of colon-separated phase paths to sign a policy for.
+ If not present, the default of
+ <citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ will be used. When this argument is present, it must appear the same number of times as the
+ <option>--pcr-private-key=</option> option. Each set of boot phase paths will be signed with the
+ corresponding private key.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--pcr-banks=<replaceable>PATH</replaceable></option></term>
+
+ <listitem><para>A comma or space-separated list of PCR banks to sign a policy for. If not present,
+ all known banks will be used (<literal>sha1</literal>, <literal>sha256</literal>,
+ <literal>sha384</literal>, <literal>sha512</literal>), which will fail if not supported by the
+ system.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--secureboot-private-key=<replaceable>SB_KEY</replaceable></option></term>
+
+ <listitem><para>A path to a private key to use for signing of the resulting binary. If the
+ <option>--signing-engine=</option> option is used, this may also be an engine-specific
+ designation.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--secureboot-certificate=<replaceable>SB_CERT</replaceable></option></term>
+
+ <listitem><para>A path to a certificate to use for signing of the resulting binary. If the
+ <option>--signing-engine=</option> option is used, this may also be an engine-specific
+ designation.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--signing-engine=<replaceable>ENGINE</replaceable></option></term>
+
+ <listitem><para>An "engine" to for signing of the resulting binary. This option is currently passed
+ verbatim to the <option>--engine=</option> option of
+ <citerefentry><refentrytitle>sbsign</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--sign-kernel</option></term>
+ <term><option>--no-sign-kernel</option></term>
+
+ <listitem><para>Override the detection of whether to sign the Linux binary itself before it is
+ embedded in the combined image. If not specified, it will be signed if a SecureBoot signing key is
+ provided via the <option>--secureboot-private-key=</option> option and the binary has not already
+ been signed. If <option>--sign-kernel</option> is specified, and the binary has already been signed,
+ the signature will be appended anyway.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--tools=<replaceable>DIR</replaceable></option></term>
+
+ <listitem><para>Specify a directory with helper tools. <command>ukify</command> will look for helper
+ tools in that directory first, and if not found, try to load them from <varname>$PATH</varname> in
+ the usual fashion.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--measure</option></term>
+ <term><option>--no-measure</option></term>
+
+ <listitem><para>Enable or disable a call to <command>systmed-measure</command> to print
+ pre-calculated PCR values. Defaults to false.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--output=<replaceable>FILENAME</replaceable></option></term>
+
+ <listitem><para>The output filename. If not specified, the name of the
+ <replaceable>LINUX</replaceable> argument, with the suffix <literal>.unsigned.efi</literal> or
+ <literal>.signed.efi</literal> will be used, depending on whether signing for SecureBoot was
+ performed.</para></listitem>
+ </varlistentry>
+
+ <xi:include href="standard-options.xml" xpointer="help" />
+ <xi:include href="standard-options.xml" xpointer="version" />
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <example>
+ <title>Minimal invocation</title>
+
+ <programlisting>ukify \
+ /lib/modules/6.0.9-300.fc37.x86_64/vmlinuz \
+ /some/path/initramfs-6.0.9-300.fc37.x86_64.img \
+ --cmdline='quiet rw'
+ </programlisting>
+
+ <para>This creates an unsigned UKI <filename>./vmlinuz.unsigned.efi</filename>.</para>
+ </example>
+
+ <example>
+ <title>All the bells and whistles</title>
+
+ <programlisting>/usr/lib/systemd/ukify \
+ /lib/modules/6.0.9-300.fc37.x86_64/vmlinuz \
+ early_cpio \
+ /some/path/initramfs-6.0.9-300.fc37.x86_64.img \
+ --pcr-private-key=pcr-private-initrd-key.pem \
+ --pcr-public-key=pcr-public-initrd-key.pem \
+ --phases='enter-initrd' \
+ --pcr-private-key=pcr-private-system-key.pem \
+ --pcr-public-key=pcr-public-system-key.pem \
+ --phases='enter-initrd:leave-initrd enter-initrd:leave-initrd:sysinit \
+ enter-initrd:leave-initrd:sysinit:ready' \
+ --pcr-banks=sha384,sha512 \
+ --secureboot-private-key=sb.key \
+ --secureboot-certificate=sb.cert \
+ --sign-kernel \
+ --cmdline='quiet rw rhgb'
+ </programlisting>
+
+ <para>This creates a signed UKI <filename index='false'>./vmlinuz.signed.efi</filename>.
+ The initrd section contains two concatenated parts, <filename index='false'>early_cpio</filename>
+ and <filename index='false'>initramfs-6.0.9-300.fc37.x86_64.img</filename>.
+ The policy embedded in the <literal>.pcrsig</literal> section will be signed for the initrd (the
+ <constant>enter-initrd</constant> phase) with the key
+ <filename index='false'>pcr-private-initrd-key.pem</filename>, and for the main system (phases
+ <constant>leave-initrd</constant>, <constant>sysinit</constant>, <constant>ready</constant>) with the
+ key <filename index='false'>pcr-private-system-key.pem</filename>. The Linux binary and the resulting
+ combined image will be signed with the SecureBoot key <filename index='false'>sb.key</filename>.</para>
+ </example>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>objcopy</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-pcrphase.service</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
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>
error('python3 jinja2 missing')
endif
+python_310 = run_command(python, '-c',
+ 'import sys; sys.exit(0 if sys.version_info >= (3,10) else 1)',
+ check : false).returncode() == 0
+if get_option('ukify') == 'auto'
+ want_ukify = python_310
+elif get_option('ukify') == 'true' and not python310
+ error('ukify requires Python >= 3.10')
+else
+ want_ukify = get_option('ukify') == 'true'
+endif
+
############################################################
gperf = find_program('gperf')
subdir('src/fuzz')
subdir('rules.d')
subdir('test')
+subdir('src/ukify/test') # needs to be last for test_env variable
############################################################
boot_link_with = [libsystemd_static, libshared_static]
endif
- public_programs += executable(
+ exe = executable(
'bootctl',
'src/boot/bootctl.c',
include_directories : includes,
versiondep],
install_rpath : rootpkglibdir,
install : true)
+ public_programs += exe
+
+ if want_tests != 'false'
+ test('test-bootctl-json',
+ test_bootctl_json_sh,
+ args : exe.full_path(),
+ depends : exe)
+ endif
public_programs += executable(
'systemd-bless-boot',
install_rpath : rootpkglibdir,
install : true,
install_dir : rootbindir)
+ public_programs += exe
endif
endif
install : true,
install_dir : rootlibexecdir)
+if have_standalone_binaries
+ executable(
+ 'systemd-shutdown.standalone',
+ systemd_shutdown_sources,
+ include_directories : includes,
+ c_args : '-DSTANDALONE',
+ link_with : [libshared_static,
+ libbasic,
+ libsystemd_static],
+ dependencies : [libmount,
+ versiondep],
+ install_rpath : rootpkglibdir,
+ install : true,
+ install_dir : rootlibexecdir)
+endif
+
executable(
'systemd-update-done',
'src/update-done/update-done.c',
args : [exe.full_path(), loaderentry_install])
endif
+if want_ukify
+ exe = custom_target(
+ 'ukify',
+ input : 'src/ukify/ukify.py',
+ output : 'ukify',
+ command : [jinja2_cmdline, '@INPUT@', '@OUTPUT@'],
+ install : true,
+ install_mode : 'rwxr-xr-x',
+ install_dir : rootlibexecdir)
+ public_programs += exe
+endif
+
############################################################
runtest_env = custom_target(
description : 'build against LLVM libFuzzer')
option('kernel-install', type: 'boolean', value: true,
description : 'install kernel-install and associated files')
+option('ukify', type : 'combo', choices : ['auto', 'true', 'false'],
+ description : 'install ukify')
option('analyze', type: 'boolean', value: true,
description : 'install systemd-analyze')
export KBUILD_BUILD_TIMESTAMP="Fri Jun 5 15:58:00 CEST 2015"
export KBUILD_BUILD_HOST="mkosi"
- make O="$BUILDDIR/mkosi.kernel" defconfig
-
- scripts/config \
- --file "$BUILDDIR/mkosi.kernel/.config" \
- --enable BPF_SYSCALL \
- --enable BPF_JIT \
- --enable BPF_JIT_ALWAYS_ON \
- --enable BPF_JIT_DEFAULT_ON \
- --enable BPF_UNPRIV_DEFAULT_OFF \
- --enable USERMODE_DRIVER \
- --enable BPF_PRELOAD \
- --enable BPF_PRELOAD_UMD \
- --enable BPF_LSM \
- --enable DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT \
- --enable DEBUG_INFO_BTF \
- --enable BTRFS_FS \
- --enable BTRFS_FS_POSIX_ACL \
- --enable PSI \
- --enable CGROUPS \
- --enable CGROUP_BPF \
- --enable MEMCG \
- --enable MEMCG_SWAP \
- --enable MEMCG_KMEM \
- --enable IMA_ARCH_POLICY \
- --enable DM_VERITY_VERIFY_ROOTHASH_SIG \
- --enable DM_VERITY_VERIFY_ROOTHASH_SIG_SECONDARY_KEYRING \
- --enable INTEGRITY_MACHINE_KEYRING \
- --enable NETFILTER_ADVANCED \
- --enable NF_CONNTRACK_MARK
-
- # Make sure all unset options are set to their default value.
- make O="$BUILDDIR/mkosi.kernel" olddefconfig
+ scripts/kconfig/merge_config.sh -O "$BUILDDIR/mkosi.kernel" \
+ ../mkosi.kernel.config \
+ tools/testing/selftests/bpf/config.x86_64 \
+ tools/testing/selftests/bpf/config
make O="$BUILDDIR/mkosi.kernel" -j "$(nproc)"
make O="$BUILDDIR/mkosi.kernel" INSTALL_PATH="$DESTDIR/usr/lib/modules/$KERNEL_RELEASE" install
mkdir -p "$DESTDIR/usr/lib/kernel/selftests"
make -C tools/testing/selftests -j "$(nproc)" O="$BUILDDIR/mkosi.kernel" KSFT_INSTALL_PATH="$DESTDIR/usr/lib/kernel/selftests" SKIP_TARGETS="" install
+
+ ln -sf /usr/lib/kernel/selftests/bpf/bpftool "$DESTDIR/usr/bin/bpftool"
fi
# 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
fuse
glib2
glibc-minimal-langpack
- glibc.i686
gnutls
iproute
iproute-tc
bpftool
docbook-xsl
dwarves
- glibc-devel.i686
glibc-static
- glibc-static.i686
gnu-efi-devel
libcap-static
pam-devel
--- /dev/null
+# CONFIG_COMPAT_BRK is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_ATA=y
+CONFIG_AUTOFS4_FS=y
+CONFIG_BINFMT_MISC=y
+CONFIG_BLK_CGROUP_IOCOST=y
+CONFIG_BLK_CGROUP_IOLATENCY=y
+CONFIG_BLK_CGROUP_IOPRIO=y
+CONFIG_BLK_CGROUP=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_MD=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_BPF_EVENTS=y
+CONFIG_BPF_JIT=y
+CONFIG_BPF_LSM=y
+CONFIG_BPF_SYSCALL=y
+CONFIG_BPF=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BTRFS_FS_POSIX_ACL=y
+CONFIG_BTRFS_FS=y
+CONFIG_CFG80211=y
+CONFIG_CFS_BANDWIDTH=y
+CONFIG_CGROUP_BPF=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_HUGETLB=y
+CONFIG_CGROUP_MISC=y
+CONFIG_CGROUP_NET_PRIO=y
+CONFIG_CGROUP_PERF=y
+CONFIG_CGROUP_PIDS=y
+CONFIG_CGROUP_RDMA=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_CGROUPS=y
+CONFIG_CONNECTOR=y
+CONFIG_CPUSETS=y
+CONFIG_CRASH_DUMP=y
+CONFIG_DEBUG_INFO_BTF=y
+CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_DEVTMPFS=y
+CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG_SECONDARY_KEYRING=y
+CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y
+CONFIG_DM_VERITY=y
+CONFIG_EFI_MIXED=y
+CONFIG_EFI_STUB=y
+CONFIG_EFI_ZBOOT=y
+CONFIG_EFI=y
+CONFIG_EXPERT=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_EXT4_FS=y
+CONFIG_HIBERNATION=y
+CONFIG_HIDRAW=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_HOTPLUG_PCI=y
+CONFIG_HPET=y
+CONFIG_HUGETLBFS=y
+CONFIG_HW_RANDOM=y
+CONFIG_HYPERVISOR_GUEST=y
+CONFIG_IMA_APPRAISE=y
+CONFIG_IMA_ARCH_POLICY=y
+CONFIG_IMA=y
+CONFIG_INET=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_MISC=y
+CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y
+CONFIG_INTEGRITY_MACHINE_KEYRING=y
+CONFIG_INTEGRITY_PLATFORM_KEYRING=y
+CONFIG_INTEGRITY_SIGNATURE=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_ISO9660_FS=y
+CONFIG_KEXEC=y
+CONFIG_KPROBES=y
+CONFIG_LOAD_UEFI_KEYS=y
+CONFIG_MAC80211=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_MD=y
+CONFIG_MEMCG_KMEM=y
+CONFIG_MEMCG=y
+CONFIG_MICROCODE_AMD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULES=y
+CONFIG_MSDOS_FS=y
+CONFIG_NAMESPACES=y
+CONFIG_NET_9P_VIRTIO=y
+CONFIG_NET_9P=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_CLS_CGROUP=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_SCHED=y
+CONFIG_NET=y
+CONFIG_NETCONSOLE=y
+CONFIG_NETDEVICES=y
+CONFIG_NETFILTER_ADVANCED=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_TARGET_MASQUERADE=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER=y
+CONFIG_NETLABEL=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_SIP=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NF_NAT=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_UTF8=y
+CONFIG_NO_HZ_FULL=y
+CONFIG_NUMA=y
+CONFIG_NVRAM=y
+CONFIG_PACKET=y
+CONFIG_PARAVIRT=y
+CONFIG_PCI=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_PERF_EVENTS=y
+CONFIG_PM_DEBUG=y
+CONFIG_PM_TRACE_RTC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_PRINTK_TIME=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROFILING=y
+CONFIG_PSI=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+CONFIG_QUOTA=y
+CONFIG_RFKILL=y
+CONFIG_RTC_CLASS=y
+CONFIG_SATA_AHCI=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_SPI_ATTRS=y
+CONFIG_SCSI_VIRTIO=y
+CONFIG_SCSI=y
+CONFIG_SECONDARY_TRUSTED_KEYRING=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_DETECT_IRQ=y
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_NR_UARTS=32
+CONFIG_SERIAL_8250_RSA=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_NONSTANDARD=y
+CONFIG_SMP=y
+CONFIG_SWAP=y
+CONFIG_SYSTEM_BLACKLIST_KEYRING=y
+CONFIG_SYSVIPC=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_TMPFS_XATTR=y
+CONFIG_TMPFS=y
+CONFIG_UNIX=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_MON=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_UHCI_HCD=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB=y
+CONFIG_USER_NS=y
+CONFIG_VFAT_FS=y
+CONFIG_VIRTIO_BLK=y
+CONFIG_VIRTIO_CONSOLE=y
+CONFIG_VIRTIO_INPUT=y
+CONFIG_VIRTIO_NET=y
+CONFIG_VIRTIO_PCI=y
+CONFIG_WATCHDOG=y
+CONFIG_X86_ACPI_CPUFREQ=y
+CONFIG_X86_CPUID=y
+CONFIG_X86_MSR=y
+CONFIG_XFRM_USER=y
+CONFIG_XFS_FS=y
+CONFIG_XFS_POSIX_ACL=y
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-10-20 10:35+0200\n"
-"PO-Revision-Date: 2022-01-13 11:16+0000\n"
+"PO-Revision-Date: 2022-12-10 12:19+0000\n"
"Last-Translator: Hugo Carvalho <hugokarvalho@hotmail.com>\n"
"Language-Team: Portuguese <https://translate.fedoraproject.org/projects/"
"systemd/master/pt/>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 4.10.1\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 "Obter o número de série do hardware"
#: src/hostname/org.freedesktop.hostname1.policy:62
-#, fuzzy
msgid "Authentication is required to get hardware serial number."
-msgstr "É necessária autenticação para definir o horário do sistema."
+msgstr "É necessária autenticação para obter o número de série do hardware."
#: src/hostname/org.freedesktop.hostname1.policy:71
-#, fuzzy
msgid "Get system description"
-msgstr "Definir fuso horário do sistema"
+msgstr "Obter descrição do sistema"
#: src/hostname/org.freedesktop.hostname1.policy:72
-#, fuzzy
msgid "Authentication is required to get system description."
-msgstr "É necessária autenticação para definir o fuso horário do sistema."
+msgstr "É necessária autenticação para obter a descrição do sistema."
#: src/import/org.freedesktop.import1.policy:22
msgid "Import a VM or container image"
ACTION=="remove", GOTO="evdev_end"
KERNEL!="event*", GOTO="evdev_end"
-# skip later rules when we find something for this input device
-IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=evdev:", \
- IMPORT{builtin}="keyboard", GOTO="evdev_end"
+# Execute the match patterns below, from least-to-most specific.
+
+# Device matching the modalias string (bustype, vendor, product, version, other properties)
+IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=evdev:",
+ ENV{.HAVE_HWDB_PROPERTIES}="1"
# AT keyboard matching by the machine's DMI data
DRIVERS=="atkbd", \
IMPORT{builtin}="hwdb 'evdev:atkbd:$attr{[dmi/id]modalias}'", \
- IMPORT{builtin}="keyboard", GOTO="evdev_end"
+ ENV{.HAVE_HWDB_PROPERTIES}="1"
-# device matching the input device name + properties + the machine's DMI data
+# Device matching the input device name and the machine's DMI data
KERNELS=="input*", \
- IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:phys:$attr{phys}:ev:$attr{capabilities/ev}:$attr{[dmi/id]modalias}'", \
- IMPORT{builtin}="keyboard", GOTO="evdev_end"
+ IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:$attr{[dmi/id]modalias}'", \
+ ENV{.HAVE_HWDB_PROPERTIES}="1"
-# device matching the input device name and the machine's DMI data
+# Device matching the input device name + properties + the machine's DMI data
KERNELS=="input*", \
- IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:$attr{[dmi/id]modalias}'", \
- IMPORT{builtin}="keyboard", GOTO="evdev_end"
+ IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:phys:$attr{phys}:ev:$attr{capabilities/ev}:$attr{[dmi/id]modalias}'", \
+ ENV{.HAVE_HWDB_PROPERTIES}="1"
+
+ENV{.HAVE_HWDB_PROPERTIES}=="1", \
+ IMPORT{builtin}="keyboard"
LABEL="evdev_end"
--luks-volume-key-size
--luks-pbkdf-type
--luks-pbkdf-hash-algorithm
+ --luks-pbkdf-force-iterations
--luks-pbkdf-time-cost
--luks-pbkdf-memory-cost
--luks-pbkdf-parallel-threads
return q;
}
+
+void *expand_to_usable(void *ptr, size_t newsize _unused_) {
+ return ptr;
+}
#pragma once
#include <alloca.h>
+#include <malloc.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
# define msan_unpoison(r, s)
#endif
-/* This returns the number of usable bytes in a malloc()ed region as per malloc_usable_size(), in a way that
- * is compatible with _FORTIFY_SOURCES. If _FORTIFY_SOURCES is used many memory operations will take the
- * object size as returned by __builtin_object_size() into account. Hence, let's return the smaller size of
- * malloc_usable_size() and __builtin_object_size() here, so that we definitely operate in safe territory by
- * both the compiler's and libc's standards. Note that __builtin_object_size() evaluates to SIZE_MAX if the
- * size cannot be determined, hence the MIN() expression should be safe with dynamically sized memory,
- * too. Moreover, when NULL is passed malloc_usable_size() is documented to return zero, and
- * __builtin_object_size() returns SIZE_MAX too, hence we also return a sensible value of 0 in this corner
- * case. */
+/* Dummy allocator to tell the compiler that the new size of p is newsize. The implementation returns the
+ * pointer as is; the only reason for its existence is as a conduit for the _alloc_ attribute. This cannot be
+ * a static inline because gcc then loses the attributes on the function.
+ * See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96503 */
+void *expand_to_usable(void *p, size_t newsize) _alloc_(2) _returns_nonnull_;
+
+static inline size_t malloc_sizeof_safe(void **xp) {
+ if (_unlikely_(!xp || !*xp))
+ return 0;
+
+ size_t sz = malloc_usable_size(*xp);
+ *xp = expand_to_usable(*xp, sz);
+ /* GCC doesn't see the _returns_nonnull_ when built with ubsan, so yet another hint to make it doubly
+ * clear that expand_to_usable won't return NULL.
+ * See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79265 */
+ if (!*xp)
+ assert_not_reached();
+ return sz;
+}
+
+/* This returns the number of usable bytes in a malloc()ed region as per malloc_usable_size(), which may
+ * return a value larger than the size that was actually allocated. Access to that additional memory is
+ * discouraged because it violates the C standard; a compiler cannot see that this as valid. To help the
+ * compiler out, the MALLOC_SIZEOF_SAFE macro 'allocates' the usable size using a dummy allocator function
+ * expand_to_usable. There is a possibility of malloc_usable_size() returning different values during the
+ * lifetime of an object, which may cause problems, but the glibc allocator does not do that at the moment. */
#define MALLOC_SIZEOF_SAFE(x) \
- MIN(malloc_usable_size(x), __builtin_object_size(x, 0))
+ malloc_sizeof_safe((void**) &__builtin_choose_expr(__builtin_constant_p(x), (void*) { NULL }, (x)))
/* Inspired by ELEMENTSOF() but operates on malloc()'ed memory areas: typesafely returns the number of items
* that fit into the specified memory block */
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)
return r;
assert(path_fd >= 0);
- d = opendir(FORMAT_PROC_FD_PATH(path_fd));
+ d = xopendirat(path_fd, ".", O_NOFOLLOW);
if (!d)
return -errno;
#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
}
char* octescape(const char *s, size_t len) {
- char *r, *t;
- const char *f;
+ char *buf, *t;
- /* Escapes all chars in bad, in addition to \ and " chars,
- * in \nnn style escaping. */
+ /* Escapes all chars in bad, in addition to \ and " chars, in \nnn style escaping. */
- r = new(char, len * 4 + 1);
- if (!r)
+ assert(s || len == 0);
+
+ t = buf = new(char, len * 4 + 1);
+ if (!buf)
return NULL;
- for (f = s, t = r; f < s + len; f++) {
+ for (size_t i = 0; i < len; i++) {
+ uint8_t u = (uint8_t) s[i];
- if (*f < ' ' || *f >= 127 || IN_SET(*f, '\\', '"')) {
+ if (u < ' ' || u >= 127 || IN_SET(u, '\\', '"')) {
*(t++) = '\\';
- *(t++) = '0' + (*f >> 6);
- *(t++) = '0' + ((*f >> 3) & 8);
- *(t++) = '0' + (*f & 8);
+ *(t++) = '0' + (u >> 6);
+ *(t++) = '0' + ((u >> 3) & 7);
+ *(t++) = '0' + (u & 7);
} else
- *(t++) = *f;
+ *(t++) = u;
}
*t = 0;
-
- return r;
-
+ return buf;
}
static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
CMSG_FOREACH(cmsg, mh)
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
- close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
+ close_many(CMSG_TYPED_DATA(cmsg, int),
+ (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
}
bool fdname_is_valid(const char *s) {
fd = openat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
if (fd < 0)
return -errno;
+ dir_fd = fd;
+
+ } else if (dir_fd == AT_FDCWD) {
+ /* Let's acquire an O_PATH fd of the current directory */
+ fd = openat(dir_fd, ".", O_PATH|O_CLOEXEC|O_NOFOLLOW|O_DIRECTORY);
+ if (fd < 0)
+ return -errno;
+ dir_fd = fd;
}
- return fchmod_and_chown(path ? fd : dir_fd, mode, uid, gid);
+ return fchmod_and_chown(dir_fd, mode, uid, gid);
}
int fchmod_and_chown_with_fallback(int fd, const char *path, mode_t mode, uid_t uid, gid_t gid) {
static int cached_emoji_enabled = -1;
if (cached_emoji_enabled < 0) {
- int val;
-
- val = getenv_bool("SYSTEMD_EMOJI");
- if (val < 0)
- cached_emoji_enabled =
- is_locale_utf8() &&
- !STRPTR_IN_SET(getenv("TERM"), "dumb", "linux");
- else
- cached_emoji_enabled = val;
+ int val = getenv_bool("SYSTEMD_EMOJI");
+ if (val >= 0)
+ return (cached_emoji_enabled = val);
+
+ const char *term = getenv("TERM");
+ if (!term || STR_IN_SET(term, "dumb", "linux"))
+ return (cached_emoji_enabled = false);
+
+ cached_emoji_enabled = is_locale_utf8();
}
return cached_emoji_enabled;
[SPECIAL_GLYPH_RECYCLING] = "~",
[SPECIAL_GLYPH_DOWNLOAD] = "\\",
[SPECIAL_GLYPH_SPARKLES] = "*",
+ [SPECIAL_GLYPH_WARNING_SIGN] = "!",
},
/* UTF-8 */
/* This emoji is a single character cell glyph in Unicode, and two in ASCII */
[SPECIAL_GLYPH_TOUCH] = u8"👆", /* actually called: BACKHAND INDEX POINTING UP */
- /* These three emojis are single character cell glyphs in Unicode and also in ASCII. */
+ /* These four emojis are single character cell glyphs in Unicode and also in ASCII. */
[SPECIAL_GLYPH_RECYCLING] = u8"♻️", /* actually called: UNIVERSAL RECYCLNG SYMBOL */
[SPECIAL_GLYPH_DOWNLOAD] = u8"⤵️", /* actually called: RIGHT ARROW CURVING DOWN */
[SPECIAL_GLYPH_SPARKLES] = u8"✨",
+ [SPECIAL_GLYPH_WARNING_SIGN] = u8"⚠️",
},
};
SPECIAL_GLYPH_RECYCLING,
SPECIAL_GLYPH_DOWNLOAD,
SPECIAL_GLYPH_SPARKLES,
+ SPECIAL_GLYPH_WARNING_SIGN,
_SPECIAL_GLYPH_MAX,
_SPECIAL_GLYPH_INVALID = -EINVAL,
} SpecialGlyph;
}
static struct hashmap_base_entry* bucket_at(HashmapBase *h, unsigned idx) {
- return (struct hashmap_base_entry*)
- ((uint8_t*) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size);
+ return CAST_ALIGN_PTR(
+ struct hashmap_base_entry,
+ (uint8_t *) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size);
}
static struct plain_hashmap_entry* plain_bucket_at(Hashmap *h, unsigned idx) {
const uint8_t *x;
char *r, *z;
+ assert(p || l == 0);
+
z = r = new(char, l * 2 + 1);
if (!r)
return NULL;
- for (x = p; x < (const uint8_t*) p + l; x++) {
+ for (x = p; x && x < (const uint8_t*) p + l; x++) {
*(z++) = hexchar(*x >> 4);
*(z++) = hexchar(*x & 15);
}
if (!r)
return -ENOMEM;
- for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
+ for (x = p; x && x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
/* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
maybe_line_break(&z, r, line_break);
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
return z - r;
}
-static int base64_append_width(
- char **prefix, int plen,
- char sep, int indent,
- const void *p, size_t l,
- int width) {
+static ssize_t base64_append_width(
+ char **prefix,
+ size_t plen,
+ char sep,
+ size_t indent,
+ const void *p,
+ size_t l,
+ size_t width) {
_cleanup_free_ char *x = NULL;
char *t, *s;
- ssize_t len, avail, line, lines;
+ size_t lines;
+ ssize_t len;
+
+ assert(prefix);
+ assert(*prefix || plen == 0);
+ assert(p || l == 0);
len = base64mem(p, l, &x);
- if (len <= 0)
+ if (len < 0)
return len;
+ if (len == 0)
+ return plen;
lines = DIV_ROUND_UP(len, width);
- if ((size_t) plen >= SSIZE_MAX - 1 - 1 ||
+ if (plen >= SSIZE_MAX - 1 - 1 ||
lines > (SSIZE_MAX - plen - 1 - 1) / (indent + width + 1))
return -ENOMEM;
- t = realloc(*prefix, (ssize_t) plen + 1 + 1 + (indent + width + 1) * lines);
+ t = realloc(*prefix, plen + 1 + 1 + (indent + width + 1) * lines);
if (!t)
return -ENOMEM;
- t[plen] = sep;
+ s = t + plen;
+ for (size_t line = 0; line < lines; line++) {
+ size_t act = MIN(width, (size_t) len);
- for (line = 0, s = t + plen + 1, avail = len; line < lines; line++) {
- int act = MIN(width, avail);
+ if (line > 0)
+ sep = '\n';
- if (line > 0 || sep == '\n') {
- memset(s, ' ', indent);
- s += indent;
+ if (s > t) {
+ *s++ = sep;
+ if (sep == '\n')
+ s = mempset(s, ' ', indent);
}
s = mempcpy(s, x + width * line, act);
- *(s++) = line < lines - 1 ? '\n' : '\0';
- avail -= act;
+ len -= act;
}
- assert(avail == 0);
+ assert(len == 0);
+ *s = '\0';
*prefix = t;
- return 0;
+ return s - t;
}
-int base64_append(
- char **prefix, int plen,
- const void *p, size_t l,
- int indent, int width) {
+ssize_t base64_append(
+ char **prefix,
+ size_t plen,
+ const void *p,
+ size_t l,
+ size_t indent,
+ size_t width) {
if (plen > width / 2 || plen + indent > width)
/* leave indent on the left, keep last column free */
- return base64_append_width(prefix, plen, '\n', indent, p, l, width - indent - 1);
+ return base64_append_width(prefix, plen, '\n', indent, p, l, width - indent);
else
/* leave plen on the left, keep last column free */
return base64_append_width(prefix, plen, ' ', plen + 1, p, l, width - plen - 1);
return base64mem_full(p, l, SIZE_MAX, ret);
}
-int base64_append(char **prefix, int plen,
- const void *p, size_t l,
- int margin, int width);
+ssize_t base64_append(
+ char **prefix,
+ size_t plen,
+ const void *p,
+ size_t l,
+ size_t margin,
+ size_t width);
int unbase64mem_full(const char *p, size_t l, bool secure, void **mem, size_t *len);
static inline int unbase64mem(const char *p, size_t l, void **mem, size_t *len) {
return unbase64mem_full(p, l, false, mem, len);
}
-static void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) {
+void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) {
assert(a);
assert(state);
siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
}
-static int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) {
+int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) {
int r;
assert(x);
return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
}
-DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func);
+DEFINE_HASH_OPS(
+ in_addr_data_hash_ops,
+ struct in_addr_data,
+ in_addr_data_hash_func,
+ in_addr_data_compare_func);
+
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+ in_addr_data_hash_ops_free,
+ struct in_addr_data,
+ in_addr_data_hash_func,
+ in_addr_data_compare_func,
+ free);
void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
assert(addr);
return memcmp(a, b, sizeof(*a));
}
-DEFINE_HASH_OPS(in6_addr_hash_ops, struct in6_addr, in6_addr_hash_func, in6_addr_compare_func);
+DEFINE_HASH_OPS(
+ in6_addr_hash_ops,
+ struct in6_addr,
+ in6_addr_hash_func,
+ in6_addr_compare_func);
+
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
in6_addr_hash_ops_free,
struct in6_addr,
* See also oss-fuzz#11344. */
#define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
+void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state);
+int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y);
void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state);
int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b);
extern const struct hash_ops in_addr_data_hash_ops;
+extern const struct hash_ops in_addr_data_hash_ops_free;
extern const struct hash_ops in6_addr_hash_ops;
extern const struct hash_ops in6_addr_hash_ops_free;
int *ret_mnt_id,
int flags) {
- _cleanup_free_ struct file_handle *h = NULL;
size_t n = ORIGINAL_MAX_HANDLE_SZ;
assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
* as NULL if there's no interest in either. */
for (;;) {
+ _cleanup_free_ struct file_handle *h = NULL;
int mnt_id = -1;
h = malloc0(offsetof(struct file_handle, f_handle) + n);
n = h->handle_bytes;
if (offsetof(struct file_handle, f_handle) + n < n) /* check for addition overflow */
return -EOVERFLOW;
-
- h = mfree(h);
}
}
return false;
}
+int mount_fd(const char *source,
+ int target_fd,
+ const char *filesystemtype,
+ unsigned long mountflags,
+ const void *data) {
+
+ if (mount(source, FORMAT_PROC_FD_PATH(target_fd), filesystemtype, mountflags, data) < 0) {
+ if (errno != ENOENT)
+ return -errno;
+
+ /* ENOENT can mean two things: either that the source is missing, or that /proc/ isn't
+ * mounted. Check for the latter to generate better error messages. */
+ if (proc_mounted() == 0)
+ return -ENOSYS;
+
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+int mount_nofollow(
+ const char *source,
+ const char *target,
+ const char *filesystemtype,
+ unsigned long mountflags,
+ const void *data) {
+
+ _cleanup_close_ int fd = -1;
+
+ /* In almost all cases we want to manipulate the mount table without following symlinks, hence
+ * mount_nofollow() is usually the way to go. The only exceptions are environments where /proc/ is
+ * not available yet, since we need /proc/self/fd/ for this logic to work. i.e. during the early
+ * initialization of namespacing/container stuff where /proc is not yet mounted (and maybe even the
+ * fs to mount) we can only use traditional mount() directly.
+ *
+ * Note that this disables following only for the final component of the target, i.e symlinks within
+ * the path of the target are honoured, as are symlinks in the source path everywhere. */
+
+ fd = open(target, O_PATH|O_CLOEXEC|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
+
+ return mount_fd(source, fd, filesystemtype, mountflags, data);
+}
+
const char *mount_propagation_flags_to_string(unsigned long flags) {
switch (flags & (MS_SHARED|MS_SLAVE|MS_PRIVATE)) {
#include <stdbool.h>
#include <sys/types.h>
+/* The limit used for /dev itself. 4MB should be enough since device nodes and symlinks don't
+ * consume any space and udev isn't supposed to create regular file either. There's no limit on the
+ * max number of inodes since such limit is hard to guess especially on large storage array
+ * systems. */
+#define TMPFS_LIMITS_DEV ",size=4m"
+
+/* The limit used for /dev in private namespaces. 4MB for contents of regular files. The number of
+ * inodes should be relatively low in private namespaces but for now use a 64k limit. */
+#define TMPFS_LIMITS_PRIVATE_DEV ",size=4m,nr_inodes=64k"
+
+/* Very little, if any use expected */
+#define TMPFS_LIMITS_EMPTY_OR_ALMOST ",size=4m,nr_inodes=1k"
+#define TMPFS_LIMITS_SYS TMPFS_LIMITS_EMPTY_OR_ALMOST
+#define TMPFS_LIMITS_SYS_FS_CGROUP TMPFS_LIMITS_EMPTY_OR_ALMOST
+
+/* On an extremely small device with only 256MB of RAM, 20% of RAM should be enough for the re-execution of
+ * PID1 because 16MB of free space is required. */
+#define TMPFS_LIMITS_RUN ",size=20%,nr_inodes=800k"
+
+/* The limit used for various nested tmpfs mounts, in particular for guests started by systemd-nspawn.
+ * 10% of RAM (using 16GB of RAM as a baseline) translates to 400k inodes (assuming 4k each) and 25%
+ * translates to 1M inodes.
+ * (On the host, /tmp is configured through a .mount unit file.) */
+#define NESTED_TMPFS_LIMITS ",size=10%,nr_inodes=400k"
+
+/* More space for volatile root and /var */
+#define TMPFS_LIMITS_VAR ",size=25%,nr_inodes=1m"
+#define TMPFS_LIMITS_ROOTFS TMPFS_LIMITS_VAR
+#define TMPFS_LIMITS_VOLATILE_STATE TMPFS_LIMITS_VAR
+
int name_to_handle_at_loop(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags);
int path_get_mnt_id(const char *path, int *ret);
int dev_is_devtmpfs(void);
+int mount_fd(const char *source, int target_fd, const char *filesystemtype, unsigned long mountflags, const void *data);
+int mount_nofollow(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data);
+
const char *mount_propagation_flags_to_string(unsigned long flags);
int mount_propagation_flags_from_string(const char *name, unsigned long *ret);
#include "memory-util.h"
#include "missing_sched.h"
#include "missing_syscall.h"
+#include "mountpoint-util.h"
#include "namespace-util.h"
#include "nulstr-util.h"
#include "parse-util.h"
}
if (FLAGS_SET(flags, FORK_NEW_MOUNTNS | FORK_MOUNTNS_SLAVE)) {
-
/* Optionally, make sure we never propagate mounts to the host. */
-
if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) {
log_full_errno(prio, errno, "Failed to remount root directory as MS_SLAVE: %m");
_exit(EXIT_FAILURE);
}
}
+ if (FLAGS_SET(flags, FORK_PRIVATE_TMP)) {
+ assert(FLAGS_SET(flags, FORK_NEW_MOUNTNS));
+
+ /* Optionally, overmount new tmpfs instance on /tmp/. */
+ r = mount_nofollow("tmpfs", "/tmp", "tmpfs",
+ MS_NOSUID|MS_NODEV,
+ "mode=01777" TMPFS_LIMITS_RUN);
+ if (r < 0) {
+ log_full_errno(prio, r, "Failed to overmount /tmp/: %m");
+ _exit(EXIT_FAILURE);
+ }
+ }
+
if (flags & FORK_CLOSE_ALL_FDS) {
/* Close the logs here in case it got reopened above, as close_all_fds() would close them for us */
log_close();
FORK_WAIT = 1 << 7, /* Wait until child exited */
FORK_NEW_MOUNTNS = 1 << 8, /* Run child in its own mount namespace */
FORK_MOUNTNS_SLAVE = 1 << 9, /* Make child's mount namespace MS_SLAVE */
- FORK_RLIMIT_NOFILE_SAFE = 1 << 10, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
- FORK_STDOUT_TO_STDERR = 1 << 11, /* Make stdout a copy of stderr */
- FORK_FLUSH_STDIO = 1 << 12, /* fflush() stdout (and stderr) before forking */
- FORK_NEW_USERNS = 1 << 13, /* Run child in its own user namespace */
- FORK_CLOEXEC_OFF = 1 << 14, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */
+ FORK_PRIVATE_TMP = 1 << 10, /* Mount new /tmp/ in the child (combine with FORK_NEW_MOUNTNS!) */
+ FORK_RLIMIT_NOFILE_SAFE = 1 << 11, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
+ FORK_STDOUT_TO_STDERR = 1 << 12, /* Make stdout a copy of stderr */
+ FORK_FLUSH_STDIO = 1 << 13, /* fflush() stdout (and stderr) before forking */
+ FORK_NEW_USERNS = 1 << 14, /* Run child in its own user namespace */
+ FORK_CLOEXEC_OFF = 1 << 15, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */
} ForkFlags;
int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
bool ratelimit_below(RateLimit *r) {
usec_t ts;
+ bool good = false;
assert(r);
/* Reset counter */
r->num = 0;
- goto good;
- }
+ good = true;
+ } else if (r->num < r->burst)
+ good = true;
- if (r->num < r->burst)
- goto good;
-
- r->num++;
- return false;
-
-good:
r->num++;
- return true;
+ return good;
}
unsigned ratelimit_num_dropped(RateLimit *r) {
#define CMSG_FOREACH(cmsg, mh) \
for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg)))
+#define CMSG_TYPED_DATA(cmsg, type) \
+ ({ \
+ struct cmsghdr *_cmsg = cmsg; \
+ _cmsg ? CAST_ALIGN_PTR(type, CMSG_DATA(_cmsg)) : (type*) NULL; \
+ })
+
struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length);
/* Type-safe, dereferencing version of cmsg_find() */
-#define CMSG_FIND_DATA(mh, level, type, ctype) \
- ({ \
- struct cmsghdr *_found; \
- _found = cmsg_find(mh, level, type, CMSG_LEN(sizeof(ctype))); \
- (ctype*) (_found ? CMSG_DATA(_found) : NULL); \
- })
+#define CMSG_FIND_DATA(mh, level, type, ctype) \
+ CMSG_TYPED_DATA(cmsg_find(mh, level, type, CMSG_LEN(sizeof(ctype))), ctype)
/* Resolves to a type that can carry cmsghdr structures. Make sure things are properly aligned, i.e. the type
* itself is placed properly in memory and the size is also aligned to what's appropriate for "cmsghdr"
return null_or_empty(&st);
}
-int path_is_read_only_fs(const char *path) {
+static int fd_is_read_only_fs(int fd) {
struct statvfs st;
- assert(path);
+ assert(fd >= 0);
- if (statvfs(path, &st) < 0)
+ if (fstatvfs(fd, &st) < 0)
return -errno;
if (st.f_flag & ST_RDONLY)
return true;
- /* On NFS, statvfs() might not reflect whether we can actually
- * write to the remote share. Let's try again with
- * access(W_OK) which is more reliable, at least sometimes. */
- if (access(path, W_OK) < 0 && errno == EROFS)
+ /* On NFS, fstatvfs() might not reflect whether we can actually write to the remote share. Let's try
+ * again with access(W_OK) which is more reliable, at least sometimes. */
+ if (access_fd(fd, W_OK) == -EROFS)
return true;
return false;
}
+int path_is_read_only_fs(const char *path) {
+ _cleanup_close_ int fd = -EBADFD;
+
+ assert(path);
+
+ fd = open(path, O_CLOEXEC | O_PATH);
+ if (fd < 0)
+ return -errno;
+
+ return fd_is_read_only_fs(fd);
+}
+
int files_same(const char *filea, const char *fileb, int flags) {
struct stat a, b;
/* We make use of the fact here that the umask() concept is using only the lower 9 bits of mode_t, although
* mode_t has space for the file type in the bits further up. We simply OR in the file type mask S_IFMT to
- * distinguish the first and the second iteration of the RUN_WITH_UMASK() loop, so that we can run the first
- * one, and exit on the second. */
+ * distinguish the first and the second iteration of the WITH_UMASK() loop, so that we can run the first one,
+ * and exit on the second. */
assert_cc((S_IFMT & 0777) == 0);
-#define RUN_WITH_UMASK(mask) \
+#define WITH_UMASK(mask) \
for (_cleanup_umask_ mode_t _saved_umask_ = umask(mask) | S_IFMT; \
FLAGS_SET(_saved_umask_, S_IFMT); \
_saved_umask_ &= 0777)
#include <endian.h>
#include <stdint.h>
+#include "unaligned-fundamental.h"
+
/* BE */
static inline uint16_t unaligned_read_be16(const void *_u) {
u->x = le64toh(a);
}
-
-#if __BYTE_ORDER == __BIG_ENDIAN
-#define unaligned_read_ne16 unaligned_read_be16
-#define unaligned_read_ne32 unaligned_read_be32
-#define unaligned_read_ne64 unaligned_read_be64
-
-#define unaligned_write_ne16 unaligned_write_be16
-#define unaligned_write_ne32 unaligned_write_be32
-#define unaligned_write_ne64 unaligned_write_be64
-#else
-#define unaligned_read_ne16 unaligned_read_le16
-#define unaligned_read_ne32 unaligned_read_le32
-#define unaligned_read_ne64 unaligned_read_le64
-
-#define unaligned_write_ne16 unaligned_write_le16
-#define unaligned_write_ne32 unaligned_write_le32
-#define unaligned_write_ne64 unaligned_write_le64
-#endif
return 1;
}
+static int binfmt_mounted_warn(void) {
+ int r;
+
+ r = binfmt_mounted();
+ if (r < 0)
+ return log_error_errno(r, "Failed to check if /proc/sys/fs/binfmt_misc is mounted: %m");
+ if (r == 0)
+ log_debug("/proc/sys/fs/binfmt_misc is not mounted in read-write mode, skipping.");
+
+ return r;
+}
+
static int run(int argc, char *argv[]) {
int r, k;
if (arg_unregister)
return disable_binfmt();
- if (argc > optind)
+ if (argc > optind) {
+ r = binfmt_mounted_warn();
+ if (r <= 0)
+ return r;
+
for (int i = optind; i < argc; i++) {
k = apply_file(argv[i], false);
if (k < 0 && r >= 0)
r = k;
}
- else {
+ } else {
_cleanup_strv_free_ char **files = NULL;
r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) CONF_PATHS_STRV("binfmt.d"));
return cat_files(NULL, files, 0);
}
+ r = binfmt_mounted_warn();
+ if (r <= 0)
+ return r;
+
/* Flush out all rules */
r = write_string_file("/proc/sys/fs/binfmt_misc/status", "-1", WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
int r;
r = sd_id128_get_machine(&arg_machine_id);
- if (IN_SET(r, -ENOENT, -ENOMEDIUM)) /* Not set or empty */
+ if (IN_SET(r, -ENOENT, -ENOMEDIUM, -ENOPKG)) /* Not set or empty */
return 0;
if (r < 0)
return log_error_errno(r, "Failed to get machine-id: %m");
if (r < 0)
return log_oom();
- RUN_WITH_UMASK(0000) {
+ WITH_UMASK(0000) {
fd_to = open(t, O_WRONLY|O_CREAT|O_CLOEXEC|O_EXCL|O_NOFOLLOW, 0644);
if (fd_to < 0)
return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", t);
/* Let's write this variable with an umask in effect, so that unprivileged users can't see the token
* and possibly get identification information or too much insight into the kernel's entropy pool
* state. */
- RUN_WITH_UMASK(0077) {
+ WITH_UMASK(0077) {
r = efi_set_variable(EFI_LOADER_VARIABLE(LoaderSystemToken), buffer, sizeof(buffer));
if (r < 0) {
if (!arg_graceful)
const char *arch = arg_arch_all ? "" : get_efi_arch();
- RUN_WITH_UMASK(0002) {
+ WITH_UMASK(0002) {
if (install) {
/* Don't create any of these directories when we are just updating. When we update
* we'll drop-in our files (unless there are newer ones already), but we won't create
static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
_cleanup_free_ char *content = NULL;
- UINTN value;
+ UINTN value = 0; /* avoid false maybe-uninitialized warning */
EFI_STATUS err;
assert(root_dir);
EFI_HANDLE *device) {
_cleanup_(file_closep) EFI_FILE *root_dir = NULL;
- EFI_HANDLE new_device;
+ EFI_HANDLE new_device = NULL; /* avoid false maybe-uninitialized warning */
EFI_STATUS err;
assert(config);
/* 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)
for (UINTN i = 0; i < n_items; i++) {
_cleanup_free_ char *content = NULL;
- UINTN contentsize;
+ UINTN contentsize = 0; /* avoid false maybe-uninitialized warning */
err = file_read(extra_dir, items[i], 0, 0, &content, &contentsize);
if (err != EFI_SUCCESS) {
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;
#include "util.h"
bool secure_boot_enabled(void) {
- bool secure;
+ bool secure = false; /* avoid false maybe-uninitialized warning */
EFI_STATUS err;
err = efivar_get_boolean_u8(EFI_GLOBAL_GUID, L"SecureBoot", &secure);
return efivar_set_raw(vendor, name, buf, sizeof(buf), flags);
}
-EFI_STATUS efivar_get(const EFI_GUID *vendor, const char16_t *name, char16_t **value) {
+EFI_STATUS efivar_get(const EFI_GUID *vendor, const char16_t *name, char16_t **ret) {
_cleanup_free_ char16_t *buf = NULL;
EFI_STATUS err;
char16_t *val;
if ((size % sizeof(char16_t)) != 0)
return EFI_INVALID_PARAMETER;
- if (!value)
+ if (!ret)
return EFI_SUCCESS;
/* Return buffer directly if it happens to be NUL terminated already */
if (size >= sizeof(char16_t) && buf[size / sizeof(char16_t) - 1] == 0) {
- *value = TAKE_PTR(buf);
+ *ret = TAKE_PTR(buf);
return EFI_SUCCESS;
}
memcpy(val, buf, size);
val[size / sizeof(char16_t) - 1] = 0; /* NUL terminate */
- *value = val;
+ *ret = val;
return EFI_SUCCESS;
}
-EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const char16_t *name, UINTN *i) {
+EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const char16_t *name, UINTN *ret) {
_cleanup_free_ char16_t *val = NULL;
EFI_STATUS err;
uint64_t u;
assert(vendor);
assert(name);
- assert(i);
err = efivar_get(vendor, name, &val);
if (err != EFI_SUCCESS)
if (!parse_number16(val, &u, NULL) || u > UINTN_MAX)
return EFI_INVALID_PARAMETER;
- *i = u;
+ if (ret)
+ *ret = u;
return EFI_SUCCESS;
}
assert(name);
err = efivar_get_raw(vendor, name, &buf, &size);
- if (err == EFI_SUCCESS && ret) {
- if (size != sizeof(uint32_t))
- return EFI_BUFFER_TOO_SMALL;
+ if (err != EFI_SUCCESS)
+ return err;
+ if (size != sizeof(uint32_t))
+ return EFI_BUFFER_TOO_SMALL;
+
+ if (ret)
*ret = (uint32_t) buf[0] << 0U | (uint32_t) buf[1] << 8U | (uint32_t) buf[2] << 16U |
(uint32_t) buf[3] << 24U;
- }
- return err;
+ return EFI_SUCCESS;
}
EFI_STATUS efivar_get_uint64_le(const EFI_GUID *vendor, const char16_t *name, uint64_t *ret) {
assert(name);
err = efivar_get_raw(vendor, name, &buf, &size);
- if (err == EFI_SUCCESS && ret) {
- if (size != sizeof(uint64_t))
- return EFI_BUFFER_TOO_SMALL;
+ if (err != EFI_SUCCESS)
+ return err;
+
+ if (size != sizeof(uint64_t))
+ return EFI_BUFFER_TOO_SMALL;
+ if (ret)
*ret = (uint64_t) buf[0] << 0U | (uint64_t) buf[1] << 8U | (uint64_t) buf[2] << 16U |
(uint64_t) buf[3] << 24U | (uint64_t) buf[4] << 32U | (uint64_t) buf[5] << 40U |
(uint64_t) buf[6] << 48U | (uint64_t) buf[7] << 56U;
- }
- return err;
+ return EFI_SUCCESS;
}
-EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const char16_t *name, char **buffer, UINTN *size) {
+EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const char16_t *name, char **ret, UINTN *ret_size) {
_cleanup_free_ char *buf = NULL;
UINTN l;
EFI_STATUS err;
buf = xmalloc(l);
err = RT->GetVariable((char16_t *) name, (EFI_GUID *) vendor, NULL, &l, buf);
- if (err == EFI_SUCCESS) {
-
- if (buffer)
- *buffer = TAKE_PTR(buf);
+ if (err != EFI_SUCCESS)
+ return err;
- if (size)
- *size = l;
- }
+ if (ret)
+ *ret = TAKE_PTR(buf);
+ if (ret_size)
+ *ret_size = l;
- return err;
+ return EFI_SUCCESS;
}
EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const char16_t *name, bool *ret) {
assert(vendor);
assert(name);
- assert(ret);
err = efivar_get_raw(vendor, name, &b, &size);
- if (err == EFI_SUCCESS)
+ if (err != EFI_SUCCESS)
+ return err;
+
+ if (ret)
*ret = *b > 0;
- return err;
+ return EFI_SUCCESS;
}
void efivar_set_time_usec(const EFI_GUID *vendor, const char16_t *name, uint64_t usec) {
EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const char16_t *name, uint64_t value, uint32_t flags);
void efivar_set_time_usec(const EFI_GUID *vendor, const char16_t *name, uint64_t usec);
-EFI_STATUS efivar_get(const EFI_GUID *vendor, const char16_t *name, char16_t **value);
-EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const char16_t *name, char **buffer, UINTN *size);
-EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const char16_t *name, UINTN *i);
+EFI_STATUS efivar_get(const EFI_GUID *vendor, const char16_t *name, char16_t **ret);
+EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const char16_t *name, char **ret, UINTN *ret_size);
+EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const char16_t *name, UINTN *ret);
EFI_STATUS efivar_get_uint32_le(const EFI_GUID *vendor, const char16_t *name, uint32_t *ret);
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);
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)
}
int parse_xml_introspect(const char *prefix, const char *xml, const XMLIntrospectOps *ops, void *userdata) {
- Context context = {
+ _cleanup_(context_reset_interface) Context context = {
.ops = ops,
.userdata = userdata,
.current = xml,
_cleanup_free_ char *name = NULL;
r = xml_tokenize(&context.current, &name, &context.xml_state, NULL);
- if (r < 0) {
- log_error("XML parse error");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "XML parse error");
- if (r == XML_END) {
- r = 0;
+ if (r == XML_END)
break;
- }
if (r == XML_TAG_OPEN) {
if (streq(name, "node")) {
r = parse_xml_node(&context, prefix, 0);
if (r < 0)
- goto finish;
- } else {
- log_error("Unexpected tag '%s' in introspection data.", name);
- r = -EBADMSG;
- goto finish;
- }
- } else if (r != XML_TEXT || !in_charset(name, WHITESPACE)) {
- log_error("Unexpected token.");
- r = -EBADMSG;
- goto finish;
- }
+ return r;
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Unexpected tag '%s' in introspection data.", name);
+ } else if (r != XML_TEXT || !in_charset(name, WHITESPACE))
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Unexpected token.");
}
-finish:
- context_reset_interface(&context);
-
- return r;
+ return 0;
}
int unit_cgroup_freezer_action(Unit *u, FreezerAction action) {
_cleanup_free_ char *path = NULL;
FreezerState target, kernel = _FREEZER_STATE_INVALID;
- int r;
+ int r, ret;
assert(u);
assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
if (!cg_freezer_supported())
return 0;
+ /* Ignore all requests to thaw init.scope or -.slice and reject all requests to freeze them */
+ if (unit_has_name(u, SPECIAL_ROOT_SLICE) || unit_has_name(u, SPECIAL_INIT_SCOPE))
+ return action == FREEZER_FREEZE ? -EPERM : 0;
+
if (!u->cgroup_realized)
return -EBUSY;
+ if (action == FREEZER_THAW) {
+ Unit *slice = UNIT_GET_SLICE(u);
+
+ if (slice) {
+ r = unit_cgroup_freezer_action(slice, FREEZER_THAW);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to thaw slice %s of unit: %m", slice->id);
+ }
+ }
+
target = action == FREEZER_FREEZE ? FREEZER_FROZEN : FREEZER_RUNNING;
r = unit_freezer_state_kernel(u, &kernel);
if (target == kernel) {
u->freezer_state = target;
- return 0;
- }
+ if (action == FREEZER_FREEZE)
+ return 0;
+ ret = 0;
+ } else
+ ret = 1;
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.freeze", &path);
if (r < 0)
log_unit_debug(u, "%s unit.", action == FREEZER_FREEZE ? "Freezing" : "Thawing");
- if (action == FREEZER_FREEZE)
- u->freezer_state = FREEZER_FREEZING;
- else
- u->freezer_state = FREEZER_THAWING;
+ if (target != kernel) {
+ if (action == FREEZER_FREEZE)
+ u->freezer_state = FREEZER_FREEZING;
+ else
+ u->freezer_state = FREEZER_THAWING;
+ }
r = write_string_file(path, one_zero(action == FREEZER_FREEZE), WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
return r;
- return 1;
+ return ret;
}
int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name) {
return 0;
}
+static void log_reload_caller(sd_bus_message *message, Manager *manager) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ const char *comm = NULL;
+ Unit *caller;
+ pid_t pid;
+
+ assert(message);
+ assert(manager);
+
+ if (sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID|SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_COMM, &creds) < 0)
+ return;
+
+ /* We need at least the PID, otherwise there's nothing to log, the rest is optional */
+ if (sd_bus_creds_get_pid(creds, &pid) < 0)
+ return;
+
+ (void) sd_bus_creds_get_comm(creds, &comm);
+ caller = manager_get_unit_by_pid(manager, pid);
+
+ log_info("Reloading requested from client PID " PID_FMT " ('%s') (from unit '%s')...",
+ pid, strna(comm), strna(caller ? caller->id : NULL));
+}
+
static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = ASSERT_PTR(userdata);
int r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+ /* Write a log message noting the unit or process who requested the Reload() */
+ log_reload_caller(message, m);
+
+ /* Check the rate limit after the authorization succeeds, to avoid denial-of-service issues. */
+ if (!ratelimit_below(&m->reload_ratelimit)) {
+ log_warning("Reloading request rejected due to rate limit.");
+ return sd_bus_error_setf(error,
+ SD_BUS_ERROR_LIMITS_EXCEEDED,
+ "Reload() request rejected due to rate limit.");
+ }
+
/* Instead of sending the reply back right away, we just
* remember that we need to and then send it after the reload
* is finished. That way the caller knows when the reload
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) {
static int method_get_unit_file_links(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ Manager *m = ASSERT_PTR(userdata);
InstallChange *changes = NULL;
size_t n_changes = 0, i;
- UnitFileFlags flags;
const char *name;
- char **p;
int runtime, r;
r = sd_bus_message_read(message, "sb", &name, &runtime);
if (r < 0)
return r;
- p = STRV_MAKE(name);
- flags = UNIT_FILE_DRY_RUN |
- (runtime ? UNIT_FILE_RUNTIME : 0);
-
- r = unit_file_disable(LOOKUP_SCOPE_SYSTEM, flags, NULL, p, &changes, &n_changes);
+ r = unit_file_disable(m->unit_file_scope,
+ UNIT_FILE_DRY_RUN | (runtime ? UNIT_FILE_RUNTIME : 0),
+ NULL, STRV_MAKE(name), &changes, &n_changes);
if (r < 0) {
log_error_errno(r, "Failed to get file links for %s: %m", name);
goto finish;
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),
if (r == 0)
reply_no_delay = true;
- assert(!u->pending_freezer_message);
+ if (u->pending_freezer_invocation) {
+ bus_unit_send_pending_freezer_message(u, true);
+ assert(!u->pending_freezer_invocation);
+ }
- r = sd_bus_message_new_method_return(message, &u->pending_freezer_message);
- if (r < 0)
- return r;
+ u->pending_freezer_invocation = sd_bus_message_ref(message);
if (reply_no_delay) {
- r = bus_unit_send_pending_freezer_message(u);
+ r = bus_unit_send_pending_freezer_message(u, false);
if (r < 0)
return r;
}
bus_unit_send_change_signal(u);
}
-int bus_unit_send_pending_freezer_message(Unit *u) {
+int bus_unit_send_pending_freezer_message(Unit *u, bool cancelled) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int r;
assert(u);
- if (!u->pending_freezer_message)
+ if (!u->pending_freezer_invocation)
return 0;
- r = sd_bus_send(NULL, u->pending_freezer_message, NULL);
+ if (cancelled)
+ r = sd_bus_message_new_method_error(
+ u->pending_freezer_invocation,
+ &reply,
+ &SD_BUS_ERROR_MAKE_CONST(
+ BUS_ERROR_FREEZE_CANCELLED, "Freeze operation aborted"));
+ else
+ r = sd_bus_message_new_method_return(u->pending_freezer_invocation, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_send(NULL, reply, NULL);
if (r < 0)
log_warning_errno(r, "Failed to send queued message, ignoring: %m");
- u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message);
+ u->pending_freezer_invocation = sd_bus_message_unref(u->pending_freezer_invocation);
return 0;
}
void bus_unit_send_change_signal(Unit *u);
void bus_unit_send_pending_change_signal(Unit *u, bool including_new);
-int bus_unit_send_pending_freezer_message(Unit *u);
+int bus_unit_send_pending_freezer_message(Unit *u, bool cancelled);
void bus_unit_send_removed_signal(Unit *u);
int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_type, bool reload_if_possible, sd_bus_error *error);
if (fd < 0)
return log_error_errno(errno, "Failed to allocate private socket: %m");
- RUN_WITH_UMASK(0077)
+ WITH_UMASK(0077)
r = bind(fd, &sa.sa, sa_len);
if (r < 0)
return log_error_errno(errno, "Failed to bind private socket: %m");
u->bus_track = sd_bus_track_unref(u->bus_track);
/* Get rid of pending freezer messages on this bus */
- if (u->pending_freezer_message && sd_bus_message_get_bus(u->pending_freezer_message) == *bus)
- u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message);
+ if (u->pending_freezer_invocation && sd_bus_message_get_bus(u->pending_freezer_invocation) == *bus)
+ u->pending_freezer_invocation = sd_bus_message_unref(u->pending_freezer_invocation);
}
/* Get rid of queued message on this bus */
assert(id);
assert(path);
assert(unit);
+ assert(read_dfd >= 0 || read_dfd == AT_FDCWD);
assert(write_dfd >= 0);
assert(left);
lc->path,
lc->encrypted,
unit,
- -1,
+ AT_FDCWD,
dfd,
uid,
ownership_ok,
Scope.RuntimeMaxSec, config_parse_sec, 0, offsetof(Scope, runtime_max_usec)
Scope.RuntimeRandomizedExtraSec, config_parse_sec, 0, offsetof(Scope, runtime_rand_extra_usec)
Scope.TimeoutStopSec, config_parse_sec, 0, offsetof(Scope, timeout_stop_usec)
+Scope.OOMPolicy, config_parse_oom_policy, 0, offsetof(Scope, oom_policy)
{# The [Install] section is ignored here #}
Install.Alias, NULL, 0, 0
Install.WantedBy, NULL, 0, 0
static int arg_default_oom_score_adjust;
static bool arg_default_oom_score_adjust_set;
static char *arg_default_smack_process_label;
+static usec_t arg_reload_limit_interval_sec;
+static unsigned arg_reload_limit_burst;
/* A copy of the original environment block */
static char **saved_env = NULL;
arg_random_seed = sz > 0 ? p : mfree(p);
arg_random_seed_size = sz;
+ } else if (proc_cmdline_key_streq(key, "systemd.reload_limit_interval_sec")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ r = parse_sec(value, &arg_reload_limit_interval_sec);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to parse systemd.reload_limit_interval_sec= argument '%s', ignoring: %m", value);
+ return 0;
+ }
+
+ } else if (proc_cmdline_key_streq(key, "systemd.reload_limit_burst")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ r = safe_atou(value, &arg_reload_limit_burst);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to parse systemd.reload_limit_burst= argument '%s', ignoring: %m", value);
+ return 0;
+ }
+
} else if (streq(key, "quiet") && !value) {
if (arg_show_status == _SHOW_STATUS_INVALID)
{ "Manager", "CtrlAltDelBurstAction", config_parse_emergency_action, 0, &arg_cad_burst_action },
{ "Manager", "DefaultOOMPolicy", config_parse_oom_policy, 0, &arg_default_oom_policy },
{ "Manager", "DefaultOOMScoreAdjust", config_parse_oom_score_adjust, 0, NULL },
+ { "Manager", "ReloadLimitIntervalSec", config_parse_sec, 0, &arg_reload_limit_interval_sec },
+ { "Manager", "ReloadLimitBurst", config_parse_unsigned, 0, &arg_reload_limit_burst },
#if ENABLE_SMACK
{ "Manager", "DefaultSmackProcessLabel", config_parse_string, 0, &arg_default_smack_process_label },
#else
m->confirm_spawn = arg_confirm_spawn;
m->service_watchdogs = arg_service_watchdogs;
m->cad_burst_action = arg_cad_burst_action;
+ /* Note that we don't do structured initialization here, otherwise it will reset the rate limit
+ * counter on every daemon-reload. */
+ m->reload_ratelimit.interval = arg_reload_limit_interval_sec;
+ m->reload_ratelimit.burst = arg_reload_limit_burst;
manager_set_watchdog(m, WATCHDOG_RUNTIME, arg_runtime_watchdog);
manager_set_watchdog(m, WATCHDOG_REBOOT, arg_reboot_watchdog);
#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 (isempty(c))
return 0;
- RUN_WITH_UMASK(0022)
+ WITH_UMASK(0022)
r = write_string_file("/run/systemd/container", c, WRITE_STRING_FILE_CREATE);
if (r < 0)
return log_warning_errno(r, "Failed to write /run/systemd/container, ignoring: %m");
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");
arg_default_oom_score_adjust_set = false;
arg_default_smack_process_label = mfree(arg_default_smack_process_label);
+
+ arg_reload_limit_interval_sec = 0;
+ arg_reload_limit_burst = 0;
}
static void determine_default_oom_score_adjust(void) {
#include <sys/epoll.h>
#include <sys/inotify.h>
#include <sys/ioctl.h>
+#include <sys/mount.h>
#include <sys/reboot.h>
#include <sys/timerfd.h>
#include <sys/utsname.h>
#include "manager-serialize.h"
#include "memory-util.h"
#include "mkdir-label.h"
+#include "mount-util.h"
#include "os-util.h"
#include "parse-util.h"
#include "path-lookup.h"
(void) sockaddr_un_unlink(&sa.un);
/* Only allow root to connect to this socket */
- RUN_WITH_UMASK(0077)
+ WITH_UMASK(0077)
r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
if (r < 0)
return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
if (!generator_path_any((const char* const*) paths))
return 0;
- RUN_WITH_UMASK(0022)
+ WITH_UMASK(0022)
r = execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, gather_environment,
args, NULL, m->transient_environment,
EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS | EXEC_DIR_SET_SYSTEMD_EXEC_PID);
return 0;
}
+static int manager_execute_generators(Manager *m, char **paths, bool remount_ro) {
+ _cleanup_strv_free_ char **ge = NULL;
+ const char *argv[] = {
+ NULL, /* Leave this empty, execute_directory() will fill something in */
+ m->lookup_paths.generator,
+ m->lookup_paths.generator_early,
+ m->lookup_paths.generator_late,
+ NULL,
+ };
+ int r;
+
+ r = build_generator_environment(m, &ge);
+ if (r < 0)
+ return log_error_errno(r, "Failed to build generator environment: %m");
+
+ if (remount_ro) {
+ /* Remount most of the filesystem tree read-only. We leave /sys/ as-is, because our code
+ * checks whether it is read-only to detect containerized execution environments. We leave
+ * /run/ as-is too, because that's where our output goes. We also leave /proc/ and /dev/shm/
+ * because they're API, and /tmp/ that safe_fork() mounted for us.
+ */
+ r = bind_remount_recursive("/", MS_RDONLY, MS_RDONLY,
+ STRV_MAKE("/sys", "/run", "/proc", "/dev/shm", "/tmp"));
+ if (r < 0)
+ log_warning_errno(r, "Read-only bind remount failed, ignoring: %m");
+ }
+
+ BLOCK_WITH_UMASK(0022);
+ return execute_directories(
+ (const char* const*) paths,
+ DEFAULT_TIMEOUT_USEC,
+ /* callbacks= */ NULL, /* callback_args= */ NULL,
+ (char**) argv,
+ ge,
+ EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS | EXEC_DIR_SET_SYSTEMD_EXEC_PID);
+}
+
static int manager_run_generators(Manager *m) {
- _cleanup_strv_free_ char **paths = NULL, **ge = NULL;
+ _cleanup_strv_free_ char **paths = NULL;
int r;
assert(m);
goto finish;
}
- const char *argv[] = {
- NULL, /* Leave this empty, execute_directory() will fill something in */
- m->lookup_paths.generator,
- m->lookup_paths.generator_early,
- m->lookup_paths.generator_late,
- NULL,
- };
-
- r = build_generator_environment(m, &ge);
- if (r < 0) {
- log_error_errno(r, "Failed to build generator environment: %m");
+ /* If we are the system manager, we fork and invoke the generators in a sanitized mount namespace. If
+ * we are the user manager, let's just execute the generators directly. We might not have the
+ * necessary privileges, and the system manager has already mounted /tmp/ and everything else for us.
+ */
+ if (MANAGER_IS_USER(m)) {
+ r = manager_execute_generators(m, paths, /* remount_ro= */ false);
goto finish;
}
- RUN_WITH_UMASK(0022)
- (void) execute_directories(
- (const char* const*) paths,
- DEFAULT_TIMEOUT_USEC,
- /* callbacks= */ NULL, /* callback_args= */ NULL,
- (char**) argv,
- ge,
- EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS | EXEC_DIR_SET_SYSTEMD_EXEC_PID);
-
- r = 0;
+ r = safe_fork("(sd-gens)",
+ FORK_RESET_SIGNALS | FORK_LOG | FORK_WAIT | FORK_NEW_MOUNTNS | FORK_MOUNTNS_SLAVE | FORK_PRIVATE_TMP,
+ NULL);
+ if (r == 0) {
+ r = manager_execute_generators(m, paths, /* remount_ro= */ true);
+ _exit(r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+ }
finish:
lookup_paths_trim_generator(&m->lookup_paths);
struct restrict_fs_bpf *restrict_fs;
char *default_smack_process_label;
+
+ /* Allow users to configure a rate limit for Reload() operations */
+ RateLimit reload_ratelimit;
};
static inline usec_t manager_default_timeout_abort_usec(Manager *m) {
mnt_init_debug(0);
if (!m->mount_monitor) {
+ unsigned mount_rate_limit_burst = 5;
int fd;
m->mount_monitor = mnt_new_monitor();
goto fail;
}
- r = sd_event_source_set_ratelimit(m->mount_event_source, 1 * USEC_PER_SEC, 5);
+ /* Let users override the default (5 in 1s), as it stalls the boot sequence on busy systems. */
+ const char *e = secure_getenv("SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST");
+ if (e) {
+ r = safe_atou(e, &mount_rate_limit_burst);
+ if (r < 0)
+ log_debug("Invalid value in $SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST, ignoring: %s", e);
+ }
+
+ r = sd_event_source_set_ratelimit(m->mount_event_source, 1 * USEC_PER_SEC, mount_rate_limit_burst);
if (r < 0) {
log_error_errno(r, "Failed to enable rate limit for mount events: %m");
goto fail;
{ "/proc", PROCFS, false },
{ "/dev", BIND_DEV, false },
{ "/sys", SYSFS, false },
- { "/run", RUN, false, .options_const = "mode=755" TMPFS_LIMITS_RUN, .flags = MS_NOSUID|MS_NODEV|MS_STRICTATIME },
+ { "/run", RUN, false, .options_const = "mode=0755" TMPFS_LIMITS_RUN, .flags = MS_NOSUID|MS_NODEV|MS_STRICTATIME },
};
/* ProtectKernelTunables= option and the related filesystem APIs */
.mode = EMPTY_DIR,
.ignore = false,
.read_only = true,
- .options_const = "mode=755" TMPFS_LIMITS_EMPTY_OR_ALMOST,
+ .options_const = "mode=0755" TMPFS_LIMITS_EMPTY_OR_ALMOST,
.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
};
}
dev = strjoina(temporary_mount, "/dev");
(void) mkdir(dev, 0755);
- r = mount_nofollow_verbose(LOG_DEBUG, "tmpfs", dev, "tmpfs", DEV_MOUNT_OPTIONS, "mode=755" TMPFS_LIMITS_PRIVATE_DEV);
+ r = mount_nofollow_verbose(LOG_DEBUG, "tmpfs", dev, "tmpfs", DEV_MOUNT_OPTIONS, "mode=0755" TMPFS_LIMITS_PRIVATE_DEV);
if (r < 0)
goto fail;
* added in the same commit: if it's supported it is thus also per-instance. */
const char *hpv = ns_info->protect_proc == PROTECT_PROC_DEFAULT ?
- "off" :
- protect_proc_to_string(ns_info->protect_proc);
+ "off" :
+ protect_proc_to_string(ns_info->protect_proc);
/* hidepid= support was added in 5.8, so we can use fsconfig()/fsopen() (which were added in
* 5.2) to check if hidepid= is supported. This avoids a noisy dmesg log by the kernel when
}
r = verity_dissect_and_mount(
- /* src_fd= */ -1, mount_entry_source(m), mount_entry_path(m), m->image_options,
- host_os_release_id, host_os_release_version_id, host_os_release_sysext_level, NULL);
+ /* src_fd= */ -1, mount_entry_source(m), mount_entry_path(m), m->image_options,
+ host_os_release_id, host_os_release_version_id, host_os_release_sysext_level, NULL);
if (r == -ENOENT && m->ignore)
return 0;
if (r == -ESTALE && host_os_release_id)
n_bind_mounts +
n_mount_images +
(n_extension_images > 0 || n_extension_directories > 0 ? /* Mount each image and directory plus an overlay per hierarchy */
- n_hierarchies + n_extension_images + n_extension_directories: 0) +
+ n_hierarchies + n_extension_images + n_extension_directories: 0) +
n_temporary_filesystems +
ns_info->private_dev +
(ns_info->protect_kernel_tunables ?
goto finish;
/* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */
- r = mount_pivot_root(root);
+ r = mount_switch_root(root, MOUNT_ATTR_PROPAGATION_INHERIT);
if (r == -EINVAL && root_directory) {
/* If we are using root_directory and we don't have privileges (ie: user manager in a user
* namespace) and the root_directory is already a mount point in the parent namespace,
r = mount_nofollow_verbose(LOG_DEBUG, root, root, NULL, MS_BIND|MS_REC, NULL);
if (r < 0)
goto finish;
- r = mount_pivot_root(root);
+ r = mount_switch_root(root, MOUNT_ATTR_PROPAGATION_INHERIT);
}
if (r < 0) {
log_debug_errno(r, "Failed to mount root with MS_MOVE: %m");
if (errno != ENOENT)
return -errno;
- RUN_WITH_UMASK(000)
+ WITH_UMASK(000)
r = mkdir_parents(prefix, 0755);
if (r < 0)
return r;
if (r < 0)
return r;
- RUN_WITH_UMASK(0077)
+ WITH_UMASK(0077)
if (!mkdtemp(x)) {
if (errno == EROFS || ERRNO_IS_DISK_SPACE(errno))
rw = false;
if (!y)
return -ENOMEM;
- RUN_WITH_UMASK(0000)
+ WITH_UMASK(0000)
if (mkdir(y, 0777 | S_ISVTX) < 0)
- return -errno;
+ return -errno;
r = label_fix_full(AT_FDCWD, y, prefix, 0);
if (r < 0)
/* Trouble: we failed to create the directory. Instead of failing, let's simulate /tmp being
* read-only. This way the service will get the EROFS result as if it was writing to the real
* file system. */
- RUN_WITH_UMASK(0000)
+ WITH_UMASK(0000)
r = mkdir_p(RUN_SYSTEMD_EMPTY, 0500);
if (r < 0)
return r;
} else {
/* If no unit context is known, use our own */
if (getcon_raw(&fcon) < 0) {
- r = -errno;
-
- log_warning_errno(r, "SELinux getcon_raw() failed%s (perm=%s): %m",
+ log_warning_errno(errno, "SELinux getcon_raw() failed%s (perm=%s): %m",
enforce ? "" : ", ignoring",
permission);
if (!enforce)
return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context: %m");
}
+ if (!fcon) {
+ if (!enforce)
+ return 0;
+
+ return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "We appear not to have any SELinux context: %m");
+ }
acon = fcon;
tclass = "system";
usec_t before_load, after_load;
char *con;
int r;
- bool initialized = false;
+ bool initialized;
assert(loaded_policy);
/* Turn off all of SELinux' own logging, we want to do that */
- selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) { .func_log = null_log });
+ selinux_set_callback(SELINUX_CB_LOG, (const union selinux_callback) { .func_log = null_log });
- /* Don't load policy in the initrd if we don't appear to have
- * it. For the real root, we check below if we've already
- * loaded policy, and return gracefully.
- */
+ /* Don't load policy in the initrd if we don't appear to have it. For the real root, we check below
+ * if we've already loaded policy, and return gracefully. */
if (in_initrd() && access(selinux_path(), F_OK) < 0)
return 0;
/* Already initialized by somebody else? */
r = getcon_raw(&con);
- /* getcon_raw can return 0, and still give us a NULL pointer if
- * /proc/self/attr/current is empty. SELinux guarantees this won't
- * happen, but that file isn't specific to SELinux, and may be provided
- * by some other arbitrary LSM with different semantics. */
+ /* getcon_raw can return 0, and still give us a NULL pointer if /proc/self/attr/current is
+ * empty. SELinux guarantees this won't happen, but that file isn't specific to SELinux, and may be
+ * provided by some other arbitrary LSM with different semantics. */
if (r == 0 && con) {
initialized = !streq(con, "kernel");
freecon(con);
- }
+ } else
+ initialized = false;
/* Make sure we have no fds open while loading the policy and
* transitioning */
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;
}
UNIT_FOREACH_DEPENDENCY(member, s, UNIT_ATOM_SLICE_OF) {
+ if (!member->cgroup_realized)
+ continue;
+
if (action == FREEZER_FREEZE)
r = UNIT_VTABLE(member)->freeze(member);
else
}
}
- r = exec_command_set(s->control_command, "/sbin/swapon", NULL);
+ r = exec_command_set(s->control_command, "/sbin/swapon", "--fixpgsz", NULL);
if (r < 0)
goto fail;
#DefaultLimitRTTIME=
#DefaultOOMPolicy=stop
#DefaultSmackProcessLabel=
+#ReloadLimitIntervalSec=
+#ReloadLimitBurst=
u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
u->bus_track = sd_bus_track_unref(u->bus_track);
u->deserialized_refs = strv_free(u->deserialized_refs);
- u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message);
+ u->pending_freezer_invocation = sd_bus_message_unref(u->pending_freezer_invocation);
unit_free_requires_mounts_for(u);
/* Let's open the file we'll write the transient settings into. This file is kept open as long as we are
* creating the transient, and is closed in unit_load(), as soon as we start loading the file. */
- RUN_WITH_UMASK(0022) {
+ WITH_UMASK(0022) {
f = fopen(path, "we");
if (!f)
return -errno;
u->freezer_state = FREEZER_FROZEN;
- bus_unit_send_pending_freezer_message(u);
+ bus_unit_send_pending_freezer_message(u, false);
}
void unit_thawed(Unit *u) {
u->freezer_state = FREEZER_RUNNING;
- bus_unit_send_pending_freezer_message(u);
+ bus_unit_send_pending_freezer_message(u, false);
}
static int unit_freezer_action(Unit *u, FreezerAction action) {
if (s != UNIT_ACTIVE)
return -EHOSTDOWN;
- if (IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_THAWING))
+ if ((IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_THAWING) && action == FREEZER_FREEZE) ||
+ (u->freezer_state == FREEZER_THAWING && action == FREEZER_THAW))
return -EALREADY;
r = method(u);
FILE *transient_file;
/* Freezer state */
- sd_bus_message *pending_freezer_message;
+ sd_bus_message *pending_freezer_invocation;
FreezerState freezer_state;
/* Job timeout and action to take */
#DefaultLimitRTPRIO=
#DefaultLimitRTTIME=
#DefaultSmackProcessLabel=
+#ReloadLimitIntervalSec=
+#ReloadLimitBurst
#include "compress.h"
#include "constants.h"
#include "dissect-image.h"
+#include "escape.h"
#include "fd-util.h"
#include "format-table.h"
#include "fs-util.h"
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
r = json_parse(pkgmeta_json, 0, &v, NULL, NULL);
- if (r < 0)
- log_warning_errno(r, "json_parse on %s failed, ignoring: %m", pkgmeta_json);
- else {
+ if (r < 0) {
+ _cleanup_free_ char *esc = cescape(pkgmeta_json);
+ log_warning_errno(r, "json_parse on \"%s\" failed, ignoring: %m", strnull(esc));
+ } else {
const char *module_name;
JsonVariant *module_json;
size_t salt_size;
int r;
+ if ((required & (FIDO2ENROLL_PIN | FIDO2ENROLL_UP | FIDO2ENROLL_UV)) && headless)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOPKG),
+ "Local verification is required to unlock this volume, but the 'headless' parameter was set.");
+
ask_password_flags |= ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_ACCEPT_CACHED;
assert(cid);
}
for (;;) {
- if (!FLAGS_SET(required, FIDO2ENROLL_PIN) || pins) {
- r = fido2_use_hmac_hash(
- device,
- rp_id ?: "io.systemd.cryptsetup",
- salt, salt_size,
- cid, cid_size,
- pins,
- required,
- ret_decrypted_key,
- ret_decrypted_key_size);
- if (!IN_SET(r,
- -ENOANO, /* needs pin */
- -ENOLCK)) /* pin incorrect */
- return r;
-
- device_exists = true; /* that a PIN is needed/wasn't correct means that we managed to
- * talk to a device */
- }
-
- if (headless)
- return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the '$PIN' environment variable.");
-
if (!device_exists) {
/* Before we inquire for the PIN we'll need, if we never talked to the device, check
* if the device actually is plugged in. Otherwise we'll ask for the PIN already when
device_exists = true; /* now we know for sure, a device exists, no need to ask again */
}
+ /* Always make an attempt before asking for PIN.
+ * fido2_use_hmac_hash() will perform a pre-flight check for whether the credential for
+ * can be found on one of the connected devices. This way, we can avoid prompting the user
+ * for a PIN when we are sure that no device can be used. */
+ r = fido2_use_hmac_hash(
+ device,
+ rp_id ?: "io.systemd.cryptsetup",
+ salt, salt_size,
+ cid, cid_size,
+ pins,
+ required,
+ ret_decrypted_key,
+ ret_decrypted_key_size);
+ if (!IN_SET(r,
+ -ENOANO, /* needs pin */
+ -ENOLCK)) /* pin incorrect */
+ return r;
+
+ device_exists = true; /* that a PIN is needed/wasn't correct means that we managed to
+ * talk to a device */
+
+ if (headless)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the '$PIN' environment variable.");
+
pins = strv_free_erase(pins);
r = ask_password_auto("Please enter security token PIN:", "drive-harddisk", NULL, "fido2-pin", "cryptsetup.fido2-pin", until, ask_password_flags, &pins);
if (r < 0)
}
}
-int find_fido2_auto_data(
+int acquire_fido2_key_auto(
struct crypt_device *cd,
- char **ret_rp_id,
- void **ret_salt,
- size_t *ret_salt_size,
- void **ret_cid,
- size_t *ret_cid_size,
- int *ret_keyslot,
- Fido2EnrollFlags *ret_required) {
-
- _cleanup_free_ void *cid = NULL, *salt = NULL;
- size_t cid_size = 0, salt_size = 0;
- _cleanup_free_ char *rp = NULL;
- int r, keyslot = -1;
+ const char *name,
+ const char *friendly_name,
+ const char *fido2_device,
+ const char *key_file,
+ size_t key_file_size,
+ uint64_t key_file_offset,
+ usec_t until,
+ bool headless,
+ void **ret_decrypted_key,
+ size_t *ret_decrypted_key_size,
+ AskPasswordFlags ask_password_flags) {
+
+ _cleanup_free_ void *cid = NULL;
+ size_t cid_size = 0;
+ int r, ret = -ENOENT;
Fido2EnrollFlags required = 0;
assert(cd);
- assert(ret_salt);
- assert(ret_salt_size);
- assert(ret_cid);
- assert(ret_cid_size);
- assert(ret_keyslot);
- assert(ret_required);
+ assert(name);
+ assert(ret_decrypted_key);
+ assert(ret_decrypted_key_size);
/* Loads FIDO2 metadata from LUKS2 JSON token headers. */
for (int token = 0; token < sym_crypt_token_max(CRYPT_LUKS2); token ++) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
JsonVariant *w;
+ _cleanup_free_ void *salt = NULL;
+ _cleanup_free_ char *rp = NULL;
+ size_t salt_size = 0;
int ks;
r = cryptsetup_get_token_as_json(cd, token, "systemd-fido2", &v);
continue;
}
- if (cid)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
- "Multiple FIDO2 tokens enrolled, cannot automatically determine token.");
-
- assert(keyslot < 0);
- keyslot = ks;
-
w = json_variant_by_key(v, "fido2-credential");
if (!w || !json_variant_is_string(w))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
SET_FLAG(required, FIDO2ENROLL_UV, json_variant_boolean(w));
} else
required |= FIDO2ENROLL_UV_OMIT; /* compat with 248 */
+
+ ret = acquire_fido2_key(
+ name,
+ friendly_name,
+ fido2_device,
+ rp,
+ cid, cid_size,
+ key_file, key_file_size, key_file_offset,
+ salt, salt_size,
+ until,
+ headless,
+ required,
+ ret_decrypted_key, ret_decrypted_key_size,
+ ask_password_flags);
+ if (ret == 0)
+ break;
}
if (!cid)
return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
"No valid FIDO2 token data found.");
- log_info("Automatically discovered security FIDO2 token unlocks volume.");
+ if (ret == -EAGAIN) /* fido2 device does not exist, or UV is blocked; caller will prompt for retry */
+ return log_debug_errno(ret, "FIDO2 token does not exist, or UV is blocked.");
+ if (ret < 0)
+ return log_error_errno(ret, "Failed to unlock LUKS volume with FIDO2 token: %m");
- *ret_rp_id = TAKE_PTR(rp);
- *ret_cid = TAKE_PTR(cid);
- *ret_cid_size = cid_size;
- *ret_salt = TAKE_PTR(salt);
- *ret_salt_size = salt_size;
- *ret_keyslot = keyslot;
- *ret_required = required;
- return 0;
+ log_info("Unlocked volume via automatically discovered security FIDO2 token.");
+ return ret;
}
size_t *ret_decrypted_key_size,
AskPasswordFlags ask_password_flags);
-int find_fido2_auto_data(
+int acquire_fido2_key_auto(
struct crypt_device *cd,
- char **ret_rp_id,
- void **ret_salt,
- size_t *ret_salt_size,
- void **ret_cid,
- size_t *ret_cid_size,
- int *ret_keyslot,
- Fido2EnrollFlags *ret_required);
+ const char *name,
+ const char *friendly_name,
+ const char *fido2_device,
+ const char *key_file,
+ size_t key_file_size,
+ uint64_t key_file_offset,
+ usec_t until,
+ bool headless,
+ void **ret_decrypted_key,
+ size_t *ret_decrypted_key_size,
+ AskPasswordFlags ask_password_flags);
#else
"FIDO2 token support not available.");
}
-static inline int find_fido2_auto_data(
+static inline int acquire_fido2_key_auto(
struct crypt_device *cd,
- char **ret_rp_id,
- void **ret_salt,
- size_t *ret_salt_size,
- void **ret_cid,
- size_t *ret_cid_size,
- int *ret_keyslot,
- Fido2EnrollFlags *ret_required) {
+ const char *name,
+ const char *friendly_name,
+ const char *fido2_device,
+ const char *key_file,
+ size_t key_file_size,
+ uint64_t key_file_offset,
+ usec_t until,
+ bool headless,
+ void **ret_decrypted_key,
+ size_t *ret_decrypted_key_size,
+ AskPasswordFlags ask_password_flags) {
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"FIDO2 token support not available.");
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
_cleanup_(erase_and_freep) void *decrypted_key = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
- _cleanup_free_ void *discovered_salt = NULL, *discovered_cid = NULL;
- size_t discovered_salt_size, discovered_cid_size, decrypted_key_size, cid_size = 0;
- _cleanup_free_ char *friendly = NULL, *discovered_rp_id = NULL;
+ size_t decrypted_key_size, cid_size = 0;
+ _cleanup_free_ char *friendly = NULL;
int keyslot = arg_key_slot, r;
const char *rp_id = NULL;
const void *cid = NULL;
* use PIN + UP when needed, and do not configure UV at all. Eventually, we should make this
* explicitly configurable. */
required = FIDO2ENROLL_PIN_IF_NEEDED | FIDO2ENROLL_UP_IF_NEEDED | FIDO2ENROLL_UV_OMIT;
- } else if (!use_libcryptsetup_plugin) {
- r = find_fido2_auto_data(
- cd,
- &discovered_rp_id,
- &discovered_salt,
- &discovered_salt_size,
- &discovered_cid,
- &discovered_cid_size,
- &keyslot,
- &required);
-
- if (IN_SET(r, -ENOTUNIQ, -ENXIO))
- return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
- "Automatic FIDO2 metadata discovery was not possible because missing or not unique, falling back to traditional unlocking.");
- if (r < 0)
- return r;
-
- if ((required & (FIDO2ENROLL_PIN | FIDO2ENROLL_UP | FIDO2ENROLL_UV)) && arg_headless)
- return log_error_errno(SYNTHETIC_ERRNO(ENOPKG),
- "Local verification is required to unlock this volume, but the 'headless' parameter was set.");
-
- rp_id = discovered_rp_id;
- key_data = discovered_salt;
- key_data_size = discovered_salt_size;
- cid = discovered_cid;
- cid_size = discovered_cid_size;
}
friendly = friendly_disk_name(crypt_get_device_name(cd), name);
"Automatic FIDO2 metadata discovery was not possible because missing or not unique, falling back to traditional unlocking.");
} else {
- r = acquire_fido2_key(
- name,
- friendly,
- arg_fido2_device,
- rp_id,
- cid, cid_size,
- key_file, arg_keyfile_size, arg_keyfile_offset,
- key_data, key_data_size,
- until,
- arg_headless,
- required,
- &decrypted_key, &decrypted_key_size,
- arg_ask_password_flags);
+ if (cid)
+ r = acquire_fido2_key(
+ name,
+ friendly,
+ arg_fido2_device,
+ rp_id,
+ cid, cid_size,
+ key_file, arg_keyfile_size, arg_keyfile_offset,
+ key_data, key_data_size,
+ until,
+ arg_headless,
+ required,
+ &decrypted_key, &decrypted_key_size,
+ arg_ask_password_flags);
+ else
+ r = acquire_fido2_key_auto(
+ cd,
+ name,
+ friendly,
+ arg_fido2_device,
+ key_file, arg_keyfile_size, arg_keyfile_offset,
+ until,
+ arg_headless,
+ &decrypted_key, &decrypted_key_size,
+ arg_ask_password_flags);
if (r >= 0)
break;
}
#include "copy.h"
#include "device-util.h"
#include "devnum-util.h"
+#include "discover-image.h"
#include "dissect-image.h"
#include "env-util.h"
#include "escape.h"
ACTION_WITH,
ACTION_COPY_FROM,
ACTION_COPY_TO,
+ ACTION_DISCOVER,
} arg_action = ACTION_DISSECT;
static const char *arg_image = NULL;
static const char *arg_path = NULL;
static PagerFlags arg_pager_flags = 0;
static bool arg_legend = true;
static bool arg_rmdir = false;
+static bool arg_in_memory = false;
static char **arg_argv = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_verity_settings, verity_settings_done);
" --mkdir Make mount directory before mounting, if missing\n"
" --rmdir Remove mount directory after unmounting\n"
" --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n"
+ " --in-memory Copy image into memory\n"
" --root-hash=HASH Specify root hash for verity\n"
" --root-hash-sig=SIG Specify pkcs7 signature of root hash for verity\n"
" as a DER encoded PKCS7, either as a path to a file\n"
" --with Mount, run command, unmount\n"
" -x --copy-from Copy files from image to host\n"
" -a --copy-to Copy files from host to image\n"
+ " --discover Discover DDIs in well known directories\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,
ARG_VERITY_DATA,
ARG_MKDIR,
ARG_RMDIR,
+ ARG_IN_MEMORY,
ARG_JSON,
ARG_MTREE,
+ ARG_DISCOVER,
};
static const struct option options[] = {
{ "verity-data", required_argument, NULL, ARG_VERITY_DATA },
{ "mkdir", no_argument, NULL, ARG_MKDIR },
{ "rmdir", no_argument, NULL, ARG_RMDIR },
+ { "in-memory", no_argument, NULL, ARG_IN_MEMORY },
{ "list", no_argument, NULL, 'l' },
{ "mtree", no_argument, NULL, ARG_MTREE },
{ "copy-from", no_argument, NULL, 'x' },
{ "copy-to", no_argument, NULL, 'a' },
{ "json", required_argument, NULL, ARG_JSON },
+ { "discover", no_argument, NULL, ARG_DISCOVER },
{}
};
break;
}
+ case ARG_IN_MEMORY:
+ arg_in_memory = true;
+ break;
+
case ARG_ROOT_HASH: {
_cleanup_free_ void *p = NULL;
size_t l;
break;
+ case ARG_DISCOVER:
+ arg_action = ACTION_DISCOVER;
+ break;
+
case '?':
return -EINVAL;
break;
+ case ACTION_DISCOVER:
+ if (optind != argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Expected no argument.");
+
+ break;
+
default:
assert_not_reached();
}
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. */
return rcode;
}
+static int action_discover(void) {
+ _cleanup_(hashmap_freep) Hashmap *images = NULL;
+ _cleanup_(table_unrefp) Table *t = NULL;
+ Image *img;
+ int r;
+
+ images = hashmap_new(&image_hash_ops);
+ if (!images)
+ return log_oom();
+
+ for (ImageClass cl = 0; cl < _IMAGE_CLASS_MAX; cl++) {
+ r = image_discover(cl, NULL, images);
+ if (r < 0)
+ return log_error_errno(r, "Failed to discover images: %m");
+ }
+
+ if ((arg_json_format_flags & JSON_FORMAT_OFF) && hashmap_isempty(images)) {
+ log_info("No images found.");
+ return 0;
+ }
+
+ t = table_new("name", "type", "class", "ro", "path", "time", "usage");
+ if (!t)
+ return log_oom();
+
+ HASHMAP_FOREACH(img, images) {
+
+ if (!IN_SET(img->type, IMAGE_RAW, IMAGE_BLOCK))
+ continue;
+
+ r = table_add_many(
+ t,
+ TABLE_STRING, img->name,
+ TABLE_STRING, image_type_to_string(img->type),
+ TABLE_STRING, image_class_to_string(img->class),
+ TABLE_BOOLEAN, img->read_only,
+ TABLE_PATH, img->path,
+ TABLE_TIMESTAMP, img->mtime != 0 ? img->mtime : img->crtime,
+ TABLE_SIZE, img->usage);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
+
+ (void) table_set_sort(t, (size_t) 0);
+
+ return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
+}
+
static int run(int argc, char *argv[]) {
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
- int r;
+ uint32_t loop_flags;
+ int open_flags, r;
- log_parse_environment();
- log_open();
+ log_setup();
r = parse_argv(argc, argv);
if (r <= 0)
if (arg_action == ACTION_UMOUNT)
return action_umount(arg_path);
+ if (arg_action == ACTION_DISCOVER)
+ return action_discover();
r = verity_settings_load(
&arg_verity_settings,
* available we turn off partition table
* support */
- r = loop_device_make_by_path(
- arg_image,
- FLAGS_SET(arg_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR,
- FLAGS_SET(arg_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
- LOCK_SH,
- &d);
+ open_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR;
+ loop_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN;
+
+ if (arg_in_memory)
+ r = loop_device_make_by_path_memory(arg_image, open_flags, loop_flags, LOCK_SH, &d);
+ else
+ r = loop_device_make_by_path(arg_image, open_flags, loop_flags, LOCK_SH, &d);
if (r < 0)
return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image);
if (path_is_read_only_fs("/sys") > 0) {
if (streq(what, "sysfs")) {
- log_info("Running in a container, ignoring fstab entry for %s.", what);
+ log_info("/sys/ is read-only (running in a container?), ignoring fstab entry for %s.", me->mnt_dir);
continue;
}
if (is_device_path(what)) {
- log_info("Running in a container, ignoring fstab device entry for %s.", what);
+ log_info("/sys/ is read-only (running in a container?), ignoring fstab device entry for %s.", what);
continue;
}
}
static int add_sysroot_mount(void) {
_cleanup_free_ char *what = NULL;
const char *opts, *fstype;
- bool default_rw;
+ bool default_rw, makefs;
+ MountPointFlags flags;
int r;
if (isempty(arg_root_what)) {
return r;
}
+ makefs = fstab_test_option(opts, "x-systemd.makefs\0");
+ flags = makefs * MOUNT_MAKEFS;
+
return add_mount("/proc/cmdline",
arg_dest,
what,
fstype,
opts,
is_device_path(what) ? 1 : 0, /* passno */
- 0, /* makefs off, growfs off, noauto off, nofail off, automount off */
+ flags, /* makefs, growfs off, noauto off, nofail off, automount off */
SPECIAL_INITRD_ROOT_FS_TARGET);
}
static int add_sysroot_usr_mount(void) {
_cleanup_free_ char *what = NULL;
const char *opts;
+ bool makefs;
+ MountPointFlags flags;
int r;
/* Returns 0 if we didn't do anything, > 0 if we either generated a unit for the /usr/ mount, or we
log_debug("Found entry what=%s where=/sysusr/usr type=%s opts=%s", what, strna(arg_usr_fstype), strempty(opts));
+ makefs = fstab_test_option(opts, "x-systemd.makefs\0");
+ flags = makefs * MOUNT_MAKEFS;
+
r = add_mount("/proc/cmdline",
arg_dest,
what,
arg_usr_fstype,
opts,
is_device_path(what) ? 1 : 0, /* passno */
- 0,
+ flags,
SPECIAL_INITRD_USR_FS_TARGET);
if (r < 0)
return r;
return ((l + ali - 1) & ~(ali - 1));
}
+#define ALIGN2(l) ALIGN_TO(l, 2)
#define ALIGN4(l) ALIGN_TO(l, 4)
#define ALIGN8(l) ALIGN_TO(l, 8)
+#define ALIGN2_PTR(p) ((void*) ALIGN2((uintptr_t) p))
+#define ALIGN4_PTR(p) ((void*) ALIGN4((uintptr_t) p))
+#define ALIGN8_PTR(p) ((void*) ALIGN8((uintptr_t) p))
#ifndef SD_BOOT
/* libefi also provides ALIGN, and we do not use them in sd-boot explicitly. */
#define ALIGN(l) ALIGN_TO(l, sizeof(void*))
#define ALIGN_PTR(p) ((void*) ALIGN((uintptr_t) (p)))
#endif
+/* Checks if the specified pointer is aligned as appropriate for the specific type */
+#define IS_ALIGNED16(p) (((uintptr_t) p) % __alignof__(uint16_t) == 0)
+#define IS_ALIGNED32(p) (((uintptr_t) p) % __alignof__(uint32_t) == 0)
+#define IS_ALIGNED64(p) (((uintptr_t) p) % __alignof__(uint64_t) == 0)
+
/* Same as ALIGN_TO but callable in constant contexts. */
#define CONST_ALIGN_TO(l, ali) \
__builtin_choose_expr( \
((l) + (ali) - 1) & ~((ali) - 1), \
VOID_0)
+/* Similar to ((t *) (void *) (p)) to cast a pointer. The macro asserts that the pointer has a suitable
+ * alignment for type "t". This exists for places where otherwise "-Wcast-align=strict" would issue a
+ * warning or if you want to assert that the cast gives a pointer of suitable alignment. */
+#define CAST_ALIGN_PTR(t, p) \
+ ({ \
+ const void *_p = (p); \
+ assert(((uintptr_t) _p) % __alignof__(t) == 0); \
+ (t *) _p; \
+ })
+
#define UPDATE_FLAG(orig, flag, b) \
((b) ? ((orig) | (flag)) : ((orig) & ~(flag)))
#define SET_FLAG(v, flag, b) \
#include "macro-fundamental.h"
#include "sha256.h"
+#include "unaligned-fundamental.h"
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define SWAP(n) \
# define SWAP64(n) (n)
#endif
-/* The condition below is from glibc's string/string-inline.c.
- * See definition of _STRING_INLINE_unaligned. */
-#if !defined(__mc68020__) && !defined(__s390__) && !defined(__i386__)
-# define UNALIGNED_P(p) (((uintptr_t) p) % __alignof__(uint32_t) != 0)
-#else
-# define UNALIGNED_P(p) false
-#endif
-
/* This array contains the bytes used to pad the buffer to the next
64-byte boundary. (FIPS 180-2:5.1.1) */
static const uint8_t fillbuf[64] = {
/* Put result from CTX in first 32 bytes following RESBUF. */
for (size_t i = 0; i < 8; ++i)
- if (UNALIGNED_P(resbuf))
- memcpy(resbuf + i * sizeof(uint32_t), (uint32_t[]) { SWAP(ctx->H[i]) }, sizeof(uint32_t));
- else
- ((uint32_t *) resbuf)[i] = SWAP(ctx->H[i]);
-
+ unaligned_write_ne32(resbuf + i * sizeof(uint32_t), SWAP(ctx->H[i]));
return resbuf;
}
/* Process available complete blocks. */
if (len >= 64) {
- if (UNALIGNED_P(buffer))
+ if (IS_ALIGNED32(buffer)) {
+ sha256_process_block(buffer, len & ~63, ctx);
+ buffer = (const char *) buffer + (len & ~63);
+ len &= 63;
+ } else
while (len > 64) {
memcpy(ctx->buffer, buffer, 64);
sha256_process_block(ctx->buffer, 64, ctx);
buffer = (const char *) buffer + 64;
len -= 64;
}
- else {
- sha256_process_block(buffer, len & ~63, ctx);
- buffer = (const char *) buffer + (len & ~63);
- len &= 63;
- }
}
/* Move remaining bytes into internal buffer. */
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#include "stdint.h"
+#include <stdint.h>
#define SHA256_DIGEST_SIZE 32
return a >= '0' && a <= '9';
}
+static inline bool ascii_ishex(sd_char a) {
+ return ascii_isdigit(a) || (a >= 'a' && a <= 'f') || (a >= 'A' && a <= 'F');
+}
+
static inline bool ascii_isalpha(sd_char a) {
/* A pure ASCII, locale independent version of isalpha() */
return (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdint.h>
+
+static inline uint16_t unaligned_read_ne16(const void *_u) {
+ const struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u;
+
+ return u->x;
+}
+
+static inline uint32_t unaligned_read_ne32(const void *_u) {
+ const struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u;
+
+ return u->x;
+}
+
+static inline uint64_t unaligned_read_ne64(const void *_u) {
+ const struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u;
+
+ return u->x;
+}
+
+static inline void unaligned_write_ne16(void *_u, uint16_t a) {
+ struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u;
+
+ u->x = a;
+}
+
+static inline void unaligned_write_ne32(void *_u, uint32_t a) {
+ struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u;
+
+ u->x = a;
+}
+
+static inline void unaligned_write_ne64(void *_u, uint64_t a) {
+ struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u;
+
+ u->x = a;
+}
_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)
ARG_IO_WEIGHT,
ARG_LUKS_PBKDF_TYPE,
ARG_LUKS_PBKDF_HASH_ALGORITHM,
+ ARG_LUKS_PBKDF_FORCE_ITERATIONS,
ARG_LUKS_PBKDF_TIME_COST,
ARG_LUKS_PBKDF_MEMORY_COST,
ARG_LUKS_PBKDF_PARALLEL_THREADS,
{ "luks-volume-key-size", required_argument, NULL, ARG_LUKS_VOLUME_KEY_SIZE },
{ "luks-pbkdf-type", required_argument, NULL, ARG_LUKS_PBKDF_TYPE },
{ "luks-pbkdf-hash-algorithm", required_argument, NULL, ARG_LUKS_PBKDF_HASH_ALGORITHM },
+ { "luks-pbkdf-force-iterations", required_argument, NULL, ARG_LUKS_PBKDF_FORCE_ITERATIONS },
{ "luks-pbkdf-time-cost", required_argument, NULL, ARG_LUKS_PBKDF_TIME_COST },
{ "luks-pbkdf-memory-cost", required_argument, NULL, ARG_LUKS_PBKDF_MEMORY_COST },
{ "luks-pbkdf-parallel-threads", required_argument, NULL, ARG_LUKS_PBKDF_PARALLEL_THREADS },
break;
case ARG_LUKS_VOLUME_KEY_SIZE:
+ case ARG_LUKS_PBKDF_FORCE_ITERATIONS:
case ARG_LUKS_PBKDF_PARALLEL_THREADS:
case ARG_RATE_LIMIT_BURST: {
const char *field =
c == ARG_LUKS_VOLUME_KEY_SIZE ? "luksVolumeKeySize" :
+ c == ARG_LUKS_PBKDF_FORCE_ITERATIONS ? "luksPbkdfForceIterations" :
c == ARG_LUKS_PBKDF_PARALLEL_THREADS ? "luksPbkdfParallelThreads" :
c == ARG_RATE_LIMIT_BURST ? "rateLimitBurst" : NULL;
unsigned n;
switch (user_record_storage(h)) {
case USER_SUBVOLUME:
- RUN_WITH_UMASK(0077)
+ WITH_UMASK(0077)
r = btrfs_subvol_make(d);
if (r >= 0) {
assert(buffer);
assert(hr);
+ bool benchmark = user_record_luks_pbkdf_force_iterations(hr) == UINT64_MAX;
+
*buffer = (struct crypt_pbkdf_type) {
.hash = user_record_luks_pbkdf_hash_algorithm(hr),
.type = user_record_luks_pbkdf_type(hr),
- .time_ms = user_record_luks_pbkdf_time_cost_usec(hr) / USEC_PER_MSEC,
+ .time_ms = benchmark ? user_record_luks_pbkdf_time_cost_usec(hr) / USEC_PER_MSEC : 0,
+ .iterations = benchmark ? 0 : user_record_luks_pbkdf_force_iterations(hr),
.max_memory_kb = user_record_luks_pbkdf_memory_cost(hr) / 1024,
.parallel_threads = user_record_luks_pbkdf_parallel_threads(hr),
+ .flags = benchmark ? 0 : CRYPT_PBKDF_NO_BENCHMARK,
};
return buffer;
return get_hardware_firmware_data("bios_version", ret);
}
+static int get_firmware_vendor(char **ret) {
+ return get_hardware_firmware_data("bios_vendor", ret);
+}
+
+static int get_firmware_date(char **ret) {
+ return get_hardware_firmware_data("bios_date", ret);
+}
+
static const char* valid_chassis(const char *chassis) {
assert(chassis);
return sd_bus_message_append(reply, "s", firmware_version);
}
+static int property_get_firmware_vendor(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_free_ char *firmware_vendor = NULL;
+
+ (void) get_firmware_vendor(&firmware_vendor);
+
+ return sd_bus_message_append(reply, "s", firmware_vendor);
+}
+
+static int property_get_firmware_date(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_free_ char *firmware_date = NULL;
+
+ (void) get_firmware_date(&firmware_date);
+
+ return sd_bus_message_append(reply, "s", firmware_date);
+}
static int property_get_hostname(
sd_bus *bus,
const char *path,
static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL, *text = NULL,
- *chassis = NULL, *vendor = NULL, *model = NULL, *serial = NULL, *firmware_version = NULL;
+ *chassis = NULL, *vendor = NULL, *model = NULL, *serial = NULL, *firmware_version = NULL,
+ *firmware_vendor = NULL, *firmware_date = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
sd_id128_t product_uuid = SD_ID128_NULL;
(void) get_hardware_serial(&serial);
}
(void) get_firmware_version(&firmware_version);
+ (void) get_firmware_vendor(&firmware_vendor);
+ (void) get_firmware_date(&firmware_date);
r = json_build(&v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("Hostname", JSON_BUILD_STRING(hn)),
JSON_BUILD_PAIR("HardwareModel", JSON_BUILD_STRING(model ?: c->data[PROP_HARDWARE_MODEL])),
JSON_BUILD_PAIR("HardwareSerial", JSON_BUILD_STRING(serial)),
JSON_BUILD_PAIR("FirmwareVersion", JSON_BUILD_STRING(firmware_version)),
+ JSON_BUILD_PAIR("FirmwareVendor", JSON_BUILD_STRING(firmware_vendor)),
+ JSON_BUILD_PAIR("FirmwareDate", JSON_BUILD_STRING(firmware_date)),
JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_ID128(product_uuid)),
JSON_BUILD_PAIR_CONDITION(sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_NULL)));
SD_BUS_PROPERTY("HardwareVendor", "s", property_get_hardware_vendor, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("HardwareModel", "s", property_get_hardware_model, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("FirmwareVersion", "s", property_get_firmware_version, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("FirmwareVendor", "s", property_get_firmware_vendor, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("FirmwareDate", "s", property_get_firmware_date, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_METHOD_WITH_ARGS("SetHostname",
SD_BUS_ARGS("s", hostname, "b", interactive),
int notify_fd;
sd_event_source *notify_event_source;
+
+ bool use_btrfs_subvol;
+ bool use_btrfs_quota;
};
#define TRANSFERS_MAX 64
assert(ret);
- m = new0(Manager, 1);
+ m = new(Manager, 1);
if (!m)
return -ENOMEM;
+ *m = (Manager) {
+ .use_btrfs_subvol = true,
+ .use_btrfs_quota = true,
+ };
+
r = sd_event_default(&m->event);
if (r < 0)
return r;
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Local name %s is invalid", local);
- r = setup_machine_directory(error);
+ r = setup_machine_directory(error, m->use_btrfs_subvol, m->use_btrfs_quota);
if (r < 0)
return r;
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Local name %s is invalid", local);
- r = setup_machine_directory(error);
+ r = setup_machine_directory(error, m->use_btrfs_subvol, m->use_btrfs_quota);
if (r < 0)
return r;
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Unknown verification mode %s", verify);
- r = setup_machine_directory(error);
+ r = setup_machine_directory(error, m->use_btrfs_subvol, m->use_btrfs_quota);
if (r < 0)
return r;
m);
}
+static void manager_parse_env(Manager *m) {
+ int r;
+
+ assert(m);
+
+ /* Same as src/import/{import,pull}.c:
+ * Let's make these relatively low-level settings also controllable via env vars. User can then set
+ * them for systemd-importd.service if they like to tweak behaviour */
+
+ r = getenv_bool("SYSTEMD_IMPORT_BTRFS_SUBVOL");
+ if (r >= 0)
+ m->use_btrfs_subvol = r;
+ else if (r != -ENXIO)
+ log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_BTRFS_SUBVOL: %m");
+
+ r = getenv_bool("SYSTEMD_IMPORT_BTRFS_QUOTA");
+ if (r >= 0)
+ m->use_btrfs_quota = r;
+ else if (r != -ENXIO)
+ log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_BTRFS_QUOTA: %m");
+}
+
static int run(int argc, char *argv[]) {
_cleanup_(manager_unrefp) Manager *m = NULL;
int r;
if (r < 0)
return log_error_errno(r, "Failed to allocate manager object: %m");
+ manager_parse_env(m);
+
r = manager_add_bus_objects(m);
if (r < 0)
return r;
#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"
/* If we got no SELinux label passed in, let's try to acquire one */
- if (getpidcon(c->pid, &con) >= 0) {
+ if (getpidcon(c->pid, &con) >= 0 && con) {
free_and_replace(c->label, con);
c->label_size = strlen(c->label);
}
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 (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(JOURNAL_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);
}
#include <fcntl.h>
#include <unistd.h>
+#include "sd-id128.h"
#include "sd-journal.h"
#include "alloc-util.h"
test_close(one);
- /* restart server */
- seqnum = 0;
+ /* If the machine-id is not initialized, the header file verification
+ * (which happens when re-opening a journal file) will fail. */
+ if (sd_id128_get_machine(NULL) >= 0) {
+ /* restart server */
+ seqnum = 0;
- assert_se(managed_journal_file_open(-1, "two.journal", O_RDWR, JOURNAL_COMPRESS, 0,
- UINT64_MAX, NULL, m, NULL, NULL, &two) == 0);
+ assert_se(managed_journal_file_open(-1, "two.journal", O_RDWR, JOURNAL_COMPRESS, 0,
+ UINT64_MAX, NULL, m, NULL, NULL, &two) == 0);
- assert_se(sd_id128_equal(two->file->header->seqnum_id, seqnum_id));
+ assert_se(sd_id128_equal(two->file->header->seqnum_id, seqnum_id));
- append_number(two, 7, &seqnum);
- printf("seqnum=%"PRIu64"\n", seqnum);
- assert_se(seqnum == 5);
+ append_number(two, 7, &seqnum);
+ printf("seqnum=%"PRIu64"\n", seqnum);
+ assert_se(seqnum == 5);
- /* So..., here we have the same seqnum in two files with the
- * same seqnum_id. */
+ /* So..., here we have the same seqnum in two files with the
+ * same seqnum_id. */
- test_close(two);
+ test_close(two);
+ }
log_info("Done...");
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SO_TIMESTAMP &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
- triple_timestamp_from_realtime(&t, timeval_load((struct timeval*) CMSG_DATA(cmsg)));
+ triple_timestamp_from_realtime(&t, timeval_load(CMSG_TYPED_DATA(cmsg, struct timeval)));
}
if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff)))
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received information refresh time option with an invalid length (%zu).", optlen);
- irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
+ irt = unaligned_read_be32(optval) * USEC_PER_SEC;
break;
}
}
#define BUS_ERROR_NOTHING_TO_CLEAN "org.freedesktop.systemd1.NothingToClean"
#define BUS_ERROR_UNIT_BUSY "org.freedesktop.systemd1.UnitBusy"
#define BUS_ERROR_UNIT_INACTIVE "org.freedesktop.systemd1.UnitInactive"
+#define BUS_ERROR_FREEZE_CANCELLED "org.freedesktop.systemd1.FreezeCancelled"
#define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine"
#define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage"
sysattr, value, ret_value ? "" : ", ignoring");
if (ret_value)
return r;
- } else if (ret_value)
- *ret_value = TAKE_PTR(value);
+ return 0;
+ }
+
+ if (ret_value)
+ *ret_value = value;
+
+ TAKE_PTR(value);
return 0;
}
} else
assert_se(r == -ENOENT);
- r = sd_device_get_sysattr_value(d, "name_assign_type", &val);
- assert_se(r >= 0 || ERRNO_IS_PRIVILEGE(r) || IN_SET(r, -ENOENT, -EINVAL));
-
- if (r > 0) {
+ r = sd_device_get_sysattr_value(d, "nsid", NULL);
+ if (r >= 0) {
unsigned x;
- assert_se(device_get_sysattr_unsigned(d, "name_assign_type", NULL) >= 0);
- assert_se(device_get_sysattr_unsigned(d, "name_assign_type", &x) >= 0);
- }
+ assert_se(device_get_sysattr_unsigned(d, "nsid", NULL) >= 0);
+ r = device_get_sysattr_unsigned(d, "nsid", &x);
+ assert_se(r >= 0);
+ assert_se((x > 0) == (r > 0));
+ } else
+ assert_se(ERRNO_IS_PRIVILEGE(r) || IN_SET(r, -ENOENT, -EINVAL));
}
TEST(sd_device_enumerator_devices) {
#include "sync-util.h"
bool id128_is_valid(const char *s) {
- size_t i, l;
+ size_t l;
assert(s);
l = strlen(s);
- if (l == 32) {
+ if (l == SD_ID128_STRING_MAX - 1)
/* Plain formatted 128bit hex string */
+ return in_charset(s, HEXDIGITS);
- for (i = 0; i < l; i++) {
- char c = s[i];
-
- if (!ascii_isdigit(c) &&
- !(c >= 'a' && c <= 'f') &&
- !(c >= 'A' && c <= 'F'))
- return false;
- }
-
- } else if (l == 36) {
-
+ if (l == SD_ID128_UUID_STRING_MAX - 1) {
/* Formatted UUID */
-
- for (i = 0; i < l; i++) {
+ for (size_t i = 0; i < l; i++) {
char c = s[i];
if (IN_SET(i, 8, 13, 18, 23)) {
if (c != '-')
return false;
- } else {
- if (!ascii_isdigit(c) &&
- !(c >= 'a' && c <= 'f') &&
- !(c >= 'A' && c <= 'F'))
- return false;
- }
+ } else if (!ascii_ishex(c))
+ return false;
}
+ return true;
+ }
- } else
- return false;
-
- return true;
+ return false;
}
-int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) {
- char buffer[36 + 2];
+int id128_read_fd(int fd, Id128FormatFlag f, sd_id128_t *ret) {
+ char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */
ssize_t l;
+ int r;
assert(fd >= 0);
- assert(f < _ID128_FORMAT_MAX);
/* Reads an 128bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both
* optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they
* aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you
- * accept". */
+ * accept".
+ *
+ * This returns the following:
+ * -ENOMEDIUM: an empty string,
+ * -ENOPKG: "uninitialized" or "uninitialized\n",
+ * -EUCLEAN: other invalid strings. */
l = loop_read(fd, buffer, sizeof(buffer), false); /* we expect a short read of either 32/33 or 36/37 chars */
if (l < 0)
switch (l) {
- case 13:
- case 14:
- /* Treat an "uninitialized" id file like an empty one */
- return f == ID128_PLAIN_OR_UNINIT && strneq(buffer, "uninitialized\n", l) ? -ENOMEDIUM : -EINVAL;
+ case STRLEN("uninitialized"):
+ case STRLEN("uninitialized\n"):
+ return strneq(buffer, "uninitialized\n", l) ? -ENOPKG : -EINVAL;
- case 33: /* plain UUID with trailing newline */
- if (buffer[32] != '\n')
- return -EINVAL;
+ case SD_ID128_STRING_MAX: /* plain UUID with trailing newline */
+ if (buffer[SD_ID128_STRING_MAX-1] != '\n')
+ return -EUCLEAN;
_fallthrough_;
- case 32: /* plain UUID without trailing newline */
- if (f == ID128_UUID)
- return -EINVAL;
+ case SD_ID128_STRING_MAX-1: /* plain UUID without trailing newline */
+ if (!FLAGS_SET(f, ID128_FORMAT_PLAIN))
+ return -EUCLEAN;
- buffer[32] = 0;
+ buffer[SD_ID128_STRING_MAX-1] = 0;
break;
- case 37: /* RFC UUID with trailing newline */
- if (buffer[36] != '\n')
- return -EINVAL;
+ case SD_ID128_UUID_STRING_MAX: /* RFC UUID with trailing newline */
+ if (buffer[SD_ID128_UUID_STRING_MAX-1] != '\n')
+ return -EUCLEAN;
_fallthrough_;
- case 36: /* RFC UUID without trailing newline */
- if (IN_SET(f, ID128_PLAIN, ID128_PLAIN_OR_UNINIT))
- return -EINVAL;
+ case SD_ID128_UUID_STRING_MAX-1: /* RFC UUID without trailing newline */
+ if (!FLAGS_SET(f, ID128_FORMAT_UUID))
+ return -EUCLEAN;
- buffer[36] = 0;
+ buffer[SD_ID128_UUID_STRING_MAX-1] = 0;
break;
default:
- return -EINVAL;
+ return -EUCLEAN;
}
- return sd_id128_from_string(buffer, ret);
+ r = sd_id128_from_string(buffer, ret);
+ return r == -EINVAL ? -EUCLEAN : r;
}
-int id128_read(const char *p, Id128Format f, sd_id128_t *ret) {
+int id128_read(const char *p, Id128FormatFlag f, sd_id128_t *ret) {
_cleanup_close_ int fd = -1;
fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY);
return id128_read_fd(fd, f, ret);
}
-int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) {
- char buffer[36 + 2];
+int id128_write_fd(int fd, Id128FormatFlag f, sd_id128_t id) {
+ char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */
size_t sz;
int r;
assert(fd >= 0);
- assert(f < _ID128_FORMAT_MAX);
+ assert(IN_SET((f & ID128_FORMAT_ANY), ID128_FORMAT_PLAIN, ID128_FORMAT_UUID));
- if (f != ID128_UUID) {
+ if (FLAGS_SET(f, ID128_FORMAT_PLAIN)) {
assert_se(sd_id128_to_string(id, buffer));
- buffer[SD_ID128_STRING_MAX - 1] = '\n';
sz = SD_ID128_STRING_MAX;
} else {
assert_se(sd_id128_to_uuid_string(id, buffer));
- buffer[SD_ID128_UUID_STRING_MAX - 1] = '\n';
sz = SD_ID128_UUID_STRING_MAX;
}
+ buffer[sz - 1] = '\n';
r = loop_write(fd, buffer, sz, false);
if (r < 0)
return r;
- if (do_sync) {
+ if (FLAGS_SET(f, ID128_SYNC_ON_WRITE)) {
r = fsync_full(fd);
if (r < 0)
return r;
return 0;
}
-int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) {
+int id128_write(const char *p, Id128FormatFlag f, sd_id128_t id) {
_cleanup_close_ int fd = -1;
fd = open(p, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, 0444);
if (fd < 0)
return -errno;
- return id128_write_fd(fd, f, id, do_sync);
+ return id128_write_fd(fd, f, id);
}
void id128_hash_func(const sd_id128_t *p, struct siphash *state) {
/* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is
* particularly relevant in VM environments, where VM managers typically place a VM uuid there. */
- r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid);
+ r = id128_read("/sys/class/dmi/id/product_uuid", ID128_FORMAT_UUID, &uuid);
if (r == -ENOENT)
- r = id128_read("/proc/device-tree/vm,uuid", ID128_UUID, &uuid);
+ r = id128_read("/proc/device-tree/vm,uuid", ID128_FORMAT_UUID, &uuid);
if (r < 0)
return r;
bool id128_is_valid(const char *s) _pure_;
-typedef enum Id128Format {
- ID128_ANY,
- ID128_PLAIN, /* formatted as 32 hex chars as-is */
- ID128_PLAIN_OR_UNINIT, /* formatted as 32 hex chars as-is; allow special "uninitialized"
- * value when reading from file (id128_read() and id128_read_fd()).
- *
- * This format should be used when reading a machine-id file. */
- ID128_UUID, /* formatted as 36 character uuid string */
- _ID128_FORMAT_MAX,
-} Id128Format;
-
-int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret);
-int id128_read(const char *p, Id128Format f, sd_id128_t *ret);
-
-int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync);
-int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync);
+typedef enum Id128FormatFlag {
+ ID128_FORMAT_PLAIN = 1 << 0, /* formatted as 32 hex chars as-is */
+ ID128_FORMAT_UUID = 1 << 1, /* formatted as 36 character uuid string */
+ ID128_FORMAT_ANY = ID128_FORMAT_PLAIN | ID128_FORMAT_UUID,
+
+ ID128_SYNC_ON_WRITE = 1 << 2, /* Sync the file after write. Used only when writing an ID. */
+} Id128FormatFlag;
+
+int id128_read_fd(int fd, Id128FormatFlag f, sd_id128_t *ret);
+int id128_read(const char *p, Id128FormatFlag f, sd_id128_t *ret);
+
+int id128_write_fd(int fd, Id128FormatFlag f, sd_id128_t id);
+int id128_write(const char *p, Id128FormatFlag f, sd_id128_t id);
void id128_hash_func(const sd_id128_t *p, struct siphash *state);
int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) _pure_;
#include "macro.h"
#include "missing_syscall.h"
#include "random-util.h"
+#include "stat-util.h"
#include "user-util.h"
_public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]) {
+ size_t k = 0;
+
assert_return(s, NULL);
- for (size_t n = 0; n < 16; n++) {
- s[n*2] = hexchar(id.bytes[n] >> 4);
- s[n*2+1] = hexchar(id.bytes[n] & 0xF);
+ for (size_t n = 0; n < sizeof(sd_id128_t); n++) {
+ s[k++] = hexchar(id.bytes[n] >> 4);
+ s[k++] = hexchar(id.bytes[n] & 0xF);
}
- s[SD_ID128_STRING_MAX-1] = 0;
+ assert(k == SD_ID128_STRING_MAX - 1);
+ s[k] = 0;
return s;
}
/* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */
- for (size_t n = 0; n < 16; n++) {
+ for (size_t n = 0; n < sizeof(sd_id128_t); n++) {
if (IN_SET(n, 4, 6, 8, 10))
s[k++] = '-';
return s;
}
-_public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
- unsigned n, i;
+_public_ int sd_id128_from_string(const char *s, sd_id128_t *ret) {
+ size_t n, i;
sd_id128_t t;
bool is_guid = false;
assert_return(s, -EINVAL);
- for (n = 0, i = 0; n < 16;) {
+ for (n = 0, i = 0; n < sizeof(sd_id128_t);) {
int a, b;
if (s[i] == '-') {
t.bytes[n++] = (a << 4) | b;
}
- if (i != (is_guid ? 36 : 32))
+ if (i != (is_guid ? SD_ID128_UUID_STRING_MAX : SD_ID128_STRING_MAX) - 1)
return -EINVAL;
if (s[i] != 0)
static thread_local sd_id128_t saved_machine_id = {};
int r;
- assert_return(ret, -EINVAL);
-
if (sd_id128_is_null(saved_machine_id)) {
- r = id128_read("/etc/machine-id", ID128_PLAIN, &saved_machine_id);
+ r = id128_read("/etc/machine-id", ID128_FORMAT_PLAIN, &saved_machine_id);
if (r < 0)
return r;
return -ENOMEDIUM;
}
- *ret = saved_machine_id;
+ if (ret)
+ *ret = saved_machine_id;
return 0;
}
static thread_local sd_id128_t saved_boot_id = {};
int r;
- assert_return(ret, -EINVAL);
-
if (sd_id128_is_null(saved_boot_id)) {
- r = id128_read("/proc/sys/kernel/random/boot_id", ID128_UUID, &saved_boot_id);
+ r = id128_read("/proc/sys/kernel/random/boot_id", ID128_FORMAT_UUID, &saved_boot_id);
+ if (r == -ENOENT && proc_mounted() == 0)
+ return -ENOSYS;
if (r < 0)
return r;
+
+ if (sd_id128_is_null(saved_boot_id))
+ return -ENOMEDIUM;
}
- *ret = saved_boot_id;
+ if (ret)
+ *ret = saved_boot_id;
return 0;
}
/* Chop off the final description string */
d = strrchr(description, ';');
if (!d)
- return -EIO;
+ return -EUCLEAN;
*d = 0;
/* Look for the permissions */
p = strrchr(description, ';');
if (!p)
- return -EIO;
+ return -EUCLEAN;
errno = 0;
perms = strtoul(p + 1, &e, 16);
if (errno > 0)
return -errno;
if (e == p + 1) /* Read at least one character */
- return -EIO;
+ return -EUCLEAN;
if (e != d) /* Must reached the end */
- return -EIO;
+ return -EUCLEAN;
if ((perms & ~MAX_PERMS) != 0)
return -EPERM;
/* Look for the group ID */
g = strrchr(description, ';');
if (!g)
- return -EIO;
+ return -EUCLEAN;
r = parse_gid(g + 1, &gid);
if (r < 0)
return r;
/* Look for the user ID */
u = strrchr(description, ';');
if (!u)
- return -EIO;
+ return -EUCLEAN;
r = parse_uid(u + 1, &uid);
if (r < 0)
return r;
if (c < 0)
return -errno;
if (c != sizeof(sd_id128_t))
- return -EIO;
+ return -EUCLEAN;
return 0;
}
static int get_invocation_from_environment(sd_id128_t *ret) {
const char *e;
+ int r;
assert(ret);
if (!e)
return -ENXIO;
- return sd_id128_from_string(e, ret);
+ r = sd_id128_from_string(e, ret);
+ return r == -EINVAL ? -EUCLEAN : r;
}
_public_ int sd_id128_get_invocation(sd_id128_t *ret) {
static thread_local sd_id128_t saved_invocation_id = {};
int r;
- assert_return(ret, -EINVAL);
-
if (sd_id128_is_null(saved_invocation_id)) {
/* We first check the environment. The environment variable is primarily relevant for user
* services, and sufficiently safe as long as no privilege boundary is involved. */
r = get_invocation_from_environment(&saved_invocation_id);
- if (r >= 0) {
- *ret = saved_invocation_id;
- return 0;
- } else if (r != -ENXIO)
- return r;
-
- /* The kernel keyring is relevant for system services (as for user services we don't store
- * the invocation ID in the keyring, as there'd be no trust benefit in that). */
- r = get_invocation_from_keyring(&saved_invocation_id);
+ if (r == -ENXIO)
+ /* The kernel keyring is relevant for system services (as for user services we don't
+ * store the invocation ID in the keyring, as there'd be no trust benefit in that). */
+ r = get_invocation_from_keyring(&saved_invocation_id);
if (r < 0)
return r;
+
+ if (sd_id128_is_null(saved_invocation_id))
+ return -ENOMEDIUM;
}
- *ret = saved_invocation_id;
+ if (ret)
+ *ret = saved_invocation_id;
return 0;
}
#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"
assert(f->header);
r = sd_id128_get_machine(&f->header->machine_id);
- if (IN_SET(r, -ENOENT, -ENOMEDIUM))
+ if (IN_SET(r, -ENOENT, -ENOMEDIUM, -ENOPKG))
/* We don't have a machine-id, let's continue without */
zero(f->header->machine_id);
else if (r < 0)
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))
int x11_write_data(Context *c) {
_cleanup_fclose_ FILE *f = NULL;
- _cleanup_free_ char *temp_path = NULL;
+ _cleanup_(unlink_and_freep) char *temp_path = NULL;
struct stat st;
int r;
r = fflush_sync_and_check(f);
if (r < 0)
- goto fail;
+ return r;
- if (rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
- r = -errno;
- goto fail;
- }
+ if (rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
+ return -errno;
if (stat("/etc/X11/xorg.conf.d/00-keyboard.conf", &st) >= 0)
c->x11_mtime = timespec_load(&st.st_mtim);
return 0;
-
-fail:
- if (temp_path)
- (void) unlink(temp_path);
-
- return r;
}
static int read_next_mapping(const char* filename,
{% if ENABLE_HOMED %}
-account sufficient pam_systemd_home.so
{% endif %}
-account sufficient pam_unix.so no_pass_expiry
-account required pam_permit.so
+account sufficient pam_unix.so no_pass_expiry
+account required pam_permit.so
{% if HAVE_SELINUX %}
-session required pam_selinux.so close
-session required pam_selinux.so nottys open
+session required pam_selinux.so close
+session required pam_selinux.so nottys open
{% endif %}
-session required pam_loginuid.so
-session optional pam_keyinit.so force revoke
+session required pam_loginuid.so
+session optional pam_keyinit.so force revoke
+session required pam_namespace.so
{% if ENABLE_HOMED %}
--session optional pam_systemd_home.so
+-session optional pam_systemd_home.so
{% endif %}
-session optional pam_systemd.so
+session optional pam_systemd.so
return r;
etc_machine_id = prefix_roota(arg_root, "/etc/machine-id");
- r = id128_read(etc_machine_id, ID128_PLAIN, &id);
+ r = id128_read(etc_machine_id, ID128_FORMAT_PLAIN, &id);
if (r < 0)
return log_error_errno(r, "Failed to read machine ID back: %m");
} else {
#include "unit-name.h"
#include "user-util.h"
-Machine* machine_new(Manager *manager, MachineClass class, const char *name) {
- Machine *m;
+DEFINE_TRIVIAL_CLEANUP_FUNC(Machine*, machine_free);
+
+int machine_new(Manager *manager, MachineClass class, const char *name, Machine **ret) {
+ _cleanup_(machine_freep) Machine *m = NULL;
+ int r;
assert(manager);
assert(class < _MACHINE_CLASS_MAX);
assert(name);
+ assert(ret);
/* Passing class == _MACHINE_CLASS_INVALID here is fine. It
* means as much as "we don't know yet", and that we'll figure
m = new0(Machine, 1);
if (!m)
- return NULL;
+ return -ENOMEM;
m->name = strdup(name);
if (!m->name)
- goto fail;
+ return -ENOMEM;
if (class != MACHINE_HOST) {
m->state_file = path_join("/run/systemd/machines", m->name);
if (!m->state_file)
- goto fail;
+ return -ENOMEM;
}
m->class = class;
- if (hashmap_put(manager->machines, m->name, m) < 0)
- goto fail;
+ r = hashmap_put(manager->machines, m->name, m);
+ if (r < 0)
+ return r;
m->manager = manager;
- return m;
-
-fail:
- free(m->state_file);
- free(m->name);
- return mfree(m);
+ *ret = TAKE_PTR(m);
+ return 0;
}
Machine* machine_free(Machine *m) {
LIST_FIELDS(Machine, gc_queue);
};
-Machine* machine_new(Manager *manager, MachineClass class, const char *name);
+int machine_new(Manager *manager, MachineClass class, const char *name, Machine **ret);
Machine* machine_free(Machine *m);
bool machine_may_gc(Machine *m, bool drop_not_started);
void machine_add_to_gc_queue(Machine *m);
return 1; /* Will call us back */
/* Set up the machine directory if necessary */
- r = setup_machine_directory(error);
+ r = setup_machine_directory(error, /* use_btrfs_subvol= */ true, /* use_btrfs_quota= */ true);
if (r < 0)
return r;
int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
Machine *machine;
+ int r;
assert(m);
assert(name);
machine = hashmap_get(m->machines, name);
if (!machine) {
- machine = machine_new(m, _MACHINE_CLASS_INVALID, name);
- if (!machine)
- return -ENOMEM;
+ r = machine_new(m, _MACHINE_CLASS_INVALID, name, &machine);
+ if (r < 0)
+ return r;
}
if (_machine)
if (!unit)
return log_oom();
- t = machine_new(m, MACHINE_HOST, ".host");
- if (!t)
- return log_oom();
+ r = machine_new(m, MACHINE_HOST, ".host", &t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create machine: %m");
t->leader = 1;
t->id = mid;
address_kernel_hash_func,
address_kernel_compare_func);
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+ address_kernel_hash_ops_free,
+ Address,
+ address_kernel_hash_func,
+ address_kernel_compare_func,
+ address_free);
+
+/* The functions below are mainly used by managing Request. */
static void address_hash_func(const Address *a, struct siphash *state) {
assert(a);
return 0;
}
-DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
- address_hash_ops_free,
- Address,
- address_hash_func,
- address_compare_func,
- address_free);
+int address_equal(const Address *a1, const Address *a2) {
+ if (a1 == a2)
+ return true;
+
+ if (!a1 || !a2)
+ return false;
+
+ return address_compare_func(a1, a2) == 0;
+}
+
+static int address_equalify(Address *address, const Address *src) {
+ int r;
+
+ assert(address);
+ assert(src);
+
+ if (address_kernel_compare_func(address, src) != 0)
+ return -EINVAL;
+
+ if (address->family == AF_INET) {
+ address->broadcast = src->broadcast;
+ r = free_and_strdup(&address->label, src->label);
+ if (r < 0)
+ return r;
+ } else {
+ address->prefixlen = src->prefixlen;
+ address->in_addr_peer = src->in_addr_peer;
+ }
+
+ return 0;
+}
int address_dup(const Address *src, Address **ret) {
_cleanup_(address_freep) Address *dest = NULL;
assert(link);
assert(address);
- r = set_ensure_put(&link->addresses, &address_hash_ops_free, address);
+ r = set_ensure_put(&link->addresses, &address_kernel_hash_ops_free, address);
if (r < 0)
return r;
if (r == 0)
* and does not have peer address. When the prefixlen is zero, then an Address object with an
* arbitrary prefixlen will be returned. */
- if (prefixlen != 0) {
+ if (family == AF_INET6 || prefixlen != 0) {
_cleanup_(address_freep) Address *tmp = NULL;
- /* If prefixlen is set, then we can use address_get(). */
+ /* In this case, we can use address_get(). */
r = address_new(&tmp);
if (r < 0)
tmp->family = family;
tmp->in_addr = *address;
tmp->prefixlen = prefixlen;
- address_set_broadcast(tmp, link);
- if (address_get(link, tmp, &a) >= 0) {
- if (ret)
- *ret = a;
+ r = address_get(link, tmp, &a);
+ if (r < 0)
+ return r;
- return 0;
+ if (family == AF_INET6) {
+ /* IPv6 addresses are managed without peer address and prefix length. Hence, we need
+ * to check them explicitly. */
+ if (in_addr_is_set(family, &a->in_addr_peer))
+ return -ENOENT;
+ if (prefixlen != 0 && a->prefixlen != prefixlen)
+ return -ENOENT;
}
- if (family == AF_INET6)
- return -ENOENT;
+ if (ret)
+ *ret = a;
- /* IPv4 addresses may have label and/or non-default broadcast address.
- * Hence, we need to always fallback below. */
+ return 0;
}
SET_FOREACH(a, link->addresses) {
ORDERED_HASHMAP_FOREACH(address, link->network->addresses_by_section) {
Address *existing;
- if (address_get(link, address, &existing) >= 0)
+ /* On update, the kernel ignores the address label and broadcast address. Hence we need to
+ * distinguish addresses with different labels or broadcast addresses. Thus, we need to check
+ * the existing address with address_equal(). Otherwise, the label or broadcast address
+ * change will not be applied when we reconfigure the interface. */
+ if (address_get(link, address, &existing) >= 0 && address_equal(address, existing))
address_unmark(existing);
}
existing = TAKE_PTR(tmp);
} else {
+ r = address_equalify(existing, address);
+ if (r < 0)
+ return r;
existing->source = address->source;
existing->provider = address->provider;
existing->duplicate_address_detection = address->duplicate_address_detection;
case RTM_NEWADDR:
if (address) {
/* update flags and etc. */
+ r = address_equalify(address, tmp);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to update properties of address %s, ignoring: %m",
+ IN_ADDR_PREFIX_TO_STRING(address->family, &address->in_addr, address->prefixlen));
+ return 0;
+ }
address->flags = tmp->flags;
address->scope = tmp->scope;
address_set_lifetime(m, address, &cinfo);
address_free(dup);
}
- /* Use address_kernel_hash_ops here. The function address_kernel_compare_func() matches
- * how kernel compares addresses, and is more lenient than address_compare_func().
- * Hence, the logic of dedup here is stricter than when address_hash_ops is used. */
+ /* Use address_kernel_hash_ops, instead of address_kernel_hash_ops_free. Otherwise, the
+ * Address objects will be freed. */
r = set_ensure_put(&addresses, &address_kernel_hash_ops, address);
if (r < 0)
return log_oom();
int network_drop_invalid_addresses(Network *network);
int address_compare_func(const Address *a1, const Address *a2);
+int address_equal(const Address *a1, const Address *a2);
DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Address, address);
const char *pretty = IN6_ADDR_TO_STRING(&address->in_addr.in6);
- if (address_get(link, address, &existing) < 0 &&
- link_get_address(link, AF_INET6, &address->in_addr, 0, &existing) < 0) {
+ if (address_get(link, address, &existing) < 0) {
/* New address. */
log_level = LOG_INFO;
goto simple_log;
r = link_down_now(link);
if (r < 0)
return r;
+
+ /* If the kind of the link is "bond", we need
+ * set the slave link down as well. */
+ if (streq_ptr(link->kind, "bond")) {
+ r = link_down_slave_links(link);
+ if (r < 0)
+ return r;
+ }
}
break;
return 0;
}
+int link_down_slave_links(Link *link) {
+ Link *slave;
+ int r;
+
+ assert(link);
+
+ SET_FOREACH(slave, link->slaves) {
+ r = link_down_now(slave);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
static int link_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
int link_request_to_bring_up_or_down(Link *link, bool up);
int link_down_now(Link *link);
+int link_down_slave_links(Link *link);
int link_remove(Link *link);
static inline void name##_enter_configuring(type *t) { \
name##_update_state(t, \
NETWORK_CONFIG_STATE_REQUESTING | \
- NETWORK_CONFIG_STATE_CONFIGURING, \
+ NETWORK_CONFIG_STATE_CONFIGURING | \
+ NETWORK_CONFIG_STATE_REMOVING, \
NETWORK_CONFIG_STATE_CONFIGURING); \
} \
static inline void name##_enter_configured(type *t) { \
return 0;
}
-static bool address_equal(const Address *a1, const Address *a2) {
- if (a1 == a2)
- return true;
-
- if (!a1 || !a2)
- return false;
-
- return address_compare_func(a1, a2) == 0;
-}
-
static void test_address_equality(void) {
_cleanup_(address_freep) Address *a1 = NULL, *a2 = NULL;
* uid/gid as seen from e.g. /proc/1/mountinfo. So we simply
* pass uid 0 and not uid_shift to tmpfs_patch_options().
*/
- r = tmpfs_patch_options("mode=755" TMPFS_LIMITS_SYS_FS_CGROUP, 0, selinux_apifs_context, &options);
+ r = tmpfs_patch_options("mode=0755" TMPFS_LIMITS_SYS_FS_CGROUP, 0, selinux_apifs_context, &options);
if (r < 0)
return log_oom();
if (!userns)
return mount_nofollow_verbose(LOG_ERR, NULL, cgroup_root, NULL,
- MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
+ MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY,
+ "mode=0755");
return 0;
}
if (r == 0) {
_cleanup_free_ char *options = NULL;
- r = tmpfs_patch_options("mode=755" TMPFS_LIMITS_SYS_FS_CGROUP, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &options);
+ r = tmpfs_patch_options("mode=0755" TMPFS_LIMITS_SYS_FS_CGROUP,
+ uid_shift == 0 ? UID_INVALID : uid_shift,
+ selinux_apifs_context,
+ &options);
if (r < 0)
return log_oom();
return r;
return mount_nofollow_verbose(LOG_ERR, NULL, cgroup_root, NULL,
- MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
+ MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY,
+ "mode=0755");
}
static int mount_unified_cgroups(const char *dest) {
#include "mkdir-label.h"
#include "mount-util.h"
#include "mountpoint-util.h"
+#include "namespace-util.h"
#include "nspawn-mount.h"
#include "parse-util.h"
#include "path-util.h"
MS_BIND|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT|extra_flags, NULL);
}
+#define PROC_DEFAULT_MOUNT_FLAGS (MS_NOSUID|MS_NOEXEC|MS_NODEV)
+#define SYS_DEFAULT_MOUNT_FLAGS (MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV)
+
int mount_all(const char *dest,
MountSettingsMask mount_settings,
uid_t uid_shift,
static const MountPoint mount_table[] = {
/* First we list inner child mounts (i.e. mounts applied *after* entering user namespacing) */
- { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ { "proc", "/proc", "proc", NULL, PROC_DEFAULT_MOUNT_FLAGS,
MOUNT_FATAL|MOUNT_IN_USERNS|MOUNT_MKDIR|MOUNT_FOLLOW_SYMLINKS }, /* we follow symlinks here since not following them requires /proc/ already being mounted, which we don't have here. */
{ "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND,
MOUNT_IN_USERNS|MOUNT_MKDIR },
/* Then we list outer child mounts (i.e. mounts applied *before* entering user namespacing) */
- { "tmpfs", "/tmp", "tmpfs", "mode=1777" NESTED_TMPFS_LIMITS, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+ { "tmpfs", "/tmp", "tmpfs", "mode=01777" NESTED_TMPFS_LIMITS, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
MOUNT_FATAL|MOUNT_APPLY_TMPFS_TMP|MOUNT_MKDIR },
- { "tmpfs", "/sys", "tmpfs", "mode=555" TMPFS_LIMITS_SYS, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ { "tmpfs", "/sys", "tmpfs", "mode=0555" TMPFS_LIMITS_SYS, MS_NOSUID|MS_NOEXEC|MS_NODEV,
MOUNT_FATAL|MOUNT_APPLY_APIVFS_NETNS|MOUNT_MKDIR },
- { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ { "sysfs", "/sys", "sysfs", NULL, SYS_DEFAULT_MOUNT_FLAGS,
MOUNT_FATAL|MOUNT_APPLY_APIVFS_RO|MOUNT_MKDIR }, /* skipped if above was mounted */
{ "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
MOUNT_FATAL|MOUNT_MKDIR }, /* skipped if above was mounted */
- { "tmpfs", "/dev", "tmpfs", "mode=755" TMPFS_LIMITS_PRIVATE_DEV, MS_NOSUID|MS_STRICTATIME,
+ { "tmpfs", "/dev", "tmpfs", "mode=0755" TMPFS_LIMITS_PRIVATE_DEV, MS_NOSUID|MS_STRICTATIME,
MOUNT_FATAL|MOUNT_MKDIR },
- { "tmpfs", "/dev/shm", "tmpfs", "mode=1777" NESTED_TMPFS_LIMITS, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+ { "tmpfs", "/dev/shm", "tmpfs", "mode=01777" NESTED_TMPFS_LIMITS, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
MOUNT_FATAL|MOUNT_MKDIR },
- { "tmpfs", "/run", "tmpfs", "mode=755" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+ { "tmpfs", "/run", "tmpfs", "mode=0755" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
MOUNT_FATAL|MOUNT_MKDIR },
{ "/run/host", "/run/host", NULL, NULL, MS_BIND,
MOUNT_FATAL|MOUNT_MKDIR|MOUNT_PREFIX_ROOT }, /* Prepare this so that we can make it read-only when we are done */
if (r < 0 && errno != EEXIST)
return log_error_errno(errno, "Failed to create %s: %m", directory);
- options = "mode=755" TMPFS_LIMITS_VOLATILE_STATE;
+ options = "mode=0755" TMPFS_LIMITS_VOLATILE_STATE;
r = tmpfs_patch_options(options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf);
if (r < 0)
return log_oom();
if (!mkdtemp(template))
return log_error_errno(errno, "Failed to create temporary directory: %m");
- options = "mode=755" TMPFS_LIMITS_ROOTFS;
+ options = "mode=0755" TMPFS_LIMITS_ROOTFS;
r = tmpfs_patch_options(options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf);
if (r < 0)
goto fail;
if (!mkdtemp(template))
return log_error_errno(errno, "Failed to create temporary directory: %m");
- options = "mode=755" TMPFS_LIMITS_ROOTFS;
+ options = "mode=0755" TMPFS_LIMITS_ROOTFS;
r = tmpfs_patch_options(options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf);
if (r < 0)
goto finish;
return r;
}
+
+#define NSPAWN_PRIVATE_FULLY_VISIBLE_PROCFS "/run/host/proc"
+#define NSPAWN_PRIVATE_FULLY_VISIBLE_SYSFS "/run/host/sys"
+
+int pin_fully_visible_fs(void) {
+ int r;
+
+ (void) mkdir_p(NSPAWN_PRIVATE_FULLY_VISIBLE_PROCFS, 0755);
+ (void) mkdir_p(NSPAWN_PRIVATE_FULLY_VISIBLE_SYSFS, 0755);
+
+ r = mount_follow_verbose(LOG_ERR, "proc", NSPAWN_PRIVATE_FULLY_VISIBLE_PROCFS, "proc", PROC_DEFAULT_MOUNT_FLAGS, NULL);
+ if (r < 0)
+ return r;
+
+ r = mount_follow_verbose(LOG_ERR, "sysfs", NSPAWN_PRIVATE_FULLY_VISIBLE_SYSFS, "sysfs", SYS_DEFAULT_MOUNT_FLAGS, NULL);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int do_wipe_fully_visible_fs(void) {
+ if (umount2(NSPAWN_PRIVATE_FULLY_VISIBLE_PROCFS, MNT_DETACH) < 0)
+ return log_error_errno(errno, "Failed to unmount temporary proc: %m");
+
+ if (rmdir(NSPAWN_PRIVATE_FULLY_VISIBLE_PROCFS) < 0)
+ return log_error_errno(errno, "Failed to remove temporary proc mountpoint: %m");
+
+ if (umount2(NSPAWN_PRIVATE_FULLY_VISIBLE_SYSFS, MNT_DETACH) < 0)
+ return log_error_errno(errno, "Failed to unmount temporary sys: %m");
+
+ if (rmdir(NSPAWN_PRIVATE_FULLY_VISIBLE_SYSFS) < 0)
+ return log_error_errno(errno, "Failed to remove temporary sys mountpoint: %m");
+
+ return 0;
+}
+
+int wipe_fully_visible_fs(int mntns_fd) {
+ _cleanup_close_ int orig_mntns_fd = -EBADF;
+ int r, rr;
+
+ r = namespace_open(0, NULL, &orig_mntns_fd, NULL, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to pin originating mount namespace: %m");
+
+ r = namespace_enter(-EBADF, mntns_fd, -EBADF, -EBADF, -EBADF);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enter mount namespace: %m");
+
+ rr = do_wipe_fully_visible_fs();
+
+ r = namespace_enter(-EBADF, orig_mntns_fd, -EBADF, -EBADF, -EBADF);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enter original mount namespace: %m");
+
+ return rr;
+}
int setup_pivot_root(const char *directory, const char *pivot_root_new, const char *pivot_root_old);
int tmpfs_patch_options(const char *options,uid_t uid_shift, const char *selinux_apifs_context, char **ret);
+int pin_fully_visible_fs(void);
+int wipe_fully_visible_fs(int mntns_fd);
const char* name;
} allow_list[] = {
/* Let's use set names where we can */
- { 0, "@aio" },
- { 0, "@basic-io" },
- { 0, "@chown" },
- { 0, "@default" },
- { 0, "@file-system" },
- { 0, "@io-event" },
- { 0, "@ipc" },
- { 0, "@mount" },
- { 0, "@network-io" },
- { 0, "@process" },
- { 0, "@resources" },
- { 0, "@setuid" },
- { 0, "@signal" },
- { 0, "@sync" },
- { 0, "@timer" },
-
- /* The following four are sets we optionally enable, in case the caps have been configured for it */
- { CAP_SYS_TIME, "@clock" },
- { CAP_SYS_MODULE, "@module" },
- { CAP_SYS_RAWIO, "@raw-io" },
- { CAP_IPC_LOCK, "@memlock" },
+ { 0, "@aio" },
+ { 0, "@basic-io" },
+ { 0, "@chown" },
+ { 0, "@default" },
+ { 0, "@file-system" },
+ { 0, "@io-event" },
+ { 0, "@ipc" },
+ { 0, "@mount" },
+ { 0, "@network-io" },
+ { 0, "@process" },
+ { 0, "@resources" },
+ { 0, "@setuid" },
+ { 0, "@signal" },
+ { 0, "@sync" },
+ { 0, "@timer" },
+
+ /* The following four are sets we optionally enable, n case the caps have been configured for it */
+ { CAP_SYS_TIME, "@clock" },
+ { CAP_SYS_MODULE, "@module" },
+ { CAP_SYS_RAWIO, "@raw-io" },
+ { CAP_IPC_LOCK, "@memlock" },
/* Plus a good set of additional syscalls which are not part of any of the groups above */
- { 0, "brk" },
- { 0, "capget" },
- { 0, "capset" },
- { 0, "copy_file_range" },
- { 0, "fadvise64" },
- { 0, "fadvise64_64" },
- { 0, "flock" },
- { 0, "get_mempolicy" },
- { 0, "getcpu" },
- { 0, "getpriority" },
- { 0, "getrandom" },
- { 0, "ioctl" },
- { 0, "ioprio_get" },
- { 0, "kcmp" },
- { 0, "madvise" },
- { 0, "mincore" },
- { 0, "mprotect" },
- { 0, "mremap" },
- { 0, "name_to_handle_at" },
- { 0, "oldolduname" },
- { 0, "olduname" },
- { 0, "personality" },
- { 0, "readahead" },
- { 0, "readdir" },
- { 0, "remap_file_pages" },
- { 0, "sched_get_priority_max" },
- { 0, "sched_get_priority_min" },
- { 0, "sched_getaffinity" },
- { 0, "sched_getattr" },
- { 0, "sched_getparam" },
- { 0, "sched_getscheduler" },
- { 0, "sched_rr_get_interval" },
+ { 0, "brk" },
+ { 0, "capget" },
+ { 0, "capset" },
+ { 0, "copy_file_range" },
+ { 0, "fadvise64" },
+ { 0, "fadvise64_64" },
+ { 0, "flock" },
+ { 0, "get_mempolicy" },
+ { 0, "getcpu" },
+ { 0, "getpriority" },
+ { 0, "getrandom" },
+ { 0, "ioctl" },
+ { 0, "ioprio_get" },
+ { 0, "kcmp" },
+ { 0, "madvise" },
+ { 0, "mincore" },
+ { 0, "mprotect" },
+ { 0, "mremap" },
+ { 0, "name_to_handle_at" },
+ { 0, "oldolduname" },
+ { 0, "olduname" },
+ { 0, "personality" },
+ { 0, "readahead" },
+ { 0, "readdir" },
+ { 0, "remap_file_pages" },
+ { 0, "sched_get_priority_max" },
+ { 0, "sched_get_priority_min" },
+ { 0, "sched_getaffinity" },
+ { 0, "sched_getattr" },
+ { 0, "sched_getparam" },
+ { 0, "sched_getscheduler" },
+ { 0, "sched_rr_get_interval" },
{ 0, "sched_rr_get_interval_time64" },
- { 0, "sched_yield" },
- { 0, "seccomp" },
- { 0, "sendfile" },
- { 0, "sendfile64" },
- { 0, "setdomainname" },
- { 0, "setfsgid" },
- { 0, "setfsgid32" },
- { 0, "setfsuid" },
- { 0, "setfsuid32" },
- { 0, "sethostname" },
- { 0, "setpgid" },
- { 0, "setsid" },
- { 0, "splice" },
- { 0, "sysinfo" },
- { 0, "tee" },
- { 0, "umask" },
- { 0, "uname" },
- { 0, "userfaultfd" },
- { 0, "vmsplice" },
+ { 0, "sched_yield" },
+ { 0, "seccomp" },
+ { 0, "sendfile" },
+ { 0, "sendfile64" },
+ { 0, "setdomainname" },
+ { 0, "setfsgid" },
+ { 0, "setfsgid32" },
+ { 0, "setfsuid" },
+ { 0, "setfsuid32" },
+ { 0, "sethostname" },
+ { 0, "setpgid" },
+ { 0, "setsid" },
+ { 0, "splice" },
+ { 0, "sysinfo" },
+ { 0, "tee" },
+ { 0, "umask" },
+ { 0, "uname" },
+ { 0, "userfaultfd" },
+ { 0, "vmsplice" },
/* The following individual syscalls are added depending on specified caps */
- { CAP_SYS_PACCT, "acct" },
- { CAP_SYS_PTRACE, "process_vm_readv" },
- { CAP_SYS_PTRACE, "process_vm_writev" },
- { CAP_SYS_PTRACE, "ptrace" },
- { CAP_SYS_BOOT, "reboot" },
- { CAP_SYSLOG, "syslog" },
- { CAP_SYS_TTY_CONFIG, "vhangup" },
+ { CAP_SYS_PACCT, "acct" },
+ { CAP_SYS_PTRACE, "process_vm_readv" },
+ { CAP_SYS_PTRACE, "process_vm_writev" },
+ { CAP_SYS_PTRACE, "ptrace" },
+ { CAP_SYS_BOOT, "reboot" },
+ { CAP_SYSLOG, "syslog" },
+ { CAP_SYS_TTY_CONFIG, "vhangup" },
/*
* The following syscalls and groups are knowingly excluded:
/* The notify socket inside the container it can use to talk to nspawn using the sd_notify(3) protocol */
#define NSPAWN_NOTIFY_SOCKET_PATH "/run/host/notify"
+#define NSPAWN_MOUNT_TUNNEL "/run/host/incoming"
#define EXIT_FORCE_RESTART 133
if (r < 0)
return log_error_errno(r, "Failed to generate random boot id: %m");
- r = id128_write(path, ID128_UUID, rnd, false);
+ r = id128_write(path, ID128_FORMAT_UUID, rnd);
if (r < 0)
return log_error_errno(r, "Failed to write boot id: %m");
return mount_nofollow_verbose(LOG_ERR, NULL, q, NULL, MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, "mode=0500");
}
-static int setup_kmsg(int kmsg_socket) {
+static int setup_kmsg(int fd_inner_socket) {
_cleanup_(unlink_and_freep) char *from = NULL;
_cleanup_free_ char *fifo = NULL;
_cleanup_close_ int fd = -1;
int r;
- assert(kmsg_socket >= 0);
+ assert(fd_inner_socket >= 0);
BLOCK_WITH_UMASK(0000);
return log_error_errno(errno, "Failed to open fifo: %m");
/* Store away the fd in the socket, so that it stays open as long as we run the child */
- r = send_one_fd(kmsg_socket, fd, 0);
+ r = send_one_fd(fd_inner_socket, fd, 0);
if (r < 0)
return log_error_errno(r, "Failed to send FIFO fd: %m");
return 0;
}
-static int setup_propagate(const char *root) {
+static int mount_tunnel_dig(const char *root) {
const char *p, *q;
int r;
if (r < 0)
return log_error_errno(r, "Failed to create /run/host: %m");
- r = userns_mkdir(root, "/run/host/incoming", 0600, 0, 0);
+ r = userns_mkdir(root, NSPAWN_MOUNT_TUNNEL, 0600, 0, 0);
if (r < 0)
- return log_error_errno(r, "Failed to create /run/host/incoming: %m");
+ return log_error_errno(r, "Failed to create "NSPAWN_MOUNT_TUNNEL": %m");
- q = prefix_roota(root, "/run/host/incoming");
+ q = prefix_roota(root, NSPAWN_MOUNT_TUNNEL);
r = mount_nofollow_verbose(LOG_ERR, p, q, NULL, MS_BIND, NULL);
if (r < 0)
return r;
if (r < 0)
return r;
- /* machined will MS_MOVE into that directory, and that's only supported for non-shared mounts. */
- return mount_nofollow_verbose(LOG_ERR, NULL, q, NULL, MS_SLAVE, NULL);
+ return 0;
+}
+
+static int mount_tunnel_open(void) {
+ int r;
+
+ r = mount_follow_verbose(LOG_ERR, NULL, NSPAWN_MOUNT_TUNNEL, NULL, MS_SLAVE, NULL);
+ if (r < 0)
+ return r;
+
+ return 0;
}
static int setup_machine_id(const char *directory) {
etc_machine_id = prefix_roota(directory, "/etc/machine-id");
- r = id128_read(etc_machine_id, ID128_PLAIN_OR_UNINIT, &id);
+ r = id128_read(etc_machine_id, ID128_FORMAT_PLAIN, &id);
if (r < 0) {
- if (!IN_SET(r, -ENOENT, -ENOMEDIUM)) /* If the file is missing or empty, we don't mind */
+ if (!IN_SET(r, -ENOENT, -ENOMEDIUM, -ENOPKG)) /* If the file is missing, empty, or uninitialized, we don't mind */
return log_error_errno(r, "Failed to read machine ID from container image: %m");
if (sd_id128_is_null(arg_uuid)) {
Barrier *barrier,
const char *directory,
bool secondary,
- int kmsg_socket,
- int rtnl_socket,
- int master_pty_socket,
+ int fd_inner_socket,
FDSet *fds,
char **os_release_pairs) {
assert(barrier);
assert(directory);
- assert(kmsg_socket >= 0);
+ assert(fd_inner_socket >= 0);
log_debug("Inner child is initializing.");
if (r < 0)
return r;
- r = setup_kmsg(kmsg_socket);
+ r = setup_kmsg(fd_inner_socket);
if (r < 0)
return r;
- kmsg_socket = safe_close(kmsg_socket);
r = mount_custom(
"/",
(void) loopback_setup();
if (arg_expose_ports) {
- r = expose_port_send_rtnl(rtnl_socket);
+ r = expose_port_send_rtnl(fd_inner_socket);
if (r < 0)
return r;
- rtnl_socket = safe_close(rtnl_socket);
}
if (arg_console_mode != CONSOLE_PIPE) {
if (r < 0)
return log_error_errno(r, "Failed to set up /dev/console: %m");
- r = send_one_fd(master_pty_socket, master, 0);
+ r = send_one_fd(fd_inner_socket, master, 0);
if (r < 0)
return log_error_errno(r, "Failed to send master fd: %m");
- master_pty_socket = safe_close(master_pty_socket);
r = setup_stdio_as_dev_console();
if (r < 0)
const char *directory,
DissectedImage *dissected_image,
bool secondary,
- int pid_socket,
- int uuid_socket,
- int notify_socket,
- int kmsg_socket,
- int rtnl_socket,
- int uid_shift_socket,
- int master_pty_socket,
- int unified_cgroup_hierarchy_socket,
+ int fd_outer_socket,
+ int fd_inner_socket,
FDSet *fds,
int netns_fd) {
_cleanup_(bind_user_context_freep) BindUserContext *bind_user_context = NULL;
_cleanup_strv_free_ char **os_release_pairs = NULL;
- _cleanup_close_ int fd = -1;
+ _cleanup_close_ int fd = -1, mntns_fd = -EBADF;
bool idmap = false;
const char *p;
pid_t pid;
assert(barrier);
assert(directory);
- assert(pid_socket >= 0);
- assert(uuid_socket >= 0);
- assert(notify_socket >= 0);
- assert(master_pty_socket >= 0);
- assert(kmsg_socket >= 0);
+ assert(fd_outer_socket >= 0);
+ assert(fd_inner_socket >= 0);
log_debug("Outer child is initializing.");
return r;
if (arg_userns_mode != USER_NAMESPACE_NO) {
+ r = namespace_open(0, NULL, &mntns_fd, NULL, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to pin outer mount namespace: %m");
+
+ l = send_one_fd(fd_outer_socket, mntns_fd, 0);
+ if (l < 0)
+ return log_error_errno(l, "Failed to send outer mount namespace fd: %m");
+ mntns_fd = safe_close(mntns_fd);
+
/* Let the parent know which UID shift we read from the image */
- l = send(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL);
+ l = send(fd_outer_socket, &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL);
if (l < 0)
return log_error_errno(errno, "Failed to send UID shift: %m");
if (l != sizeof(arg_uid_shift))
* UID shift we just read from the image is available. If yes, it will send the UID
* shift back to us, if not it will pick a different one, and send it back to us. */
- l = recv(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), 0);
+ l = recv(fd_outer_socket, &arg_uid_shift, sizeof(arg_uid_shift), 0);
if (l < 0)
return log_error_errno(errno, "Failed to recv UID shift: %m");
if (l != sizeof(arg_uid_shift))
(uid_t) bind_user_context->data[i].host_group->gid,
};
- l = send(uid_shift_socket, map, sizeof(map), MSG_NOSIGNAL);
+ l = send(fd_outer_socket, map, sizeof(map), MSG_NOSIGNAL);
if (l < 0)
return log_error_errno(errno, "Failed to send user UID map: %m");
if (l != sizeof(map))
if (r < 0)
return r;
- l = send(unified_cgroup_hierarchy_socket, &arg_unified_cgroup_hierarchy, sizeof(arg_unified_cgroup_hierarchy), MSG_NOSIGNAL);
+ l = send(fd_outer_socket, &arg_unified_cgroup_hierarchy, sizeof(arg_unified_cgroup_hierarchy), MSG_NOSIGNAL);
if (l < 0)
return log_error_errno(errno, "Failed to send cgroup mode: %m");
if (l != sizeof(arg_unified_cgroup_hierarchy))
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Short write while sending cgroup mode.");
-
- unified_cgroup_hierarchy_socket = safe_close(unified_cgroup_hierarchy_socket);
}
- /* Mark everything as shared so our mounts get propagated down. This is required to make new bind
- * mounts available in systemd services inside the container that create a new mount namespace. See
- * https://github.com/systemd/systemd/issues/3860 Further submounts (such as /dev) done after this
- * will inherit the shared propagation mode.
- *
- * IMPORTANT: Do not overmount the root directory anymore from now on to enable moving the root
- * directory mount to root later on.
- * https://github.com/systemd/systemd/issues/3847#issuecomment-562735251
- */
- r = mount_nofollow_verbose(LOG_ERR, NULL, directory, NULL, MS_SHARED|MS_REC, NULL);
- if (r < 0)
- return r;
-
r = recursive_chown(directory, arg_uid_shift, arg_uid_range);
if (r < 0)
return r;
if (r < 0)
return r;
- r = setup_propagate(directory);
+ r = mount_tunnel_dig(directory);
if (r < 0)
return r;
return r;
}
- r = mount_move_root(directory);
+ /* Mark everything as shared so our mounts get propagated down. This is required to make new bind
+ * mounts available in systemd services inside the container that create a new mount namespace. See
+ * https://github.com/systemd/systemd/issues/3860 Further submounts (such as /dev) done after this
+ * will inherit the shared propagation mode.
+ *
+ * IMPORTANT: Do not overmount the root directory anymore from now on to enable moving the root
+ * directory mount to root later on.
+ * https://github.com/systemd/systemd/issues/3847#issuecomment-562735251
+ */
+ r = mount_switch_root(directory, MOUNT_ATTR_PROPAGATION_SHARED);
if (r < 0)
return log_error_errno(r, "Failed to move root directory: %m");
+ /* We finished setting up the rootfs which is a shared mount. The mount tunnel needs to be a
+ * dependent mount otherwise we can't MS_MOVE mounts that were propagated from the host into
+ * the container. */
+ r = mount_tunnel_open();
+ if (r < 0)
+ return r;
+
+ if (arg_userns_mode != USER_NAMESPACE_NO) {
+ /* In order to mount procfs and sysfs in an unprivileged container the kernel
+ * requires that a fully visible instance is already present in the target mount
+ * namespace. Mount one here so the inner child can mount its own instances. Later
+ * we umount the temporary instances created here before we actually exec the
+ * payload. Since the rootfs is shared the umount will propagate into the container.
+ * Note, the inner child wouldn't be able to unmount the instances on its own since
+ * it doesn't own the originating mount namespace. IOW, the outer child needs to do
+ * this. */
+ r = pin_fully_visible_fs();
+ if (r < 0)
+ return r;
+ }
+
fd = setup_notify_child();
if (fd < 0)
return fd;
if (pid < 0)
return log_error_errno(errno, "Failed to fork inner child: %m");
if (pid == 0) {
- pid_socket = safe_close(pid_socket);
- uuid_socket = safe_close(uuid_socket);
- notify_socket = safe_close(notify_socket);
- uid_shift_socket = safe_close(uid_shift_socket);
+ fd_outer_socket = safe_close(fd_outer_socket);
/* The inner child has all namespaces that are requested, so that we all are owned by the
* user if user namespaces are turned on. */
return log_error_errno(r, "Failed to join network namespace: %m");
}
- r = inner_child(barrier, directory, secondary, kmsg_socket, rtnl_socket, master_pty_socket, fds, os_release_pairs);
+ r = inner_child(barrier, directory, secondary, fd_inner_socket, fds, os_release_pairs);
if (r < 0)
_exit(EXIT_FAILURE);
_exit(EXIT_SUCCESS);
}
- l = send(pid_socket, &pid, sizeof(pid), MSG_NOSIGNAL);
+ l = send(fd_outer_socket, &pid, sizeof(pid), MSG_NOSIGNAL);
if (l < 0)
return log_error_errno(errno, "Failed to send PID: %m");
if (l != sizeof(pid))
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Short write while sending PID.");
- l = send(uuid_socket, &arg_uuid, sizeof(arg_uuid), MSG_NOSIGNAL);
+ l = send(fd_outer_socket, &arg_uuid, sizeof(arg_uuid), MSG_NOSIGNAL);
if (l < 0)
return log_error_errno(errno, "Failed to send machine ID: %m");
if (l != sizeof(arg_uuid))
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Short write while sending machine ID.");
- l = send_one_fd(notify_socket, fd, 0);
+ l = send_one_fd(fd_outer_socket, fd, 0);
if (l < 0)
return log_error_errno(l, "Failed to send notify fd: %m");
- pid_socket = safe_close(pid_socket);
- uuid_socket = safe_close(uuid_socket);
- notify_socket = safe_close(notify_socket);
- master_pty_socket = safe_close(master_pty_socket);
- kmsg_socket = safe_close(kmsg_socket);
- rtnl_socket = safe_close(rtnl_socket);
+ fd_outer_socket = safe_close(fd_outer_socket);
+ fd_inner_socket = safe_close(fd_inner_socket);
netns_fd = safe_close(netns_fd);
return 0;
_cleanup_(release_lock_file) LockFile uid_shift_lock = LOCK_FILE_INIT;
_cleanup_close_ int etc_passwd_lock = -1;
_cleanup_close_pair_ int
- kmsg_socket_pair[2] = { -1, -1 },
- rtnl_socket_pair[2] = { -1, -1 },
- pid_socket_pair[2] = { -1, -1 },
- uuid_socket_pair[2] = { -1, -1 },
- notify_socket_pair[2] = { -1, -1 },
- uid_shift_socket_pair[2] = { -1, -1 },
- master_pty_socket_pair[2] = { -1, -1 },
- unified_cgroup_hierarchy_socket_pair[2] = { -1, -1};
-
- _cleanup_close_ int notify_socket = -1;
+ fd_inner_socket_pair[2] = { -EBADF, -EBADF },
+ fd_outer_socket_pair[2] = { -EBADF, -EBADF };
+
+ _cleanup_close_ int notify_socket = -1, mntns_fd = -EBADF, fd_kmsg_fifo = -EBADF;
_cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
_cleanup_(sd_event_source_unrefp) sd_event_source *notify_event_source = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
if (r < 0)
return log_error_errno(r, "Cannot initialize IPC barrier: %m");
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0)
- return log_error_errno(errno, "Failed to create kmsg socket pair: %m");
-
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, rtnl_socket_pair) < 0)
- return log_error_errno(errno, "Failed to create rtnl socket pair: %m");
-
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pid_socket_pair) < 0)
- return log_error_errno(errno, "Failed to create pid socket pair: %m");
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, fd_inner_socket_pair) < 0)
+ return log_error_errno(errno, "Failed to create inner socket pair: %m");
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uuid_socket_pair) < 0)
- return log_error_errno(errno, "Failed to create id socket pair: %m");
-
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, notify_socket_pair) < 0)
- return log_error_errno(errno, "Failed to create notify socket pair: %m");
-
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, master_pty_socket_pair) < 0)
- return log_error_errno(errno, "Failed to create console socket pair: %m");
-
- if (arg_userns_mode != USER_NAMESPACE_NO)
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0)
- return log_error_errno(errno, "Failed to create uid shift socket pair: %m");
-
- if (arg_unified_cgroup_hierarchy == CGROUP_UNIFIED_UNKNOWN)
- if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, unified_cgroup_hierarchy_socket_pair) < 0)
- return log_error_errno(errno, "Failed to create unified cgroup socket pair: %m");
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, fd_outer_socket_pair) < 0)
+ return log_error_errno(errno, "Failed to create outer socket pair: %m");
/* Child can be killed before execv(), so handle SIGCHLD in order to interrupt
* parent's blocking calls and give it a chance to call wait() and terminate. */
/* The outer child only has a file system namespace. */
barrier_set_role(&barrier, BARRIER_CHILD);
- kmsg_socket_pair[0] = safe_close(kmsg_socket_pair[0]);
- rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
- pid_socket_pair[0] = safe_close(pid_socket_pair[0]);
- uuid_socket_pair[0] = safe_close(uuid_socket_pair[0]);
- notify_socket_pair[0] = safe_close(notify_socket_pair[0]);
- master_pty_socket_pair[0] = safe_close(master_pty_socket_pair[0]);
- uid_shift_socket_pair[0] = safe_close(uid_shift_socket_pair[0]);
- unified_cgroup_hierarchy_socket_pair[0] = safe_close(unified_cgroup_hierarchy_socket_pair[0]);
+ fd_inner_socket_pair[0] = safe_close(fd_inner_socket_pair[0]);
+ fd_outer_socket_pair[0] = safe_close(fd_outer_socket_pair[0]);
(void) reset_all_signal_handlers();
(void) reset_signal_mask();
arg_directory,
dissected_image,
secondary,
- pid_socket_pair[1],
- uuid_socket_pair[1],
- notify_socket_pair[1],
- kmsg_socket_pair[1],
- rtnl_socket_pair[1],
- uid_shift_socket_pair[1],
- master_pty_socket_pair[1],
- unified_cgroup_hierarchy_socket_pair[1],
+ fd_outer_socket_pair[1],
+ fd_inner_socket_pair[1],
fds,
child_netns_fd);
if (r < 0)
fdset_close(fds);
- kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]);
- rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]);
- pid_socket_pair[1] = safe_close(pid_socket_pair[1]);
- uuid_socket_pair[1] = safe_close(uuid_socket_pair[1]);
- notify_socket_pair[1] = safe_close(notify_socket_pair[1]);
- master_pty_socket_pair[1] = safe_close(master_pty_socket_pair[1]);
- uid_shift_socket_pair[1] = safe_close(uid_shift_socket_pair[1]);
- unified_cgroup_hierarchy_socket_pair[1] = safe_close(unified_cgroup_hierarchy_socket_pair[1]);
+ fd_inner_socket_pair[1] = safe_close(fd_inner_socket_pair[1]);
+ fd_outer_socket_pair[1] = safe_close(fd_outer_socket_pair[1]);
if (arg_userns_mode != USER_NAMESPACE_NO) {
+ mntns_fd = receive_one_fd(fd_outer_socket_pair[0], 0);
+ if (mntns_fd < 0)
+ return log_error_errno(mntns_fd, "Failed to receive mount namespace fd from outer child: %m");
+
/* The child just let us know the UID shift it might have read from the image. */
- l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof arg_uid_shift, 0);
+ l = recv(fd_outer_socket_pair[0], &arg_uid_shift, sizeof arg_uid_shift, 0);
if (l < 0)
return log_error_errno(errno, "Failed to read UID shift: %m");
if (l != sizeof arg_uid_shift)
if (r < 0)
return log_error_errno(r, "Failed to pick suitable UID/GID range: %m");
- l = send(uid_shift_socket_pair[0], &arg_uid_shift, sizeof arg_uid_shift, MSG_NOSIGNAL);
+ l = send(fd_outer_socket_pair[0], &arg_uid_shift, sizeof arg_uid_shift, MSG_NOSIGNAL);
if (l < 0)
return log_error_errno(errno, "Failed to send UID shift: %m");
if (l != sizeof arg_uid_shift)
return log_oom();
for (size_t i = 0; i < n_bind_user_uid; i++) {
- l = recv(uid_shift_socket_pair[0], bind_user_uid + i*4, sizeof(uid_t)*4, 0);
+ l = recv(fd_outer_socket_pair[0], bind_user_uid + i*4, sizeof(uid_t)*4, 0);
if (l < 0)
return log_error_errno(errno, "Failed to read user UID map pair: %m");
if (l != sizeof(uid_t)*4)
if (arg_unified_cgroup_hierarchy == CGROUP_UNIFIED_UNKNOWN) {
/* The child let us know the support cgroup mode it might have read from the image. */
- l = recv(unified_cgroup_hierarchy_socket_pair[0], &arg_unified_cgroup_hierarchy, sizeof(arg_unified_cgroup_hierarchy), 0);
+ l = recv(fd_outer_socket_pair[0], &arg_unified_cgroup_hierarchy, sizeof(arg_unified_cgroup_hierarchy), 0);
if (l < 0)
return log_error_errno(errno, "Failed to read cgroup mode: %m");
if (l != sizeof(arg_unified_cgroup_hierarchy))
return -EIO;
/* And now retrieve the PID of the inner child. */
- l = recv(pid_socket_pair[0], pid, sizeof *pid, 0);
+ l = recv(fd_outer_socket_pair[0], pid, sizeof *pid, 0);
if (l < 0)
return log_error_errno(errno, "Failed to read inner child PID: %m");
if (l != sizeof *pid)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading inner child PID.");
/* We also retrieve container UUID in case it was generated by outer child */
- l = recv(uuid_socket_pair[0], &arg_uuid, sizeof arg_uuid, 0);
+ l = recv(fd_outer_socket_pair[0], &arg_uuid, sizeof arg_uuid, 0);
if (l < 0)
return log_error_errno(errno, "Failed to read container machine ID: %m");
if (l != sizeof(arg_uuid))
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading container machined ID.");
/* We also retrieve the socket used for notifications generated by outer child */
- notify_socket = receive_one_fd(notify_socket_pair[0], 0);
+ notify_socket = receive_one_fd(fd_outer_socket_pair[0], 0);
if (notify_socket < 0)
return log_error_errno(notify_socket,
"Failed to receive notification socket from the outer child: %m");
if (r < 0)
return r;
+ if (arg_userns_mode != USER_NAMESPACE_NO) {
+ r = wipe_fully_visible_fs(mntns_fd);
+ if (r < 0)
+ return r;
+ mntns_fd = safe_close(mntns_fd);
+ }
+
/* Let the child know that we are ready and wait that the child is completely ready now. */
if (!barrier_place_and_sync(&barrier)) /* #5 */
return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Child died too early.");
/* Exit when the child exits */
(void) sd_event_add_signal(event, NULL, SIGCHLD, on_sigchld, PID_TO_PTR(*pid));
+ /* Retrieve the kmsg fifo allocated by inner child */
+ fd_kmsg_fifo = receive_one_fd(fd_inner_socket_pair[0], 0);
+ if (fd_kmsg_fifo < 0)
+ return log_error_errno(fd_kmsg_fifo, "Failed to receive kmsg fifo from inner child: %m");
+
if (arg_expose_ports) {
- r = expose_port_watch_rtnl(event, rtnl_socket_pair[0], on_address_change, expose_args, &rtnl);
+ r = expose_port_watch_rtnl(event, fd_inner_socket_pair[0], on_address_change, expose_args, &rtnl);
if (r < 0)
return r;
(void) expose_port_execute(rtnl, &expose_args->fw_ctx, arg_expose_ports, AF_INET6, &expose_args->address6);
}
- rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
-
if (arg_console_mode != CONSOLE_PIPE) {
_cleanup_close_ int fd = -1;
PTYForwardFlags flags = 0;
/* Retrieve the master pty allocated by inner child */
- fd = receive_one_fd(master_pty_socket_pair[0], 0);
+ fd = receive_one_fd(fd_inner_socket_pair[0], 0);
if (fd < 0)
return log_error_errno(fd, "Failed to receive master pty from the inner child: %m");
*master = TAKE_FD(fd);
}
+ fd_inner_socket_pair[0] = safe_close(fd_inner_socket_pair[0]);
+
r = sd_event_loop(event);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
/* Normally redundant, but better safe than sorry */
(void) kill(*pid, SIGKILL);
+ fd_kmsg_fifo = safe_close(fd_kmsg_fifo);
+
if (arg_private_network) {
/* Move network interfaces back to the parent network namespace. We use `safe_fork`
* to avoid having to move the parent to the child network namespace. */
if (r < 0)
return r;
- log_debug("oomd dry-run: Would have tried to kill %s with recurse=%s", cg_path, true_false(recurse));
+ log_info("oomd dry-run: Would have tried to kill %s with recurse=%s", cg_path, true_false(recurse));
return 0;
}
static sd_id128_t *arg_filter_partitions = NULL;
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 sd_id128_t *arg_defer_partitions = NULL;
+static size_t arg_n_defer_partitions = 0;
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_filter_partitions, freep);
-typedef struct Partition Partition;
typedef struct FreeArea FreeArea;
-typedef struct Context Context;
typedef enum EncryptMode {
ENCRYPT_OFF,
_VERITY_MODE_INVALID = -EINVAL,
} VerityMode;
-struct Partition {
+typedef enum MinimizeMode {
+ MINIMIZE_OFF,
+ MINIMIZE_BEST,
+ MINIMIZE_GUESS,
+ _MINIMIZE_MODE_MAX,
+ _MINIMIZE_MODE_INVALID = -EINVAL,
+} MinimizeMode;
+
+typedef struct Partition {
char *definition_path;
char **drop_in_files;
FreeArea *allocated_to_area;
char *copy_blocks_path;
+ bool copy_blocks_path_is_our_file;
bool copy_blocks_auto;
const char *copy_blocks_root;
int copy_blocks_fd;
EncryptMode encrypt;
VerityMode verity;
char *verity_match_key;
- bool minimize;
+ MinimizeMode minimize;
uint64_t gpt_flags;
int no_auto;
size_t roothash_size;
char *split_name_format;
- char *split_name_resolved;
+ char *split_path;
- Partition *siblings[_VERITY_MODE_MAX];
+ struct Partition *siblings[_VERITY_MODE_MAX];
- LIST_FIELDS(Partition, partitions);
-};
+ LIST_FIELDS(struct Partition, partitions);
+} Partition;
#define PARTITION_IS_FOREIGN(p) (!(p)->definition_path)
#define PARTITION_EXISTS(p) (!!(p)->current_partition)
uint64_t allocated;
};
-struct Context {
+typedef struct Context {
LIST_HEAD(Partition, partitions);
size_t n_partitions;
uint64_t grain_size;
sd_id128_t seed;
-};
+
+ char *node;
+ bool node_is_our_file;
+ int backing_fd;
+
+ bool from_scratch;
+} Context;
static const char *encrypt_mode_table[_ENCRYPT_MODE_MAX] = {
[ENCRYPT_OFF] = "off",
[VERITY_SIG] = "signature",
};
+static const char *minimize_mode_table[_MINIMIZE_MODE_MAX] = {
+ [MINIMIZE_OFF] = "off",
+ [MINIMIZE_BEST] = "best",
+ [MINIMIZE_GUESS] = "guess",
+};
+
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(encrypt_mode, EncryptMode, ENCRYPT_KEY_FILE);
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(verity_mode, VerityMode);
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(minimize_mode, MinimizeMode, MINIMIZE_BEST);
static uint64_t round_down_size(uint64_t v, uint64_t p) {
return (v / p) * p;
if (p->new_partition)
fdisk_unref_partition(p->new_partition);
- free(p->copy_blocks_path);
+ if (p->copy_blocks_path_is_our_file)
+ unlink_and_free(p->copy_blocks_path);
+ else
+ free(p->copy_blocks_path);
safe_close(p->copy_blocks_fd);
free(p->format);
free(p->roothash);
free(p->split_name_format);
- free(p->split_name_resolved);
+ unlink_and_free(p->split_path);
return mfree(p);
}
return arg_filter_partitions_type == FILTER_PARTITIONS_INCLUDE;
}
-static bool partition_skip(const Partition *p) {
+static bool partition_defer(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]))
+ for (size_t i = 0; i < arg_n_defer_partitions; i++)
+ if (sd_id128_equal(p->type.uuid, arg_defer_partitions[i]))
return true;
return false;
if (context->fdisk_context)
fdisk_unref_context(context->fdisk_context);
+ safe_close(context->backing_fd);
+ if (context->node_is_our_file)
+ unlink_and_free(context->node);
+ else
+ free(context->node);
+
return mfree(context);
}
}
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_verity, verity_mode, VerityMode, VERITY_OFF, "Invalid verity mode");
+static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_minimize, minimize_mode, MinimizeMode, MINIMIZE_OFF, "Invalid minimize mode");
static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
{ "Partition", "NoAuto", config_parse_tristate, 0, &p->no_auto },
{ "Partition", "GrowFileSystem", config_parse_tristate, 0, &p->growfs },
{ "Partition", "SplitName", config_parse_string, 0, &p->split_name_format },
- { "Partition", "Minimize", config_parse_bool, 0, &p->minimize },
+ { "Partition", "Minimize", config_parse_minimize, 0, &p->minimize },
{}
};
int r;
return log_oom();
}
- if (p->minimize && !p->format)
+ if (p->minimize != MINIMIZE_OFF && !p->format)
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"Minimize= can only be enabled if Format= is set");
+ if (p->minimize == MINIMIZE_BEST && !fstype_is_ro(p->format))
+ return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+ "Minimize=best can only be used with read-only filesystems");
+
if ((!strv_isempty(p->copy_files) || !strv_isempty(p->make_directories)) && !mkfs_supports_root_option(p->format) && geteuid() != 0)
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EPERM),
"Need to be root to populate %s filesystems with CopyFiles=/MakeDirectories=",
return 0;
}
-static int context_load_partition_table(
- Context *context,
- const char *node,
- int *backing_fd) {
-
+static int context_load_partition_table(Context *context) {
_cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
_cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL;
uint64_t left_boundary = UINT64_MAX, first_lba, last_lba, nsectors;
int r;
assert(context);
- assert(node);
- assert(backing_fd);
assert(!context->fdisk_context);
assert(!context->free_areas);
assert(context->start == UINT64_MAX);
/* 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 (context->backing_fd < 0) {
c = fdisk_new_context();
if (!c)
return log_oom();
- r = fdisk_assign_device(c, node, arg_dry_run);
+ r = fdisk_assign_device(c, context->node, arg_dry_run);
} else
- r = fdisk_new_context_fd(*backing_fd, arg_dry_run, &c);
+ r = fdisk_new_context_fd(context->backing_fd, arg_dry_run, &c);
if (r == -EINVAL && arg_size_auto) {
struct stat st;
/* libfdisk returns EINVAL if opening a file of size zero. Let's check for that, and accept
* it if automatic sizing is requested. */
- if (*backing_fd < 0)
- r = stat(node, &st);
+ if (context->backing_fd < 0)
+ r = stat(context->node, &st);
else
- r = fstat(*backing_fd, &st);
+ r = fstat(context->backing_fd, &st);
if (r < 0)
- return log_error_errno(errno, "Failed to stat block device '%s': %m", node);
+ return log_error_errno(errno, "Failed to stat block device '%s': %m", context->node);
if (S_ISREG(st.st_mode) && st.st_size == 0) {
/* User the fallback values if we have no better idea */
r = -EINVAL;
}
if (r < 0)
- return log_error_errno(r, "Failed to open device '%s': %m", node);
+ return log_error_errno(r, "Failed to open device '%s': %m", context->node);
- if (*backing_fd < 0) {
+ if (context->backing_fd < 0) {
/* If we have no fd referencing the device yet, make a copy of the fd now, so that we have one */
- *backing_fd = fd_reopen(fdisk_get_devfd(c), O_RDONLY|O_CLOEXEC);
- if (*backing_fd < 0)
- return log_error_errno(*backing_fd, "Failed to duplicate fdisk fd: %m");
+ context->backing_fd = fd_reopen(fdisk_get_devfd(c), O_RDONLY|O_CLOEXEC);
+ if (context->backing_fd < 0)
+ return log_error_errno(context->backing_fd, "Failed to duplicate fdisk fd: %m");
/* Tell udev not to interfere while we are processing the device */
- if (flock(*backing_fd, arg_dry_run ? LOCK_SH : LOCK_EX) < 0)
+ if (flock(context->backing_fd, arg_dry_run ? LOCK_SH : LOCK_EX) < 0)
return log_error_errno(errno, "Failed to lock block device: %m");
}
case EMPTY_REFUSE:
/* Refuse empty disks, insist on an existing GPT partition table */
if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT))
- return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has no GPT disk label, not repartitioning.", node);
+ return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has no GPT disk label, not repartitioning.", context->node);
break;
/* Require an empty disk, refuse any existing partition table */
r = fdisk_has_label(c);
if (r < 0)
- return log_error_errno(r, "Failed to determine whether disk %s has a disk label: %m", node);
+ return log_error_errno(r, "Failed to determine whether disk %s has a disk label: %m", context->node);
if (r > 0)
- return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s already has a disk label, refusing.", node);
+ return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s already has a disk label, refusing.", context->node);
from_scratch = true;
break;
/* Allow both an empty disk and an existing partition table, but only GPT */
r = fdisk_has_label(c);
if (r < 0)
- return log_error_errno(r, "Failed to determine whether disk %s has a disk label: %m", node);
+ return log_error_errno(r, "Failed to determine whether disk %s has a disk label: %m", context->node);
if (r > 0) {
if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT))
- return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has non-GPT disk label, not repartitioning.", node);
+ return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has non-GPT disk label, not repartitioning.", context->node);
} else
from_scratch = true;
return gpt_partition_type_uuid_to_string(p->type.uuid);
}
-static int context_dump_partitions(Context *context, const char *node) {
+static int context_dump_partitions(Context *context) {
_cleanup_(table_unrefp) Table *t = NULL;
uint64_t sum_padding = 0, sum_size = 0;
int r;
- const size_t roothash_col = 13, dropin_files_col = 14;
- bool has_roothash = false, has_dropin_files = false;
+ const size_t roothash_col = 13, dropin_files_col = 14, split_path_col = 15;
+ bool has_roothash = false, has_dropin_files = false, has_split_path = false;
if ((arg_json_format_flags & JSON_FORMAT_OFF) && context->n_partitions == 0) {
log_info("Empty partition table.");
return 0;
}
- t = table_new("type", "label", "uuid", "file", "node", "offset", "old size", "raw size", "size", "old padding", "raw padding", "padding", "activity", "roothash", "drop-in files");
+ t = table_new("type",
+ "label",
+ "uuid",
+ "file",
+ "node",
+ "offset",
+ "old size",
+ "raw size",
+ "size",
+ "old padding",
+ "raw padding",
+ "padding",
+ "activity",
+ "roothash",
+ "drop-in files",
+ "split path");
if (!t)
return log_oom();
if (!DEBUG_LOGGING) {
if (arg_json_format_flags & JSON_FORMAT_OFF)
(void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
- (size_t) 8, (size_t) 11, roothash_col, dropin_files_col);
+ (size_t) 8, (size_t) 11, roothash_col, dropin_files_col,
+ split_path_col);
else
(void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
(size_t) 5, (size_t) 6, (size_t) 7, (size_t) 9, (size_t) 10,
- (size_t) 12, roothash_col, dropin_files_col);
+ (size_t) 12, roothash_col, dropin_files_col,
+ split_path_col);
}
(void) table_set_align_percent(t, table_get_cell(t, 0, 5), 100);
activity = "resize";
label = partition_label(p);
- partname = p->partno != UINT64_MAX ? fdisk_partname(node, p->partno+1) : NULL;
+ partname = p->partno != UINT64_MAX ? fdisk_partname(context->node, p->partno+1) : NULL;
r = format_size_change(p->current_size, p->new_size, &size_change);
if (r < 0)
TABLE_STRING, padding_change, TABLE_SET_COLOR, !p->partitions_next && sum_padding > 0 ? ansi_underline() : NULL,
TABLE_STRING, activity ?: "unchanged",
TABLE_STRING, rh,
- TABLE_STRV, p->drop_in_files);
+ TABLE_STRV, p->drop_in_files,
+ TABLE_STRING, empty_to_null(p->split_path) ?: "-");
if (r < 0)
return table_log_add_error(r);
has_roothash = has_roothash || !isempty(rh);
has_dropin_files = has_dropin_files || !strv_isempty(p->drop_in_files);
+ has_split_path = has_split_path || !isempty(p->split_path);
}
if ((arg_json_format_flags & JSON_FORMAT_OFF) && (sum_padding > 0 || sum_size > 0)) {
TABLE_STRING, b,
TABLE_EMPTY,
TABLE_EMPTY,
+ TABLE_EMPTY,
TABLE_EMPTY);
if (r < 0)
return table_log_add_error(r);
return log_error_errno(r, "Failed to set columns to display: %m");
}
+ if (!has_split_path) {
+ r = table_hide_column_from_display(t, split_path_col);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set columns to display: %m");
+ }
+
return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
}
return 0;
}
-static int context_dump_partition_bar(Context *context, const char *node) {
+static int context_dump_partition_bar(Context *context) {
_cleanup_free_ Partition **bar = NULL;
_cleanup_free_ size_t *start_array = NULL;
Partition *last = NULL;
} else if (i == context->n_partitions - j) {
_cleanup_free_ char *hint = NULL;
- (void) partition_hint(p, node, &hint);
+ (void) partition_hint(p, context->node, &hint);
if (streq_ptr(line[start_array[j-1]], special_glyph(SPECIAL_GLYPH_TREE_VERTICAL)))
d = strjoin(special_glyph(SPECIAL_GLYPH_TREE_BRANCH), " ", strna(hint));
return false;
}
-static int context_dump(Context *context, const char *node, bool late) {
+static int context_dump(Context *context, bool late) {
int r;
assert(context);
- assert(node);
if (arg_pretty == 0 && FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF))
return 0;
if (late && FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF) && !context_has_roothash(context))
return 0;
- r = context_dump_partitions(context, node);
+ r = context_dump_partitions(context);
if (r < 0)
return r;
if (FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF) && !late) {
putc('\n', stdout);
- r = context_dump_partition_bar(context, node);
+ r = context_dump_partition_bar(context);
if (r < 0)
return r;
return 0;
}
-static int context_wipe_and_discard(Context *context, bool from_scratch) {
+static int context_wipe_and_discard(Context *context) {
int r;
assert(context);
if (!p->allocated_to_area)
continue;
- if (partition_skip(p))
+ if (partition_defer(p))
continue;
r = context_wipe_partition(context, p);
if (r < 0)
return r;
- if (!from_scratch) {
+ if (!context->from_scratch) {
r = context_discard_partition(context, p);
if (r < 0)
return r;
}
}
- if (!from_scratch) {
+ if (!context->from_scratch) {
r = context_discard_gap_after(context, NULL);
if (r < 0)
return r;
if (p->verity != VERITY_HASH)
return 0;
- if (partition_skip(p))
+ if (partition_defer(p))
return 0;
assert_se(dp = p->siblings[VERITY_DATA]);
if (PARTITION_EXISTS(p))
return 0;
- if (partition_skip(p))
+ if (partition_defer(p))
return 0;
assert_se(hp = p->siblings[VERITY_HASH]);
if (PARTITION_EXISTS(p)) /* Never copy over existing partitions */
continue;
- if (partition_skip(p))
+ if (partition_defer(p))
continue;
assert(p->new_size != UINT64_MAX);
if (p->copy_blocks_fd >= 0)
continue;
- if (partition_skip(p))
+ if (partition_defer(p))
continue;
assert(p->offset != UINT64_MAX);
if (p->dropped)
continue;
- if (partition_skip(p))
+ if (partition_defer(p))
continue;
assert(p->new_size != UINT64_MAX);
return 0;
}
-static int split_name_printf(Partition *p) {
+static int split_name_printf(Partition *p, char **ret) {
assert(p);
const Specifier table[] = {
{}
};
- return specifier_printf(p->split_name_format, NAME_MAX, table, arg_root, p, &p->split_name_resolved);
+ return specifier_printf(p->split_name_format, NAME_MAX, table, arg_root, p, ret);
+}
+
+static int split_node(const char *node, char **ret_base, char **ret_ext) {
+ _cleanup_free_ char *base = NULL, *ext = NULL;
+ char *e;
+ int r;
+
+ assert(node);
+ assert(ret_base);
+ assert(ret_ext);
+
+ r = path_extract_filename(node, &base);
+ if (r == O_DIRECTORY || r == -EADDRNOTAVAIL)
+ return log_error_errno(r, "Device node %s cannot be a directory", node);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract filename from %s: %m", node);
+
+ e = endswith(base, ".raw");
+ if (e) {
+ ext = strdup(e);
+ if (!ext)
+ return log_oom();
+
+ *e = 0;
+ }
+
+ *ret_base = TAKE_PTR(base);
+ *ret_ext = TAKE_PTR(ext);
+
+ return 0;
}
static int split_name_resolve(Context *context) {
+ _cleanup_free_ char *parent = NULL, *base = NULL, *ext = NULL;
int r;
+ assert(context);
+
+ r = path_extract_directory(context->node, &parent);
+ if (r < 0 && r != -EDESTADDRREQ)
+ return log_error_errno(r, "Failed to extract directory from %s: %m", context->node);
+
+ r = split_node(context->node, &base, &ext);
+ if (r < 0)
+ return r;
+
LIST_FOREACH(partitions, p, context->partitions) {
+ _cleanup_free_ char *resolved = NULL;
+
if (p->dropped)
continue;
if (!p->split_name_format)
continue;
- r = split_name_printf(p);
+ r = split_name_printf(p, &resolved);
if (r < 0)
return log_error_errno(r, "Failed to resolve specifiers in %s: %m", p->split_name_format);
+
+ if (parent)
+ p->split_path = strjoin(parent, "/", base, ".", resolved, ext);
+ else
+ p->split_path = strjoin(base, ".", resolved, ext);
+ if (!p->split_path)
+ return log_oom();
}
LIST_FOREACH(partitions, p, context->partitions) {
- if (!p->split_name_resolved)
+ if (!p->split_path)
continue;
LIST_FOREACH(partitions, q, context->partitions) {
if (p == q)
continue;
- if (!q->split_name_resolved)
+ if (!q->split_path)
continue;
- if (!streq(p->split_name_resolved, q->split_name_resolved))
+ if (!streq(p->split_path, q->split_path))
continue;
return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
"%s and %s have the same resolved split name \"%s\", refusing",
- p->definition_path, q->definition_path, p->split_name_resolved);
+ p->definition_path, q->definition_path, p->split_path);
}
}
return 0;
}
-static int split_node(const char *node, char **ret_base, char **ret_ext) {
- _cleanup_free_ char *base = NULL, *ext = NULL;
- char *e;
- int r;
-
- assert(node);
- assert(ret_base);
- assert(ret_ext);
-
- r = path_extract_filename(node, &base);
- if (r == O_DIRECTORY || r == -EADDRNOTAVAIL)
- return log_error_errno(r, "Device node %s cannot be a directory", arg_node);
- if (r < 0)
- return log_error_errno(r, "Failed to extract filename from %s: %m", arg_node);
-
- e = endswith(base, ".raw");
- if (e) {
- ext = strdup(e);
- if (!ext)
- return log_oom();
-
- *e = 0;
- }
-
- *ret_base = TAKE_PTR(base);
- *ret_ext = TAKE_PTR(ext);
-
- return 0;
-}
-
static int context_split(Context *context) {
- _cleanup_free_ char *base = NULL, *ext = NULL;
- _cleanup_close_ int dir_fd = -1;
int fd = -1, r;
if (!arg_split)
return 0;
assert(context);
- assert(arg_node);
/* We can't do resolution earlier because the partition UUIDs for verity partitions are only filled
* in after they've been generated. */
if (r < 0)
return r;
- r = split_node(arg_node, &base, &ext);
- if (r < 0)
- return r;
-
- dir_fd = r = open_parent(arg_node, O_PATH|O_CLOEXEC, 0);
- if (r == -EDESTADDRREQ)
- dir_fd = AT_FDCWD;
- else if (r < 0)
- return log_error_errno(r, "Failed to open parent directory of %s: %m", arg_node);
-
LIST_FOREACH(partitions, p, context->partitions) {
- _cleanup_free_ char *fname = NULL;
_cleanup_close_ int fdt = -1;
if (p->dropped)
continue;
- if (!p->split_name_resolved)
+ if (!p->split_path)
continue;
- if (partition_skip(p))
+ if (partition_defer(p))
continue;
- fname = strjoin(base, ".", p->split_name_resolved, ext);
- if (!fname)
- return log_oom();
-
- fdt = openat(dir_fd, fname, O_WRONLY|O_NOCTTY|O_CLOEXEC|O_NOFOLLOW|O_CREAT|O_EXCL, 0666);
+ fdt = open(p->split_path, O_WRONLY|O_NOCTTY|O_CLOEXEC|O_NOFOLLOW|O_CREAT|O_EXCL, 0666);
if (fdt < 0)
- return log_error_errno(errno, "Failed to open %s: %m", fname);
+ return log_error_errno(fdt, "Failed to open split partition file %s: %m", p->split_path);
if (fd < 0)
assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
r = copy_bytes(fd, fdt, p->new_size, COPY_REFLINK|COPY_HOLES);
if (r < 0)
- return log_error_errno(r, "Failed to copy to split partition %s: %m", fname);
+ return log_error_errno(r, "Failed to copy to split partition %s: %m", p->split_path);
}
return 0;
}
-static int context_write_partition_table(
- Context *context,
- const char *node,
- bool from_scratch) {
-
+static int context_write_partition_table(Context *context) {
_cleanup_(fdisk_unref_tablep) struct fdisk_table *original_table = NULL;
int capable, r;
assert(context);
- if (!from_scratch && !context_changed(context)) {
+ if (!context->from_scratch && !context_changed(context)) {
log_info("No changes.");
return 0;
}
log_info("Applying changes.");
- if (from_scratch) {
+ if (context->from_scratch) {
r = context_wipe_range(context, 0, context->total);
if (r < 0)
return r;
/* Wipe fs signatures and discard sectors where the new partitions are going to be placed and in the
* gaps between partitions, just to be sure. */
- r = context_wipe_and_discard(context, from_scratch);
+ r = context_wipe_and_discard(context);
if (r < 0)
return r;
else if (capable > 0) {
log_info("Telling kernel to reread partition table.");
- if (from_scratch)
+ if (context->from_scratch)
r = fdisk_reread_partition_table(context->fdisk_context);
else
r = fdisk_reread_changes(context->fdisk_context, original_table);
else if (fd < 0)
return log_error_errno(fd, "Failed to determine machine ID of image: %m");
else {
- r = id128_read_fd(fd, ID128_PLAIN_OR_UNINIT, &context->seed);
- if (r == -ENOMEDIUM)
+ r = id128_read_fd(fd, ID128_FORMAT_PLAIN, &context->seed);
+ if (IN_SET(r, -ENOMEDIUM, -ENOPKG))
log_info("No machine ID set, using randomized partition UUIDs.");
else if (r < 0)
return log_error_errno(r, "Failed to parse machine ID of image: %m");
return 0;
}
-static int context_factory_reset(Context *context, bool from_scratch) {
+static int context_factory_reset(Context *context) {
size_t n = 0;
int r;
if (arg_factory_reset <= 0)
return 0;
- if (from_scratch) /* Nothing to reset if we start from scratch */
+ if (context->from_scratch) /* Nothing to reset if we start from scratch */
return 0;
if (arg_dry_run) {
if (!p->format)
continue;
- if (!p->minimize)
+ if (p->minimize == MINIMIZE_OFF)
continue;
if (!partition_needs_populate(p))
* loopback file for us. */
if (fstype_is_ro(p->format)) {
p->copy_blocks_path = TAKE_PTR(temp);
+ p->copy_blocks_path_is_our_file = true;
continue;
}
}
p->copy_blocks_path = TAKE_PTR(temp);
+ p->copy_blocks_path_is_our_file = true;
}
return 0;
" Ignore partitions not of the specified types\n"
" --exclude-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
" Ignore partitions of the specified types\n"
- " --skip-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
+ " --defer-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",
ARG_SPLIT,
ARG_INCLUDE_PARTITIONS,
ARG_EXCLUDE_PARTITIONS,
- ARG_SKIP_PARTITIONS,
+ ARG_DEFER_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 },
+ { "defer-partitions", required_argument, NULL, ARG_DEFER_PARTITIONS },
{}
};
break;
- case ARG_SKIP_PARTITIONS:
- r = parse_partition_types(optarg, &arg_skip_partitions, &arg_n_skip_partitions);
+ case ARG_DEFER_PARTITIONS:
+ r = parse_partition_types(optarg, &arg_defer_partitions, &arg_n_defer_partitions);
if (r < 0)
return r;
return 0;
}
-static int find_root(char **ret, int *ret_fd) {
+static int find_root(Context *context) {
_cleanup_free_ char *device = NULL;
int r;
- assert(ret);
- assert(ret_fd);
+ assert(context);
if (arg_node) {
if (arg_empty == EMPTY_CREATE) {
if (fd < 0)
return log_error_errno(errno, "Failed to create '%s': %m", arg_node);
- *ret = TAKE_PTR(s);
- *ret_fd = TAKE_FD(fd);
+ context->node = TAKE_PTR(s);
+ context->node_is_our_file = true;
+ context->backing_fd = TAKE_FD(fd);
return 0;
}
/* Note that we don't specify a root argument here: if the user explicitly configured a node
* we'll take it relative to the host, not the image */
- r = acquire_root_devno(arg_node, NULL, O_RDONLY|O_CLOEXEC, ret, ret_fd);
+ r = acquire_root_devno(arg_node, NULL, O_RDONLY|O_CLOEXEC, &context->node, &context->backing_fd);
if (r == -EUCLEAN)
return btrfs_log_dev_root(LOG_ERR, r, arg_node);
if (r < 0)
FOREACH_STRING(p, "/", "/usr") {
- r = acquire_root_devno(p, arg_root, O_RDONLY|O_DIRECTORY|O_CLOEXEC, ret, ret_fd);
+ r = acquire_root_devno(p, arg_root, O_RDONLY|O_DIRECTORY|O_CLOEXEC, &context->node,
+ &context->backing_fd);
if (r < 0) {
if (r == -EUCLEAN)
return btrfs_log_dev_root(LOG_ERR, r, p);
} else if (r < 0)
return log_error_errno(r, "Failed to read symlink /run/systemd/volatile-root: %m");
else {
- r = acquire_root_devno(device, NULL, O_RDONLY|O_CLOEXEC, ret, ret_fd);
+ r = acquire_root_devno(device, NULL, O_RDONLY|O_CLOEXEC, &context->node, &context->backing_fd);
if (r == -EUCLEAN)
return btrfs_log_dev_root(LOG_ERR, r, device);
if (r < 0)
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
_cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
_cleanup_(context_freep) Context* context = NULL;
- _cleanup_free_ char *node = NULL;
- _cleanup_close_ int backing_fd = -1;
- bool from_scratch, node_is_our_loop = false;
+ bool node_is_our_loop = false;
int r;
log_show_color(true);
if (r < 0)
return r;
- r = find_root(&node, &backing_fd);
+ r = find_root(context);
if (r < 0)
return r;
if (arg_size != UINT64_MAX) {
r = resize_backing_fd(
- node,
- &backing_fd,
+ context->node,
+ &context->backing_fd,
node_is_our_loop ? arg_image : NULL,
node_is_our_loop ? loop_device : NULL);
if (r < 0)
return r;
}
- r = context_load_partition_table(context, node, &backing_fd);
+ r = context_load_partition_table(context);
if (r == -EHWPOISON)
return 77; /* Special return value which means "Not GPT, so not doing anything". This isn't
* really an error when called at boot. */
if (r < 0)
return r;
- from_scratch = r > 0; /* Starting from scratch */
+ context->from_scratch = r > 0; /* Starting from scratch */
if (arg_can_factory_reset) {
r = context_can_factory_reset(context);
return 0;
}
- r = context_factory_reset(context, from_scratch);
+ r = context_factory_reset(context);
if (r < 0)
return r;
if (r > 0) {
/* Reload the reduced partition table */
context_unload_partition_table(context);
- r = context_load_partition_table(context, node, &backing_fd);
+ r = context_load_partition_table(context);
if (r < 0)
return r;
}
-#if 0
- (void) context_dump_partitions(context, node);
- putchar('\n');
-#endif
-
r = context_read_seed(context, arg_root);
if (r < 0)
return r;
assert(arg_size != UINT64_MAX);
r = resize_backing_fd(
- node,
- &backing_fd,
+ context->node,
+ &context->backing_fd,
node_is_our_loop ? arg_image : NULL,
node_is_our_loop ? loop_device : NULL);
if (r < 0)
return r;
- r = context_load_partition_table(context, node, &backing_fd);
+ r = context_load_partition_table(context);
if (r < 0)
return r;
}
/* Now calculate where each new partition gets placed */
context_place_partitions(context);
- (void) context_dump(context, node, /*late=*/ false);
+ (void) context_dump(context, /*late=*/ false);
- r = context_write_partition_table(context, node, from_scratch);
+ r = context_write_partition_table(context);
if (r < 0)
return r;
if (r < 0)
return r;
- (void) context_dump(context, node, /*late=*/ true);
+ (void) context_dump(context, /*late=*/ true);
+
+ context->node = mfree(context->node);
+
+ LIST_FOREACH(partitions, p, context->partitions)
+ p->split_path = mfree(p->split_path);
return 0;
}
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;
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
_cleanup_fclose_ FILE *f = NULL;
- _cleanup_(etc_hosts_free) EtcHosts h = {};
+ _cleanup_(etc_hosts_clear) EtcHosts h = {};
if (!getenv("SYSTEMD_LOG_LEVEL"))
log_set_max_level(LOG_CRIT);
--- /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')
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;
return 0;
}
-DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i) {
- DnsTxtItem *n;
-
- if (!i)
- return NULL;
-
- n = i->items_next;
+DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *first) {
+ LIST_FOREACH(items, i, first)
+ free(i);
- free(i);
- return dns_txt_item_free_all(n);
+ return NULL;
}
bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) {
+ DnsTxtItem *bb = b;
if (a == b)
return true;
- if (!a != !b)
- return false;
+ LIST_FOREACH(items, aa, a) {
+ if (!bb)
+ return false;
- if (!a)
- return true;
-
- if (a->length != b->length)
- return false;
+ if (memcmp_nn(aa->data, aa->length, bb->data, bb->length) != 0)
+ return false;
- if (memcmp(a->data, b->data, a->length) != 0)
- return false;
+ bb = bb->items_next;
+ }
- return dns_txt_item_equal(a->items_next, b->items_next);
+ return !bb;
}
DnsTxtItem *dns_txt_item_copy(DnsTxtItem *first) {
DnsTxtItem *j;
j = memdup(i, offsetof(DnsTxtItem, data) + i->length + 1);
- if (!j) {
- dns_txt_item_free_all(copy);
- return NULL;
- }
+ if (!j)
+ return dns_txt_item_free_all(copy);
LIST_INSERT_AFTER(items, copy, end, j);
end = j;
int dns_txt_item_new_empty(DnsTxtItem **ret) {
DnsTxtItem *i;
+ assert(ret);
+
/* RFC 6763, section 6.1 suggests to treat
* empty TXT RRs as equivalent to a TXT record
* with a single empty string. */
return -ENOMEM;
*ret = i;
-
return 0;
}
return 0;
}
+static int txt_to_json(DnsTxtItem *items, JsonVariant **ret) {
+ JsonVariant **elements = NULL;
+ size_t n = 0;
+ int r;
+
+ assert(ret);
+
+ LIST_FOREACH(items, i, items) {
+ if (!GREEDY_REALLOC(elements, n + 1)) {
+ r = -ENOMEM;
+ goto finalize;
+ }
+
+ r = json_variant_new_octescape(elements + n, i->data, i->length);
+ if (r < 0)
+ goto finalize;
+
+ n++;
+ }
+
+ r = json_variant_new_array(ret, elements, n);
+
+finalize:
+ for (size_t i = 0; i < n; i++)
+ json_variant_unref(elements[i]);
+
+ free(elements);
+ return r;
+}
+
int dns_resource_record_to_json(DnsResourceRecord *rr, JsonVariant **ret) {
_cleanup_(json_variant_unrefp) JsonVariant *k = NULL;
int r;
case DNS_TYPE_TXT: {
_cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
- LIST_FOREACH(items, i, rr->txt.items) {
- _cleanup_(json_variant_unrefp) JsonVariant *b = NULL;
-
- r = json_variant_new_octescape(&b, i->data, i->length);
- if (r < 0)
- return r;
-
- r = json_variant_append_array(&l, b);
- if (r < 0)
- return r;
- }
-
- if (!l) {
- r = json_variant_new_array(&l, NULL, 0);
- if (r < 0)
- return r;
- }
+ r = txt_to_json(rr->txt.items, &l);
+ if (r < 0)
+ return r;
return json_build(ret,
JSON_BUILD_OBJECT(
/* Recheck /etc/hosts at most once every 2s */
#define ETC_HOSTS_RECHECK_USEC (2*USEC_PER_SEC)
-static void etc_hosts_item_free(EtcHostsItem *item) {
- strv_free(item->names);
- free(item);
+static EtcHostsItemByAddress *etc_hosts_item_by_address_free(EtcHostsItemByAddress *item) {
+ if (!item)
+ return NULL;
+
+ set_free(item->names);
+ return mfree(item);
}
-static void etc_hosts_item_by_name_free(EtcHostsItemByName *item) {
+DEFINE_TRIVIAL_CLEANUP_FUNC(EtcHostsItemByAddress*, etc_hosts_item_by_address_free);
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ by_address_hash_ops,
+ struct in_addr_data,
+ in_addr_data_hash_func,
+ in_addr_data_compare_func,
+ EtcHostsItemByAddress,
+ etc_hosts_item_by_address_free);
+
+static EtcHostsItemByName *etc_hosts_item_by_name_free(EtcHostsItemByName *item) {
+ if (!item)
+ return NULL;
+
free(item->name);
- free(item->addresses);
- free(item);
+ set_free(item->addresses);
+ return mfree(item);
}
-void etc_hosts_free(EtcHosts *hosts) {
- hosts->by_address = hashmap_free_with_destructor(hosts->by_address, etc_hosts_item_free);
- hosts->by_name = hashmap_free_with_destructor(hosts->by_name, etc_hosts_item_by_name_free);
- hosts->no_address = set_free_free(hosts->no_address);
+DEFINE_TRIVIAL_CLEANUP_FUNC(EtcHostsItemByName*, etc_hosts_item_by_name_free);
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ by_name_hash_ops,
+ char,
+ dns_name_hash_func,
+ dns_name_compare_func,
+ EtcHostsItemByName,
+ etc_hosts_item_by_name_free);
+
+void etc_hosts_clear(EtcHosts *hosts) {
+ assert(hosts);
+
+ hosts->by_address = hashmap_free(hosts->by_address);
+ hosts->by_name = hashmap_free(hosts->by_name);
+ hosts->no_address = set_free(hosts->no_address);
}
void manager_etc_hosts_flush(Manager *m) {
- etc_hosts_free(&m->etc_hosts);
+ etc_hosts_clear(&m->etc_hosts);
m->etc_hosts_stat = (struct stat) {};
}
_cleanup_free_ char *address_str = NULL;
struct in_addr_data address = {};
bool found = false;
- EtcHostsItem *item;
+ EtcHostsItemByAddress *item;
int r;
assert(hosts);
item = hashmap_get(hosts->by_address, &address);
if (!item) {
- r = hashmap_ensure_allocated(&hosts->by_address, &in_addr_data_hash_ops);
- if (r < 0)
- return log_oom();
+ _cleanup_(etc_hosts_item_by_address_freep) EtcHostsItemByAddress *new_item = NULL;
- item = new(EtcHostsItem, 1);
- if (!item)
+ new_item = new(EtcHostsItemByAddress, 1);
+ if (!new_item)
return log_oom();
- *item = (EtcHostsItem) {
+ *new_item = (EtcHostsItemByAddress) {
.address = address,
};
- r = hashmap_put(hosts->by_address, &item->address, item);
- if (r < 0) {
- free(item);
+ r = hashmap_ensure_put(&hosts->by_address, &by_address_hash_ops, &new_item->address, new_item);
+ if (r < 0)
return log_oom();
- }
+
+ item = TAKE_PTR(new_item);
}
}
if (r == 0)
break;
- found = true;
-
r = dns_name_is_valid_ldh(name);
if (r <= 0) {
if (r < 0)
continue;
}
+ found = true;
+
if (!item) {
/* Optimize the case where we don't need to store any addresses, by storing
* only the name in a dedicated Set instead of the hashmap */
- r = set_ensure_consume(&hosts->no_address, &dns_name_hash_ops, TAKE_PTR(name));
+ r = set_ensure_consume(&hosts->no_address, &dns_name_hash_ops_free, TAKE_PTR(name));
if (r < 0)
- return r;
+ return log_oom();
continue;
}
- r = strv_extend_with_size(&item->names, &item->n_names, name);
- if (r < 0)
- return log_oom();
-
bn = hashmap_get(hosts->by_name, name);
if (!bn) {
- r = hashmap_ensure_allocated(&hosts->by_name, &dns_name_hash_ops);
- if (r < 0)
+ _cleanup_(etc_hosts_item_by_name_freep) EtcHostsItemByName *new_item = NULL;
+ _cleanup_free_ char *name_copy = NULL;
+
+ name_copy = strdup(name);
+ if (!name_copy)
return log_oom();
- bn = new0(EtcHostsItemByName, 1);
- if (!bn)
+ new_item = new(EtcHostsItemByName, 1);
+ if (!new_item)
return log_oom();
- r = hashmap_put(hosts->by_name, name, bn);
- if (r < 0) {
- free(bn);
+ *new_item = (EtcHostsItemByName) {
+ .name = TAKE_PTR(name_copy),
+ };
+
+ r = hashmap_ensure_put(&hosts->by_name, &by_name_hash_ops, new_item->name, new_item);
+ if (r < 0)
return log_oom();
- }
- bn->name = TAKE_PTR(name);
+ bn = TAKE_PTR(new_item);
}
- if (!GREEDY_REALLOC(bn->addresses, bn->n_addresses + 1))
- return log_oom();
+ if (!set_contains(bn->addresses, &address)) {
+ _cleanup_free_ struct in_addr_data *address_copy = NULL;
+
+ address_copy = newdup(struct in_addr_data, &address, 1);
+ if (!address_copy)
+ return log_oom();
- bn->addresses[bn->n_addresses++] = &item->address;
+ r = set_ensure_consume(&bn->addresses, &in_addr_data_hash_ops_free, TAKE_PTR(address_copy));
+ if (r < 0)
+ return log_oom();
+ }
+
+ r = set_ensure_consume(&item->names, &dns_name_hash_ops_free, TAKE_PTR(name));
+ if (r < 0)
+ return log_oom();
}
if (!found)
- log_warning("/etc/hosts:%u: line is missing any hostnames", nr);
+ log_warning("/etc/hosts:%u: line is missing any valid hostnames", nr);
return 0;
}
},
};
- EtcHostsItem *item;
-
assert(hosts);
/* Removes the 'localhost' entry from what we loaded. But only if the mapping is exclusively between
* mappings. */
for (size_t j = 0; j < ELEMENTSOF(local_in_addrs); j++) {
- bool all_localhost, in_order;
+ bool all_localhost, all_local_address;
+ EtcHostsItemByAddress *item;
+ const char *name;
item = hashmap_get(hosts->by_address, local_in_addrs + j);
if (!item)
/* Check whether all hostnames the loopback address points to are localhost ones */
all_localhost = true;
- STRV_FOREACH(i, item->names)
- if (!is_localhost(*i)) {
+ SET_FOREACH(name, item->names)
+ if (!is_localhost(name)) {
all_localhost = false;
break;
}
/* Now check if the names listed for this address actually all point back just to this
* address (or the other loopback address). If not, let's stay away from this too. */
- in_order = true;
- STRV_FOREACH(i, item->names) {
+ all_local_address = true;
+ SET_FOREACH(name, item->names) {
EtcHostsItemByName *n;
- bool all_local_address;
+ struct in_addr_data *a;
- n = hashmap_get(hosts->by_name, *i);
+ n = hashmap_get(hosts->by_name, name);
if (!n) /* No reverse entry? Then almost certainly the entry already got deleted from
* the previous iteration of this loop, i.e. via the other protocol */
break;
/* Now check if the addresses of this item are all localhost addresses */
- all_local_address = true;
- for (size_t m = 0; m < n->n_addresses; m++)
- if (!in_addr_is_localhost(n->addresses[m]->family, &n->addresses[m]->address)) {
+ SET_FOREACH(a, n->addresses)
+ if (!in_addr_is_localhost(a->family, &a->address)) {
all_local_address = false;
break;
}
- if (!all_local_address) {
- in_order = false;
+ if (!all_local_address)
break;
- }
}
- if (!in_order)
+ if (!all_local_address)
continue;
- STRV_FOREACH(i, item->names) {
- EtcHostsItemByName *n;
-
- n = hashmap_remove(hosts->by_name, *i);
- if (n)
- etc_hosts_item_by_name_free(n);
- }
+ SET_FOREACH(name, item->names)
+ etc_hosts_item_by_name_free(hashmap_remove(hosts->by_name, name));
assert_se(hashmap_remove(hosts->by_address, local_in_addrs + j) == item);
- etc_hosts_item_free(item);
+ etc_hosts_item_by_address_free(item);
}
}
int etc_hosts_parse(EtcHosts *hosts, FILE *f) {
- _cleanup_(etc_hosts_free) EtcHosts t = {};
+ _cleanup_(etc_hosts_clear) EtcHosts t = {};
unsigned nr = 0;
int r;
strip_localhost(&t);
- etc_hosts_free(hosts);
+ etc_hosts_clear(hosts);
*hosts = t;
t = (EtcHosts) {}; /* prevent cleanup */
return 0;
return 1;
}
-int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
- bool found_a = false, found_aaaa = false;
- struct in_addr_data k = {};
- EtcHostsItemByName *bn;
- DnsResourceKey *t;
- const char *name;
- unsigned i;
+static int etc_hosts_lookup_by_address(
+ EtcHosts *hosts,
+ DnsQuestion *q,
+ const char *name,
+ const struct in_addr_data *address,
+ DnsAnswer **answer) {
+
+ DnsResourceKey *t, *found_ptr = NULL;
+ EtcHostsItemByAddress *item;
int r;
- assert(m);
+ assert(hosts);
assert(q);
+ assert(name);
+ assert(address);
assert(answer);
- if (!m->read_etc_hosts)
+ item = hashmap_get(hosts->by_address, address);
+ if (!item)
return 0;
- (void) manager_etc_hosts_read(m);
+ /* We have an address in /etc/hosts that matches the queried name. Let's return successful. Actual data
+ * we'll only return if the request was for PTR. */
- name = dns_question_first_name(q);
- if (!name)
- return 0;
+ DNS_QUESTION_FOREACH(t, q) {
+ if (!IN_SET(t->type, DNS_TYPE_PTR, DNS_TYPE_ANY))
+ continue;
+ if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY))
+ continue;
- r = dns_name_address(name, &k.family, &k.address);
- if (r > 0) {
- EtcHostsItem *item;
- DnsResourceKey *found_ptr = NULL;
+ r = dns_name_equal(dns_resource_key_name(t), name);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ found_ptr = t;
+ break;
+ }
+ }
- item = hashmap_get(m->etc_hosts.by_address, &k);
- if (!item)
- return 0;
+ if (found_ptr) {
+ const char *n;
+
+ r = dns_answer_reserve(answer, set_size(item->names));
+ if (r < 0)
+ return r;
- /* We have an address in /etc/hosts that matches the queried name. Let's return successful. Actual data
- * we'll only return if the request was for PTR. */
+ SET_FOREACH(n, item->names) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
- DNS_QUESTION_FOREACH(t, q) {
- if (!IN_SET(t->type, DNS_TYPE_PTR, DNS_TYPE_ANY))
- continue;
- if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY))
- continue;
+ rr = dns_resource_record_new(found_ptr);
+ if (!rr)
+ return -ENOMEM;
- r = dns_name_equal(dns_resource_key_name(t), name);
- if (r < 0)
- return r;
- if (r > 0) {
- found_ptr = t;
- break;
- }
- }
+ rr->ptr.name = strdup(n);
+ if (!rr->ptr.name)
+ return -ENOMEM;
- if (found_ptr) {
- r = dns_answer_reserve(answer, item->n_names);
+ r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED, NULL);
if (r < 0)
return r;
+ }
+ }
- STRV_FOREACH(n, item->names) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
- rr = dns_resource_record_new(found_ptr);
- if (!rr)
- return -ENOMEM;
+ return 1;
+}
- rr->ptr.name = strdup(*n);
- if (!rr->ptr.name)
- return -ENOMEM;
+static int etc_hosts_lookup_by_name(
+ EtcHosts *hosts,
+ DnsQuestion *q,
+ const char *name,
+ DnsAnswer **answer) {
- r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED, NULL);
- if (r < 0)
- return r;
- }
- }
+ bool found_a = false, found_aaaa = false;
+ const struct in_addr_data *a;
+ EtcHostsItemByName *item;
+ DnsResourceKey *t;
+ int r;
- return 1;
- }
+ assert(hosts);
+ assert(q);
+ assert(name);
+ assert(answer);
- bn = hashmap_get(m->etc_hosts.by_name, name);
- if (bn) {
- r = dns_answer_reserve(answer, bn->n_addresses);
+ item = hashmap_get(hosts->by_name, name);
+ if (item) {
+ r = dns_answer_reserve(answer, set_size(item->addresses));
if (r < 0)
return r;
} else {
/* Check if name was listed with no address. If yes, continue to return an answer. */
- if (!set_contains(m->etc_hosts.no_address, name))
+ if (!set_contains(hosts->no_address, name))
return 0;
}
break;
}
- for (i = 0; bn && i < bn->n_addresses; i++) {
+ SET_FOREACH(a, item ? item->addresses : NULL) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
- if ((!found_a && bn->addresses[i]->family == AF_INET) ||
- (!found_aaaa && bn->addresses[i]->family == AF_INET6))
+ if ((!found_a && a->family == AF_INET) ||
+ (!found_aaaa && a->family == AF_INET6))
continue;
- r = dns_resource_record_new_address(&rr, bn->addresses[i]->family, &bn->addresses[i]->address, bn->name);
+ r = dns_resource_record_new_address(&rr, a->family, &a->address, item->name);
if (r < 0)
return r;
return found_a || found_aaaa;
}
+
+int manager_etc_hosts_lookup(Manager *m, DnsQuestion *q, DnsAnswer **answer) {
+ struct in_addr_data k;
+ const char *name;
+
+ assert(m);
+ assert(q);
+ assert(answer);
+
+ if (!m->read_etc_hosts)
+ return 0;
+
+ (void) manager_etc_hosts_read(m);
+
+ name = dns_question_first_name(q);
+ if (!name)
+ return 0;
+
+ if (dns_name_address(name, &k.family, &k.address) > 0)
+ return etc_hosts_lookup_by_address(&m->etc_hosts, q, name, &k, answer);
+
+ return etc_hosts_lookup_by_name(&m->etc_hosts, q, name, answer);
+}
#include "resolved-dns-question.h"
#include "resolved-dns-answer.h"
-typedef struct EtcHostsItem {
+typedef struct EtcHostsItemByAddress {
struct in_addr_data address;
-
- char **names;
- size_t n_names;
-} EtcHostsItem;
+ Set *names;
+} EtcHostsItemByAddress;
typedef struct EtcHostsItemByName {
char *name;
-
- struct in_addr_data **addresses;
- size_t n_addresses;
+ Set *addresses;
} EtcHostsItemByName;
int etc_hosts_parse(EtcHosts *hosts, FILE *f);
-void etc_hosts_free(EtcHosts *hosts);
+void etc_hosts_clear(EtcHosts *hosts);
void manager_etc_hosts_flush(Manager *m);
int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer);
return;
}
- _cleanup_(etc_hosts_free) EtcHosts hosts = {};
+ _cleanup_(etc_hosts_clear) EtcHosts hosts = {};
assert_se(etc_hosts_parse(&hosts, f) == 0);
}
-#define address_equal_4(_addr, _address) \
- ((_addr)->family == AF_INET && \
- !memcmp(&(_addr)->address.in, &(struct in_addr) { .s_addr = (_address) }, 4))
+#define has_4(_set, _address_str) \
+ set_contains(_set, &(struct in_addr_data) { .family = AF_INET, .address.in = { .s_addr = inet_addr(_address_str) } })
-#define address_equal_6(_addr, ...) \
- ((_addr)->family == AF_INET6 && \
- !memcmp(&(_addr)->address.in6, &(struct in6_addr) { .s6_addr = __VA_ARGS__}, 16) )
+#define has_6(_set, ...) \
+ set_contains(_set, &(struct in_addr_data) { .family = AF_INET6, .address.in6 = { .s6_addr = __VA_ARGS__ } })
TEST(parse_etc_hosts) {
_cleanup_(unlink_tempfilep) char
assert_se(fflush_and_check(f) >= 0);
rewind(f);
- _cleanup_(etc_hosts_free) EtcHosts hosts = {};
+ _cleanup_(etc_hosts_clear) EtcHosts hosts = {};
assert_se(etc_hosts_parse(&hosts, f) == 0);
EtcHostsItemByName *bn;
assert_se(bn = hashmap_get(hosts.by_name, "some.where"));
- assert_se(bn->n_addresses == 3);
- assert_se(MALLOC_ELEMENTSOF(bn->addresses) >= 3);
- assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.4")));
- assert_se(address_equal_4(bn->addresses[1], inet_addr("1.2.3.5")));
- assert_se(address_equal_6(bn->addresses[2], {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5}));
+ assert_se(set_size(bn->addresses) == 3);
+ assert_se(has_4(bn->addresses, "1.2.3.4"));
+ assert_se(has_4(bn->addresses, "1.2.3.5"));
+ assert_se(has_6(bn->addresses, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5}));
assert_se(bn = hashmap_get(hosts.by_name, "dash"));
- assert_se(bn->n_addresses == 1);
- assert_se(MALLOC_ELEMENTSOF(bn->addresses) >= 1);
- assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.6")));
+ assert_se(set_size(bn->addresses) == 1);
+ assert_se(has_4(bn->addresses, "1.2.3.6"));
assert_se(bn = hashmap_get(hosts.by_name, "dash-dash.where-dash"));
- assert_se(bn->n_addresses == 1);
- assert_se(MALLOC_ELEMENTSOF(bn->addresses) >= 1);
- assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.6")));
+ assert_se(set_size(bn->addresses) == 1);
+ assert_se(has_4(bn->addresses, "1.2.3.6"));
/* See https://tools.ietf.org/html/rfc1035#section-2.3.1 */
FOREACH_STRING(s, "bad-dash-", "-bad-dash", "-bad-dash.bad-")
assert_se(!hashmap_get(hosts.by_name, s));
assert_se(bn = hashmap_get(hosts.by_name, "before.comment"));
- assert_se(bn->n_addresses == 4);
- assert_se(MALLOC_ELEMENTSOF(bn->addresses) >= 4);
- assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.9")));
- assert_se(address_equal_4(bn->addresses[1], inet_addr("1.2.3.10")));
- assert_se(address_equal_4(bn->addresses[2], inet_addr("1.2.3.11")));
- assert_se(address_equal_4(bn->addresses[3], inet_addr("1.2.3.12")));
+ assert_se(set_size(bn->addresses) == 4);
+ assert_se(has_4(bn->addresses, "1.2.3.9"));
+ assert_se(has_4(bn->addresses, "1.2.3.10"));
+ assert_se(has_4(bn->addresses, "1.2.3.11"));
+ assert_se(has_4(bn->addresses, "1.2.3.12"));
assert_se(!hashmap_get(hosts.by_name, "within.comment"));
assert_se(!hashmap_get(hosts.by_name, "within.comment2"));
assert_se(!set_contains(hosts.no_address, "multi.colon"));
assert_se(bn = hashmap_get(hosts.by_name, "some.other"));
- assert_se(bn->n_addresses == 1);
- assert_se(MALLOC_ELEMENTSOF(bn->addresses) >= 1);
- assert_se(address_equal_6(bn->addresses[0], {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5}));
+ assert_se(set_size(bn->addresses) == 1);
+ assert_se(has_6(bn->addresses, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5}));
assert_se( set_contains(hosts.no_address, "some.where"));
assert_se( set_contains(hosts.no_address, "some.other"));
}
static void test_parse_file_one(const char *fname) {
- _cleanup_(etc_hosts_free) EtcHosts hosts = {};
+ _cleanup_(etc_hosts_clear) EtcHosts hosts = {};
_cleanup_fclose_ FILE *f;
log_info("/* %s(\"%s\") */", __func__, fname);
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.
return r;
sa_len = r;
- RUN_WITH_UMASK(0177)
+ WITH_UMASK(0177)
if (bind(fd, &sa.sa, sa_len) < 0)
return -errno;
continue;
}
- RUN_WITH_UMASK(0000)
+ WITH_UMASK(0000)
r = mkdirat(fd, table[i].dir, table[i].mode);
if (r < 0) {
log_full_errno(IN_SET(errno, EEXIST, EROFS) || table[i].ignore_failure ? LOG_DEBUG : LOG_ERR, errno,
#include <sys/vfs.h>
#include "binfmt-util.h"
+#include "errno-util.h"
+#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
#include "missing_magic.h"
#include "stat-util.h"
+int binfmt_mounted(void) {
+ _cleanup_close_ int fd = -EBADF;
+ int r;
+
+ fd = RET_NERRNO(open("/proc/sys/fs/binfmt_misc", O_CLOEXEC | O_DIRECTORY | O_PATH));
+ if (fd == -ENOENT)
+ return false;
+ if (fd < 0)
+ return fd;
+
+ r = fd_is_fs_type(fd, BINFMTFS_MAGIC);
+ if (r <= 0)
+ return r;
+
+ return access_fd(fd, W_OK) >= 0;
+}
+
int disable_binfmt(void) {
int r;
* We are a bit careful here, since binfmt_misc might still be an autofs which we don't want to
* trigger. */
- r = path_is_fs_type("/proc/sys/fs/binfmt_misc", BINFMTFS_MAGIC);
- if (r == 0 || r == -ENOENT) {
- log_debug("binfmt_misc is not mounted, not detaching entries.");
- return 0;
- }
+ r = binfmt_mounted();
if (r < 0)
return log_warning_errno(r, "Failed to determine whether binfmt_misc is mounted: %m");
+ if (r == 0) {
+ log_debug("binfmt_misc is not mounted in read-write mode, not detaching entries.");
+ return 0;
+ }
r = write_string_file("/proc/sys/fs/binfmt_misc/status", "-1", WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+int binfmt_mounted(void);
int disable_binfmt(void);
if (id[0] == '@') {
if (!strcaseeq(id, "@saved"))
return -1;
+ if (!config->entry_selected)
+ return -1;
id = config->entry_selected;
}
assert(config);
if (!FLAGS_SET(json_format, JSON_FORMAT_OFF)) {
+ _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
+
for (size_t i = 0; i < config->n_entries; i++) {
_cleanup_free_ char *opts = NULL;
const BootEntry *e = config->entries + i;
if (r < 0)
return log_oom();
- json_variant_dump(v, json_format, stdout, NULL);
+ r = json_variant_append_array(&array, v);
+ if (r < 0)
+ return log_oom();
}
+ json_variant_dump(array, json_format | JSON_FORMAT_EMPTY_ARRAY, NULL, NULL);
+
} else {
for (size_t n = 0; n < config->n_entries; n++) {
r = show_boot_entry(
}
finish:
- if (!found)
- return -ENODATA;
-
- return 0;
+ return found ? 0 : -ENODATA;
}
int btrfs_qgroup_get_quota_fd(int fd, uint64_t qgroupid, BtrfsQuotaInfo *ret) {
if (r < 0)
return r;
- RUN_WITH_UMASK(0000) {
+ WITH_UMASK(0000) {
if (copy_flags & COPY_MAC_CREATE) {
r = mac_selinux_create_file_prepare(to, S_IFREG);
if (r < 0)
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#if HAVE_LINUX_MEMFD_H
+#include <linux/memfd.h>
+#endif
#include "alloc-util.h"
#include "copy.h"
#include "fs-util.h"
#include "io-util.h"
#include "memfd-util.h"
+#include "missing_mman.h"
+#include "missing_syscall.h"
#include "tmpfile-util.h"
/* When the data is smaller or equal to 64K, try to place the copy in a memfd/pipe */
return fd_reopen(tmp_fd, O_RDONLY|O_CLOEXEC);
}
+
+int memfd_clone_fd(int fd, const char *name, int mode) {
+ _cleanup_close_ int mfd = -EBADF;
+ bool ro;
+ int r;
+
+ /* Creates a clone of a regular file in a memfd. Unlike copy_data_fd() this returns strictly a memfd
+ * (and if it can't it will fail). Thus the resulting fd is seekable, and definitely reports as
+ * S_ISREG. */
+
+ assert(fd >= 0);
+ assert(name);
+ assert(IN_SET(mode & O_ACCMODE, O_RDONLY, O_RDWR));
+ assert((mode & ~(O_RDONLY|O_RDWR|O_CLOEXEC)) == 0);
+
+ ro = (mode & O_ACCMODE) == O_RDONLY;
+
+ mfd = memfd_create(name,
+ ((FLAGS_SET(mode, O_CLOEXEC) || ro) ? MFD_CLOEXEC : 0) |
+ (ro ? MFD_ALLOW_SEALING : 0));
+ if (mfd < 0)
+ return -errno;
+
+ r = copy_bytes(fd, mfd, UINT64_MAX, COPY_REFLINK);
+ if (r < 0)
+ return r;
+
+ if (ro) {
+ _cleanup_close_ int rfd = -1;
+
+ r = memfd_set_sealed(mfd);
+ if (r < 0)
+ return r;
+
+ rfd = fd_reopen(mfd, mode);
+ if (rfd < 0)
+ return rfd;
+
+ return TAKE_FD(rfd);
+ }
+
+ off_t f = lseek(mfd, 0, SEEK_SET);
+ if (f < 0)
+ return -errno;
+
+ return TAKE_FD(mfd);
+}
int acquire_data_fd(const void *data, size_t size, unsigned flags);
int copy_data_fd(int fd);
+int memfd_clone_fd(int fd, const char *name, int mode);
static int image_new(
ImageType t,
+ ImageClass c,
const char *pretty,
const char *path,
const char *filename,
*i = (Image) {
.n_ref = 1,
.type = t,
+ .class = c,
.read_only = read_only,
.crtime = crtime,
.mtime = mtime,
}
static int image_make(
+ ImageClass c,
const char *pretty,
int dfd,
const char *path,
return r;
r = image_new(IMAGE_SUBVOLUME,
+ c,
pretty,
path,
filename,
/* It's just a normal directory. */
r = image_new(IMAGE_DIRECTORY,
+ c,
pretty,
path,
filename,
}
r = image_new(IMAGE_RAW,
+ c,
pretty,
path,
filename,
}
r = image_new(IMAGE_BLOCK,
+ c,
pretty,
path,
filename,
if (!S_ISREG(st.st_mode))
continue;
- r = image_make(name, dirfd(d), resolved, raw, &st, ret);
+ r = image_make(class, name, dirfd(d), resolved, raw, &st, ret);
} else {
if (!S_ISDIR(st.st_mode) && !S_ISBLK(st.st_mode))
continue;
- r = image_make(name, dirfd(d), resolved, name, &st, ret);
+ r = image_make(class, name, dirfd(d), resolved, name, &st, ret);
}
if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
continue;
}
if (class == IMAGE_MACHINE && streq(name, ".host")) {
- r = image_make(".host", AT_FDCWD, NULL, empty_to_root(root), NULL, ret);
+ r = image_make(class, ".host", AT_FDCWD, NULL, empty_to_root(root), NULL, ret);
if (r < 0)
return r;
* overridden by another, different image earlier in the search path */
if (path_equal(path, "/"))
- return image_make(".host", AT_FDCWD, NULL, "/", NULL, ret);
+ return image_make(IMAGE_MACHINE, ".host", AT_FDCWD, NULL, "/", NULL, ret);
- return image_make(NULL, AT_FDCWD, NULL, path, NULL, ret);
+ return image_make(_IMAGE_CLASS_INVALID, NULL, AT_FDCWD, NULL, path, NULL, ret);
}
int image_find_harder(ImageClass class, const char *name_or_path, const char *root, Image **ret) {
if (hashmap_contains(h, pretty))
continue;
- r = image_make(pretty, dirfd(d), resolved, de->d_name, &st, &image);
+ r = image_make(class, pretty, dirfd(d), resolved, de->d_name, &st, &image);
if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
continue;
if (r < 0)
if (class == IMAGE_MACHINE && !hashmap_contains(h, ".host")) {
_cleanup_(image_unrefp) Image *image = NULL;
- r = image_make(".host", AT_FDCWD, NULL, empty_to_root("/"), NULL, &image);
+ r = image_make(IMAGE_MACHINE, ".host", AT_FDCWD, NULL, empty_to_root("/"), NULL, &image);
if (r < 0)
return r;
if (fd < 0)
log_debug_errno(errno, "Failed to open %s: %m", path);
else {
- r = id128_read_fd(fd, ID128_PLAIN, &machine_id);
+ r = id128_read_fd(fd, ID128_FORMAT_PLAIN, &machine_id);
if (r < 0)
log_debug_errno(r, "Image %s contains invalid machine ID.", i->name);
}
};
DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType);
+
+static const char* const image_class_table[_IMAGE_CLASS_MAX] = {
+ [IMAGE_MACHINE] = "machine",
+ [IMAGE_PORTABLE] = "portable",
+ [IMAGE_EXTENSION] = "extension",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(image_class, ImageClass);
unsigned n_ref;
ImageType type;
+ ImageClass class;
char *name;
char *path;
bool read_only;
const char* image_type_to_string(ImageType t) _const_;
ImageType image_type_from_string(const char *s) _pure_;
+const char* image_class_to_string(ImageClass cl) _const_;
+ImageClass image_class_from_string(const char *s) _pure_;
+
int image_path_lock(const char *path, int operation, LockFile *global, LockFile *local);
int image_name_lock(const char *name, int operation, LockFile *ret);
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;
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;
}
}
.mount_node_fd = TAKE_FD(mount_node_fd),
.offset = (uint64_t) start * 512,
.size = (uint64_t) size * 512,
+ .gpt_flags = pflags,
};
}
int mount_node_fd;
uint64_t size;
uint64_t offset;
+ uint64_t gpt_flags;
};
#define DISSECTED_PARTITION_NULL \
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);
}
}
-DEFINE_HASH_OPS(dns_name_hash_ops, char, dns_name_hash_func, dns_name_compare_func);
+DEFINE_HASH_OPS(
+ dns_name_hash_ops,
+ char,
+ dns_name_hash_func,
+ dns_name_compare_func);
+
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+ dns_name_hash_ops_free,
+ char,
+ dns_name_hash_func,
+ dns_name_compare_func,
+ free);
int dns_name_equal(const char *x, const char *y) {
int r, q;
void dns_name_hash_func(const char *s, struct siphash *state);
int dns_name_compare_func(const char *a, const char *b);
extern const struct hash_ops dns_name_hash_ops;
+extern const struct hash_ops dns_name_hash_ops_free;
int dns_name_between(const char *a, const char *b, const char *c);
int dns_name_equal(const char *x, const char *y);
#include "dlfcn-util.h"
#include "elf-util.h"
#include "errno-util.h"
+#include "escape.h"
#include "fileio.h"
#include "fd-util.h"
#include "format-util.h"
}
r = json_parse(payload, 0, &v, NULL, NULL);
- if (r < 0)
- return log_error_errno(r, "json_parse on %s failed: %m", payload);
+ if (r < 0) {
+ _cleanup_free_ char *esc = cescape(payload);
+ return log_error_errno(r, "json_parse on \"%s\" failed: %m", strnull(esc));
+ }
/* If we have a build-id, merge it in the same JSON object so that it appears all
* nicely together in the logs/metadata. */
}
}
+PartitionDesignator partition_verity_to_data(PartitionDesignator d) {
+ switch (d) {
+
+ case PARTITION_ROOT_VERITY:
+ return PARTITION_ROOT;
+
+ case PARTITION_USR_VERITY:
+ return PARTITION_USR;
+
+ default:
+ return _PARTITION_DESIGNATOR_INVALID;
+ }
+}
+
+PartitionDesignator partition_verity_sig_to_data(PartitionDesignator d) {
+ switch (d) {
+
+ case PARTITION_ROOT_VERITY_SIG:
+ return PARTITION_ROOT;
+
+ case PARTITION_USR_VERITY_SIG:
+ return PARTITION_USR;
+
+ default:
+ return _PARTITION_DESIGNATOR_INVALID;
+ }
+}
static const char *const partition_designator_table[_PARTITION_DESIGNATOR_MAX] = {
[PARTITION_ROOT] = "root",
PartitionDesignator partition_verity_of(PartitionDesignator p);
PartitionDesignator partition_verity_sig_of(PartitionDesignator p);
+PartitionDesignator partition_verity_to_data(PartitionDesignator d);
+PartitionDesignator partition_verity_sig_to_data(PartitionDesignator d);
const char* partition_designator_to_string(PartitionDesignator d) _const_;
PartitionDesignator partition_designator_from_string(const char *name) _pure_;
_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,
v->source = json_source_ref(from->source);
}
+static int _json_variant_array_put_element(JsonVariant *array, JsonVariant *element) {
+ assert(array);
+ JsonVariant *w = array + 1 + array->n_elements;
+
+ uint16_t d = json_variant_depth(element);
+ if (d >= DEPTH_MAX) /* Refuse too deep nesting */
+ return -ELNRNG;
+ if (d >= array->depth)
+ array->depth = d + 1;
+ array->n_elements ++;
+
+ *w = (JsonVariant) {
+ .is_embedded = true,
+ .parent = array,
+ };
+
+ json_variant_set(w, element);
+ json_variant_copy_source(w, element);
+
+ if (!json_variant_is_normalized(element))
+ array->normalized = false;
+
+ return 0;
+}
+
int json_variant_new_array(JsonVariant **ret, JsonVariant **array, size_t n) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
- bool normalized = true;
+ int r;
assert_return(ret, -EINVAL);
if (n == 0) {
*v = (JsonVariant) {
.n_ref = 1,
.type = JSON_VARIANT_ARRAY,
+ .normalized = true,
};
- for (v->n_elements = 0; v->n_elements < n; v->n_elements++) {
- JsonVariant *w = v + 1 + v->n_elements,
- *c = array[v->n_elements];
- uint16_t d;
-
- d = json_variant_depth(c);
- if (d >= DEPTH_MAX) /* Refuse too deep nesting */
- return -ELNRNG;
- if (d >= v->depth)
- v->depth = d + 1;
-
- *w = (JsonVariant) {
- .is_embedded = true,
- .parent = v,
- };
-
- json_variant_set(w, c);
- json_variant_copy_source(w, c);
-
- if (!json_variant_is_normalized(c))
- normalized = false;
+ while (v->n_elements < n) {
+ r = _json_variant_array_put_element(v, array[v->n_elements]);
+ if (r < 0)
+ return r;
}
- v->normalized = normalized;
-
*ret = TAKE_PTR(v);
return 0;
}
explicit_bzero_safe(v, json_variant_size(v));
}
+static unsigned json_variant_n_ref(const JsonVariant *v) {
+ /* Return the number of references to v.
+ * 0 => NULL or not a regular object or embedded.
+ * >0 => number of references
+ */
+
+ if (!v || !json_variant_is_regular(v) || v->is_embedded)
+ return 0;
+
+ assert(v->n_ref > 0);
+ return v->n_ref;
+}
+
JsonVariant *json_variant_ref(JsonVariant *v) {
if (!v)
return NULL;
}
int json_variant_dump(JsonVariant *v, JsonFormatFlags flags, FILE *f, const char *prefix) {
- if (!v)
- return 0;
+ if (!v) {
+ if (flags & JSON_FORMAT_EMPTY_ARRAY)
+ v = JSON_VARIANT_MAGIC_EMPTY_ARRAY;
+ else
+ return 0;
+ }
if (!f)
f = stdout;
if (!*v || json_variant_is_null(*v))
blank = true;
- else if (!json_variant_is_array(*v))
- return -EINVAL;
- else
+ else if (json_variant_is_array(*v))
blank = json_variant_elements(*v) == 0;
+ else
+ return -EINVAL;
- if (blank)
+ if (blank) {
r = json_variant_new_array(&nv, (JsonVariant*[]) { element }, 1);
- else {
- _cleanup_free_ JsonVariant **array = new(JsonVariant*, json_variant_elements(*v) + 1);
+ if (r < 0)
+ return r;
+ } else if (json_variant_n_ref(*v) == 1) {
+ /* Let's bump the reference count on element. We can't do the realloc if we're appending *v
+ * to itself, or one of the objects embedded in *v to *v. If the reference count grows, we
+ * need to fall back to the other method below. */
+
+ _unused_ _cleanup_(json_variant_unrefp) JsonVariant *dummy = json_variant_ref(element);
+ if (json_variant_n_ref(*v) == 1) {
+ /* We hold the only reference. Let's mutate the object. */
+ size_t size = json_variant_elements(*v);
+ void *old = *v;
+
+ if (!GREEDY_REALLOC(*v, size + 1 + 1))
+ return -ENOMEM;
+
+ if (old != *v)
+ /* Readjust the parent pointers to the new address */
+ for (size_t i = 1; i < size; i++)
+ (*v)[1 + i].parent = *v;
+
+ return _json_variant_array_put_element(*v, element);
+ }
+ }
+
+ if (!blank) {
+ size_t size = json_variant_elements(*v);
+
+ _cleanup_free_ JsonVariant **array = new(JsonVariant*, size + 1);
if (!array)
return -ENOMEM;
- size_t size = json_variant_elements(*v);
for (size_t i = 0; i < size; i++)
array[i] = json_variant_by_index(*v, i);
array[size] = element;
r = json_variant_new_array(&nv, array, size + 1);
+ if (r < 0)
+ return r;
}
- if (r < 0)
- return r;
json_variant_propagate_sensitive(*v, nv);
JSON_VARIANT_REPLACE(*v, TAKE_PTR(nv));
return r;
}
-int json_parse(const char *input, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) {
- return json_parse_internal(&input, NULL, flags, ret, ret_line, ret_column, false);
+int json_parse_with_source(
+ const char *input,
+ const char *source,
+ JsonParseFlags flags,
+ JsonVariant **ret,
+ unsigned *ret_line,
+ unsigned *ret_column) {
+
+ _cleanup_(json_source_unrefp) JsonSource *s = NULL;
+
+ if (source) {
+ s = json_source_new(source);
+ if (!s)
+ return -ENOMEM;
+ }
+
+ return json_parse_internal(&input, s, flags, ret, ret_line, ret_column, false);
}
-int json_parse_continue(const char **p, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) {
- return json_parse_internal(p, NULL, flags, ret, ret_line, ret_column, true);
+int json_parse_with_source_continue(
+ const char **p,
+ const char *source,
+ JsonParseFlags flags,
+ JsonVariant **ret,
+ unsigned *ret_line,
+ unsigned *ret_column) {
+
+ _cleanup_(json_source_unrefp) JsonSource *s = NULL;
+
+ if (source) {
+ s = json_source_new(source);
+ if (!s)
+ return -ENOMEM;
+ }
+
+ return json_parse_internal(p, s, flags, ret, ret_line, ret_column, true);
}
-int json_parse_file_at(FILE *f, int dir_fd, const char *path, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) {
- _cleanup_(json_source_unrefp) JsonSource *source = NULL;
+int json_parse_file_at(
+ FILE *f,
+ int dir_fd,
+ const char *path,
+ JsonParseFlags flags,
+ JsonVariant **ret,
+ unsigned *ret_line,
+ unsigned *ret_column) {
+
_cleanup_free_ char *text = NULL;
int r;
if (isempty(text))
return -ENODATA;
- if (path) {
- source = json_source_new(path);
- if (!source)
- return -ENOMEM;
- }
-
- const char *p = text;
- return json_parse_internal(&p, source, flags, ret, ret_line, ret_column, false);
+ return json_parse_with_source(text, path, flags, ret, ret_line, ret_column);
}
int json_buildv(JsonVariant **ret, va_list ap) {
JSON_FORMAT_SSE = 1 << 6, /* prefix/suffix with W3C server-sent events */
JSON_FORMAT_SEQ = 1 << 7, /* prefix/suffix with RFC 7464 application/json-seq */
JSON_FORMAT_FLUSH = 1 << 8, /* call fflush() after dumping JSON */
- JSON_FORMAT_OFF = 1 << 9, /* make json_variant_format() fail with -ENOEXEC */
+ JSON_FORMAT_EMPTY_ARRAY = 1 << 9, /* output "[]" for empty input */
+ JSON_FORMAT_OFF = 1 << 10, /* make json_variant_format() fail with -ENOEXEC */
} JsonFormatFlags;
int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret);
JSON_PARSE_SENSITIVE = 1 << 0, /* mark variant as "sensitive", i.e. something containing secret key material or such */
} JsonParseFlags;
-int json_parse(const char *string, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
-int json_parse_continue(const char **p, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
+int json_parse_with_source(const char *string, const char *source, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
+int json_parse_with_source_continue(const char **p, const char *source, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
+
+static inline int json_parse(const char *string, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) {
+ return json_parse_with_source(string, NULL, flags, ret, ret_line, ret_column);
+}
+static inline int json_parse_continue(const char **p, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) {
+ return json_parse_with_source_continue(p, NULL, flags, ret, ret_line, ret_column);
+}
+
int json_parse_file_at(FILE *f, int dir_fd, const char *path, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column);
static inline int json_parse_file(FILE *f, const char *path, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) {
const char *rp_id,
const void *cid,
size_t cid_size,
- char **pins,
Fido2EnrollFlags flags) {
assert(path);
"Failed to open FIDO2 device %s: %s", path, sym_fido_strerr(r));
r = verify_features(d, path, LOG_ERR, NULL, NULL, &has_up, &has_uv);
+ if (r == -ENODEV) { /* Not a FIDO2 device or lacking HMAC-SECRET extension */
+ log_debug_errno(r, "%s is not a FIDO2 device, or it lacks the hmac-secret extension", path);
+ return false;
+ }
if (r < 0)
return r;
if (r < 0)
return r;
+ /* FIDO2 devices may not support pre-flight requests with UV, at least not
+ * without user interaction [1]. As a result, let's just return true
+ * here and go ahead with trying the unlock directly.
+ * Reference:
+ * 1: https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#sctn-getAssert-authnr-alg
+ * See section 7.4 */
+ if (has_uv && FLAGS_SET(flags, FIDO2ENROLL_UV)) {
+ log_debug("Pre-flight requests with UV are unsupported, device: %s", path);
+ return true;
+ }
+
/* According to CTAP 2.1 specification, to do pre-flight we need to set up option to false
* with optionally pinUvAuthParam in assertion[1]. But for authenticator that doesn't support
* user presence, once up option is present, the authenticator may return CTAP2_ERR_UNSUPPORTED_OPTION[2].
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to set assertion user presence: %s", sym_fido_strerr(r));
-
- /* According to CTAP 2.1 specification, if the credential is marked as userVerificationRequired,
- * we may pass pinUvAuthParam parameter or set uv option to true in order to check whether the
- * credential is in the token. Here we choose to use PIN (pinUvAuthParam) to achieve this.
- * If we don't do that, the authenticator will remove the credential from the applicable
- * credentials list, hence CTAP2_ERR_NO_CREDENTIALS error will be returned.
- * Please see
- * https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#sctn-getAssert-authnr-alg
- * step 7.4 for detailed information.
- */
- if (FLAGS_SET(flags, FIDO2ENROLL_UV) && has_uv) {
- if (strv_isempty(pins))
- r = FIDO_ERR_PIN_REQUIRED;
- else
- STRV_FOREACH(i, pins) {
- r = sym_fido_dev_get_assert(d, a, *i);
- if (r != FIDO_ERR_PIN_INVALID)
- break;
- }
-
- } else
- r = sym_fido_dev_get_assert(d, a, NULL);
+ r = sym_fido_dev_get_assert(d, a, NULL);
switch (r) {
case FIDO_OK:
if (r < 0)
return log_error_errno(r, "FIDO2 support is not installed.");
- if (device)
+ if (device) {
+ r = fido2_is_cred_in_specific_token(device, rp_id, cid, cid_size, required);
+ if (r == 0)
+ /* The caller is expected to attempt other key slots in this case,
+ * therefore, do not spam the console with error logs here. */
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADSLT),
+ "The credential is not in the token %s.", device);
+ if (r < 0)
+ return log_error_errno(r, "Token returned error during pre-flight: %m");
+
return fido2_use_hmac_hash_specific_token(device, rp_id, salt, salt_size, cid, cid_size, pins, required, ret_hmac, ret_hmac_size);
+ }
di = sym_fido_dev_info_new(allocated);
if (!di)
goto finish;
}
- r = fido2_is_cred_in_specific_token(path, rp_id, cid, cid_size, pins, required);
- /* We handle PIN related errors here since we have used PIN in the check.
- * If PIN error happens, we can avoid pin retries decreased the second time. */
- if (IN_SET(r, -ENOANO, /* → pin required */
- -ENOLCK, /* → pin incorrect */
- -EOWNERDEAD)) { /* → uv blocked */
- /* If it's not the last device, try next one */
- if (i < found - 1)
- continue;
-
- /* -EOWNERDEAD is returned when uv is blocked. Map it to EAGAIN so users can reinsert
- * the token and try again. */
- if (r == -EOWNERDEAD)
- r = -EAGAIN;
+ r = fido2_is_cred_in_specific_token(path, rp_id, cid, cid_size, required);
+ if (r < 0) {
+ log_error_errno(r, "Token returned error during pre-flight: %m");
goto finish;
- } else if (r == -ENODEV) /* not a FIDO2 device or lacking HMAC-SECRET extension */
- continue;
- else if (r < 0)
- log_error_errno(r, "Failed to determine whether the credential is in the token, trying anyway: %m");
- else if (r == 0) {
+ }
+ if (r == 0) {
log_debug("The credential is not in the token %s, skipping.", path);
continue;
}
#include "alloc-util.h"
#include "blockdev-util.h"
+#include "data-fd-util.h"
#include "device-util.h"
#include "devnum-util.h"
#include "env-util.h"
return loop_device_make_internal(path, fd, open_flags, 0, 0, 0, loop_flags, lock_op, ret);
}
+int loop_device_make_by_path_memory(
+ const char *path,
+ int open_flags,
+ uint32_t loop_flags,
+ int lock_op,
+ LoopDevice **ret) {
+
+ _cleanup_close_ int fd = -EBADF, mfd = -EBADF;
+ _cleanup_free_ char *fn = NULL;
+ struct stat st;
+ int r;
+
+ assert(path);
+ assert(IN_SET(open_flags, O_RDWR, O_RDONLY));
+ assert(ret);
+
+ loop_flags &= ~LO_FLAGS_DIRECT_IO; /* memfds don't support O_DIRECT, hence LO_FLAGS_DIRECT_IO can't be used either */
+
+ fd = open(path, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|O_RDONLY);
+ if (fd < 0)
+ return -errno;
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISREG(st.st_mode) && !S_ISBLK(st.st_mode))
+ return -EBADF;
+
+ r = path_extract_filename(path, &fn);
+ if (r < 0)
+ return r;
+
+ mfd = memfd_clone_fd(fd, fn, open_flags|O_CLOEXEC);
+ if (mfd < 0)
+ return mfd;
+
+ fd = safe_close(fd); /* Let's close the original early */
+
+ return loop_device_make_internal(NULL, mfd, open_flags, 0, 0, 0, loop_flags, lock_op, ret);
+}
+
static LoopDevice* loop_device_free(LoopDevice *d) {
_cleanup_close_ int control = -1;
int r;
int loop_device_make(int fd, int open_flags, uint64_t offset, uint64_t size, uint32_t block_size, uint32_t loop_flags, int lock_op, LoopDevice **ret);
int loop_device_make_by_path(const char *path, int open_flags, uint32_t loop_flags, int lock_op, LoopDevice **ret);
+int loop_device_make_by_path_memory(const char *path, int open_flags, uint32_t loop_flags, int lock_op, LoopDevice **ret);
int loop_device_open(sd_device *dev, int open_flags, int lock_op, LoopDevice **ret);
int loop_device_open_from_fd(int fd, int open_flags, int lock_op, LoopDevice **ret);
int loop_device_open_from_path(const char *path, int open_flags, int lock_op, LoopDevice **ret);
dbus_machine_id = prefix_roota(root, "/var/lib/dbus/machine-id");
fd = open(dbus_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fd >= 0) {
- if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0) {
+ if (id128_read_fd(fd, ID128_FORMAT_PLAIN, ret) >= 0) {
log_info("Initializing machine ID from D-Bus machine ID.");
return 0;
}
etc_machine_id = prefix_roota(root, "/etc/machine-id");
- RUN_WITH_UMASK(0000) {
+ WITH_UMASK(0000) {
/* We create this 0444, to indicate that this isn't really
* something you should ever modify. Of course, since the file
* will be owned by root it doesn't matter much, but maybe
if (sd_id128_is_null(machine_id)) {
/* Try to read any existing machine ID */
- if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0)
+ if (id128_read_fd(fd, ID128_FORMAT_PLAIN, ret) >= 0)
return 0;
/* Hmm, so, the id currently stored is not useful, then let's generate one */
if (r < 0)
return log_error_errno(r, "Failed to sync %s: %m", etc_machine_id);
} else {
- r = id128_write_fd(fd, ID128_PLAIN, machine_id, true);
+ r = id128_write_fd(fd, ID128_FORMAT_PLAIN | ID128_SYNC_ON_WRITE, machine_id);
if (r < 0)
return log_error_errno(r, "Failed to write %s: %m", etc_machine_id);
else
run_machine_id = prefix_roota(root, "/run/machine-id");
- RUN_WITH_UMASK(0022)
- r = id128_write(run_machine_id, ID128_PLAIN, machine_id, false);
+ WITH_UMASK(0022)
+ r = id128_write(run_machine_id, ID128_FORMAT_PLAIN, machine_id);
if (r < 0) {
(void) unlink(run_machine_id);
return log_error_errno(r, "Cannot write %s: %m", run_machine_id);
"%s is not on a temporary file system.",
etc_machine_id);
- r = id128_read_fd(fd, ID128_PLAIN, &id);
+ r = id128_read_fd(fd, ID128_FORMAT_PLAIN, &id);
if (r < 0)
return log_error_errno(r, "We didn't find a valid machine ID in %s: %m", etc_machine_id);
return r;
/* Update a persistent version of etc_machine_id */
- r = id128_write(etc_machine_id, ID128_PLAIN, id, true);
+ r = id128_write(etc_machine_id, ID128_FORMAT_PLAIN | ID128_SYNC_ON_WRITE, id);
if (r < 0)
return log_error_errno(r, "Cannot write %s. This is mandatory to get a persistent machine ID: %m", etc_machine_id);
return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
}
-int setup_machine_directory(sd_bus_error *error) {
+int setup_machine_directory(sd_bus_error *error, bool use_btrfs_subvol, bool use_btrfs_quota) {
int r;
r = check_btrfs();
if (r == 0)
return 0;
+ if (!use_btrfs_subvol)
+ return 0;
+
(void) btrfs_subvol_make_label("/var/lib/machines");
+ if (!use_btrfs_quota)
+ return 0;
+
r = btrfs_quota_enable("/var/lib/machines", true);
if (r < 0)
log_warning_errno(r, "Failed to enable quota for /var/lib/machines, ignoring: %m");
if (r < 0)
log_warning_errno(r, "Failed to set up default quota hierarchy for /var/lib/machines, ignoring: %m");
- return 1;
+ return 0;
}
#include "sd-bus.h"
-int setup_machine_directory(sd_bus_error *error);
+int setup_machine_directory(sd_bus_error *error, bool use_btrfs_subvol, bool use_btrfs_quota);
return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mksquashfs binary not available.");
if (r < 0)
return log_error_errno(r, "Failed to determine whether mksquashfs binary exists: %m");
+
+ } else if (streq(fstype, "erofs")) {
+ r = find_executable("mkfs.erofs", &mkfs);
+ if (r == -ENOENT)
+ return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mkfs.erofs binary not available.");
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether mkfs.erofs binary exists: %m");
+
} else if (fstype_is_ro(fstype)) {
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Don't know how to create read-only file system '%s', refusing.",
root, node,
"-quiet",
"-noappend");
+
+ else if (streq(fstype, "erofs"))
+
+ argv = strv_new(mkfs,
+ "-U", vol_id,
+ node, root);
else
/* Generic fallback for all other file systems */
argv = strv_new(mkfs, node);
if (STR_IN_SET(fstype, "ext2", "ext3", "ext4", "btrfs", "f2fs", "xfs", "vfat", "swap"))
log_info("%s successfully formatted as %s (label \"%s\", uuid %s)",
node, fstype, label, vol_id);
+ else if (streq(fstype, "erofs"))
+ log_info("%s successfully formatted as %s (uuid %s, no label)",
+ node, fstype, vol_id);
else
log_info("%s successfully formatted as %s (no label or uuid specified)",
node, fstype);
#endif
static const MountPoint mount_table[] = {
- { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
NULL, MNT_FATAL|MNT_IN_CONTAINER|MNT_FOLLOW_SYMLINK },
- { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
NULL, MNT_FATAL|MNT_IN_CONTAINER },
- { "devtmpfs", "/dev", "devtmpfs", "mode=755" TMPFS_LIMITS_DEV, MS_NOSUID|MS_STRICTATIME,
+ { "devtmpfs", "/dev", "devtmpfs", "mode=0755" TMPFS_LIMITS_DEV, MS_NOSUID|MS_STRICTATIME,
NULL, MNT_FATAL|MNT_IN_CONTAINER },
- { "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ { "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
NULL, MNT_NONE },
#if ENABLE_SMACK
- { "smackfs", "/sys/fs/smackfs", "smackfs", "smackfsdef=*", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ { "smackfs", "/sys/fs/smackfs", "smackfs", "smackfsdef=*", MS_NOSUID|MS_NOEXEC|MS_NODEV,
mac_smack_use, MNT_FATAL },
- { "tmpfs", "/dev/shm", "tmpfs", "mode=1777,smackfsroot=*", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+ { "tmpfs", "/dev/shm", "tmpfs", "mode=01777,smackfsroot=*", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
mac_smack_use, MNT_FATAL },
#endif
- { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+ { "tmpfs", "/dev/shm", "tmpfs", "mode=01777", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
NULL, MNT_FATAL|MNT_IN_CONTAINER },
- { "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC,
+ { "devpts", "/dev/pts", "devpts", "mode=0620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC,
NULL, MNT_IN_CONTAINER },
#if ENABLE_SMACK
- { "tmpfs", "/run", "tmpfs", "mode=755,smackfsroot=*" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+ { "tmpfs", "/run", "tmpfs", "mode=0755,smackfsroot=*" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
mac_smack_use, MNT_FATAL },
#endif
- { "tmpfs", "/run", "tmpfs", "mode=755" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+ { "tmpfs", "/run", "tmpfs", "mode=0755" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
NULL, MNT_FATAL|MNT_IN_CONTAINER },
- { "cgroup2", "/sys/fs/cgroup", "cgroup2", "nsdelegate,memory_recursiveprot", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ { "cgroup2", "/sys/fs/cgroup", "cgroup2", "nsdelegate,memory_recursiveprot", MS_NOSUID|MS_NOEXEC|MS_NODEV,
cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
- { "cgroup2", "/sys/fs/cgroup", "cgroup2", "nsdelegate", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ { "cgroup2", "/sys/fs/cgroup", "cgroup2", "nsdelegate", MS_NOSUID|MS_NOEXEC|MS_NODEV,
cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
- { "cgroup2", "/sys/fs/cgroup", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ { "cgroup2", "/sys/fs/cgroup", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
- { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755" TMPFS_LIMITS_SYS_FS_CGROUP, MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
+ { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=0755" TMPFS_LIMITS_SYS_FS_CGROUP, MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
- { "cgroup2", "/sys/fs/cgroup/unified", "cgroup2", "nsdelegate", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ { "cgroup2", "/sys/fs/cgroup/unified", "cgroup2", "nsdelegate", MS_NOSUID|MS_NOEXEC|MS_NODEV,
cg_is_hybrid_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
- { "cgroup2", "/sys/fs/cgroup/unified", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ { "cgroup2", "/sys/fs/cgroup/unified", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
cg_is_hybrid_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
- { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV,
cg_is_legacy_wanted, MNT_IN_CONTAINER },
- { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV,
cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
- { "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+#if ENABLE_PSTORE
+ { "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
NULL, MNT_NONE },
+#endif
#if ENABLE_EFI
- { "efivarfs", "/sys/firmware/efi/efivars", "efivarfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ { "efivarfs", "/sys/firmware/efi/efivars", "efivarfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
is_efi_boot, MNT_NONE },
#endif
- { "bpf", "/sys/fs/bpf", "bpf", "mode=700", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ { "bpf", "/sys/fs/bpf", "bpf", "mode=0700", MS_NOSUID|MS_NOEXEC|MS_NODEV,
NULL, MNT_NONE, },
};
}
/* Now that we mounted everything, let's make the tmpfs the cgroup file systems are mounted into read-only. */
- (void) mount_nofollow("tmpfs", "/sys/fs/cgroup", "tmpfs", MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755" TMPFS_LIMITS_SYS_FS_CGROUP);
+ (void) mount_nofollow("tmpfs", "/sys/fs/cgroup", "tmpfs",
+ MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY,
+ "mode=0755" TMPFS_LIMITS_SYS_FS_CGROUP);
return 0;
}
#include "set.h"
#include "stat-util.h"
#include "stdio-util.h"
+#include "string-table.h"
#include "string-util.h"
#include "strv.h"
#include "tmpfile-util.h"
#include "user-util.h"
-int mount_fd(const char *source,
- int target_fd,
- const char *filesystemtype,
- unsigned long mountflags,
- const void *data) {
-
- if (mount(source, FORMAT_PROC_FD_PATH(target_fd), filesystemtype, mountflags, data) < 0) {
- if (errno != ENOENT)
- return -errno;
-
- /* ENOENT can mean two things: either that the source is missing, or that /proc/ isn't
- * mounted. Check for the latter to generate better error messages. */
- if (proc_mounted() == 0)
- return -ENOSYS;
-
- return -ENOENT;
- }
-
- return 0;
-}
-
-int mount_nofollow(
- const char *source,
- const char *target,
- const char *filesystemtype,
- unsigned long mountflags,
- const void *data) {
-
- _cleanup_close_ int fd = -1;
-
- /* In almost all cases we want to manipulate the mount table without following symlinks, hence
- * mount_nofollow() is usually the way to go. The only exceptions are environments where /proc/ is
- * not available yet, since we need /proc/self/fd/ for this logic to work. i.e. during the early
- * initialization of namespacing/container stuff where /proc is not yet mounted (and maybe even the
- * fs to mount) we can only use traditional mount() directly.
- *
- * Note that this disables following only for the final component of the target, i.e symlinks within
- * the path of the target are honoured, as are symlinks in the source path everywhere. */
-
- fd = open(target, O_PATH|O_CLOEXEC|O_NOFOLLOW);
- if (fd < 0)
- return -errno;
-
- return mount_fd(source, fd, filesystemtype, mountflags, data);
-}
-
int umount_recursive(const char *prefix, int flags) {
int n = 0, r;
bool again;
return 0;
}
-int mount_move_root(const char *path) {
- assert(path);
-
- if (chdir(path) < 0)
- return -errno;
-
- if (mount(path, "/", NULL, MS_MOVE, NULL) < 0)
- return -errno;
+static const char *const mount_attr_propagation_type_table[_MOUNT_ATTR_PROPAGATION_TYPE_MAX] = {
+ [MOUNT_ATTR_PROPAGATION_INHERIT] = "inherited",
+ [MOUNT_ATTR_PROPAGATION_PRIVATE] = "private",
+ [MOUNT_ATTR_PROPAGATION_DEPENDENT] = "dependent",
+ [MOUNT_ATTR_PROPAGATION_SHARED] = "shared",
+};
- if (chroot(".") < 0)
- return -errno;
+DEFINE_STRING_TABLE_LOOKUP(mount_attr_propagation_type, MountAttrPropagationType);
- return RET_NERRNO(chdir("/"));
+unsigned int mount_attr_propagation_type_to_flag(MountAttrPropagationType t) {
+ switch (t) {
+ case MOUNT_ATTR_PROPAGATION_INHERIT:
+ return 0;
+ case MOUNT_ATTR_PROPAGATION_PRIVATE:
+ return MS_PRIVATE;
+ case MOUNT_ATTR_PROPAGATION_DEPENDENT:
+ return MS_SLAVE;
+ case MOUNT_ATTR_PROPAGATION_SHARED:
+ return MS_SHARED;
+ default:
+ assert_not_reached();
+ }
}
-int mount_pivot_root(const char *path) {
- _cleanup_close_ int fd_oldroot = -EBADF, fd_newroot = -EBADF;
-
- assert(path);
-
- /* pivot_root() isn't currently supported in the initramfs. */
- if (in_initrd())
- return mount_move_root(path);
+static inline int mount_switch_root_pivot(const char *path, int fd_newroot) {
+ _cleanup_close_ int fd_oldroot = -EBADF;
fd_oldroot = open("/", O_PATH|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
if (fd_oldroot < 0)
return log_debug_errno(errno, "Failed to open old rootfs");
- fd_newroot = open(path, O_PATH|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
- if (fd_newroot < 0)
- return log_debug_errno(errno, "Failed to open new rootfs '%s': %m", path);
-
- /* Change into the new rootfs. */
- if (fchdir(fd_newroot) < 0)
- return log_debug_errno(errno, "Failed to change into new rootfs '%s': %m", path);
-
/* Let the kernel tuck the new root under the old one. */
if (pivot_root(".", ".") < 0)
return log_debug_errno(errno, "Failed to pivot root to new rootfs '%s': %m", path);
-
/* At this point the new root is tucked under the old root. If we want
* to unmount it we cannot be fchdir()ed into it. So escape back to the
* old root. */
return 0;
}
+static inline int mount_switch_root_move(const char *path) {
+ if (mount(path, "/", NULL, MS_MOVE, NULL) < 0)
+ return log_debug_errno(errno, "Failed to move new rootfs '%s': %m", path);
+
+ if (chroot(".") < 0)
+ return log_debug_errno(errno, "Failed to chroot to new rootfs '%s': %m", path);
+
+ if (chdir("/"))
+ return log_debug_errno(errno, "Failed to chdir to new rootfs '%s': %m", path);
+
+ return 0;
+}
+
+int mount_switch_root(const char *path, MountAttrPropagationType type) {
+ int r;
+ _cleanup_close_ int fd_newroot = -EBADF;
+ unsigned int flags;
+
+ assert(path);
+
+ fd_newroot = open(path, O_PATH|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
+ if (fd_newroot < 0)
+ return log_debug_errno(errno, "Failed to open new rootfs '%s': %m", path);
+
+ /* Change into the new rootfs. */
+ if (fchdir(fd_newroot) < 0)
+ return log_debug_errno(errno, "Failed to change into new rootfs '%s': %m", path);
+
+ r = mount_switch_root_pivot(path, fd_newroot);
+ if (r < 0) {
+ /* Failed to pivot_root() fallback to MS_MOVE. For example, this may happen if the
+ * rootfs is an initramfs in which case pivot_root() isn't supported. */
+ log_debug_errno(r, "Failed to pivot into new rootfs '%s': %m", path);
+ r = mount_switch_root_move(path);
+ }
+ if (r < 0)
+ return log_debug_errno(r, "Failed to switch to new rootfs '%s': %m", path);
+
+ /* Finally, let's establish the requested propagation type. */
+ flags = mount_attr_propagation_type_to_flag(type);
+ if ((flags != 0) && mount(NULL, ".", NULL, flags|MS_REC, 0) < 0)
+ return log_debug_errno(errno, "Failed to turn new rootfs '%s' into %s mount: %m",
+ mount_attr_propagation_type_to_string(type), path);
+
+ return 0;
+}
int repeat_unmount(const char *path, int flags) {
bool done = false;
/* This extracts mount flags from the mount options, and stores
* non-mount-flag options to '*ret_remaining_options'.
* E.g.,
- * "rw,nosuid,nodev,relatime,size=1630748k,mode=700,uid=1000,gid=1000"
+ * "rw,nosuid,nodev,relatime,size=1630748k,mode=0700,uid=1000,gid=1000"
* is split to MS_NOSUID|MS_NODEV|MS_RELATIME and
- * "size=1630748k,mode=700,uid=1000,gid=1000".
+ * "size=1630748k,mode=0700,uid=1000,gid=1000".
* See more examples in test-mount-util.c.
*
* If 'options' does not contain any non-mount-flag options,
#include "errno-util.h"
#include "macro.h"
-/* The limit used for /dev itself. 4MB should be enough since device nodes and symlinks don't
- * consume any space and udev isn't supposed to create regular file either. There's no limit on the
- * max number of inodes since such limit is hard to guess especially on large storage array
- * systems. */
-#define TMPFS_LIMITS_DEV ",size=4m"
-
-/* The limit used for /dev in private namespaces. 4MB for contents of regular files. The number of
- * inodes should be relatively low in private namespaces but for now use a 64k limit. */
-#define TMPFS_LIMITS_PRIVATE_DEV ",size=4m,nr_inodes=64k"
-
-/* Very little, if any use expected */
-#define TMPFS_LIMITS_EMPTY_OR_ALMOST ",size=4m,nr_inodes=1k"
-#define TMPFS_LIMITS_SYS TMPFS_LIMITS_EMPTY_OR_ALMOST
-#define TMPFS_LIMITS_SYS_FS_CGROUP TMPFS_LIMITS_EMPTY_OR_ALMOST
-
-/* On an extremely small device with only 256MB of RAM, 20% of RAM should be enough for the re-execution of
- * PID1 because 16MB of free space is required. */
-#define TMPFS_LIMITS_RUN ",size=20%,nr_inodes=800k"
-
-/* The limit used for various nested tmpfs mounts, in particular for guests started by systemd-nspawn.
- * 10% of RAM (using 16GB of RAM as a baseline) translates to 400k inodes (assuming 4k each) and 25%
- * translates to 1M inodes.
- * (On the host, /tmp is configured through a .mount unit file.) */
-#define NESTED_TMPFS_LIMITS ",size=10%,nr_inodes=400k"
-
-/* More space for volatile root and /var */
-#define TMPFS_LIMITS_VAR ",size=25%,nr_inodes=1m"
-#define TMPFS_LIMITS_ROOTFS TMPFS_LIMITS_VAR
-#define TMPFS_LIMITS_VOLATILE_STATE TMPFS_LIMITS_VAR
-
-int mount_fd(const char *source, int target_fd, const char *filesystemtype, unsigned long mountflags, const void *data);
-int mount_nofollow(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data);
+typedef enum MountAttrPropagationType {
+ MOUNT_ATTR_PROPAGATION_INHERIT, /* no special MS_* propagation flags */
+ MOUNT_ATTR_PROPAGATION_PRIVATE, /* MS_PRIVATE */
+ MOUNT_ATTR_PROPAGATION_DEPENDENT, /* MS_SLAVE */
+ MOUNT_ATTR_PROPAGATION_SHARED, /* MS_SHARE */
+
+ _MOUNT_ATTR_PROPAGATION_TYPE_MAX,
+ _MOUNT_ATTR_PROPAGATION_TYPE_INVALID = -EINVAL,
+} MountAttrPropagationType;
+
+const char* mount_attr_propagation_type_to_string(MountAttrPropagationType t) _const_;
+MountAttrPropagationType mount_attr_propagation_type_from_string(const char *s) _pure_;
+unsigned int mount_attr_propagation_type_to_flag(MountAttrPropagationType t);
int repeat_unmount(const char *path, int flags);
int umount_recursive(const char *target, int flags);
int bind_remount_one_with_mountinfo(const char *path, unsigned long new_flags, unsigned long flags_mask, FILE *proc_self_mountinfo);
-int mount_move_root(const char *path);
-int mount_pivot_root(const char *path);
+int mount_switch_root(const char *path, MountAttrPropagationType type);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(FILE*, endmntent, NULL);
#define _cleanup_endmntent_ _cleanup_(endmntentp)
return 0;
}
- RUN_WITH_UMASK(0022) {
+ WITH_UMASK(0022) {
r = write_string_file("/run/systemd/reboot-param", parameter,
WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
if (r < 0)
return 0;
/* If the old label is identical to the new one, suppress any kind of error */
- if (getfilecon_raw(FORMAT_PROC_FD_PATH(fd), &oldcon) >= 0 && streq(fcon, oldcon))
+ if (getfilecon_raw(FORMAT_PROC_FD_PATH(fd), &oldcon) >= 0 && streq_ptr(fcon, oldcon))
return 0;
return log_enforcing_errno(r, "Unable to fix SELinux security context of %s: %m", label_path);
if (getcon_raw(&mycon) < 0)
return -errno;
+ if (!mycon)
+ return -EOPNOTSUPP;
if (getfilecon_raw(exe, &fcon) < 0)
return -errno;
+ if (!fcon)
+ return -EOPNOTSUPP;
sclass = string_to_security_class("process");
if (sclass == 0)
#endif
}
-int mac_selinux_get_our_label(char **label) {
-#if HAVE_SELINUX
- assert(label);
+int mac_selinux_get_our_label(char **ret) {
+ assert(ret);
+#if HAVE_SELINUX
if (!mac_selinux_use())
return -EOPNOTSUPP;
- return RET_NERRNO(getcon_raw(label));
+ _cleanup_freecon_ char *con = NULL;
+ if (getcon_raw(&con) < 0)
+ return -errno;
+ if (!con)
+ return -EOPNOTSUPP;
+
+ *ret = TAKE_PTR(con);
+ return 0;
#else
return -EOPNOTSUPP;
#endif
if (getcon_raw(&mycon) < 0)
return -errno;
+ if (!mycon)
+ return -EOPNOTSUPP;
if (getpeercon_raw(socket_fd, &peercon) < 0)
return -errno;
+ if (!peercon)
+ return -EOPNOTSUPP;
- if (!exec_label) /* If there is no context set for next exec let's use context of target executable */
+ if (!exec_label) { /* If there is no context set for next exec let's use context of target executable */
if (getfilecon_raw(exe, &fcon) < 0)
return -errno;
+ if (!fcon)
+ return -EOPNOTSUPP;
+ }
bcon = context_new(mycon);
if (!bcon)
(void) mkdir_parents_label(p, directory_mode);
/* Enforce the right access mode for the socket */
- RUN_WITH_UMASK(~socket_mode) {
+ WITH_UMASK(~socket_mode) {
r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
if (r == -EADDRINUSE) {
/* Unlink and try again */
/* Translate error for missing os-release file to EUNATCH. */
return fd == -ENOENT ? -EUNATCH : fd;
- r = id128_read_fd(fd, ID128_PLAIN, &id);
+ r = id128_read_fd(fd, ID128_FORMAT_PLAIN, &id);
} else
r = sd_id128_get_machine(&id);
if (r < 0)
return r;
}
+#define RETRY_UNSEAL_MAX 30u
+
int tpm2_unseal(const char *device,
uint32_t hash_pcr_mask,
uint16_t pcr_bank,
if (r < 0)
goto finish;
- r = tpm2_make_policy_session(
- c.esys_context,
- primary,
- hmac_session,
- TPM2_SE_POLICY,
- hash_pcr_mask,
- pcr_bank,
- pubkey, pubkey_size,
- pubkey_pcr_mask,
- signature,
- !!pin,
- &session,
- &policy_digest,
- /* ret_pcr_bank= */ NULL);
- if (r < 0)
- goto finish;
+ for (unsigned i = RETRY_UNSEAL_MAX;; i--) {
+ r = tpm2_make_policy_session(
+ c.esys_context,
+ primary,
+ hmac_session,
+ TPM2_SE_POLICY,
+ hash_pcr_mask,
+ pcr_bank,
+ pubkey, pubkey_size,
+ pubkey_pcr_mask,
+ signature,
+ !!pin,
+ &session,
+ &policy_digest,
+ /* ret_pcr_bank= */ NULL);
+ if (r < 0)
+ goto finish;
- /* If we know the policy hash to expect, and it doesn't match, we can shortcut things here, and not
- * wait until the TPM2 tells us to go away. */
- if (known_policy_hash_size > 0 &&
- memcmp_nn(policy_digest->buffer, policy_digest->size, known_policy_hash, known_policy_hash_size) != 0)
- return log_error_errno(SYNTHETIC_ERRNO(EPERM),
- "Current policy digest does not match stored policy digest, cancelling "
- "TPM2 authentication attempt.");
+ /* If we know the policy hash to expect, and it doesn't match, we can shortcut things here, and not
+ * wait until the TPM2 tells us to go away. */
+ if (known_policy_hash_size > 0 &&
+ memcmp_nn(policy_digest->buffer, policy_digest->size, known_policy_hash, known_policy_hash_size) != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Current policy digest does not match stored policy digest, cancelling "
+ "TPM2 authentication attempt.");
- log_debug("Unsealing HMAC key.");
+ log_debug("Unsealing HMAC key.");
- rc = sym_Esys_Unseal(
- c.esys_context,
- hmac_key,
- session,
- hmac_session, /* use HMAC session to enable parameter encryption */
- ESYS_TR_NONE,
- &unsealed);
- if (rc != TSS2_RC_SUCCESS) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to unseal HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
- goto finish;
+ rc = sym_Esys_Unseal(
+ c.esys_context,
+ hmac_key,
+ session,
+ hmac_session, /* use HMAC session to enable parameter encryption */
+ ESYS_TR_NONE,
+ &unsealed);
+ if (rc == TPM2_RC_PCR_CHANGED && i > 0) {
+ log_debug("A PCR value changed during the TPM2 policy session, restarting HMAC key unsealing (%u tries left).", i);
+ session = tpm2_flush_context_verbose(c.esys_context, session);
+ continue;
+ }
+ if (rc != TSS2_RC_SUCCESS) {
+ r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to unseal HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
+ goto finish;
+ }
+
+ break;
}
secret = memdup(unsealed->buffer, unsealed->size);
printf(" PBKDF Type: %s\n", hr->luks_pbkdf_type);
if (hr->luks_pbkdf_hash_algorithm)
printf(" PBKDF Hash: %s\n", hr->luks_pbkdf_hash_algorithm);
+ if (hr->luks_pbkdf_force_iterations != UINT64_MAX)
+ printf(" PBKDF Iters: %" PRIu64 "\n", hr->luks_pbkdf_force_iterations);
if (hr->luks_pbkdf_time_cost_usec != UINT64_MAX)
printf(" PBKDF Time: %s\n", FORMAT_TIMESPAN(hr->luks_pbkdf_time_cost_usec, 0));
if (hr->luks_pbkdf_memory_cost != UINT64_MAX)
.luks_discard = -1,
.luks_offline_discard = -1,
.luks_volume_key_size = UINT64_MAX,
+ .luks_pbkdf_force_iterations = UINT64_MAX,
.luks_pbkdf_time_cost_usec = UINT64_MAX,
.luks_pbkdf_memory_cost = UINT64_MAX,
.luks_pbkdf_parallel_threads = UINT64_MAX,
{ "luksVolumeKeySize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_volume_key_size), 0 },
{ "luksPbkdfHashAlgorithm", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_hash_algorithm), JSON_SAFE },
{ "luksPbkdfType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_type), JSON_SAFE },
+ { "luksPbkdfForceIterations", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_force_iterations), 0 },
{ "luksPbkdfTimeCostUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_time_cost_usec), 0 },
{ "luksPbkdfMemoryCost", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_memory_cost), 0 },
{ "luksPbkdfParallelThreads", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_parallel_threads), 0 },
{ "luksVolumeKeySize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_volume_key_size), 0 },
{ "luksPbkdfHashAlgorithm", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_hash_algorithm), JSON_SAFE },
{ "luksPbkdfType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_type), JSON_SAFE },
+ { "luksPbkdfForceIterations", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_force_iterations), 0 },
{ "luksPbkdfTimeCostUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_time_cost_usec), 0 },
{ "luksPbkdfMemoryCost", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_memory_cost), 0 },
{ "luksPbkdfParallelThreads", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_parallel_threads), 0 },
return h->luks_pbkdf_type ?: "argon2id";
}
+uint64_t user_record_luks_pbkdf_force_iterations(UserRecord *h) {
+ assert(h);
+
+ /* propagate default "benchmark" mode as itself */
+ if (h->luks_pbkdf_force_iterations == UINT64_MAX)
+ return UINT64_MAX;
+
+ /* clamp everything else to actually accepted number of iterations of libcryptsetup */
+ return CLAMP(h->luks_pbkdf_force_iterations, 1U, UINT32_MAX);
+}
+
uint64_t user_record_luks_pbkdf_time_cost_usec(UserRecord *h) {
assert(h);
uint64_t luks_volume_key_size;
char *luks_pbkdf_hash_algorithm;
char *luks_pbkdf_type;
+ uint64_t luks_pbkdf_force_iterations;
uint64_t luks_pbkdf_time_cost_usec;
uint64_t luks_pbkdf_memory_cost;
uint64_t luks_pbkdf_parallel_threads;
const char *user_record_luks_cipher_mode(UserRecord *h);
uint64_t user_record_luks_volume_key_size(UserRecord *h);
const char* user_record_luks_pbkdf_type(UserRecord *h);
+uint64_t user_record_luks_pbkdf_force_iterations(UserRecord *h);
usec_t user_record_luks_pbkdf_time_cost_usec(UserRecord *h);
uint64_t user_record_luks_pbkdf_memory_cost(UserRecord *h);
uint64_t user_record_luks_pbkdf_parallel_threads(UserRecord *h);
(void) sockaddr_un_unlink(&sockaddr.un);
- RUN_WITH_UMASK(~m & 0777) {
+ WITH_UMASK(~m & 0777) {
r = mac_selinux_bind(fd, &sockaddr.sa, sockaddr_len);
if (r < 0)
return r;
if (r < 0)
return log_debug_errno(r, "Failed to open connection to systemd: %m");
+ /* Wait for 1.5 seconds at maximum for freeze operation */
+ (void) sd_bus_set_method_call_timeout(bus, 1500 * USEC_PER_MSEC);
+
r = bus_call_method(bus, bus_systemd_mgr, *method, &error, NULL, "s", SPECIAL_USER_SLICE);
if (r < 0)
return log_debug_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r));
}
static int execute_s2h(const SleepConfig *sleep_config) {
- _unused_ _cleanup_(freeze_thaw_user_slice) const char *auto_method_thaw = NULL;
+ _unused_ _cleanup_(freeze_thaw_user_slice) const char *auto_method_thaw = "ThawUnit";
int r, k;
assert(sleep_config);
r = freeze_thaw_user_slice(&(const char*) { "FreezeUnit" });
if (r < 0)
log_debug_errno(r, "Failed to freeze unit user.slice, ignoring: %m");
- else
- auto_method_thaw = "ThawUnit"; /* from now on we want automatic thawing */;
r = check_wakeup_type();
if (r < 0)
_cleanup_close_ int orig_stdout_fd = -1;
int r;
+ if (size > 16*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"
_cleanup_strv_free_ char **names = NULL;
UnitActiveState active_state;
sd_bus *bus;
+ bool not_found = true, ok = false;
int r;
- bool found = false;
r = acquire_bus(BUS_MANAGER, &bus);
if (r < 0)
return log_error_errno(r, "Failed to expand names: %m");
STRV_FOREACH(name, names) {
+ _cleanup_free_ char *load_state = NULL;
+
r = get_state_one_unit(bus, *name, &active_state);
if (r < 0)
return r;
+ r = unit_load_state(bus, *name, &load_state);
+ if (r < 0)
+ return r;
+
if (!arg_quiet)
puts(unit_active_state_to_string(active_state));
for (int i = 0; i < nb_states; ++i)
- if (good_states[i] == active_state)
- found = true;
+ if (good_states[i] == active_state) {
+ ok = true;
+ break;
+ }
+
+ if (!streq(load_state, "not-found"))
+ not_found = false;
}
- /* use the given return code for the case that we won't find
- * any unit which matches the list */
- return found ? 0 : code;
+ /* We use LSB code 4 ("program or service status is unknown")
+ * when the corresponding unit file doesn't exist. */
+ return ok ? EXIT_SUCCESS : not_found ? EXIT_PROGRAM_OR_SERVICES_STATUS_UNKNOWN : code;
}
int verb_is_active(int argc, char *argv[], void *userdata) {
int verb_is_enabled(int argc, char *argv[], void *userdata) {
_cleanup_strv_free_ char **names = NULL;
- bool enabled;
+ bool not_found, enabled;
int r;
r = mangle_names("to check", strv_skip(argv, 1), &names);
if (r < 0)
return r;
- enabled = r > 0;
+ not_found = r == 0; /* Doesn't have SysV support or SYSV_UNIT_NOT_FOUND */
+ enabled = r == SYSV_UNIT_ENABLED;
if (install_client_side()) {
STRV_FOREACH(name, names) {
UnitFileState state;
r = unit_file_get_state(arg_scope, arg_root, *name, &state);
- if (r < 0)
+ if (r == -ENOENT) {
+ if (!arg_quiet)
+ puts("not-found");
+ continue;
+ } else if (r < 0)
return log_error_errno(r, "Failed to get unit file state for %s: %m", *name);
+ else
+ not_found = false;
if (IN_SET(state,
UNIT_FILE_ENABLED,
const char *s;
r = bus_call_method(bus, bus_systemd_mgr, "GetUnitFileState", &error, &reply, "s", *name);
- if (r < 0)
- return log_error_errno(r, "Failed to get unit file state for %s: %s", *name, bus_error_message(&error, r));
+ if (r == -ENOENT) {
+ sd_bus_error_free(&error);
+
+ if (!arg_quiet)
+ puts("not-found");
+ continue;
+ } else if (r < 0)
+ return log_error_errno(r,
+ "Failed to get unit file state for %s: %s",
+ *name,
+ bus_error_message(&error, r));
+ else
+ not_found = false;
r = sd_bus_message_read(reply, "s", &s);
if (r < 0)
}
}
- return enabled ? EXIT_SUCCESS : EXIT_FAILURE;
+ return enabled ? EXIT_SUCCESS : not_found ? EXIT_PROGRAM_OR_SERVICES_STATUS_UNKNOWN : EXIT_FAILURE;
}
#if HAVE_SYSV_COMPAT
_cleanup_(lookup_paths_free) LookupPaths paths = {};
unsigned f = 0;
+ SysVUnitEnableState enable_state = SYSV_UNIT_NOT_FOUND;
/* Processes all SysV units, and reshuffles the array so that afterwards only the native units remain */
if (j == EXIT_SUCCESS) {
if (!arg_quiet)
puts("enabled");
- r = 1;
+ enable_state = SYSV_UNIT_ENABLED;
} else {
if (!arg_quiet)
puts("disabled");
+ if (enable_state != SYSV_UNIT_ENABLED)
+ enable_state = SYSV_UNIT_DISABLED;
}
} else if (j != EXIT_SUCCESS)
strv_remove(args + f, name);
}
+ if (streq(verb, "is-enabled"))
+ return enable_state;
#endif
return r;
}
EXIT_PROGRAM_OR_SERVICES_STATUS_UNKNOWN = 4,
};
+typedef enum SysVUnitEnableState {
+ SYSV_UNIT_NOT_FOUND = 0,
+ SYSV_UNIT_DISABLED,
+ SYSV_UNIT_ENABLED,
+} SysVUnitEnableState;
+
int enable_sysv_units(const char *verb, char **args);
int action_to_runlevel(void) _pure_;
#include "rlimit-util.h"
#include "sigbus.h"
#include "signal-util.h"
+#include "stat-util.h"
#include "string-table.h"
#include "systemctl-add-dependency.h"
#include "systemctl-cancel-job.h"
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);
if (r <= 0)
goto finish;
+ if (proc_mounted() == 0)
+ log_warning("%s%s/proc/ is not mounted. This is not a supported mode of operation. Please fix\n"
+ "your invocation environment to mount /proc/ and /sys/ properly. Proceeding anyway.\n"
+ "Your mileage may vary.",
+ emoji_enabled() ? special_glyph(SPECIAL_GLYPH_WARNING_SIGN) : "",
+ emoji_enabled() ? " " : "");
+
if (arg_action != ACTION_SYSTEMCTL && running_in_chroot() > 0) {
if (!arg_quiet)
log_info("Running in chroot, ignoring request.");
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;
int r;
r = sd_id128_get_machine(&id);
- if (IN_SET(r, -ENOENT, -ENOMEDIUM))
+ if (IN_SET(r, -ENOENT, -ENOMEDIUM, -ENOPKG))
return (void) log_tests_skipped("/etc/machine-id missing");
assert_se(r >= 0);
"true \"\\$dollar\"");
}
+static void test_octescape_one(const char *s, const char *expected) {
+ _cleanup_free_ char *ret;
+
+ assert_se(ret = octescape(s, strlen_ptr(s)));
+ log_debug("octescape(\"%s\") → \"%s\" (expected: \"%s\")", strnull(s), ret, expected);
+ assert_se(streq(ret, expected));
+}
+
+TEST(octescape) {
+ test_octescape_one(NULL, "");
+ test_octescape_one("", "");
+ test_octescape_one("foo", "foo");
+ test_octescape_one("\"\\\"", "\\042\\134\\042");
+ test_octescape_one("\123\213\222", "\123\\213\\222");
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);
static const char *arg_test_dir = NULL;
TEST(chase_symlinks) {
- _cleanup_free_ char *result = NULL;
+ _cleanup_free_ char *result = NULL, *pwd = NULL;
_cleanup_close_ int pfd = -1;
char *temp;
const char *top, *p, *pslash, *q, *qslash;
assert_se(path_equal(result, p));
result = mfree(result);
+ /* Relative paths */
+
+ assert_se(safe_getcwd(&pwd) >= 0);
+
+ assert_se(chdir(temp) >= 0);
+
+ p = "this/is/a/relative/path";
+ r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result, NULL);
+ assert_se(r == 0);
+
+ p = strjoina(temp, "/", p);
+ assert_se(path_equal(result, p));
+ result = mfree(result);
+
+ p = "this/is/a/relative/path";
+ r = chase_symlinks(p, temp, CHASE_NONEXISTENT, &result, NULL);
+ assert_se(r == 0);
+
+ p = strjoina(temp, "/", p);
+ assert_se(path_equal(result, p));
+ result = mfree(result);
+
+ assert_se(chdir(pwd) >= 0);
+
/* Path which doesn't exist, but contains weird stuff */
p = strjoina(temp, "/idontexist/..");
assert_se(symlink("/usr/../etc/./machine-id", p) >= 0);
r = chase_symlinks(p, NULL, 0, NULL, &pfd);
- if (r != -ENOENT) {
+ if (r != -ENOENT && sd_id128_get_machine(NULL) >= 0) {
_cleanup_close_ int fd = -1;
sd_id128_t a, b;
assert_se(fd >= 0);
safe_close(pfd);
- assert_se(id128_read_fd(fd, ID128_PLAIN, &a) >= 0);
+ assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &a) >= 0);
assert_se(sd_id128_get_machine(&b) >= 0);
assert_se(sd_id128_equal(a, b));
}
}
}
+TEST(verity_mappings) {
+ for (PartitionDesignator p = 0; p < _PARTITION_DESIGNATOR_MAX; p++) {
+ PartitionDesignator q;
+
+ q = partition_verity_of(p);
+ assert_se(q < 0 || partition_verity_to_data(q) == p);
+
+ q = partition_verity_sig_of(p);
+ assert_se(q < 0 || partition_verity_sig_to_data(q) == p);
+
+ q = partition_verity_to_data(p);
+ assert_se(q < 0 || partition_verity_of(q) == p);
+
+ q = partition_verity_sig_to_data(p);
+ assert_se(q < 0 || partition_verity_sig_of(q) == p);
+ }
+}
+
DEFINE_TEST_MAIN(LOG_INFO);
assert_se(undecchar('9') == 9);
}
+static void test_hexmem_one(const char *in, const char *expected) {
+ _cleanup_free_ char *result = NULL;
+ _cleanup_free_ void *mem = NULL;
+ size_t len;
+
+ assert_se(result = hexmem(in, strlen_ptr(in)));
+ log_debug("hexmem(\"%s\") → \"%s\" (expected: \"%s\")", strnull(in), result, expected);
+ assert_se(streq(result, expected));
+
+ assert_se(unhexmem(result, SIZE_MAX, &mem, &len) >= 0);
+ assert_se(memcmp_safe(mem, in, len) == 0);
+}
+
+TEST(hexmem) {
+ test_hexmem_one(NULL, "");
+ test_hexmem_one("", "");
+ test_hexmem_one("foo", "666f6f");
+}
+
static void test_unhexmem_one(const char *s, size_t l, int retval) {
_cleanup_free_ char *hex = NULL;
_cleanup_free_ void *mem = NULL;
}
}
+static void test_base64_append_one(char **buf, size_t *len, const char *in, const char *expected) {
+ ssize_t new_len;
+
+ new_len = base64_append(buf, *len, in, strlen_ptr(in), 8, 12);
+ assert_se(new_len >= 0);
+ log_debug("base64_append_one(\"%s\")\nresult:\n%s\nexpected:\n%s", in, strnull(*buf), strnull(expected));
+ assert_se((size_t) new_len == strlen_ptr(*buf));
+ assert_se(streq_ptr(*buf, expected));
+ *len = new_len;
+}
+
+TEST(base64_append) {
+ _cleanup_free_ char *buf = NULL;
+ size_t len = 0;
+
+ test_base64_append_one(&buf, &len, "", NULL);
+ test_base64_append_one(&buf, &len, "f",
+ "Zg==");
+ test_base64_append_one(&buf, &len, "fo",
+ "Zg== Zm8=");
+ test_base64_append_one(&buf, &len, "foo",
+ "Zg== Zm8=\n"
+ " Zm9v");
+ test_base64_append_one(&buf, &len, "foob",
+ "Zg== Zm8=\n"
+ " Zm9v\n"
+ " Zm9v\n"
+ " Yg==");
+ test_base64_append_one(&buf, &len, "fooba",
+ "Zg== Zm8=\n"
+ " Zm9v\n"
+ " Zm9v\n"
+ " Yg==\n"
+ " Zm9v\n"
+ " YmE=");
+ test_base64_append_one(&buf, &len, "foobar",
+ "Zg== Zm8=\n"
+ " Zm9v\n"
+ " Zm9v\n"
+ " Yg==\n"
+ " Zm9v\n"
+ " YmE=\n"
+ " Zm9v\n"
+ " YmFy");
+
+ assert_se(free_and_strdup(&buf, "hogehogehogehoge") >= 0);
+ len = strlen(buf);
+
+ test_base64_append_one(&buf, &len, "",
+ "hogehogehogehoge");
+ test_base64_append_one(&buf, &len, "f",
+ "hogehogehogehoge\n"
+ " Zg==");
+ test_base64_append_one(&buf, &len, "fo",
+ "hogehogehogehoge\n"
+ " Zg==\n"
+ " Zm8=");
+ test_base64_append_one(&buf, &len, "foo",
+ "hogehogehogehoge\n"
+ " Zg==\n"
+ " Zm8=\n"
+ " Zm9v");
+ test_base64_append_one(&buf, &len, "foob",
+ "hogehogehogehoge\n"
+ " Zg==\n"
+ " Zm8=\n"
+ " Zm9v\n"
+ " Zm9v\n"
+ " Yg==");
+ test_base64_append_one(&buf, &len, "fooba",
+ "hogehogehogehoge\n"
+ " Zg==\n"
+ " Zm8=\n"
+ " Zm9v\n"
+ " Zm9v\n"
+ " Yg==\n"
+ " Zm9v\n"
+ " YmE=");
+ test_base64_append_one(&buf, &len, "foobar",
+ "hogehogehogehoge\n"
+ " Zg==\n"
+ " Zm8=\n"
+ " Zm9v\n"
+ " Zm9v\n"
+ " Yg==\n"
+ " Zm9v\n"
+ " YmE=\n"
+ " Zm9v\n"
+ " YmFy");
+
+ assert_se(free_and_strdup(&buf, "hogehogehogehoge") >= 0);
+ len = strlen(buf);
+
+ test_base64_append_one(&buf, &len, "foobarfoobarfoobarfoobar",
+ "hogehogehogehoge\n"
+ " Zm9v\n"
+ " YmFy\n"
+ " Zm9v\n"
+ " YmFy\n"
+ " Zm9v\n"
+ " YmFy\n"
+ " Zm9v\n"
+ " YmFy");
+
+ assert_se(free_and_strdup(&buf, "aaa") >= 0);
+ len = strlen(buf);
+
+ test_base64_append_one(&buf, &len, "foobarfoobarfoobarfoobar",
+ "aaa Zm9vYmFy\n"
+ " Zm9vYmFy\n"
+ " Zm9vYmFy\n"
+ " Zm9vYmFy");
+}
+
static void test_unbase64mem_one(const char *input, const char *output, int ret) {
_cleanup_free_ void *buffer = NULL;
size_t size = 0;
assert_se(!sd_id128_in_set(id, ID128_WALDI));
assert_se(!sd_id128_in_set(id, ID128_WALDI, ID128_WALDI));
- if (sd_booted() > 0 && access("/etc/machine-id", F_OK) >= 0) {
+ if (sd_booted() > 0 && sd_id128_get_machine(NULL) >= 0) {
assert_se(sd_id128_get_machine(&id) == 0);
printf("machine: %s\n", sd_id128_to_string(id, t));
/* First, write as UUID */
assert_se(sd_id128_randomize(&id) >= 0);
- assert_se(id128_write_fd(fd, ID128_UUID, id, false) >= 0);
+ assert_se(id128_write_fd(fd, ID128_FORMAT_UUID, id) >= 0);
assert_se(lseek(fd, 0, SEEK_SET) == 0);
- assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) == -EINVAL);
+ assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2) == -EUCLEAN);
assert_se(lseek(fd, 0, SEEK_SET) == 0);
- assert_se(id128_read_fd(fd, ID128_UUID, &id2) >= 0);
+ assert_se(id128_read_fd(fd, ID128_FORMAT_UUID, &id2) >= 0);
assert_se(sd_id128_equal(id, id2));
assert_se(lseek(fd, 0, SEEK_SET) == 0);
- assert_se(id128_read_fd(fd, ID128_ANY, &id2) >= 0);
+ assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, &id2) >= 0);
assert_se(sd_id128_equal(id, id2));
/* Second, write as plain */
assert_se(ftruncate(fd, 0) >= 0);
assert_se(sd_id128_randomize(&id) >= 0);
- assert_se(id128_write_fd(fd, ID128_PLAIN, id, false) >= 0);
+ assert_se(id128_write_fd(fd, ID128_FORMAT_PLAIN, id) >= 0);
assert_se(lseek(fd, 0, SEEK_SET) == 0);
- assert_se(id128_read_fd(fd, ID128_UUID, &id2) == -EINVAL);
+ assert_se(id128_read_fd(fd, ID128_FORMAT_UUID, &id2) == -EUCLEAN);
assert_se(lseek(fd, 0, SEEK_SET) == 0);
- assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) >= 0);
+ assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2) >= 0);
assert_se(sd_id128_equal(id, id2));
assert_se(lseek(fd, 0, SEEK_SET) == 0);
- assert_se(id128_read_fd(fd, ID128_ANY, &id2) >= 0);
+ assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, &id2) >= 0);
assert_se(sd_id128_equal(id, id2));
/* Third, write plain without trailing newline */
assert_se(write(fd, sd_id128_to_string(id, t), 32) == 32);
assert_se(lseek(fd, 0, SEEK_SET) == 0);
- assert_se(id128_read_fd(fd, ID128_UUID, &id2) == -EINVAL);
+ assert_se(id128_read_fd(fd, ID128_FORMAT_UUID, &id2) == -EUCLEAN);
assert_se(lseek(fd, 0, SEEK_SET) == 0);
- assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) >= 0);
+ assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2) >= 0);
assert_se(sd_id128_equal(id, id2));
- /* Third, write UUID without trailing newline */
+ /* Fourth, write UUID without trailing newline */
assert_se(lseek(fd, 0, SEEK_SET) == 0);
assert_se(ftruncate(fd, 0) >= 0);
assert_se(write(fd, sd_id128_to_uuid_string(id, q), 36) == 36);
assert_se(lseek(fd, 0, SEEK_SET) == 0);
- assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) == -EINVAL);
+ assert_se(id128_read_fd(fd, ID128_FORMAT_PLAIN, &id2) == -EUCLEAN);
assert_se(lseek(fd, 0, SEEK_SET) == 0);
- assert_se(id128_read_fd(fd, ID128_UUID, &id2) >= 0);
+ assert_se(id128_read_fd(fd, ID128_FORMAT_UUID, &id2) >= 0);
assert_se(sd_id128_equal(id, id2));
- if (sd_booted() > 0 && access("/etc/machine-id", F_OK) >= 0) {
+ /* Fifth, tests for "uninitialized" */
+ assert_se(lseek(fd, 0, SEEK_SET) == 0);
+ assert_se(ftruncate(fd, 0) >= 0);
+ assert_se(write(fd, "uninitialized", STRLEN("uninitialized")) == STRLEN("uninitialized"));
+ assert_se(lseek(fd, 0, SEEK_SET) == 0);
+ assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, NULL) == -ENOPKG);
+
+ assert_se(lseek(fd, 0, SEEK_SET) == 0);
+ assert_se(ftruncate(fd, 0) >= 0);
+ assert_se(write(fd, "uninitialized\n", STRLEN("uninitialized\n")) == STRLEN("uninitialized\n"));
+ assert_se(lseek(fd, 0, SEEK_SET) == 0);
+ assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, NULL) == -ENOPKG);
+
+ assert_se(lseek(fd, 0, SEEK_SET) == 0);
+ assert_se(ftruncate(fd, 0) >= 0);
+ assert_se(write(fd, "uninitialized\nfoo", STRLEN("uninitialized\nfoo")) == STRLEN("uninitialized\nfoo"));
+ assert_se(lseek(fd, 0, SEEK_SET) == 0);
+ assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, NULL) == -EUCLEAN);
+
+ assert_se(lseek(fd, 0, SEEK_SET) == 0);
+ assert_se(ftruncate(fd, 0) >= 0);
+ assert_se(write(fd, "uninit", STRLEN("uninit")) == STRLEN("uninit"));
+ assert_se(lseek(fd, 0, SEEK_SET) == 0);
+ assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, NULL) == -EUCLEAN);
+
+ if (sd_booted() > 0 && sd_id128_get_machine(NULL) >= 0) {
assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id) >= 0);
assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id2) >= 0);
assert_se(sd_id128_equal(id, id2));
unsigned iterations = slow_tests_enabled() ? 1000000 : 1000;
usec_t t, q;
- if (access("/etc/machine-id", F_OK) < 0 && errno == ENOENT)
- return (void) log_tests_skipped("/etc/machine-id does not exist");
+ if (sd_id128_get_machine(NULL) < 0)
+ return (void) log_tests_skipped("/etc/machine-id is not initialized");
log_info("/* %s (%u iterations) */", __func__, iterations);
assert_se(a = path_join(p, "foo"));
assert_se(b = path_join(p, "bar"));
- RUN_WITH_UMASK(0077)
+ WITH_UMASK(0077)
assert_se(write_string_file(a, "wups", WRITE_STRING_FILE_CREATE) >= 0);
assert_se(lstat(a, &stat1) >= 0);
assert_se(json_variant_equal(v, w));
}
+static inline void json_array_append_with_source_one(bool source) {
+ _cleanup_(json_variant_unrefp) JsonVariant *a, *b;
+
+ /* Parse two sources, each with a different name and line/column numbers */
+
+ assert_se(json_parse_with_source(" [41]", source ? "string 1" : NULL, 0,
+ &a, NULL, NULL) >= 0);
+ assert_se(json_parse_with_source("\n\n [42]", source ? "string 2" : NULL, 0,
+ &b, NULL, NULL) >= 0);
+
+ assert_se(json_variant_is_array(a));
+ assert_se(json_variant_elements(a) == 1);
+ assert_se(json_variant_is_array(b));
+ assert_se(json_variant_elements(b) == 1);
+
+ /* Verify source information */
+
+ const char *s1, *s2;
+ unsigned line1, col1, line2, col2;
+ assert_se(json_variant_get_source(a, &s1, &line1, &col1) >= 0);
+ assert_se(json_variant_get_source(b, &s2, &line2, &col2) >= 0);
+
+ assert_se(streq_ptr(s1, source ? "string 1" : NULL));
+ assert_se(streq_ptr(s2, source ? "string 2" : NULL));
+ assert_se(line1 == 1);
+ assert_se(col1 == 2);
+ assert_se(line2 == 3);
+ assert_se(col2 == 4);
+
+ /* Append one elem from the second array (and source) to the first. */
+
+ JsonVariant *elem;
+ assert_se(elem = json_variant_by_index(b, 0));
+ assert_se(json_variant_is_integer(elem));
+ assert_se(json_variant_elements(elem) == 0);
+
+ assert_se(json_variant_append_array(&a, elem) >= 0);
+
+ assert_se(json_variant_is_array(a));
+ assert_se(json_variant_elements(a) == 2);
+
+ /* Verify that source information was propagated correctly */
+
+ assert_se(json_variant_get_source(elem, &s1, &line1, &col1) >= 0);
+ assert_se(elem = json_variant_by_index(a, 1));
+ assert_se(json_variant_get_source(elem, &s2, &line2, &col2) >= 0);
+
+ assert_se(streq_ptr(s1, source ? "string 2" : NULL));
+ assert_se(streq_ptr(s2, source ? "string 2" : NULL));
+ assert_se(line1 == 3);
+ assert_se(col1 == 5);
+ assert_se(line2 == 3);
+ assert_se(col2 == 5);
+}
+
+TEST(json_array_append_with_source) {
+ json_array_append_with_source_one(true);
+}
+
+TEST(json_array_append_without_source) {
+ json_array_append_with_source_one(false);
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);
#include <stdio.h>
#include <unistd.h>
+#include "sd-id128.h"
+
#include "all-units.h"
#include "alloc-util.h"
#include "capability-util.h"
_cleanup_free_ char *mid = NULL, *bid = NULL, *host = NULL, *gid = NULL, *group = NULL, *uid = NULL, *user = NULL;
- if (access("/etc/machine-id", F_OK) >= 0)
+ if (sd_id128_get_machine(NULL) >= 0)
assert_se(specifier_machine_id('m', NULL, NULL, NULL, &mid) >= 0 && mid);
if (sd_booted() > 0)
assert_se(specifier_boot_id('b', NULL, NULL, NULL, &bid) >= 0 && bid);
#define dump_glyph(x) log_info(STRINGIFY(x) ": %s", special_glyph(x))
TEST(dump_special_glyphs) {
- assert_cc(SPECIAL_GLYPH_SPARKLES + 1 == _SPECIAL_GLYPH_MAX);
+ assert_cc(SPECIAL_GLYPH_WARNING_SIGN + 1 == _SPECIAL_GLYPH_MAX);
log_info("is_locale_utf8: %s", yes_no(is_locale_utf8()));
dump_glyph(SPECIAL_GLYPH_RECYCLING);
dump_glyph(SPECIAL_GLYPH_DOWNLOAD);
dump_glyph(SPECIAL_GLYPH_SPARKLES);
+ dump_glyph(SPECIAL_GLYPH_WARNING_SIGN);
}
DEFINE_TEST_MAIN(LOG_INFO);
assert_se(!ISPOWEROF2(u));
}
+TEST(ALIGNED) {
+ assert_se(IS_ALIGNED16(NULL));
+ assert_se(IS_ALIGNED32(NULL));
+ assert_se(IS_ALIGNED64(NULL));
+
+ uint64_t u64;
+ uint32_t u32;
+ uint16_t u16;
+
+ assert_se(IS_ALIGNED16(&u16));
+ assert_se(IS_ALIGNED16(&u32));
+ assert_se(IS_ALIGNED16(&u64));
+ assert_se(IS_ALIGNED32(&u32));
+ assert_se(IS_ALIGNED32(&u64));
+ assert_se(IS_ALIGNED64(&u64));
+
+ _align_(32) uint8_t ua256;
+ _align_(8) uint8_t ua64;
+ _align_(4) uint8_t ua32;
+ _align_(2) uint8_t ua16;
+
+ assert_se(IS_ALIGNED16(&ua256));
+ assert_se(IS_ALIGNED32(&ua256));
+ assert_se(IS_ALIGNED64(&ua256));
+
+ assert_se(IS_ALIGNED16(&ua64));
+ assert_se(IS_ALIGNED32(&ua64));
+ assert_se(IS_ALIGNED64(&ua64));
+
+ assert_se(IS_ALIGNED16(&ua32));
+ assert_se(IS_ALIGNED32(&ua32));
+
+ assert_se(IS_ALIGNED16(&ua16));
+
+#ifdef __x86_64__
+ /* Conditionalized on x86-64, since there we know for sure that all three types are aligned to
+ * their size. Too lazy to figure it out for other archs */
+ void *p = UINT_TO_PTR(1); /* definitely not aligned */
+ assert_se(!IS_ALIGNED16(p));
+ assert_se(!IS_ALIGNED32(p));
+ assert_se(!IS_ALIGNED64(p));
+
+ assert_se(IS_ALIGNED16(ALIGN2_PTR(p)));
+ assert_se(IS_ALIGNED32(ALIGN4_PTR(p)));
+ assert_se(IS_ALIGNED64(ALIGN8_PTR(p)));
+
+ p = UINT_TO_PTR(-1); /* also definitely not aligned */
+ assert_se(!IS_ALIGNED16(p));
+ assert_se(!IS_ALIGNED32(p));
+ assert_se(!IS_ALIGNED64(p));
+#endif
+}
+
DEFINE_TEST_MAIN(LOG_INFO);
#include "missing_mount.h"
#include "mkdir.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "namespace-util.h"
#include "path-util.h"
#include "process-util.h"
assert_se(f == (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC));
assert_se(opts == NULL);
- assert_se(mount_option_mangle("ro,nosuid,nodev,noexec,mode=755", 0, &f, &opts) == 0);
+ assert_se(mount_option_mangle("ro,nosuid,nodev,noexec,mode=0755", 0, &f, &opts) == 0);
assert_se(f == (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC));
- assert_se(streq(opts, "mode=755"));
+ assert_se(streq(opts, "mode=0755"));
opts = mfree(opts);
- assert_se(mount_option_mangle("rw,nosuid,foo,hogehoge,nodev,mode=755", 0, &f, &opts) == 0);
+ assert_se(mount_option_mangle("rw,nosuid,foo,hogehoge,nodev,mode=0755", 0, &f, &opts) == 0);
assert_se(f == (MS_NOSUID|MS_NODEV));
- assert_se(streq(opts, "foo,hogehoge,mode=755"));
+ assert_se(streq(opts, "foo,hogehoge,mode=0755"));
opts = mfree(opts);
assert_se(mount_option_mangle("rw,nosuid,nodev,noexec,relatime,net_cls,net_prio", MS_RDONLY, &f, &opts) == 0);
assert_se(streq(opts, "net_cls,net_prio"));
opts = mfree(opts);
- assert_se(mount_option_mangle("rw,nosuid,nodev,relatime,size=1630748k,mode=700,uid=1000,gid=1000", MS_RDONLY, &f, &opts) == 0);
+ assert_se(mount_option_mangle("rw,nosuid,nodev,relatime,size=1630748k,mode=0700,uid=1000,gid=1000", MS_RDONLY, &f, &opts) == 0);
assert_se(f == (MS_NOSUID|MS_NODEV|MS_RELATIME));
- assert_se(streq(opts, "size=1630748k,mode=700,uid=1000,gid=1000"));
+ assert_se(streq(opts, "size=1630748k,mode=0700,uid=1000,gid=1000"));
opts = mfree(opts);
- assert_se(mount_option_mangle("size=1630748k,rw,gid=1000,,,nodev,relatime,,mode=700,nosuid,uid=1000", MS_RDONLY, &f, &opts) == 0);
+ assert_se(mount_option_mangle("size=1630748k,rw,gid=1000,,,nodev,relatime,,mode=0700,nosuid,uid=1000", MS_RDONLY, &f, &opts) == 0);
assert_se(f == (MS_NOSUID|MS_NODEV|MS_RELATIME));
- assert_se(streq(opts, "size=1630748k,gid=1000,mode=700,uid=1000"));
+ assert_se(streq(opts, "size=1630748k,gid=1000,mode=0700,uid=1000"));
opts = mfree(opts);
- assert_se(mount_option_mangle("rw,exec,size=8143984k,nr_inodes=2035996,mode=755", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, &f, &opts) == 0);
+ assert_se(mount_option_mangle("rw,exec,size=8143984k,nr_inodes=2035996,mode=0755", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, &f, &opts) == 0);
assert_se(f == (MS_NOSUID|MS_NODEV));
- assert_se(streq(opts, "size=8143984k,nr_inodes=2035996,mode=755"));
+ assert_se(streq(opts, "size=8143984k,nr_inodes=2035996,mode=0755"));
opts = mfree(opts);
assert_se(mount_option_mangle("rw,relatime,fmask=0022,,,dmask=0022", MS_RDONLY, &f, &opts) == 0);
assert_se(mount_option_mangle("rw,relatime,fmask=0022,dmask=0022,\"hogehoge", MS_RDONLY, &f, &opts) < 0);
- assert_se(mount_option_mangle("mode=1777,size=10%,nr_inodes=400k,uid=496107520,gid=496107520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\"", 0, &f, &opts) == 0);
+ assert_se(mount_option_mangle("mode=01777,size=10%,nr_inodes=400k,uid=496107520,gid=496107520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\"", 0, &f, &opts) == 0);
assert_se(f == 0);
- assert_se(streq(opts, "mode=1777,size=10%,nr_inodes=400k,uid=496107520,gid=496107520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\""));
+ assert_se(streq(opts, "mode=01777,size=10%,nr_inodes=400k,uid=496107520,gid=496107520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\""));
opts = mfree(opts);
}
u = umask(0111);
n = 0;
- RUN_WITH_UMASK(0123) {
+ WITH_UMASK(0123) {
assert_se(umask(000) == 0123);
n++;
}
assert_se(n == 1);
assert_se(umask(u) == 0111);
- RUN_WITH_UMASK(0135) {
+ WITH_UMASK(0135) {
assert_se(umask(000) == 0135);
n++;
}
assert_se(n == 2);
assert_se(umask(0111) == u);
- RUN_WITH_UMASK(0315) {
+ WITH_UMASK(0315) {
assert_se(umask(000) == 0315);
n++;
break;
#include <stdio.h>
#include <stdlib.h>
+#include "sd-id128.h"
+
#include "alloc-util.h"
#include "all-units.h"
#include "glob-util.h"
assert_se(short_hostname);
assert_se(specifier_pretty_hostname('q', NULL, NULL, NULL, &pretty_hostname) == 0);
assert_se(pretty_hostname);
- if (access("/etc/machine-id", F_OK) >= 0) {
+ if (sd_id128_get_machine(NULL) >= 0) {
assert_se(specifier_machine_id('m', NULL, NULL, NULL, &machine_id) >= 0);
assert_se(machine_id);
}
m->event_timeout = sd_event_source_unref(m->event_timeout);
r = manager_listen_setup(m);
- if (r < 0)
- return log_warning_errno(r, "Failed to set up connection socket: %m");
+ if (r < 0) {
+ log_warning_errno(r, "Failed to set up connection socket: %m");
+ return manager_connect(m);
+ }
/*
* Set transmit timestamp, remember it; the server will send that back
assert(ai->ai_addrlen >= offsetof(struct sockaddr, sa_data));
if (!IN_SET(ai->ai_addr->sa_family, AF_INET, AF_INET6)) {
- log_warning("Unsuitable address protocol for %s", m->current_server_name->string);
+ log_debug("Ignoring unsuitable address protocol for %s.", m->current_server_name->string);
continue;
}
if (m->current_server_address && m->current_server_address->addresses_next)
manager_set_server_address(m, m->current_server_address->addresses_next);
else {
- static const struct addrinfo hints = {
- .ai_flags = AI_NUMERICSERV|AI_ADDRCONFIG,
- .ai_socktype = SOCK_DGRAM,
- };
-
/* Hmm, we are through all addresses, let's look for the next host instead */
if (m->current_server_name && m->current_server_name->names_next)
manager_set_server_name(m, m->current_server_name->names_next);
log_debug("Resolving %s...", m->current_server_name->string);
+ struct addrinfo hints = {
+ .ai_flags = AI_NUMERICSERV|AI_ADDRCONFIG,
+ .ai_socktype = SOCK_DGRAM,
+ .ai_family = socket_ipv6_is_supported() ? AF_UNSPEC : AF_INET,
+ };
+
r = resolve_getaddrinfo(m->resolve, &m->resolve_query, m->current_server_name->string, "123", &hints, manager_resolve_handler, NULL, m);
if (r < 0)
return log_error_errno(r, "Failed to create resolver: %m");
if (dir_fd < 0)
return dir_fd;
- RUN_WITH_UMASK(0000) {
+ WITH_UMASK(0000) {
mac_selinux_create_file_prepare(path, S_IFREG);
fd = RET_NERRNO(openat(dir_fd, bn, O_CREAT|O_EXCL|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode));
mac_selinux_create_file_clear();
if (fd == -ENOENT) {
creation = CREATION_NORMAL; /* Didn't work without O_CREATE, try again with */
- RUN_WITH_UMASK(0000) {
+ WITH_UMASK(0000) {
mac_selinux_create_file_prepare(path, S_IFREG);
fd = RET_NERRNO(openat(dir_fd, bn, O_CREAT|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode));
mac_selinux_create_file_clear();
subvol = false;
else {
- RUN_WITH_UMASK((~mode) & 0777)
+ WITH_UMASK((~mode) & 0777)
r = btrfs_subvol_make_fd(pfd, bn);
}
} else
r = 0;
if (!subvol || ERRNO_IS_NOT_SUPPORTED(r))
- RUN_WITH_UMASK(0000)
+ WITH_UMASK(0000)
r = mkdirat_label(pfd, bn, mode);
creation = r >= 0 ? CREATION_NORMAL : CREATION_EXISTING;
if (dfd < 0)
return dfd;
- RUN_WITH_UMASK(0000) {
+ WITH_UMASK(0000) {
mac_selinux_create_file_prepare(i->path, file_type);
r = RET_NERRNO(mknodat(dfd, bn, i->mode | file_type, i->major_minor));
mac_selinux_create_file_clear();
if (i->append_or_force) {
fd = safe_close(fd);
- RUN_WITH_UMASK(0000) {
+ WITH_UMASK(0000) {
mac_selinux_create_file_prepare(i->path, file_type);
r = mknodat_atomic(dfd, bn, i->mode | file_type, i->major_minor);
mac_selinux_create_file_clear();
if (pfd < 0)
return pfd;
- RUN_WITH_UMASK(0000) {
+ WITH_UMASK(0000) {
mac_selinux_create_file_prepare(i->path, S_IFIFO);
r = RET_NERRNO(mkfifoat(pfd, bn, i->mode));
mac_selinux_create_file_clear();
if (i->append_or_force) {
fd = safe_close(fd);
- RUN_WITH_UMASK(0000) {
+ WITH_UMASK(0000) {
mac_selinux_create_file_prepare(i->path, S_IFIFO);
r = mkfifoat_atomic(pfd, bn, i->mode);
mac_selinux_create_file_clear();
if (r == -ENOENT)
r = rm_if_wrong_type_safe(S_IFDIR, parent_fd, &parent_st, t, AT_SYMLINK_NOFOLLOW);
if (r == -ENOENT) {
- RUN_WITH_UMASK(0000)
+ WITH_UMASK(0000)
r = mkdirat_label(parent_fd, t, 0755);
if (r < 0) {
_cleanup_free_ char *parent_name = NULL;
if (r < 0 && r != -ENOENT)
return r;
} else
- RUN_WITH_UMASK(0000)
+ WITH_UMASK(0000)
(void) mkdir_parents_label(i->path, 0755);
return 0;
const UdevBuiltin udev_builtin_keyboard = {
.name = "keyboard",
.cmd = builtin_keyboard,
- .help = "Keyboard scan code to key mapping",
+ .help = "Keyboard scancode mapping and touchpad/pointingstick characteristics",
};
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Either --update or --test must be used.");
+ log_notice("udevadm hwdb is deprecated. Use systemd-hwdb instead.");
+
if (arg_update) {
r = hwdb_update(arg_root, arg_hwdb_bin_dir, arg_strict, true);
if (r < 0)
--- /dev/null
+LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURsVENDQW4yZ0F3SUJBZ0lVTzlqUWhhblhj
+b3ViOERzdXlMMWdZbksrR1lvd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1dURUxNQWtHQTFVRUJoTUNX
+Rmd4RlRBVEJnTlZCQWNNREVSbFptRjFiSFFnUTJsMGVURWNNQm9HQTFVRQpDZ3dUUkdWbVlYVnNk
+Q0JEYjIxd1lXNTVJRXgwWkRFVk1CTUdBMVVFQXd3TWEyVjVJSE5wWjI1cGJtbG5NQ0FYCkRUSXlN
+VEF5T1RFM01qY3dNVm9ZRHpNd01qSXdNekF4TVRjeU56QXhXakJaTVFzd0NRWURWUVFHRXdKWVdE
+RVYKTUJNR0ExVUVCd3dNUkdWbVlYVnNkQ0JEYVhSNU1Sd3dHZ1lEVlFRS0RCTkVaV1poZFd4MElF
+TnZiWEJoYm5rZwpUSFJrTVJVd0V3WURWUVFEREF4clpYa2djMmxuYm1sdWFXY3dnZ0VpTUEwR0NT
+cUdTSWIzRFFFQkFRVUFBNElCCkR3QXdnZ0VLQW9JQkFRREtVeHR4Y0d1aGYvdUp1SXRjWEhvdW0v
+RE9RL1RJM3BzUWlaR0ZWRkJzbHBicU5wZDUKa2JDaUFMNmgrY1FYaGRjUmlOT1dBR0wyMFZ1T2Rv
+VTZrYzlkdklGQnFzKzc2NHhvWGY1UGd2SlhvQUxSUGxDZAp4YVdPQzFsOFFIRHpxZ09SdnREMWNI
+WFoveTkvZ1YxVU1GK1FlYm12aUhRN0U4eGw1T2h5MG1TQVZYRDhBTitsCjdpMUR6N0NuTzhrMVph
+alhqYXlpNWV1WEV0TnFSZXNuVktRRElTQ0t2STFueUxySWxHRU1GZmFuUmRLQWthZ3MKalJnTmVh
+T3N3aklHNjV6UzFVdjJTZXcxVFpIaFhtUmd5TzRVT0JySHZlSml2T2hObzU3UlRKd0M2K2lGY0FG
+aApSSnorVmM2QUlSSkI1ZWtJUmdCN3VDNEI5ZmwydXdZKytMODNBZ01CQUFHalV6QlJNQjBHQTFV
+ZERnUVdCQlFqCllIMnpzVFlPQU51MkcweXk1QkxlOHBvbWZUQWZCZ05WSFNNRUdEQVdnQlFqWUgy
+enNUWU9BTnUyRzB5eTVCTGUKOHBvbWZUQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0li
+M0RRRUJDd1VBQTRJQkFRQ2dxcmFXaE51dQptUmZPUjVxcURVcC83RkpIL1N6Zk1vaDBHL2lWRkhv
+OUpSS0tqMUZ2Q0VZc1NmeThYTmdaUDI5eS81Z0h4cmcrCjhwZWx6bWJLczdhUTRPK01TcmIzTm11
+V1IzT0M0alBoNENrM09ZbDlhQy9iYlJqSWFvMDJ6K29XQWNZZS9xYTEKK2ZsemZWVEUwMHJ5V1RM
+K0FJdDFEZEVqaG01WXNtYlgvbWtacUV1TjBtSVhhRXhSVE9walczUWRNeVRQaURTdApvanQvQWMv
+R2RUWDd0QkhPTk44Z3djaC91V293aVNORERMUm1wM2VScnlOZ3RPKzBISUd5Qm16ZWNsM0VlVEo2
+CnJzOGRWUFhqR1Z4dlZDb2tqQllrOWdxbkNGZEJCMGx4VXVNZldWdVkyRUgwSjI3aGh4SXNFc3ls
+VTNIR1EyK2MKN1JicVY4VTNSRzA4Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
--- /dev/null
+LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZB
+QVNDQktnd2dnU2tBZ0VBQW9JQkFRREtVeHR4Y0d1aGYvdUoKdUl0Y1hIb3VtL0RPUS9USTNwc1Fp
+WkdGVkZCc2xwYnFOcGQ1a2JDaUFMNmgrY1FYaGRjUmlOT1dBR0wyMFZ1Twpkb1U2a2M5ZHZJRkJx
+cys3NjR4b1hmNVBndkpYb0FMUlBsQ2R4YVdPQzFsOFFIRHpxZ09SdnREMWNIWFoveTkvCmdWMVVN
+RitRZWJtdmlIUTdFOHhsNU9oeTBtU0FWWEQ4QU4rbDdpMUR6N0NuTzhrMVphalhqYXlpNWV1WEV0
+TnEKUmVzblZLUURJU0NLdkkxbnlMcklsR0VNRmZhblJkS0FrYWdzalJnTmVhT3N3aklHNjV6UzFV
+djJTZXcxVFpIaApYbVJneU80VU9Cckh2ZUppdk9oTm81N1JUSndDNitpRmNBRmhSSnorVmM2QUlS
+SkI1ZWtJUmdCN3VDNEI5ZmwyCnV3WSsrTDgzQWdNQkFBRUNnZ0VBQkhZQ28rU3JxdHJzaStQU3hz
+MlBNQm5tSEZZcFBvaVIrTEpmMEFYRTVEQUoKMGM0MFZzemNqU1hoRGljNHFLQWQxdGdpZWlzMkEy
+VW9WS0xPV3pVOTBqNUd4MURoMWEzaTRhWTQ1ajNuNUFDMgpMekRsakNVQWVucExsYzdCN3MxdjJM
+WFJXNmdJSVM5Y043NTlkVTYvdktyQ2FsbGkzcTZZRWlNUzhQMHNsQnZFCkZtdEc1elFsOVJjV0gr
+cHBqdzlIMTJSZ3BldUVJVEQ2cE0vd2xwcXZHRlUwcmZjM0NjMHhzaWdNTnh1Z1FJNGgKbnpjWDVs
+OEs0SHdvbmhOTG9TYkh6OU5BK3p3QkpuUlZVSWFaaEVjSThtaEVPWHRaRkpYc01aRnhjS2l3SHFS
+dApqUUVHOHJRa3lPLytXMmR5Z2czV1lNYXE1OWpUWVdIOUsrQmFyeEMzRVFLQmdRRFBNSFMycjgz
+ZUpRTTlreXpkCndDdnlmWGhQVlVtbVJnOGwyWng0aC9tci9mNUdDeW5SdzRzT2JuZGVQd29tZ1Iz
+cFBleFFGWlFFSExoZ1RGY3UKVk5uYXcrTzBFL1VnL01pRGswZDNXU0hVZXZPZnM1cEM2b3hYNjNT
+VENwNkVLT2VEZlpVMW9OeHRsZ0YyRVhjcgpmVlZpSzFKRGk3N2dtaENLcFNGcjBLK3gyUUtCZ1FE
+NS9VUC9hNU52clExdUhnKzR0SzJZSFhSK1lUOFREZG00Ck8xZmh5TU5lOHRYSkd5UUJjTktVTWg2
+M2VyR1MwWlRWdGdkNHlGS3RuOGtLU2U4TmlacUl1aitVUVIyZ3pEQVAKQ2VXcXl2Y2pRNmovU1Yw
+WjVvKzlTNytiOStpWWx5RTg2bGZobHh5Z21aNnptYisxUUNteUtNVUdBNis5VmUvMgo1MHhDMXBB
+L2p3S0JnUUNEOHA4UnpVcDFZK3I1WnVaVzN0RGVJSXZqTWpTeVFNSGE0QWhuTm1tSjREcjBUcDIy
+CmFpci82TmY2WEhsUlpqOHZVSEZUMnpvbG1FalBneTZ1WWZsUCtocmtqeVU0ZWVRVTcxRy9Mek45
+UjBRcCs4Nk4KT1NSaHhhQzdHRE0xaFh0VFlVSUtJa1RmUVgzeXZGTEJqcE0yN3RINEZHSmVWWitk
+UEdiWmE5REltUUtCZ1FENQpHTU5qeExiQnhhY25QYThldG5KdnE1SUR5RFRJY0xtc1dQMkZ6cjNX
+WTVSZzhybGE4aWZ5WVVxNE92cXNPRWZjCjk2ZlVVNUFHejd2TWs4VXZNUmtaK3JRVnJ4aXR2Q2g3
+STdxRkIvOWdWVEFWU080TE8vR29oczBqeGRBd0ZBK2IKbWtyOVQ4ekh2cXNqZlNWSW51bXRTL0Nl
+d0plaHl2cjBoSjg1em9Fbnd3S0JnR1h6UXVDSjJDb3NHVVhEdnlHKwpyRzVBd3pUZGd0bHg4YTBK
+NTg1OWtZbVd0cW5WYUJmbFdrRmNUcHNEaGZ2ZWVDUkswc29VRlNPWkcranpsbWJrCkpRL09aVkZJ
+dG9MSVZCeE9qeWVXNlhUSkJXUzFRSkVHckkwY0tTbXNKcENtUXVPdUxMVnZYczV0U21CVmc5RXQK
+MjZzUkZwcjVWWmsrZlNRa3RhbkM4NGV1Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
--- /dev/null
+LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZB
+QVNDQktnd2dnU2tBZ0VBQW9JQkFRQzVuOHFhbzVNZ1BJUVcKc0F5Y2R3dnB1bjdNNHlRSW9FL3I3
+ekFGTG1hZlBXclo3d2JaaUIyTkY1MVdHOEo4bnlDQkI3M0RLcmZaeWs5cwphQXdXVW5RR2t0dGFv
+RXpXRzZSRTM3dXdQOUpVM09YdklTNTBhcy9KSHVHNlJPYmE2V0NOOFp2TTdkZGpvTDFKCkZlYnBS
+SXI1Vi82VStMTFhrUnRNYVczUnZ6T0xYeU1NT2QzOEcxZ0d0VlRHcm90ejVldFgrTUNVU2lOVGFE
+OVUKN1dEZXVsZXVpMlRnK1I3TGRoSXg3ZTQ5cEhRM3d6a1NxeFQ4SGpoU3ZURWpITWVSNjIwaUhF
+ZW9uYzdsMXVnagpzY1pwTktHdk13bXUvU2ptWFp6UkpOdjVOU0txcEVnQll2RnFkS3dUdlc4MWl6
+SUFvN3paMkx6NDJYb25zSWJ2CjNrbGZqTG1mQWdNQkFBRUNnZ0VBQXozYm8yeTAzb3kvLzhkdVNQ
+TTVSWWtvdXJwQ3dGWFFYMzNyV0VQUnJmazgKR3ZjMkp1bGVIcjhwVTc0alhOcklqZ2hORTVIMDZQ
+eEQrOUFyV2Q1eHdVV2lTQWhobnlHWGNrNTM4Q0dGTWs4egpRc1JSRTk1anA0Ny9BU28vMzlYUWhs
+b1FUdmxlV0JLUUM2MHl2YU1oVEM1eHR6ZEtwRUlYK0hNazVGTlMrcDJVCmxtL3AzVE1YWDl1bmc5
+Mk9pTzUzV1VreFpQN2cwTVJHbGJrNzhqc1dkdjFYY0tLRjhuVmU5WC9NR1lTYlVLNy8KM2NYazFR
+WTRUdVZaQlBFSE12RFRpWWwxbmdDd1ZuL2MyY3JQU3hJRFdFWlhEdm90SFUwQkNQZURVckxGa0F5
+cQpDaloza3MzdEh4am42STkraEVNcUJDMzY1MHFjdDNkZ0RVV2loc2MzdVFLQmdRRG1mVTNKc29K
+QWFOdmxCbXgyClhzRDRqbXlXV1F2Z244cVNVNG03a2JKdmprcUJ6VnB0T0ZsYmk2ejZHOXR6ZHNX
+a0dJSjh3T0ZRb1hlM0dKOFIKSlVpeEFXTWZOM1JURGo5VjVXbzZJdE5EbzM1N3dNbVVYOW1qeThF
+YXp0RE1BckdSNGJva0Q5RjY3clhqSGdSMQpaZVcvSDlUWHFUV1l4VHl6UDB3ZDBQeUZ4d0tCZ1FE
+T0swWHVQS0o0WG00WmFCemN0OTdETXdDcFBSVmVvUWU3CmkzQjRJQ3orWFZ4cVM2amFTY2xNeEVm
+Nk5tM2tLNERDR1dwVkpXcm9qNjlMck1KWnQzTlI2VUJ5NzNqUVBSamsKRXk5N3YrR04yVGwwNjFw
+ZUxUM0dRS2RhT2VxWldpdElOcFc1dUxHL1poMGhoRUY5c1lSVTRtUFYwUWpla2kvdgp1bnVmcWx0
+TmFRS0JnQTl6TE1pdFg0L0R0NkcxZVlYQnVqdXZDRlpYcDdVcDRPRklHajVwZU1XRGl6a0NNK0tJ
+CldXMEtndERORnp1NUpXeG5mQyt5bWlmV2V2alovS2Vna1N2VVJQbXR0TzF3VWd5RzhVVHVXcXo1
+QTV4MkFzMGcKVTYxb0ZneWUrbDRDZkRha0k5OFE5R0RDS1kwTTBRMnhnK0g0MTBLUmhCYzJlV2dt
+Z1FxcW5KSzNBb0dCQU1rZgpnOWZXQlBVQndjdzlPYkxFR0tjNkVSSUlTZG1IbytCOE5kcXFJTnAv
+djFEZXdEazZ0QXFVakZiMlZCdTdxSjh4ClpmN3NRcS9ldzdaQ01WS09XUXgyVEc0VFdUdGo3dTFJ
+SGhGTjdiNlFRN0hnaXNiR3diV3VpdFBGSGl3OXYyMXgKK253MFJnb2VscHFFeDlMVG92R2Y3SjdB
+ampONlR4TkJTNnBGNlUzSkFvR0JBT0tnbHlRWDJpVG5oMXd4RG1TVQo4RXhoQVN3S09iNS8yRmx4
+aUhtUHVjNTZpS0tHY0lkV1cxMUdtbzdJakNzSTNvRm9iRkFjKzBXZkMvQTdMNWlmCjNBYVNWcmh0
+cThRRklRaUtyYUQ0YlRtRk9Famg5QVVtUHMrWnd1OE9lSXJBSWtwZDV3YmlhTEJLd0pRbVdtSFAK
+dUNBRTA3cXlSWXJ0c3QvcnVSSG5IdFA1Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
--- /dev/null
+LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2QUlCQURBTkJna3Foa2lHOXcwQkFRRUZB
+QVNDQktZd2dnU2lBZ0VBQW9JQkFRQzJ2Nk1oZHg3a3VjUHIKbmtFNFIrY3FnV2Y5T3B1c2h2M2o3
+SG50K08wdi84d2l2T1BFNTlLMHYvRWJOOG94TDZEWUNXU0JCRU4vREJ5MgpMUTYwbldSdHBZN2Ju
+bEcrcEtVeTRvSDRNZXZCR2JqZUhrak9LU3dNYVVWNGs4UmVSSjg4cVZ1U1MxSnVORW1NCmd5SERF
+NGFPNG5ndG5UUFZZdzUydVBIcG1rN0E4VFdXN2lLZE5JWWZWOCtuR1pENXIzRWllekRsUUNORG54
+UkcKdm5uSFZ6VFhZR3RwY2xaeWlJclpVekpBNFFPZnRueXB5UDVrQS94NVM1MU9QeGFxWlA3eGtP
+S0NicUUvZmZvMApFTi9rTno0N0ZoUGUxbVBHUkZZWldHZXg0aWFPdHlLdHhnU1FYYkdlNEVoeVR4
+SjJlT3U4QUVoVklTdjh6UU9nClNtbWx2UGQvQWdNQkFBRUNnZ0VBUUFpRERRRlR3bG96QTVhMmpK
+VnBNdlFYNzF0L1c2TUxTRGMrZS90cWhKU1IKUHlUSGZHR3NhMmdMLy9qNjhHUWJiRWRTUDRDeWM4
+eFhMU0E1bEdESDVVR0svbm9KYzQ3MlVZK2JjYzl3SjMrdgpUcWoyNHNIN2JMZmdQMEVybjhwVXIy
+azZMRmNYSVlWUnRobm1sUmQ4NFFrS2loVVlxZTdsRFFWOXdsZ3V1eHpRCnBmVEtDTWk1bXJlYjIx
+OExHS0QrMUxjVmVYZjExamc3Z2JnMllLZ1dOQ2R3VmIyUzJ5V0hTTjBlT3hPd21kWXIKSUVCekpG
+eEc2MFJxSlJ1RzVIam9iemc2cy9ycUo1THFta3JhUWh6bHFPQVZLblpGOHppbG9vcDhXUXBQY3RN
+cwp0cHBjczhtYkFkWHpoSTVjN0U1VVpDM2NJcEd6SE4raDZLK0F3R3ZEeVFLQmdRRDRBOTdQM29v
+dGhoMHZHQmFWCnZWOXhHTm1YbW5TeUg0b29HcmJvaG1JWkkwVlhZdms5dWViSUJjbDZRMUx4WnN3
+UFpRMVh5TUhpTjY1Z0E1emgKai9HZGcrdDlvcU5CZml0TUFrUTl1aWxvaXlKVWhYblk5REMvRitl
+ZksycEpNbHdkci9qWEttRHpkQUZBVDgyWQpWRmJ3MlpLVi9GNEJNMUtCdDBZN0RPTmlad0tCZ1FD
+OG9kZk0waytqL25VSzQ4TEV2MGtGbVNMdWdnTVlkM3hVCmZibmx0cUhFTVpJZU45OFVHK2hBWEdw
+dU1Ya0JPM2Mwcm5ZRDVXZkNBRzFxT1V2ZTZzdHd6N0VuK3hWdlkvcWEKU3ZTaDRzMzhnZlBIeXhR
+aGJvNWRwQTZUT3pwT0MyVi9rVXBVRUdJSmVVVllhQ05uWXNpUjRWUGVWL1lvR1htSwpQV29KbnAw
+REtRS0JnQlk3cXBheDJXczVVWlp1TDJBZkNOWkhwd0hySzdqb0VPZUZkWTRrdGRpUkM5OUlsUlZP
+CmUvekVZQXBnektldFVtK3kzRjVaTmVCRW81SWg0TWRyc3ZvdTRFWno5UFNqRGRpVGYzQ1ZKcThq
+Z2VGWDBkTjgKR0g2WTh2K1cwY0ZjRFZ2djhYdkFaYzZOUUt0Mk8vVUM0b1JXek1nN1JtWVBKcjlR
+SWJDYmVDclRBb0dBTjdZbApJbDFMSUVoYkVTaExzZ2c4N09aWnBzL0hVa2FYOWV4Y0p6aFZkcmlk
+UzBkOUgxZE90Uk9XYTQwNUMrQWdTUEx0CjhDQ2xFR3RINVlPZW9Pdi93Z1hWY05WN2N6YTRJVEhh
+SnFYeDZJNEpEZzB3bU44cU5RWHJPQmphRTRyU0kyY3AKNk1JZDhtWmEwTTJSQjB2cHFRdy8xUDl0
+dUZJdHoySnNHd001cEdFQ2dZQVVnQVV3WENBcEtZVkZFRmxHNlBhYwpvdTBhdzdGNm1aMi9NNUcv
+ek9tMHFDYnNXdGFNU09TdUEvNmlVOXB0NDBaWUFONFUvd2ZxbncyVkVoRnA3dzFNCnpZWmJCRDBx
+ZVlkcDRmc1NuWXFMZmJBVmxQLzB6dmEzdkwwMlJFa25WalBVSnAvaGpKVWhBK21WN252VDZ5VjQK
+cTg4SWVvOEx3Q1c1c2Jtd2lyU3Btdz09Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
--- /dev/null
+LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FR
+OEFNSUlCQ2dLQ0FRRUF1Wi9LbXFPVElEeUVGckFNbkhjTAo2YnArek9Na0NLQlA2Kzh3QlM1bW56
+MXEyZThHMllnZGpSZWRWaHZDZko4Z2dRZTl3eXEzMmNwUGJHZ01GbEowCkJwTGJXcUJNMWh1a1JO
+KzdzRC9TVk56bDd5RXVkR3JQeVI3aHVrVG0ydWxnamZHYnpPM1hZNkM5U1JYbTZVU0sKK1ZmK2xQ
+aXkxNUViVEdsdDBiOHppMThqRERuZC9CdFlCclZVeHE2TGMrWHJWL2pBbEVvalUyZy9WTzFnM3Jw
+WApyb3RrNFBrZXkzWVNNZTN1UGFSME44TTVFcXNVL0I0NFVyMHhJeHpIa2V0dEloeEhxSjNPNWRi
+b0k3SEdhVFNoCnJ6TUpydjBvNWwyYzBTVGIrVFVpcXFSSUFXTHhhblNzRTcxdk5Zc3lBS084MmRp
+OCtObDZKN0NHNzk1Slg0eTUKbndJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
--- /dev/null
+LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FR
+OEFNSUlCQ2dLQ0FRRUF0citqSVhjZTVMbkQ2NTVCT0VmbgpLb0ZuL1RxYnJJYjk0K3g1N2ZqdEwv
+L01JcnpqeE9mU3RML3hHemZLTVMrZzJBbGtnUVJEZnd3Y3RpME90SjFrCmJhV08yNTVSdnFTbE11
+S0IrREhyd1JtNDNoNUl6aWtzREdsRmVKUEVYa1NmUEtsYmtrdFNialJKaklNaHd4T0cKanVKNExa
+MHoxV01PZHJqeDZacE93UEUxbHU0aW5UU0dIMWZQcHhtUSthOXhJbnN3NVVBalE1OFVScjU1eDFj
+MAoxMkJyYVhKV2NvaUsyVk15UU9FRG43WjhxY2orWkFQOGVVdWRUajhXcW1UKzhaRGlnbTZoUDMz
+Nk5CRGY1RGMrCk94WVQzdFpqeGtSV0dWaG5zZUltanJjaXJjWUVrRjJ4bnVCSWNrOFNkbmpydkFC
+SVZTRXIvTTBEb0VwcHBiejMKZndJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+if want_ukify and want_tests != 'false'
+ test('test-ukify',
+ files('test_ukify.py'),
+ env : test_env)
+endif
--- /dev/null
+[tool:pytest]
+addopts = --flakes
--- /dev/null
+#!/usr/bin/env python3
+# SPDX-License-Identifier: LGPL-2.1+
+
+# pylint: disable=missing-docstring,redefined-outer-name,invalid-name
+# pylint: disable=unused-import,import-outside-toplevel,useless-else-on-loop
+# pylint: disable=consider-using-with,wrong-import-position,unspecified-encoding
+
+import base64
+import json
+import os
+import pathlib
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+
+try:
+ import pytest
+except ImportError:
+ sys.exit(77)
+
+try:
+ # pyflakes: noqa
+ import pefile # noqa
+except ImportError:
+ sys.exit(77)
+
+# We import ukify.py, which is a template file. But only __version__ is
+# substituted, which we don't care about here. Having the .py suffix makes it
+# easier to import the file.
+sys.path.append(os.path.dirname(__file__) + '/..')
+import ukify
+
+
+def test_guess_efi_arch():
+ arch = ukify.guess_efi_arch()
+ assert arch in ukify.EFI_ARCHES
+
+def test_shell_join():
+ assert ukify.shell_join(['a', 'b', ' ']) == "a b ' '"
+
+def test_round_up():
+ assert ukify.round_up(0) == 0
+ assert ukify.round_up(4095) == 4096
+ assert ukify.round_up(4096) == 4096
+ assert ukify.round_up(4097) == 8192
+
+def test_parse_args_minimal():
+ opts = ukify.parse_args('arg1 arg2'.split())
+ assert opts.linux == pathlib.Path('arg1')
+ assert opts.initrd == [pathlib.Path('arg2')]
+ assert opts.os_release in (pathlib.Path('/etc/os-release'),
+ pathlib.Path('/usr/lib/os-release'))
+
+def test_parse_args_many():
+ opts = ukify.parse_args(
+ ['/ARG1', '///ARG2', '/ARG3 WITH SPACE',
+ '--cmdline=a b c',
+ '--os-release=K1=V1\nK2=V2',
+ '--devicetree=DDDDTTTT',
+ '--splash=splash',
+ '--pcrpkey=PATH',
+ '--uname=1.2.3',
+ '--stub=STUBPATH',
+ '--pcr-private-key=PKEY1',
+ '--pcr-public-key=PKEY2',
+ '--pcr-banks=SHA1,SHA256',
+ '--signing-engine=ENGINE',
+ '--secureboot-private-key=SBKEY',
+ '--secureboot-certificate=SBCERT',
+ '--sign-kernel',
+ '--no-sign-kernel',
+ '--tools=TOOLZ///',
+ '--output=OUTPUT',
+ '--measure',
+ '--no-measure',
+ ])
+ assert opts.linux == pathlib.Path('/ARG1')
+ assert opts.initrd == [pathlib.Path('/ARG2'), pathlib.Path('/ARG3 WITH SPACE')]
+ assert opts.os_release == 'K1=V1\nK2=V2'
+ assert opts.devicetree == pathlib.Path('DDDDTTTT')
+ assert opts.splash == pathlib.Path('splash')
+ assert opts.pcrpkey == pathlib.Path('PATH')
+ assert opts.uname == '1.2.3'
+ assert opts.stub == pathlib.Path('STUBPATH')
+ assert opts.pcr_private_keys == [pathlib.Path('PKEY1')]
+ assert opts.pcr_public_keys == [pathlib.Path('PKEY2')]
+ assert opts.pcr_banks == ['SHA1', 'SHA256']
+ assert opts.signing_engine == 'ENGINE'
+ assert opts.sb_key == 'SBKEY'
+ assert opts.sb_cert == 'SBCERT'
+ assert opts.sign_kernel is False
+ assert opts.tools == pathlib.Path('TOOLZ/')
+ assert opts.output == pathlib.Path('OUTPUT')
+ assert opts.measure is False
+
+def test_parse_sections():
+ opts = ukify.parse_args(
+ ['/ARG1', '/ARG2',
+ '--section=test:TESTTESTTEST',
+ '--section=test2:@FILE',
+ ])
+
+ assert opts.linux == pathlib.Path('/ARG1')
+ assert opts.initrd == [pathlib.Path('/ARG2')]
+ assert len(opts.sections) == 2
+
+ assert opts.sections[0].name == 'test'
+ assert isinstance(opts.sections[0].content, pathlib.Path)
+ assert opts.sections[0].tmpfile
+ assert opts.sections[0].offset is None
+ assert opts.sections[0].measure is False
+
+ assert opts.sections[1].name == 'test2'
+ assert opts.sections[1].content == pathlib.Path('FILE')
+ assert opts.sections[1].tmpfile is None
+ assert opts.sections[1].offset is None
+ assert opts.sections[1].measure is False
+
+def test_help(capsys):
+ with pytest.raises(SystemExit):
+ ukify.parse_args(['--help'])
+ out = capsys.readouterr()
+ assert '--section' in out.out
+ assert not out.err
+
+def test_help_error(capsys):
+ with pytest.raises(SystemExit):
+ ukify.parse_args(['a', 'b', '--no-such-option'])
+ out = capsys.readouterr()
+ assert not out.out
+ assert '--no-such-option' in out.err
+ assert len(out.err.splitlines()) == 1
+
+@pytest.fixture(scope='session')
+def kernel_initrd():
+ try:
+ text = subprocess.check_output(['bootctl', 'list', '--json=short'],
+ text=True)
+ except subprocess.CalledProcessError:
+ return None
+
+ items = json.loads(text)
+
+ for item in items:
+ try:
+ linux = f"{item['root']}{item['linux']}"
+ initrd = f"{item['root']}{item['initrd'][0]}"
+ except (KeyError, IndexError):
+ pass
+ return [linux, initrd]
+ else:
+ return None
+
+def test_check_splash():
+ try:
+ # pyflakes: noqa
+ import PIL # noqa
+ except ImportError:
+ pytest.skip('PIL not available')
+
+ with pytest.raises(OSError):
+ ukify.check_splash(os.devnull)
+
+def test_basic_operation(kernel_initrd, tmpdir):
+ if kernel_initrd is None:
+ pytest.skip('linux+initrd not found')
+
+ output = f'{tmpdir}/basic.efi'
+ opts = ukify.parse_args(kernel_initrd + [f'--output={output}'])
+ try:
+ ukify.check_inputs(opts)
+ except OSError as e:
+ pytest.skip(str(e))
+
+ ukify.make_uki(opts)
+
+ # let's check that objdump likes the resulting file
+ subprocess.check_output(['objdump', '-h', output])
+
+def test_sections(kernel_initrd, tmpdir):
+ if kernel_initrd is None:
+ pytest.skip('linux+initrd not found')
+
+ output = f'{tmpdir}/basic.efi'
+ opts = ukify.parse_args([
+ *kernel_initrd,
+ f'--output={output}',
+ '--uname=1.2.3',
+ '--cmdline=ARG1 ARG2 ARG3',
+ '--os-release=K1=V1\nK2=V2\n',
+ '--section=.test:CONTENTZ',
+ ])
+
+ try:
+ ukify.check_inputs(opts)
+ except OSError as e:
+ pytest.skip(str(e))
+
+ ukify.make_uki(opts)
+
+ # let's check that objdump likes the resulting file
+ dump = subprocess.check_output(['objdump', '-h', output], text=True)
+
+ for sect in 'text osrel cmdline linux initrd uname test'.split():
+ assert re.search(fr'^\s*\d+\s+.{sect}\s+0', dump, re.MULTILINE)
+
+
+def unbase64(filename):
+ tmp = tempfile.NamedTemporaryFile()
+ base64.decode(filename.open('rb'), tmp)
+ tmp.flush()
+ return tmp
+
+
+def test_uname_scraping(kernel_initrd):
+ if kernel_initrd is None:
+ pytest.skip('linux+initrd not found')
+
+ uname = ukify.Uname.scrape(kernel_initrd[0])
+ assert re.match(r'\d+\.\d+\.\d+', uname)
+
+
+def test_efi_signing(kernel_initrd, tmpdir):
+ if kernel_initrd is None:
+ pytest.skip('linux+initrd not found')
+ if not shutil.which('sbsign'):
+ pytest.skip('sbsign not found')
+
+ ourdir = pathlib.Path(__file__).parent
+ cert = unbase64(ourdir / 'example.signing.crt.base64')
+ key = unbase64(ourdir / 'example.signing.key.base64')
+
+ output = f'{tmpdir}/signed.efi'
+ opts = ukify.parse_args([
+ *kernel_initrd,
+ f'--output={output}',
+ '--uname=1.2.3',
+ '--cmdline=ARG1 ARG2 ARG3',
+ f'--secureboot-certificate={cert.name}',
+ f'--secureboot-private-key={key.name}',
+ ])
+
+ try:
+ ukify.check_inputs(opts)
+ except OSError as e:
+ pytest.skip(str(e))
+
+ ukify.make_uki(opts)
+
+ if shutil.which('sbverify'):
+ # let's check that sbverify likes the resulting file
+ dump = subprocess.check_output([
+ 'sbverify',
+ '--cert', cert.name,
+ output,
+ ], text=True)
+
+ assert 'Signature verification OK' in dump
+
+def test_pcr_signing(kernel_initrd, tmpdir):
+ if kernel_initrd is None:
+ pytest.skip('linux+initrd not found')
+ if os.getuid() != 0:
+ pytest.skip('must be root to access tpm2')
+ if subprocess.call(['systemd-creds', 'has-tpm2', '-q']) != 0:
+ pytest.skip('tpm2 is not available')
+
+ ourdir = pathlib.Path(__file__).parent
+ pub = unbase64(ourdir / 'example.tpm2-pcr-public.pem.base64')
+ priv = unbase64(ourdir / 'example.tpm2-pcr-private.pem.base64')
+
+ output = f'{tmpdir}/signed.efi'
+ opts = ukify.parse_args([
+ *kernel_initrd,
+ f'--output={output}',
+ '--uname=1.2.3',
+ '--cmdline=ARG1 ARG2 ARG3',
+ '--os-release=ID=foobar\n',
+ '--pcr-banks=sha1', # use sha1 as that is most likely to be supported
+ f'--pcrpkey={pub.name}',
+ f'--pcr-public-key={pub.name}',
+ f'--pcr-private-key={priv.name}',
+ ])
+
+ try:
+ ukify.check_inputs(opts)
+ except OSError as e:
+ pytest.skip(str(e))
+
+ ukify.make_uki(opts)
+
+ # let's check that objdump likes the resulting file
+ dump = subprocess.check_output(['objdump', '-h', output], text=True)
+
+ for sect in 'text osrel cmdline linux initrd uname pcrsig'.split():
+ assert re.search(fr'^\s*\d+\s+.{sect}\s+0', dump, re.MULTILINE)
+
+ # objcopy fails when called without an output argument (EPERM).
+ # It also fails when called with /dev/null (file truncated).
+ # It also fails when called with /dev/zero (because it reads the
+ # output file, infinitely in this case.)
+ # So let's just call it with a dummy output argument.
+ subprocess.check_call([
+ 'objcopy',
+ *(f'--dump-section=.{n}={tmpdir}/out.{n}' for n in (
+ 'pcrpkey', 'pcrsig', 'osrel', 'uname', 'cmdline')),
+ output,
+ tmpdir / 'dummy',
+ ],
+ text=True)
+
+ assert open(tmpdir / 'out.pcrpkey').read() == open(pub.name).read()
+ assert open(tmpdir / 'out.osrel').read() == 'ID=foobar\n'
+ assert open(tmpdir / 'out.uname').read() == '1.2.3'
+ assert open(tmpdir / 'out.cmdline').read() == 'ARG1 ARG2 ARG3'
+ sig = open(tmpdir / 'out.pcrsig').read()
+ sig = json.loads(sig)
+ assert list(sig.keys()) == ['sha1']
+ assert len(sig['sha1']) == 4 # four items for four phases
+
+def test_pcr_signing2(kernel_initrd, tmpdir):
+ if kernel_initrd is None:
+ pytest.skip('linux+initrd not found')
+ if os.getuid() != 0:
+ pytest.skip('must be root to access tpm2')
+ if subprocess.call(['systemd-creds', 'has-tpm2', '-q']) != 0:
+ pytest.skip('tpm2 is not available')
+
+ ourdir = pathlib.Path(__file__).parent
+ pub = unbase64(ourdir / 'example.tpm2-pcr-public.pem.base64')
+ priv = unbase64(ourdir / 'example.tpm2-pcr-private.pem.base64')
+ pub2 = unbase64(ourdir / 'example.tpm2-pcr-public2.pem.base64')
+ priv2 = unbase64(ourdir / 'example.tpm2-pcr-private2.pem.base64')
+
+ # simulate a microcode file
+ with open(f'{tmpdir}/microcode', 'wb') as microcode:
+ microcode.write(b'1234567890')
+
+ output = f'{tmpdir}/signed.efi'
+ opts = ukify.parse_args([
+ kernel_initrd[0], microcode.name, kernel_initrd[1],
+ f'--output={output}',
+ '--uname=1.2.3',
+ '--cmdline=ARG1 ARG2 ARG3',
+ '--os-release=ID=foobar\n',
+ '--pcr-banks=sha1', # use sha1 as that is most likely to be supported
+ f'--pcrpkey={pub2.name}',
+ f'--pcr-public-key={pub.name}',
+ f'--pcr-private-key={priv.name}',
+ '--phases=enter-initrd enter-initrd:leave-initrd',
+ f'--pcr-public-key={pub2.name}',
+ f'--pcr-private-key={priv2.name}',
+ '--phases=sysinit ready shutdown final', # yes, those phase paths are not reachable
+ ])
+
+ try:
+ ukify.check_inputs(opts)
+ except OSError as e:
+ pytest.skip(str(e))
+
+ ukify.make_uki(opts)
+
+ # let's check that objdump likes the resulting file
+ dump = subprocess.check_output(['objdump', '-h', output], text=True)
+
+ for sect in 'text osrel cmdline linux initrd uname pcrsig'.split():
+ assert re.search(fr'^\s*\d+\s+.{sect}\s+0', dump, re.MULTILINE)
+
+ subprocess.check_call([
+ 'objcopy',
+ *(f'--dump-section=.{n}={tmpdir}/out.{n}' for n in (
+ 'pcrpkey', 'pcrsig', 'osrel', 'uname', 'cmdline', 'initrd')),
+ output,
+ tmpdir / 'dummy',
+ ],
+ text=True)
+
+ assert open(tmpdir / 'out.pcrpkey').read() == open(pub2.name).read()
+ assert open(tmpdir / 'out.osrel').read() == 'ID=foobar\n'
+ assert open(tmpdir / 'out.uname').read() == '1.2.3'
+ assert open(tmpdir / 'out.cmdline').read() == 'ARG1 ARG2 ARG3'
+ assert open(tmpdir / 'out.initrd', 'rb').read(10) == b'1234567890'
+
+ sig = open(tmpdir / 'out.pcrsig').read()
+ sig = json.loads(sig)
+ assert list(sig.keys()) == ['sha1']
+ assert len(sig['sha1']) == 6 # six items for six phases paths
+
+if __name__ == '__main__':
+ pytest.main([__file__, '-v'])
--- /dev/null
+#!/usr/bin/env python3
+# SPDX-License-Identifier: LGPL-2.1+
+
+# pylint: disable=missing-docstring,invalid-name,import-outside-toplevel
+# pylint: disable=consider-using-with,unspecified-encoding,line-too-long
+# pylint: disable=too-many-locals,too-many-statements,too-many-return-statements
+# pylint: disable=too-many-branches
+
+import argparse
+import collections
+import dataclasses
+import fnmatch
+import itertools
+import json
+import os
+import pathlib
+import re
+import shlex
+import subprocess
+import tempfile
+import typing
+
+
+__version__ = '{{GIT_VERSION}}'
+
+EFI_ARCH_MAP = {
+ # host_arch glob : [efi_arch, 32_bit_efi_arch if mixed mode is supported]
+ 'x86_64' : ['x64', 'ia32'],
+ 'i[3456]86' : ['ia32'],
+ 'aarch64' : ['aa64'],
+ 'arm[45678]*l' : ['arm'],
+ 'riscv64' : ['riscv64'],
+}
+EFI_ARCHES: list[str] = sum(EFI_ARCH_MAP.values(), [])
+
+def guess_efi_arch():
+ arch = os.uname().machine
+
+ for glob, mapping in EFI_ARCH_MAP.items():
+ if fnmatch.fnmatch(arch, glob):
+ efi_arch, *fallback = mapping
+ break
+ else:
+ raise ValueError(f'Unsupported architecture {arch}')
+
+ # This makes sense only on some architectures, but it also probably doesn't
+ # hurt on others, so let's just apply the check everywhere.
+ if fallback:
+ fw_platform_size = pathlib.Path('/sys/firmware/efi/fw_platform_size')
+ try:
+ size = fw_platform_size.read_text().strip()
+ except FileNotFoundError:
+ pass
+ else:
+ if int(size) == 32:
+ efi_arch = fallback[0]
+
+ print(f'Host arch {arch!r}, EFI arch {efi_arch!r}')
+ return efi_arch
+
+
+def shell_join(cmd):
+ # TODO: drop in favour of shlex.join once shlex.join supports pathlib.Path.
+ return ' '.join(shlex.quote(str(x)) for x in cmd)
+
+
+def pe_executable_size(filename):
+ import pefile
+
+ pe = pefile.PE(filename)
+ section = pe.sections[-1]
+ return section.VirtualAddress + section.Misc_VirtualSize
+
+
+def round_up(x, blocksize=4096):
+ return (x + blocksize - 1) // blocksize * blocksize
+
+
+def maybe_decompress(filename):
+ """Decompress file if compressed. Return contents."""
+ f = open(filename, 'rb')
+ start = f.read(4)
+ f.seek(0)
+
+ if start.startswith(b'\x7fELF'):
+ # not compressed
+ return f.read()
+
+ if start.startswith(b'\x1f\x8b'):
+ import gzip
+ return gzip.open(f).read()
+
+ if start.startswith(b'\x28\xb5\x2f\xfd'):
+ import zstd
+ return zstd.uncompress(f.read())
+
+ if start.startswith(b'\x02\x21\x4c\x18'):
+ import lz4.frame
+ return lz4.frame.decompress(f.read())
+
+ if start.startswith(b'\x04\x22\x4d\x18'):
+ print('Newer lz4 stream format detected! This may not boot!')
+ import lz4.frame
+ return lz4.frame.decompress(f.read())
+
+ if start.startswith(b'\x89LZO'):
+ # python3-lzo is not packaged for Fedora
+ raise NotImplementedError('lzo decompression not implemented')
+
+ if start.startswith(b'BZh'):
+ import bz2
+ return bz2.open(f).read()
+
+ if start.startswith(b'\x5d\x00\x00'):
+ import lzma
+ return lzma.open(f).read()
+
+ raise NotImplementedError(f'unknown file format (starts with {start})')
+
+
+class Uname:
+ # This class is here purely as a namespace for the functions
+
+ VERSION_PATTERN = r'(?P<version>[a-z0-9._-]+) \([^ )]+\) (?:#.*)'
+
+ NOTES_PATTERN = r'^\s+Linux\s+0x[0-9a-f]+\s+OPEN\n\s+description data: (?P<version>[0-9a-f ]+)\s*$'
+
+ # Linux version 6.0.8-300.fc37.ppc64le (mockbuild@buildvm-ppc64le-03.iad2.fedoraproject.org)
+ # (gcc (GCC) 12.2.1 20220819 (Red Hat 12.2.1-2), GNU ld version 2.38-24.fc37)
+ # #1 SMP Fri Nov 11 14:39:11 UTC 2022
+ TEXT_PATTERN = rb'Linux version (?P<version>\d\.\S+) \('
+
+ @classmethod
+ def scrape_x86(cls, filename, opts=None):
+ # Based on https://gitlab.archlinux.org/archlinux/mkinitcpio/mkinitcpio/-/blob/master/functions#L136
+ # and https://www.kernel.org/doc/html/latest/x86/boot.html#the-real-mode-kernel-header
+ with open(filename, 'rb') as f:
+ f.seek(0x202)
+ magic = f.read(4)
+ if magic != b'HdrS':
+ raise ValueError('Real-Mode Kernel Header magic not found')
+ f.seek(0x20E)
+ offset = f.read(1)[0] + f.read(1)[0]*256 # Pointer to kernel version string
+ f.seek(0x200 + offset)
+ text = f.read(128)
+ text = text.split(b'\0', maxsplit=1)[0]
+ text = text.decode()
+
+ if not (m := re.match(cls.VERSION_PATTERN, text)):
+ raise ValueError(f'Cannot parse version-host-release uname string: {text!r}')
+ return m.group('version')
+
+ @classmethod
+ def scrape_elf(cls, filename, opts=None):
+ readelf = find_tool('readelf', opts=opts)
+
+ cmd = [
+ readelf,
+ '--notes',
+ filename,
+ ]
+
+ print('+', shell_join(cmd))
+ notes = subprocess.check_output(cmd, text=True)
+
+ if not (m := re.search(cls.NOTES_PATTERN, notes, re.MULTILINE)):
+ raise ValueError('Cannot find Linux version note')
+
+ text = ''.join(chr(int(c, 16)) for c in m.group('version').split())
+ return text.rstrip('\0')
+
+ @classmethod
+ def scrape_generic(cls, filename, opts=None):
+ # import libarchive
+ # libarchive-c fails with
+ # ArchiveError: Unrecognized archive format (errno=84, retcode=-30, archive_p=94705420454656)
+
+ # Based on https://gitlab.archlinux.org/archlinux/mkinitcpio/mkinitcpio/-/blob/master/functions#L209
+
+ text = maybe_decompress(filename)
+ if not (m := re.search(cls.TEXT_PATTERN, text)):
+ raise ValueError(f'Cannot find {cls.TEXT_PATTERN!r} in {filename}')
+
+ return m.group('version').decode()
+
+ @classmethod
+ def scrape(cls, filename, opts=None):
+ for func in (cls.scrape_x86, cls.scrape_elf, cls.scrape_generic):
+ try:
+ version = func(filename, opts=opts)
+ print(f'Found uname version: {version}')
+ return version
+ except ValueError as e:
+ print(str(e))
+ return None
+
+
+@dataclasses.dataclass
+class Section:
+ name: str
+ content: pathlib.Path
+ tmpfile: typing.IO | None = None
+ flags: list[str] | None = dataclasses.field(default=None)
+ offset: int | None = None
+ measure: bool = False
+
+ @classmethod
+ def create(cls, name, contents, flags=None, measure=False):
+ if isinstance(contents, str | bytes):
+ mode = 'wt' if isinstance(contents, str) else 'wb'
+ tmp = tempfile.NamedTemporaryFile(mode=mode, prefix=f'tmp{name}')
+ tmp.write(contents)
+ tmp.flush()
+ contents = pathlib.Path(tmp.name)
+ else:
+ tmp = None
+
+ return cls(name, contents, tmpfile=tmp, flags=flags, measure=measure)
+
+ @classmethod
+ def parse_arg(cls, s):
+ try:
+ name, contents, *rest = s.split(':')
+ except ValueError as e:
+ raise ValueError(f'Cannot parse section spec (name or contents missing): {s!r}') from e
+ if rest:
+ raise ValueError(f'Cannot parse section spec (extraneous parameters): {s!r}')
+
+ if contents.startswith('@'):
+ contents = pathlib.Path(contents[1:])
+
+ return cls.create(name, contents)
+
+ def size(self):
+ return self.content.stat().st_size
+
+ def check_name(self):
+ # PE section names with more than 8 characters are legal, but our stub does
+ # not support them.
+ if not self.name.isascii() or not self.name.isprintable():
+ raise ValueError(f'Bad section name: {self.name!r}')
+ if len(self.name) > 8:
+ raise ValueError(f'Section name too long: {self.name!r}')
+
+
+@dataclasses.dataclass
+class UKI:
+ executable: list[pathlib.Path|str]
+ sections: list[Section] = dataclasses.field(default_factory=list, init=False)
+ offset: int | None = dataclasses.field(default=None, init=False)
+
+ def __post_init__(self):
+ self.offset = round_up(pe_executable_size(self.executable))
+
+ def add_section(self, section):
+ assert self.offset
+ assert section.offset is None
+
+ if section.name in [s.name for s in self.sections]:
+ raise ValueError(f'Duplicate section {section.name}')
+
+ section.offset = self.offset
+ self.offset += round_up(section.size())
+ self.sections += [section]
+
+
+def parse_banks(s):
+ banks = re.split(r',|\s+', s)
+ # TODO: do some sanity checking here
+ return banks
+
+
+KNOWN_PHASES = (
+ 'enter-initrd',
+ 'leave-initrd',
+ 'sysinit',
+ 'ready',
+ 'shutdown',
+ 'final',
+)
+
+def parse_phase_paths(s):
+ # Split on commas or whitespace here. Commas might be hard to parse visually.
+ paths = re.split(r',|\s+', s)
+
+ for path in paths:
+ for phase in path.split(':'):
+ if phase not in KNOWN_PHASES:
+ raise argparse.ArgumentTypeError(f'Unknown boot phase {phase!r} ({path=})')
+
+ return paths
+
+
+def check_splash(filename):
+ if filename is None:
+ return
+
+ # import is delayed, to avoid import when the splash image is not used
+ try:
+ from PIL import Image
+ except ImportError:
+ return
+
+ img = Image.open(filename, formats=['BMP'])
+ print(f'Splash image {filename} is {img.width}×{img.height} pixels')
+
+
+def check_inputs(opts):
+ for name, value in vars(opts).items():
+ if name in {'output', 'tools'}:
+ continue
+
+ if not isinstance(value, pathlib.Path):
+ continue
+
+ # Open file to check that we can read it, or generate an exception
+ value.open().close()
+
+ check_splash(opts.splash)
+
+
+def find_tool(name, fallback=None, opts=None):
+ if opts and opts.tools:
+ tool = opts.tools / name
+ if tool.exists():
+ return tool
+
+ return fallback or name
+
+
+def combine_signatures(pcrsigs):
+ combined = collections.defaultdict(list)
+ for pcrsig in pcrsigs:
+ for bank, sigs in pcrsig.items():
+ for sig in sigs:
+ if sig not in combined[bank]:
+ combined[bank] += [sig]
+ return json.dumps(combined)
+
+
+def call_systemd_measure(uki, linux, opts):
+ measure_tool = find_tool('systemd-measure',
+ '/usr/lib/systemd/systemd-measure',
+ opts=opts)
+
+ banks = opts.pcr_banks or ()
+
+ # PCR measurement
+
+ if opts.measure:
+ pp_groups = opts.phase_path_groups or []
+
+ cmd = [
+ measure_tool,
+ 'calculate',
+ f'--linux={linux}',
+ *(f"--{s.name.removeprefix('.')}={s.content}"
+ for s in uki.sections
+ if s.measure),
+ *(f'--bank={bank}'
+ for bank in banks),
+ # For measurement, the keys are not relevant, so we can lump all the phase paths
+ # into one call to systemd-measure calculate.
+ *(f'--phase={phase_path}'
+ for phase_path in itertools.chain.from_iterable(pp_groups)),
+ ]
+
+ print('+', shell_join(cmd))
+ subprocess.check_call(cmd)
+
+ # PCR signing
+
+ if opts.pcr_private_keys:
+ n_priv = len(opts.pcr_private_keys or ())
+ pp_groups = opts.phase_path_groups or [None] * n_priv
+ pub_keys = opts.pcr_public_keys or [None] * n_priv
+
+ pcrsigs = []
+
+ cmd = [
+ measure_tool,
+ 'sign',
+ f'--linux={linux}',
+ *(f"--{s.name.removeprefix('.')}={s.content}"
+ for s in uki.sections
+ if s.measure),
+ *(f'--bank={bank}'
+ for bank in banks),
+ ]
+
+ for priv_key, pub_key, group in zip(opts.pcr_private_keys,
+ pub_keys,
+ pp_groups):
+ extra = [f'--private-key={priv_key}']
+ if pub_key:
+ extra += [f'--public-key={pub_key}']
+ extra += [f'--phase={phase_path}' for phase_path in group or ()]
+
+ print('+', shell_join(cmd + extra))
+ pcrsig = subprocess.check_output(cmd + extra, text=True)
+ pcrsig = json.loads(pcrsig)
+ pcrsigs += [pcrsig]
+
+ combined = combine_signatures(pcrsigs)
+ uki.add_section(Section.create('.pcrsig', combined))
+
+
+def join_initrds(initrds):
+ match initrds:
+ case []:
+ return None
+ case [initrd]:
+ return initrd
+ case multiple:
+ seq = []
+ for file in multiple:
+ initrd = file.read_bytes()
+ padding = b'\0' * round_up(len(initrd), 4) # pad to 32 bit alignment
+ seq += [initrd, padding]
+
+ return b''.join(seq)
+
+ assert False
+
+
+def make_uki(opts):
+ # kernel payload signing
+
+ sbsign_tool = find_tool('sbsign', opts=opts)
+ sbsign_invocation = [
+ sbsign_tool,
+ '--key', opts.sb_key,
+ '--cert', opts.sb_cert,
+ ]
+
+ if opts.signing_engine is not None:
+ sbsign_invocation += ['--engine', opts.signing_engine]
+
+ sign_kernel = opts.sign_kernel
+ if sign_kernel is None and opts.sb_key:
+ # figure out if we should sign the kernel
+ sbverify_tool = find_tool('sbverify', opts=opts)
+
+ cmd = [
+ sbverify_tool,
+ '--list',
+ opts.linux,
+ ]
+
+ print('+', shell_join(cmd))
+ info = subprocess.check_output(cmd, text=True)
+
+ # sbverify has wonderful API
+ if 'No signature table present' in info:
+ sign_kernel = True
+
+ if sign_kernel:
+ linux_signed = tempfile.NamedTemporaryFile(prefix='linux-signed')
+ linux = linux_signed.name
+
+ cmd = [
+ *sbsign_invocation,
+ opts.linux,
+ '--output', linux,
+ ]
+
+ print('+', shell_join(cmd))
+ subprocess.check_call(cmd)
+ else:
+ linux = opts.linux
+
+ if opts.uname is None:
+ print('Kernel version not specified, starting autodetection 😖.')
+ opts.uname = Uname.scrape(opts.linux, opts=opts)
+
+ uki = UKI(opts.stub)
+ initrd = join_initrds(opts.initrd)
+
+ # TODO: derive public key from from opts.pcr_private_keys?
+ pcrpkey = opts.pcrpkey
+ if pcrpkey is None:
+ if opts.pcr_public_keys and len(opts.pcr_public_keys) == 1:
+ pcrpkey = opts.pcr_public_keys[0]
+
+ sections = [
+ # name, content, measure?
+ ('.osrel', opts.os_release, True ),
+ ('.cmdline', opts.cmdline, True ),
+ ('.dtb', opts.devicetree, True ),
+ ('.splash', opts.splash, True ),
+ ('.pcrpkey', pcrpkey, True ),
+ ('.initrd', initrd, True ),
+ ('.uname', opts.uname, False),
+
+ # linux shall be last to leave breathing room for decompression.
+ # We'll add it later.
+ ]
+
+ for name, content, measure in sections:
+ if content:
+ uki.add_section(Section.create(name, content, measure=measure))
+
+ # systemd-measure doesn't know about those extra sections
+ for section in opts.sections:
+ uki.add_section(section)
+
+ # PCR measurement and signing
+
+ call_systemd_measure(uki, linux, opts=opts)
+
+ # UKI creation
+
+ uki.add_section(
+ Section.create('.linux', linux, measure=True,
+ flags=['code', 'readonly']))
+
+ if opts.sb_key:
+ unsigned = tempfile.NamedTemporaryFile(prefix='uki')
+ output = unsigned.name
+ else:
+ output = opts.output
+
+ objcopy_tool = find_tool('objcopy', opts=opts)
+
+ cmd = [
+ objcopy_tool,
+ opts.stub,
+ *itertools.chain.from_iterable(
+ ('--add-section', f'{s.name}={s.content}',
+ '--change-section-vma', f'{s.name}=0x{s.offset:x}')
+ for s in uki.sections),
+ *itertools.chain.from_iterable(
+ ('--set-section-flags', f"{s.name}={','.join(s.flags)}")
+ for s in uki.sections
+ if s.flags is not None),
+ output,
+ ]
+ print('+', shell_join(cmd))
+ subprocess.check_call(cmd)
+
+ # UKI signing
+
+ if opts.sb_key:
+ cmd = [
+ *sbsign_invocation,
+ unsigned.name,
+ '--output', opts.output,
+ ]
+ print('+', shell_join(cmd))
+ subprocess.check_call(cmd)
+
+ # We end up with no executable bits, let's reapply them
+ os.umask(umask := os.umask(0))
+ os.chmod(opts.output, 0o777 & ~umask)
+
+ print(f"Wrote {'signed' if opts.sb_key else 'unsigned'} {opts.output}")
+
+
+def parse_args(args=None):
+ p = argparse.ArgumentParser(
+ description='Build and sign Unified Kernel Images',
+ allow_abbrev=False,
+ usage='''\
+usage: ukify [options…] linux initrd…
+ ukify -h | --help
+''')
+
+ # Suppress printing of usage synopsis on errors
+ p.error = lambda message: p.exit(2, f'{p.prog}: error: {message}\n')
+
+ p.add_argument('linux',
+ type=pathlib.Path,
+ help='vmlinuz file [.linux section]')
+ p.add_argument('initrd',
+ type=pathlib.Path,
+ nargs='*',
+ help='initrd files [.initrd section]')
+
+ p.add_argument('--cmdline',
+ metavar='TEXT|@PATH',
+ help='kernel command line [.cmdline section]')
+
+ p.add_argument('--os-release',
+ metavar='TEXT|@PATH',
+ help='path to os-release file [.osrel section]')
+
+ p.add_argument('--devicetree',
+ metavar='PATH',
+ type=pathlib.Path,
+ help='Device Tree file [.dtb section]')
+ p.add_argument('--splash',
+ metavar='BMP',
+ type=pathlib.Path,
+ help='splash image bitmap file [.splash section]')
+ p.add_argument('--pcrpkey',
+ metavar='KEY',
+ type=pathlib.Path,
+ help='embedded public key to seal secrets to [.pcrpkey section]')
+ p.add_argument('--uname',
+ metavar='VERSION',
+ help='"uname -r" information [.uname section]')
+
+ p.add_argument('--efi-arch',
+ metavar='ARCH',
+ choices=('ia32', 'x64', 'arm', 'aa64', 'riscv64'),
+ help='target EFI architecture')
+
+ p.add_argument('--stub',
+ type=pathlib.Path,
+ help='path the the sd-stub file [.text,.data,… sections]')
+
+ p.add_argument('--section',
+ dest='sections',
+ metavar='NAME:TEXT|@PATH',
+ type=Section.parse_arg,
+ action='append',
+ default=[],
+ help='additional section as name and contents [NAME section]')
+
+ p.add_argument('--pcr-private-key',
+ dest='pcr_private_keys',
+ metavar='PATH',
+ type=pathlib.Path,
+ action='append',
+ help='private part of the keypair for signing PCR signatures')
+ p.add_argument('--pcr-public-key',
+ dest='pcr_public_keys',
+ metavar='PATH',
+ type=pathlib.Path,
+ action='append',
+ help='public part of the keypair for signing PCR signatures')
+ p.add_argument('--phases',
+ dest='phase_path_groups',
+ metavar='PHASE-PATH…',
+ type=parse_phase_paths,
+ action='append',
+ help='phase-paths to create signatures for')
+
+ p.add_argument('--pcr-banks',
+ metavar='BANK…',
+ type=parse_banks)
+
+ p.add_argument('--signing-engine',
+ metavar='ENGINE',
+ help='OpenSSL engine to use for signing')
+ p.add_argument('--secureboot-private-key',
+ dest='sb_key',
+ help='path to key file or engine-specific designation for SB signing')
+ p.add_argument('--secureboot-certificate',
+ dest='sb_cert',
+ help='path to certificate file or engine-specific designation for SB signing')
+
+ p.add_argument('--sign-kernel',
+ action=argparse.BooleanOptionalAction,
+ help='Sign the embedded kernel')
+
+ p.add_argument('--tools',
+ type=pathlib.Path,
+ help='a directory with systemd-measure and other tools')
+
+ p.add_argument('--output', '-o',
+ type=pathlib.Path,
+ help='output file path')
+
+ p.add_argument('--measure',
+ action=argparse.BooleanOptionalAction,
+ help='print systemd-measure output for the UKI')
+
+ p.add_argument('--version',
+ action='version',
+ version=f'ukify {__version__}')
+
+ opts = p.parse_args(args)
+
+ if opts.cmdline and opts.cmdline.startswith('@'):
+ opts.cmdline = pathlib.Path(opts.cmdline[1:])
+
+ if opts.os_release is not None and opts.os_release.startswith('@'):
+ opts.os_release = pathlib.Path(opts.os_release[1:])
+ elif opts.os_release is None:
+ p = pathlib.Path('/etc/os-release')
+ if not p.exists():
+ p = pathlib.Path('/usr/lib/os-release')
+ opts.os_release = p
+
+ if opts.efi_arch is None:
+ opts.efi_arch = guess_efi_arch()
+
+ if opts.stub is None:
+ opts.stub = f'/usr/lib/systemd/boot/efi/linux{opts.efi_arch}.efi.stub'
+
+ if opts.signing_engine is None:
+ opts.sb_key = pathlib.Path(opts.sb_key) if opts.sb_key else None
+ opts.sb_cert = pathlib.Path(opts.sb_cert) if opts.sb_cert else None
+
+ if bool(opts.sb_key) ^ bool(opts.sb_cert):
+ raise ValueError('--secureboot-private-key= and --secureboot-certificate= must be specified together')
+
+ if opts.sign_kernel and not opts.sb_key:
+ raise ValueError('--sign-kernel requires --secureboot-private-key= and --secureboot-certificate= to be specified')
+
+ n_pcr_pub = None if opts.pcr_public_keys is None else len(opts.pcr_public_keys)
+ n_pcr_priv = None if opts.pcr_private_keys is None else len(opts.pcr_private_keys)
+ n_phase_path_groups = None if opts.phase_path_groups is None else len(opts.phase_path_groups)
+ if n_pcr_pub is not None and n_pcr_pub != n_pcr_priv:
+ raise ValueError('--pcr-public-key= specifications must match --pcr-private-key=')
+ if n_phase_path_groups is not None and n_phase_path_groups != n_pcr_priv:
+ raise ValueError('--phases= specifications must match --pcr-private-key=')
+
+ if opts.output is None:
+ suffix = '.efi' if opts.sb_key else '.unsigned.efi'
+ opts.output = opts.linux.name + suffix
+
+ for section in opts.sections:
+ section.check_name()
+
+ return opts
+
+
+def main():
+ opts = parse_args()
+ check_inputs(opts)
+ make_uki(opts)
+
+
+if __name__ == '__main__':
+ main()
(void) sockaddr_un_unlink(&sockaddr.un);
- RUN_WITH_UMASK(0000)
+ WITH_UMASK(0000)
if (bind(m->listen_fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0)
return log_error_errno(errno, "Failed to bind socket: %m");
if (r < 0)
return log_error_errno(r, "Couldn't generate volatile sysroot directory: %m");
- r = mount_nofollow_verbose(LOG_ERR, "tmpfs", "/run/systemd/volatile-sysroot", "tmpfs", MS_STRICTATIME, "mode=755" TMPFS_LIMITS_ROOTFS);
+ r = mount_nofollow_verbose(LOG_ERR, "tmpfs", "/run/systemd/volatile-sysroot", "tmpfs", MS_STRICTATIME, "mode=0755" TMPFS_LIMITS_ROOTFS);
if (r < 0)
goto finish_rmdir;
if (r < 0)
return log_error_errno(r, "Couldn't create overlay sysroot directory: %m");
- r = mount_nofollow_verbose(LOG_ERR, "tmpfs", "/run/systemd/overlay-sysroot", "tmpfs", MS_STRICTATIME, "mode=755" TMPFS_LIMITS_ROOTFS);
+ r = mount_nofollow_verbose(LOG_ERR, "tmpfs", "/run/systemd/overlay-sysroot", "tmpfs", MS_STRICTATIME, "mode=0755" TMPFS_LIMITS_ROOTFS);
if (r < 0)
goto finish;
if ! get_bool "${TEST_NO_QEMU:=}"; then
install_dmevent
instmods dm_verity =md
+ instmods erofs
generate_module_dependencies
image_install -o /sbin/mksquashfs
+ image_install -o /bin/mkfs.erofs
fi
inst_binary mcopy
--- /dev/null
+{"config":"default @saved","loader":[""]}
\ No newline at end of file
install_dir : testdata_dir)
endif
+test_bootctl_json_sh = find_program('test-bootctl-json.sh')
test_fstab_generator_sh = find_program('test-fstab-generator.sh')
test_network_generator_conversion_sh = find_program('test-network-generator-conversion.sh')
test_systemctl_enable_sh = find_program('test-systemctl-enable.sh')
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -e
+set -o pipefail
+
+bootctl="${1:?}"
+
+"$bootctl" --no-pager list >/dev/null || {
+ echo "$bootctl list failed, skipping tests" 1>&2
+ exit 77
+}
+
+set -x
+
+"$bootctl" list --json=pretty | python3 -m json.tool >/dev/null
+"$bootctl" list --json=short | python3 -m json.tool >/dev/null
+
+command -v jq >/dev/null || {
+ echo "jq is not available, skipping jq tests" 1>&2
+ exit 0
+}
+
+"$bootctl" list --json=pretty | jq . >/dev/null
+"$bootctl" list --json=short | jq . >/dev/null
cat
chmod
chown
+ chroot
cmp
cryptsetup
cut
fi
# Partition sizes are in MiBs
- local root_size=500
+ local root_size=1000
local data_size=50
if ! get_bool "$NO_BUILD"; then
if meson configure "${BUILD_DIR:?}" | grep 'static-lib\|standalone-binaries' | awk '{ print $2 }' | grep -q 'true'; then
output = check_output('ip -d link show bond199')
print(output)
- self.assertRegex(output, 'active_slave dummy98')
+ self.assertIn('active_slave dummy98', output)
def test_bond_primary_slave(self):
copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
output = check_output('ip -d link show bond199')
print(output)
- self.assertRegex(output, 'primary dummy98')
+ self.assertIn('primary dummy98', output)
+
+ # for issue #25627
+ mkdir_p(os.path.join(network_unit_dir, '23-bond199.network.d'))
+ for mac in ['00:11:22:33:44:55', '00:11:22:33:44:56']:
+ with open(os.path.join(network_unit_dir, '23-bond199.network.d/mac.conf'), mode='w', encoding='utf-8') as f:
+ f.write(f'[Link]\nMACAddress={mac}\n')
+
+ networkctl_reload()
+ self.wait_online(['dummy98:enslaved', 'bond199:degraded'])
+
+ output = check_output('ip -d link show bond199')
+ print(output)
+ self.assertIn(f'link/ether {mac}', output)
def test_bond_operstate(self):
copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
rmdir($udev_tmpfs);
mkdir($udev_tmpfs) || die "unable to create udev_tmpfs: $udev_tmpfs\n";
- if (system("mount", "-o", "rw,mode=755,nosuid,noexec", "-t", "tmpfs", "tmpfs", $udev_tmpfs)) {
+ if (system("mount", "-o", "rw,mode=0755,nosuid,noexec", "-t", "tmpfs", "tmpfs", $udev_tmpfs)) {
warn "unable to mount tmpfs";
return 0;
}
fi
}
+# shellcheck source=test/units/assert.sh
+. "$(dirname "$0")"/assert.sh
+
trap at_exit EXIT
# Create a simple unit file for testing
systemctl list-jobs --after --before
systemctl list-jobs "*"
+# is-* verbs
+# Should return 4 for a missing unit file
+assert_rc 4 systemctl --quiet is-active not-found.service
+assert_rc 4 systemctl --quiet is-failed not-found.service
+assert_rc 4 systemctl --quiet is-enabled not-found.service
+# is-active: return 3 when the unit exists but inactive
+assert_rc 3 systemctl --quiet is-active "$UNIT_NAME"
+# is-enabled: return 1 when the unit exists but disabled
+assert_rc 1 systemctl --quiet is-enabled "$UNIT_NAME"
+
# Basic service management
systemctl start --show-transaction "$UNIT_NAME"
systemctl status -n 5 "$UNIT_NAME"
grep -q -F "MARKER=1" "${image_dir}/mount/usr/lib/os-release"
systemd-dissect --umount "${image_dir}/mount"
+systemd-dissect --root-hash "${roothash}" --mount "${image}.gpt" --in-memory "${image_dir}/mount"
+grep -q -F -f "$os_release" "${image_dir}/mount/usr/lib/os-release"
+grep -q -F -f "$os_release" "${image_dir}/mount/etc/os-release"
+grep -q -F "MARKER=1" "${image_dir}/mount/usr/lib/os-release"
+systemd-dissect --umount "${image_dir}/mount"
+
# add explicit -p MountAPIVFS=yes once to test the parser
systemd-run -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
rmdir /etc/extensions/app-nodistro
rm /var/lib/extensions/app-nodistro.raw
+mkdir -p /run/machines /run/portables /run/extensions
+touch /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
+
+systemd-dissect --discover --json=short > /tmp/discover.json
+grep -q -F '{"name":"a","type":"raw","class":"machine","ro":false,"path":"/run/machines/a.raw"' /tmp/discover.json
+grep -q -F '{"name":"b","type":"raw","class":"portable","ro":false,"path":"/run/portables/b.raw"' /tmp/discover.json
+grep -q -F '{"name":"c","type":"raw","class":"extension","ro":false,"path":"/run/extensions/c.raw"' /tmp/discover.json
+rm /tmp/discover.json /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
+
echo OK >/testok
exit 0
--dry-run=no \
--seed="$seed" \
--empty=force \
- --skip-partitions=home,root \
+ --defer-partitions=home,root \
"$imgs/zzz"
output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
# shellcheck disable=SC2064
trap "rm -rf '$defs' '$imgs'" RETURN
- for format in ext4 vfat; do
+ for format in ext4 vfat erofs; do
if ! command -v "mkfs.$format" >/dev/null; then
continue
fi
systemd-analyze log-level info
+# Test that rate-limiting daemon-reload works
+mkdir -p /run/systemd/system.conf.d/
+cat >/run/systemd/system.conf.d/50-test-59-reload.conf <<EOF
+[Manager]
+ReloadLimitIntervalSec=9
+ReloadLimitBurst=3
+EOF
+
+# Pick up the new config
+systemctl daemon-reload
+
+# The timeout will hit (and the test will fail) if the reloads are not rate-limited
+timeout 15 bash -c 'while systemctl daemon-reload --no-block; do true; done'
+
+# Rate limit should reset after 9s
+sleep 10
+
+systemctl daemon-reload
+
echo OK >/testok
exit 0
testcase_virtio_scsi_identically_named_partitions() {
local num
- if [[ -n "${ASAN_OPTIONS:-}" ]] || [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then
+ if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
num=$((4 * 4))
else
num=$((16 * 8))
local -a devices symlinks
local -A running
- if [[ -n "${ASAN_OPTIONS:-}" ]] || [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then
+ if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
num_part=2
iterations=10
timeout=240
/dev/disk/by-id/ata-foobar_deadbeeflvm{0..3}
)
- if [[ -n "${ASAN_OPTIONS:-}" ]] || [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then
+ if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
timeout=180
else
timeout=30
helper_check_device_units
# Same as above, but now with more "stress"
- if [[ -n "${ASAN_OPTIONS:-}" ]] || [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then
+ if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
iterations=10
else
iterations=50
helper_check_device_units
# Create & remove LVs in a loop, i.e. with more "stress"
- if [[ -n "${ASAN_OPTIONS:-}" ]]; then
+ if [[ -v ASAN_OPTIONS ]]; then
iterations=8
partitions=16
elif [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then
systemd-analyze cat-config systemd/system.conf foo/bar systemd/journald.conf >/dev/null
systemd-analyze cat-config foo/bar
+if [[ ! -v ASAN_OPTIONS ]]; then
+ # check that systemd-analyze cat-config paths work in a chroot
+ mkdir -p /tmp/root
+ mount --bind / /tmp/root
+ systemd-analyze cat-config systemd/system-preset >/tmp/out1
+ chroot /tmp/root systemd-analyze cat-config systemd/system-preset >/tmp/out2
+ diff /tmp/out{1,2}
+fi
+
+# verify
mkdir -p /tmp/img/usr/lib/systemd/system/
mkdir -p /tmp/img/opt/
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
# Start monitoring queries
systemd-run -u resmontest.service -p Type=notify resolvectl monitor
+# Wait for the monitoring service to become active
+for _ in {0..9}; do
+ [[ "$(systemctl show -P ActiveState resmontest.service)" == "active" ]] && break
+ sleep .5
+done
# We need to manually propagate the DS records of onlinesign.test. to the parent
# zone, since they're generated online
After=proc-sys-fs-binfmt_misc.mount
After=local-fs.target
Before=sysinit.target shutdown.target
-ConditionPathIsReadWrite=/proc/sys/
+ConditionPathIsMountPoint=/proc/sys/fs/binfmt_misc
ConditionDirectoryNotEmpty=|/lib/binfmt.d
ConditionDirectoryNotEmpty=|/usr/lib/binfmt.d
ConditionDirectoryNotEmpty=|/usr/local/lib/binfmt.d