*.[ch] whitespace=tab-in-indent,trailing-space
-test/dmidecode-dumps/*.bin binary
+*.gpg binary generated
+*.bmp binary
+
+# Mark files as "generated", i.e. no license applies to them.
+# This includes output from programs, directive lists generated by grepping
+# for all possibilities, samples from fuzzers, files from /proc, packet samples,
+# and anything else where no copyright can be asserted.
+#
+# Use 'git check-attr generated -- <path>' to query the attribute.
+[attr]generated
CC="clang-$COMPILER_VERSION"
CXX="clang++-$COMPILER_VERSION"
AR="llvm-ar-$COMPILER_VERSION"
- # Latest LLVM stack deb packages provided by https://apt.llvm.org/
- # Following snippet was borrowed from https://apt.llvm.org/llvm.sh
- wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
- add-apt-repository -y "deb http://apt.llvm.org/$RELEASE/ llvm-toolchain-$RELEASE-$COMPILER_VERSION main"
- PACKAGES+=("clang-$COMPILER_VERSION" "lldb-$COMPILER_VERSION" "lld-$COMPILER_VERSION" "clangd-$COMPILER_VERSION")
+
+ # ATTOW llvm-11 got into focal-updates, which conflicts with llvm-11
+ # provided by the apt.llvm.org repositories. Let's use the system
+ # llvm package if available in such cases to avoid that.
+ if ! apt show --quiet "llvm-$COMPILER_VERSION" &>/dev/null; then
+ # Latest LLVM stack deb packages provided by https://apt.llvm.org/
+ # Following snippet was borrowed from https://apt.llvm.org/llvm.sh
+ wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
+ add-apt-repository -y "deb http://apt.llvm.org/$RELEASE/ llvm-toolchain-$RELEASE-$COMPILER_VERSION main"
+ PACKAGES+=("clang-$COMPILER_VERSION" "lldb-$COMPILER_VERSION" "lld-$COMPILER_VERSION" "clangd-$COMPILER_VERSION")
+ fi
elif [[ "$COMPILER" == gcc ]]; then
CC="gcc-$COMPILER_VERSION"
CXX="g++-$COMPILER_VERSION"
/mkosi.output/
/mkosi.default
# Ignore any mkosi config files with "local" in the name
-/mkosi.default.d/*local*.conf
+/mkosi.default.d/**/*local*.conf
/tags
--- /dev/null
+Copyright (c) <dates>, <Copyright Holder> (<URL|email>),
+with Reserved Font Name <Reserved Font Name>.
+Copyright (c) <dates>, <additional Copyright Holder> (<URL|email>),
+with Reserved Font Name <additional Reserved Font Name>.
+Copyright (c) <dates>, <additional Copyright Holder> (<URL|email>).
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
## Main License
-The systemd project uses single-line references to Unique Licese Identifiers as
+The systemd project uses single-line references to Unique License Identifiers as
defined by the Linux Foundation's SPDX project (https://spdx.org/). The line in
each individual source file identifies the license applicable to that file.
- src/basic/siphash24.h
- src/systemctl/systemd-sysv-install.SKELETON
- tools/check-includes.pl
- - all examples, code and scripts, under man/ except where otherwise noted
+ - all examples under man/
* the following sources are under **Public Domain** (LicenseRef-murmurhash2-public-domain):
- src/basic/MurmurHash2.c
- src/basic/MurmurHash2.h
- src/libsystemd/sd-journal/lookup3.h
* the tools/chromiumos/gen_autosuspend_rules.py script is licensed under the
**BSD-3-Clause** license.
+ * Heebo fonts under docs/fonts/ are licensed under the **SIL Open Font License 1.1**,
* any files under test/ without an explicit license we assume non-copyrightable
(eg: computer-generated fuzzer data)
fallback logic to get a regular user created on uninitialized systems.
- store PKCS#11 + FIDO2 token info in LUKS2 header, compatible with
systemd-cryptsetup, so that it can unlock homed volumes
- - try to unmount in regular intervals when home dir was busy when we
- tried because idle.
- - keep an fd to the homedir open at all times, to keep the fs pinned
- (autofs and such) while user is logged in.
* add a new switch --auto-definitions=yes/no or so to systemd-repart. If
specified, synthesize a definition automatically if we can: enlarge last
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+@@
+expression x;
+@@
+- strdupa(x)
++ strdupa_safe(x)
+@@
+expression x, n;
+@@
+- strndupa(x, n)
++ strndupa_safe(x, n)
--- /dev/null
+*.png binary generated
+*.woff binary
- Avoid fixed-size string buffers, unless you really know the maximum size and
that maximum size is small. It is often nicer to use dynamic memory,
- `alloca()` or VLAs. If you do allocate fixed-size strings on the stack, then
- it is probably only OK if you either use a maximum size such as `LINE_MAX`,
- or count in detail the maximum size a string can have. (`DECIMAL_STR_MAX` and
- `DECIMAL_STR_WIDTH` macros are your friends for this!)
+ `alloca_safe()` or VLAs. If you do allocate fixed-size strings on the stack,
+ then it is probably only OK if you either use a maximum size such as
+ `LINE_MAX`, or count in detail the maximum size a string can
+ have. (`DECIMAL_STR_MAX` and `DECIMAL_STR_WIDTH` macros are your friends for
+ this!)
Or in other words, if you use `char buf[256]` then you are likely doing
something wrong!
- Make use of `_cleanup_free_` and friends. It makes your code much nicer to
read (and shorter)!
-- Use `alloca()`, but never forget that it is not OK to invoke `alloca()`
- within a loop or within function call parameters. `alloca()` memory is
- released at the end of a function, and not at the end of a `{}` block. Thus,
- if you invoke it in a loop, you keep increasing the stack pointer without
- ever releasing memory again. (VLAs have better behavior in this case, so
- consider using them as an alternative.) Regarding not using `alloca()`
- within function parameters, see the BUGS section of the `alloca(3)` man page.
+- Do not use `alloca()`, `strdupa()` or `strndupa()` directly. Use
+ `alloca_safe()`, `strdupa_safe()` or `strndupa_safe()` instead. (The
+ difference is that the latter include an assertion that the specified size is
+ below a safety threshold, so that the program rather aborts than runs into
+ possible stack overruns.)
+
+- Use `alloca_safe()`, but never forget that it is not OK to invoke
+ `alloca_safe()` within a loop or within function call
+ parameters. `alloca_safe()` memory is released at the end of a function, and
+ not at the end of a `{}` block. Thus, if you invoke it in a loop, you keep
+ increasing the stack pointer without ever releasing memory again. (VLAs have
+ better behavior in this case, so consider using them as an alternative.)
+ Regarding not using `alloca_safe()` within function parameters, see the BUGS
+ section of the `alloca(3)` man page.
- If you want to concatenate two or more strings, consider using `strjoina()`
or `strjoin()` rather than `asprintf()`, as the latter is a lot slower. This
We'd like to apologize in advance if we are not able to process and reply to your issue or PR right-away. We have a lot of work to do, but we are trying our best!
Thank you very much for your contributions!
+
+# Backward Compatibility And External Dependencies
+
+We strive to keep backward compatibility where possible and reasonable. The following are general guidelines, not hard
+rules, and case-by-case exceptions might be applied at the discretion of the maintainers. The current set of build time
+and runtime dependencies are documented in the [README](../README).
+
+## New features
+
+It is fine for new features/functionality/tools/daemons to require bleeding edge external dependencies, provided there
+are runtime and build time graceful fallbacks (e.g.: daemon will not be built, runtime functionality will be skipped with
+clear log message).
+In case a new feature is added to both `systemd` and one of its dependencies, we expect the corresponding feature code to
+be merged upstream in the dependency before accepting our side of the implementation.
+Making use of new kernel syscalls can be achieved through compat wrappers in our tree (see: `src/basic/missing_syscall_def.h`),
+and does not need to wait for glibc support.
+
+## External Build/Runtime Dependencies
+
+It is often tempting to bump external dependencies minimum versions to cut cruft, and in general it's an essential part
+of the maintenance process. But as a generic rule, existing dependencies should not be bumped without very strong
+reasons. When possible, we try to keep compatibility with the most recent LTS releases of each mainstream distribution
+for optional components, and with all currently maintained (i.e.: not EOL) LTS releases for core components. When in
+doubt, ask before committing time to work on contributions if it's not clear that cutting support would be obviously
+acceptable.
+
+## Kernel Requirements
+
+Same principles as with other dependencies should be applied. It is fine to require newer kernel versions for additional
+functionality or optional features, but very strong reasons should be required for breaking compatibility for existing
+functionality, especially for core components. It is not uncommon, for example, for embedded systems to be stuck on older
+kernel versions due to hardware requirements, so do not assume everybody is running with latest and greatest at all times.
+In general, [currently maintained LTS branches](https://www.kernel.org/category/releases.html) should keep being supported
+for existing functionality, especially for core components.
+
+## `libsystemd.so`
+
+`libsystemd.so` is a shared public library, so breaking ABI/API compatibility creates a lot of work for its users, and should
+always be avoided apart from the most extreme circumstances. For example, always add a new interface instead of modifying
+the signature of an existing function. It is fine to mark an interface as deprecated to gently nudge users toward a newer one,
+but in general support for the old one should be maintained whenever possible.
+Symbol versioning and the compiler's deprecated attribute should be used when managing the lifetime of a public interface.
+
+## `libudev.so`
+
+`libudev.so` is a shared public library, and is still maintained, but should not gain new symbols at this point.
useful for debugging. Currently only supported by
`systemd-cryptsetup-generator`.
+* `$SYSTEMD_INTEGRITYTAB` — if set, use this path instead of
+ `/etc/integritytab`. Only useful for debugging. Currently only supported by
+ `systemd-integritysetup-generator`.
+
* `$SYSTEMD_VERITYTAB` — if set, use this path instead of
`/etc/veritytab`. Only useful for debugging. Currently only supported by
`systemd-veritysetup-generator`.
against any of the certificates in `/etc/verity.d/*.crt` (and similar
directores in `/usr/lib/`, `/run`, …) or passed to the kernel for validation
against its built-in certificates.
+
+`systemd-cryptsetup`:
+
+* `$SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE` – takes a boolean, which controls
+ whether to use the libcryptsetup "token" plugin module logic even when
+ activating via FIDO2, PKCS#11, TPM2, i.e. mechanisms natively supported by
+ `systemd-cryptsetup`. Defaults to enabled.
{
"type": "cppdbg",
"program": "/usr/lib/systemd/systemd",
- "processId": "${command:pickProcess}",
+ "processId": "${command:pickRemoteProcess}",
"request": "attach",
"name": "systemd",
"pipeTransport": {
# Site settings
+# SPDX-License-Identifier: LGPL-2.1-or-later
title: systemd
baseurl: "" # the subpath of your site, e.g. /blog/
url: "http://systemd.io" # the base hostname & protocol for your site
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
<footer class="site-footer">
<p>© systemd, 2021</p>
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
<header class="site-header">
<div class="wrapper">
<!DOCTYPE html>
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
<html>
{% include head.html %}
<svg xmlns="http://www.w3.org/2000/svg" width="202" height="26">
+ <!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<path overflow="visible" font-weight="400" d="M0 0v26h10v-4H4V4h6V0zm76 0v4h6v18h-6v4h10V0z" style="line-height:normal;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000;text-transform:none;text-orientation:mixed;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1" color="#000" font-family="sans-serif" fill="#201a26"/>
<path word-spacing="0" letter-spacing=".2" font-size="12" font-weight="700" style="line-height:1.25;-inkscape-font-specification:'Heebo Bold';text-align:start" d="M113.498 14.926q-4.5-.96-4.5-3.878 0-1.079.609-1.981.621-.902 1.781-1.441 1.16-.54 2.707-.54 1.63 0 2.848.528 1.219.516 1.875 1.453.656.926.656 2.121h-3.539q0-.762-.457-1.183-.457-.434-1.394-.434-.774 0-1.243.363-.457.364-.457.938 0 .55.516.89.527.34 1.781.575 1.5.28 2.543.738 1.043.445 1.653 1.242.62.797.62 2.027 0 1.114-.667 2.004-.657.88-1.887 1.383-1.219.504-2.836.504-1.711 0-2.965-.621-1.242-.633-1.898-1.617-.645-.985-.645-2.051h3.34q.036.914.656 1.36.621.433 1.594.433.902 0 1.383-.34.492-.351.492-.937 0-.364-.223-.61-.21-.258-.773-.48-.55-.223-1.57-.446zm19.384-7.606l-5.086 14.58q-.293.831-.726 1.523-.434.703-1.266 1.195-.832.504-2.098.504-.457 0-.75-.048-.281-.046-.785-.176v-2.672q.176.02.527.02.95 0 1.418-.293.47-.293.715-.961l.352-.926-4.43-12.738h3.797l2.262 7.687 2.285-7.687zm5.884 7.606q-4.5-.96-4.5-3.878 0-1.079.61-1.981.62-.902 1.781-1.441 1.16-.54 2.707-.54 1.629 0 2.848.528 1.218.516 1.875 1.453.656.926.656 2.121h-3.539q0-.762-.457-1.183-.457-.434-1.395-.434-.773 0-1.242.363-.457.364-.457.938 0 .55.516.89.527.34 1.781.575 1.5.28 2.543.738 1.043.445 1.652 1.242.621.797.621 2.027 0 1.114-.668 2.004-.656.88-1.886 1.383-1.219.504-2.836.504-1.711 0-2.965-.621-1.242-.633-1.899-1.617-.644-.985-.644-2.051h3.34q.036.914.656 1.36.621.433 1.594.433.902 0 1.383-.34.492-.351.492-.937 0-.364-.223-.61-.21-.258-.773-.48-.551-.223-1.57-.446zm13.983 2.403q.574 0 .984-.082v2.66q-.914.328-2.086.328-3.727 0-3.727-3.797V9.899h-1.793V7.321h1.793v-3.14h3.54v3.14h2.132v2.578h-2.133v6.129q0 .75.293 1.031.293.27.997.27zm14.228-2.519h-8.016q.2 1.183.985 1.886.785.691 2.015.691.914 0 1.688-.34.785-.351 1.336-1.042l1.699 1.957q-.668.96-1.957 1.617-1.278.656-3 .656-1.946 0-3.387-.82-1.43-.82-2.203-2.227-.762-1.406-.762-3.105v-.446q0-1.898.715-3.386.715-1.489 2.063-2.32 1.347-.844 3.187-.844 1.793 0 3.059.761 1.265.762 1.922 2.168.656 1.395.656 3.293zm-3.469-2.65q-.024-1.03-.574-1.628-.54-.598-1.617-.598-1.008 0-1.582.668-.563.668-.739 1.84h4.512zm19.923-5.073q1.934 0 2.989 1.148 1.054 1.148 1.054 3.727v8.039h-3.539V11.95q0-.797-.21-1.23-.212-.446-.61-.61-.387-.164-.984-.164-.715 0-1.219.352-.504.34-.797.972.02.082.02.27V20h-3.54v-8.015q0-.797-.21-1.242-.211-.445-.61-.621-.386-.176-.996-.176-.68 0-1.183.304-.492.293-.797.844V20h-3.539V7.32h3.316l.118 1.419q.633-.797 1.547-1.22.926-.433 2.086-.433 1.172 0 2.016.48.855.47 1.312 1.442.633-.926 1.582-1.418.961-.504 2.203-.504zM201.398 2v18h-3.187l-.176-1.359q-1.243 1.594-3.212 1.594-1.535 0-2.66-.82-1.113-.832-1.699-2.285-.574-1.454-.574-3.317v-.246q0-1.934.574-3.398.586-1.465 1.7-2.274 1.124-.808 2.683-.808 1.805 0 3.012 1.37V2.001zm-5.672 15.376q1.488 0 2.133-1.266v-4.898q-.61-1.266-2.11-1.266-1.207 0-1.77.984-.55.985-.55 2.637v.246q0 1.629.54 2.602.55.96 1.757.96z" font-family="Heebo" fill="#201a26"/>
<path d="M45 13L63 3v20z" fill="#30d475"/>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
+ <!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<g transform="translate(380 -506.52)">
<rect ry="16.875" rx="16.875" y="2409.281" x="4128.568" height="90" width="90" fill="#201a26" transform="matrix(.17778 0 0 .17778 -1113.968 78.203)" stroke-width="5.625"/>
<g fill="none" stroke="#fff" stroke-width="2">
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
@font-face {
font-family: 'Heebo';
src: url('fonts/heebo-regular.woff');
#!/usr/bin/env python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
from html.parser import HTMLParser
from enum import Enum
#!/usr/bin/env python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
import re
import sys
</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>set-timeout</option> <replaceable>TIMEOUT</replaceable></term>
+ <term><option>set-timeout-oneshot</option> <replaceable>TIMEOUT</replaceable></term>
+
+ <listitem><para>Sets the boot loader menu timeout in seconds. The <option>set-timeout-oneshot</option>
+ command will set the timeout only for the next boot. See
+ <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ for details about the syntax of time spans.</para>
+
+ <para>If this is set to <option>menu-hidden</option> or <option>0</option> no menu is shown and
+ the default entry will be booted immediately, while setting this to <option>menu-force</option>
+ disables the timeout while always showing the menu. When an empty string ("") is specified the
+ bootloader will revert to its default menu timeout.</para></listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
of the current PCR state.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>token-timeout=</option></term>
+
+ <listitem><para>Specifies how long to wait at most for configured security devices (i.e. FIDO2,
+ PKCS#11, TPM2) to show up. Takes a time value in seconds (but other time units may be specified too,
+ see <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ for supported formats). Defaults to 30s. Once the specified timeout elapsed authentication via
+ password is attempted. Note that this timeout applies to waiting for the security device to show up —
+ it does not apply to the PIN prompt for the device (should one be needed) or similar. Pass 0 to turn
+ off the time-out and wait forever.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>try-empty-password=</option></term>
<varlistentry>
<term><option>x-systemd.device-timeout=</option></term>
- <listitem><para>Specifies how long systemd should wait for a device to show up
- before giving up on the entry. The argument is a time in seconds or explicitly
- specified units of
- <literal>s</literal>,
- <literal>min</literal>,
- <literal>h</literal>,
- <literal>ms</literal>.
+ <listitem><para>Specifies how long systemd should wait for a block device to show up before
+ giving up on the entry. The argument is a time in seconds or explicitly specified units of
+ <literal>s</literal>, <literal>min</literal>, <literal>h</literal>, <literal>ms</literal>.
</para></listitem>
</varlistentry>
node is not allowed if any of the other storage backends are used.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--drop-caches=</option><replaceable>BOOL</replaceable></term>
+
+ <listitem><para>Automatically flush OS file system caches on logout. This is useful in combination
+ with the fscrypt storage backend to ensure the OS does not keep decrypted versions of the files and
+ directories in memory (and accessible) after logout. This option is also supported on other backends,
+ but should not bring any benefit there. Defaults to off, except if the selected storage backend is
+ fscrypt, where it defaults to on. Note that flushing OS caches will negatively influence performance
+ of the OS shortly after logout.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>--fs-type=</option><replaceable>TYPE</replaceable></term>
--- /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="integritytab" conditional='HAVE_LIBCRYPTSETUP' xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>integritytab</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>integritytab</refentrytitle>
+ <manvolnum>5</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>integritytab</refname>
+ <refpurpose>Configuration for integrity block devices</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>/etc/integritytab</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <filename>/etc/integritytab</filename> file describes
+ integrity protected block devices that are set up during
+ system boot.</para>
+
+ <para>Empty lines and lines starting with the <literal>#</literal>
+ character are ignored. Each of the remaining lines describes one
+ verity integrity protected block device. Fields are delimited by
+ white space.</para>
+
+ <para>Each line is in the form<programlisting><replaceable>volume-name</replaceable> <replaceable>block-device</replaceable>
+ <replaceable>[keyfile|-]</replaceable> <replaceable>[options|-]</replaceable></programlisting>
+ The first two fields are mandatory, the remaining two are optional and only required if user specified non-default options during integrity format.</para>
+
+ <para>The first field contains the name of the resulting integrity volume; its block device is set up
+ below <filename>/dev/mapper/</filename>.</para>
+
+ <para>The second field contains a path to the underlying block device, or a specification of a block device via
+ <literal>UUID=</literal> followed by the UUID,
+ <literal>PARTUUID=</literal> followed by the partition UUID,
+ <literal>LABEL=</literal> followed by the label,
+ <literal>PARTLABEL=</literal> followed by the partition label,
+ </para>
+
+ <para>The third field if present contains an absolute filename path to a key file or a <literal>-</literal>
+ to specify none. When the filename is present, the "integrity-algorithm" defaults to <literal>hmac-sha256</literal>
+ with the key length derived from the number of bytes in the key file. At this time the only supported integrity algorithm
+ when using key file is hmac-sha256. The maximum size of the key file is 4096 bytes.
+ </para>
+
+ <para>The fourth field, if present, is a comma-delimited list of options or a <literal>-</literal> to specify none. The following options are
+ recognized:</para>
+ <variablelist>
+
+ <varlistentry>
+ <term><option>allow-discards</option></term>
+
+ <listitem><para>
+ Allow the use of discard (TRIM) requests for the device.
+ This option is available since the Linux kernel version 5.7.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>journal-watermark=[0..100]%</option></term>
+
+ <listitem><para>
+ Journal watermark in percent. When the journal percentage exceeds this watermark, the journal flush will be started. Setting a value of
+ "0%" uses default value.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>journal-commit-time=[0..N]</option></term>
+
+ <listitem><para>
+ Commit time in milliseconds. When this time passes (and no explicit flush operation was issued), the journal is written. Setting a value of
+ zero uses default value.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>data-device=/dev/disk/by-...</option></term>
+
+ <listitem><para>
+ Specify a separate block device that contains existing data. The second field specified in the
+ integritytab for block device then will contain calculated integrity tags and journal for data-device,
+ but not the end user data.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>integrity-algorithm=[crc32c|crc32|sha1|sha256|hmac-sha256]</option></term>
+
+ <listitem><para>
+ The algorithm used for integrity checking. The default is crc32c. Must match option used during format.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>At early boot and when the system manager configuration is
+ reloaded, this file is translated into native systemd units by
+ <citerefentry><refentrytitle>systemd-integritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+ <example>
+ <title>/etc/integritytab</title>
+ <para>Set up two integrity protected block devices. </para>
+
+ <programlisting>home PARTUUID=4973d0b8-1b15-c449-96ec-94bab7f6a7b8 - journal-commit-time=10,allow-discards,journal-watermark=55%
+data PARTUUID=5d4b1808-be76-774d-88af-03c4c3a41761 - allow-discards
+</programlisting>
+ </example>
+
+ <example>
+ <title>/etc/integritytab</title>
+ <para>Set up 1 integrity protected block device using defaults </para>
+
+ <programlisting>home PARTUUID=4973d0b8-1b15-c449-96ec-94bab7f6a7b8</programlisting>
+ </example>
+
+ <example>
+ <title>/etc/integritytab</title>
+ <para>Set up 1 integrity device using existing data block device which contains user data </para>
+
+ <programlisting>home PARTUUID=4973d0b8-1b15-c449-96ec-94bab7f6a7b8 - data-device=/dev/disk/by-uuid/9276d9c0-d4e3-4297-b4ff-3307cd0d092f</programlisting>
+ </example>
+
+ <example>
+ <title>/etc/integritytab</title>
+ <para>Set up 1 integrity device using a HMAC key file using defaults </para>
+
+ <programlisting>home PARTUUID=4973d0b8-1b15-c449-96ec-94bab7f6a7b8 /etc/hmac.key</programlisting>
+ </example>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-integritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-integritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry project='die-net'><refentrytitle>integritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ </para>
+ </refsect1>
+
+</refentry>
<term><varname>systemd.default_timeout_start_sec=</varname></term>
<listitem>
- <para>Overwrites the default start job timeout <varname>DefaultTimeoutStartSec=</varname> at boot. For details,
- see <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+ <para>Overrrides the default start job timeout <varname>DefaultTimeoutStartSec=</varname> at
+ boot. For details, see
+ <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
</listitem>
</varlistentry>
<term><varname>systemd.watchdog_device=</varname></term>
<listitem>
- <para>Overwrites the watchdog device path <varname>WatchdogDevice=</varname>. For details, see
+ <para>Overrrides the watchdog device path <varname>WatchdogDevice=</varname>. For details, see
+ <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>systemd.watchdog_sec=</varname></term>
+
+ <listitem>
+ <para>Overrrides the watchdog timeout settings otherwise configured with
+ <varname>RuntimeWatchdog=</varname>, <varname>RebootWatchdog=</varname> and
+ <varname>KExecWatchdogSec=</varname>. Takes a time value (if no unit is specified, seconds is the
+ implicitly assumed time unit) or the special strings <literal>off</literal> or
+ <literal>default</literal>. For details, see
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
</listitem>
</varlistentry>
will be stored as an EFI variable in that case, overriding this option.
</para>
- <para>If the timeout is disabled, the default entry will be booted
- immediately. The menu can be shown by pressing and holding a key before
- systemd-boot is launched.</para>
+ <para>If set to <literal>menu-hidden</literal> or <literal>0</literal> no menu
+ is shown and the default entry will be booted immediately. The menu can be shown
+ by pressing and holding a key before systemd-boot is launched. Setting this to
+ <literal>menu-force</literal> disables the timeout while always showing the menu.</para>
</listitem>
</varlistentry>
['hostname', '5', [], ''],
['hostnamectl', '1', [], 'ENABLE_HOSTNAMED'],
['hwdb', '7', [], 'ENABLE_HWDB'],
+ ['integritytab', '5', [], 'HAVE_LIBCRYPTSETUP'],
['journal-remote.conf', '5', ['journal-remote.conf.d'], 'HAVE_MICROHTTPD'],
['journal-upload.conf', '5', ['journal-upload.conf.d'], 'HAVE_MICROHTTPD'],
['journalctl', '1', [], ''],
'8',
['systemd-initctl', 'systemd-initctl.socket'],
'HAVE_SYSV_COMPAT'],
+ ['systemd-integritysetup-generator', '8', [], 'HAVE_LIBCRYPTSETUP'],
+ ['systemd-integritysetup@.service',
+ '8',
+ ['systemd-integritysetup'],
+ 'HAVE_LIBCRYPTSETUP'],
['systemd-journal-gatewayd.service',
'8',
['systemd-journal-gatewayd', 'systemd-journal-gatewayd.socket'],
--- /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="systemd-integritysetup-generator" conditional='HAVE_LIBCRYPTSETUP'>
+
+ <refentryinfo>
+ <title>systemd-integritysetup-generator</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-integritysetup-generator</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-integritysetup-generator</refname>
+ <refpurpose>Unit generator for integrity protected block devices</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>/usr/lib/systemd/system-generators/systemd-integritysetup-generator</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><filename>systemd-integritysetup-generator</filename> is a generator that translates <filename>/etc/integritytab</filename> entries into
+ native systemd units early at boot. This will create
+ <citerefentry><refentrytitle>systemd-integritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ units as necessary.</para>
+
+ <para><command>systemd-integritysetup-generator</command> implements
+ <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-integritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry project='die-net'><refentrytitle>integritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ </para>
+ </refsect1>
+
+</refentry>
--- /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="systemd-integritysetup@.service" conditional='HAVE_LIBCRYPTSETUP'>
+
+ <refentryinfo>
+ <title>systemd-integritysetup@.service</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-integritysetup@.service</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-integritysetup@.service</refname>
+ <refname>systemd-integritysetup</refname>
+ <refpurpose>Disk integrity protection logic</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>systemd-integritysetup@.service</filename></para>
+ <para><filename>/usr/lib/systemd/systemd-integritysetup</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><filename>systemd-integritysetup@.service</filename> is a service responsible for setting up integrity
+ protected block devices. It should be instantiated for each device that requires integrity
+ protection.</para>
+
+ <para>At early boot and when the system manager configuration is reloaded, entries from /etc/integritytab are converted into
+ <filename>systemd-integritysetup@.service</filename> units by
+ <citerefentry><refentrytitle>systemd-integritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+ <para><filename>systemd-integritysetup@.service</filename> calls <command>systemd-integritysetup</command>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Commands</title>
+
+ <para>The following commands are understood by <command>systemd-integritysetup</command>:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>attach</option>
+ <replaceable>volume</replaceable>
+ <replaceable>device</replaceable>
+ [<replaceable>key-file|-</replaceable>]
+ [<replaceable>option(s)|-</replaceable>]
+ </term>
+
+ <listitem><para>Create a block device <replaceable>volume</replaceable> using
+ <replaceable>device</replaceable>. See integritytab man page and
+ <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/dm-integrity.html">
+ Kernel dm-integrity</ulink> documentation for details.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>detach</option>
+ <replaceable>volume</replaceable>
+ </term>
+
+ <listitem><para>Detach (destroy) the block device
+ <replaceable>volume</replaceable>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>help</option>
+ </term>
+
+ <listitem><para>Print short information about command syntax.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>integritytab</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-integritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry project='die-net'><refentrytitle>integritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
<para>If the tool is invoked without the <option>--commit</option>
switch, <filename>/etc/machine-id</filename> is initialized with a
- valid, new machined ID if it is missing or empty. The new machine
+ valid, new machine ID if it is missing or empty. The new machine
ID will be acquired in the following fashion:</para>
<orderedlist>
<listitem><para>The initial RAM disk (initrd) will be looked for in the <literal>.initrd</literal> PE
section.</para></listitem>
+ <listitem><para>A compiled binary DeviceTree will be looked for in the <literal>.dtb</literal> PE
+ section.</para></listitem>
+
<listitem><para>The kernel command line to pass to the invoked kernel will be looked for in the
<literal>.cmdline</literal> PE section.</para></listitem>
SecureBoot, or don't include a kernel command line PE section in the kernel image file. If a command line
is accepted via EFI invocation parameters to the EFI binary it is measured into TPM PCR 8 (if a TPM is
present).</para>
+
+ <para>If a DeviceTree is embedded in the <literal>.dtb</literal> section, it replaces an existing
+ DeviceTree in the corresponding EFI configuration table. systemd-stub will ask the firmware via the
+ <literal>EFI_DT_FIXUP_PROTOCOL</literal> for hardware specific fixups to the DeviceTree.</para>
</refsect1>
<refsect1>
<programlisting>objcopy \
--add-section .osrel=os-release --change-section-vma .osrel=0x20000 \
--add-section .cmdline=cmdline.txt --change-section-vma .cmdline=0x30000 \
- --add-section .splash=splash.bmp --change-section-vma .splash=0x40000 \
+ --add-section .dtb=devicetree.dtb --change-section-vma .dtb=0x40000 \
+ --add-section .splash=splash.bmp --change-section-vma .splash=0x100000 \
--add-section .linux=vmlinux --change-section-vma .linux=0x2000000 \
--add-section .initrd=initrd.cpio --change-section-vma .initrd=0x3000000 \
/usr/lib/systemd/boot/efi/linuxx64.efi.stub \
<term><varname>RebootWatchdogSec=</varname></term>
<term><varname>KExecWatchdogSec=</varname></term>
- <listitem><para>Configure the hardware watchdog at runtime and at reboot. Takes a timeout value in seconds (or
- in other time units if suffixed with <literal>ms</literal>, <literal>min</literal>, <literal>h</literal>,
- <literal>d</literal>, <literal>w</literal>). If <varname>RuntimeWatchdogSec=</varname> is set to a non-zero
- value, the watchdog hardware (<filename>/dev/watchdog</filename> or the path specified with
- <varname>WatchdogDevice=</varname> or the kernel option <varname>systemd.watchdog-device=</varname>) will be
- programmed to automatically reboot the system if it is not contacted within the specified timeout interval. The
- system manager will ensure to contact it at least once in half the specified timeout interval. This feature
- requires a hardware watchdog device to be present, as it is commonly the case in embedded and server
- systems. Not all hardware watchdogs allow configuration of all possible reboot timeout values, in which case
- the closest available timeout is picked. <varname>RebootWatchdogSec=</varname> may be used to configure the
- hardware watchdog when the system is asked to reboot. It works as a safety net to ensure that the reboot takes
- place even if a clean reboot attempt times out. Note that the <varname>RebootWatchdogSec=</varname> timeout
- applies only to the second phase of the reboot, i.e. after all regular services are already terminated, and
- after the system and service manager process (PID 1) got replaced by the <filename>systemd-shutdown</filename>
- binary, see system <citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry>
- for details. During the first phase of the shutdown operation the system and service manager remains running
- and hence <varname>RuntimeWatchdogSec=</varname> is still honoured. In order to define a timeout on this first
- phase of system shutdown, configure <varname>JobTimeoutSec=</varname> and <varname>JobTimeoutAction=</varname>
- in the [Unit] section of the <filename>shutdown.target</filename> unit. By default
- <varname>RuntimeWatchdogSec=</varname> defaults to 0 (off), and <varname>RebootWatchdogSec=</varname> to
- 10min. <varname>KExecWatchdogSec=</varname> may be used to additionally enable the watchdog when kexec
- is being executed rather than when rebooting. Note that if the kernel does not reset the watchdog on kexec (depending
- on the specific hardware and/or driver), in this case the watchdog might not get disabled after kexec succeeds
- and thus the system might get rebooted, unless <varname>RuntimeWatchdogSec=</varname> is also enabled at the same time.
- For this reason it is recommended to enable <varname>KExecWatchdogSec=</varname> only if
- <varname>RuntimeWatchdogSec=</varname> is also enabled.
- These settings have no effect if a hardware watchdog is not available.</para></listitem>
+ <listitem><para>Configure the hardware watchdog at runtime and at reboot. Takes a timeout value in
+ seconds (or in other time units if suffixed with <literal>ms</literal>, <literal>min</literal>,
+ <literal>h</literal>, <literal>d</literal>, <literal>w</literal>), or the special strings
+ <literal>off</literal> or <literal>default</literal>. If set to <literal>off</literal>
+ (alternatively: <literal>0</literal>) the watchdog logic is disabled: no watchdog device is opened,
+ configured, or pinged. If set to the special string <literal>default</literal> the watchdog is opened
+ and pinged in regular intervals, but the timeout is not changed from the default. If set to any other
+ time value the watchdog timeout is configured to the specified value (or a value close to it,
+ depending on hardware capabilities).</para>
+
+ <para>If <varname>RuntimeWatchdogSec=</varname> is set to a non-zero value, the watchdog hardware
+ (<filename>/dev/watchdog0</filename> or the path specified with <varname>WatchdogDevice=</varname> or
+ the kernel option <varname>systemd.watchdog-device=</varname>) will be programmed to automatically
+ reboot the system if it is not contacted within the specified timeout interval. The system manager
+ will ensure to contact it at least once in half the specified timeout interval. This feature requires
+ a hardware watchdog device to be present, as it is commonly the case in embedded and server
+ systems. Not all hardware watchdogs allow configuration of all possible reboot timeout values, in
+ which case the closest available timeout is picked.</para>
+
+ <para><varname>RebootWatchdogSec=</varname> may be used to configure the hardware watchdog when the
+ system is asked to reboot. It works as a safety net to ensure that the reboot takes place even if a
+ clean reboot attempt times out. Note that the <varname>RebootWatchdogSec=</varname> timeout applies
+ only to the second phase of the reboot, i.e. after all regular services are already terminated, and
+ after the system and service manager process (PID 1) got replaced by the
+ <filename>systemd-shutdown</filename> binary, see system
+ <citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+ details. During the first phase of the shutdown operation the system and service manager remains
+ running and hence <varname>RuntimeWatchdogSec=</varname> is still honoured. In order to define a
+ timeout on this first phase of system shutdown, configure <varname>JobTimeoutSec=</varname> and
+ <varname>JobTimeoutAction=</varname> in the [Unit] section of the
+ <filename>shutdown.target</filename> unit. By default <varname>RuntimeWatchdogSec=</varname> defaults
+ to 0 (off), and <varname>RebootWatchdogSec=</varname> to 10min.</para>
+
+ <para><varname>KExecWatchdogSec=</varname> may be used to additionally enable the watchdog when kexec
+ is being executed rather than when rebooting. Note that if the kernel does not reset the watchdog on
+ kexec (depending on the specific hardware and/or driver), in this case the watchdog might not get
+ disabled after kexec succeeds and thus the system might get rebooted, unless
+ <varname>RuntimeWatchdogSec=</varname> is also enabled at the same time. For this reason it is
+ recommended to enable <varname>KExecWatchdogSec=</varname> only if
+ <varname>RuntimeWatchdogSec=</varname> is also enabled.</para>
+
+ <para>These settings have no effect if a hardware watchdog is not available.</para></listitem>
</varlistentry>
<varlistentry>
<listitem><para>Configure the hardware watchdog device that the
runtime and shutdown watchdog timers will open and use. Defaults
- to <filename>/dev/watchdog</filename>. This setting has no
+ to <filename>/dev/watchdog0</filename>. This setting has no
effect if a hardware watchdog is not available.</para></listitem>
</varlistentry>
<refnamediv>
<refname>systemd-veritysetup-generator</refname>
- <refpurpose>Unit generator for integrity protected block devices</refpurpose>
+ <refpurpose>Unit generator for verity protected block devices</refpurpose>
</refnamediv>
<refsynopsisdiv>
<title>Description</title>
<para><filename>systemd-veritysetup-generator</filename> is a generator that translates kernel command line options
- configuring integrity-protected block devices (verity) into native systemd units early at boot and when
+ configuring verity protected block devices into native systemd units early at boot and when
configuration of the system manager is reloaded. This will create
<citerefentry><refentrytitle>systemd-veritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
units as necessary.</para>
data devices to use are automatically derived from the specified hash value. Specifically, the data partition
device is looked for under a GPT partition UUID derived from the first 128bit of the root hash, the hash
partition device is looked for under a GPT partition UUID derived from the last 128bit of the root hash. Hence
- it is usually sufficient to specify the root hash to boot from an integrity protected root file system, as
+ it is usually sufficient to specify the root hash to boot from a verity protected root file system, as
device paths are automatically determined from it — as long as the partition table is properly set up.</para>
</listitem>
</varlistentry>
<term><varname>systemd.verity_root_hash=</varname></term>
<listitem><para>These two settings take block device paths as arguments and may be used to explicitly
- configure the data partition and hash partition to use for setting up the integrity protection for the root file
+ configure the data partition and hash partition to use for setting up the verity protection for the root file
system. If not specified, these paths are automatically derived from the <varname>roothash=</varname> argument
(see above).</para></listitem>
</varlistentry>
<refnamediv>
<refname>systemd-veritysetup@.service</refname>
<refname>systemd-veritysetup</refname>
- <refpurpose>Disk integrity protection logic</refpurpose>
+ <refpurpose>Disk verity protection logic</refpurpose>
</refnamediv>
<refsynopsisdiv>
<refsect1>
<title>Description</title>
- <para><filename>systemd-veritysetup@.service</filename> is a service responsible for setting up integrity
- protection (verity) block devices. It should be instantiated for each device that requires integrity
+ <para><filename>systemd-veritysetup@.service</filename> is a service responsible for setting up verity
+ protection block devices. It should be instantiated for each device that requires verity
protection.</para>
<para>At early boot and when the system manager configuration is reloaded kernel command line configuration for
- integrity protected block devices is translated into <filename>systemd-veritysetup@.service</filename> units by
+ verity protected block devices is translated into <filename>systemd-veritysetup@.service</filename> units by
<citerefentry><refentrytitle>systemd-veritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<para><filename>systemd-veritysetup@.service</filename> calls <command>systemd-veritysetup</command>.</para>
<literal>information-request</literal>. Defaults to unset.</para>
</listitem>
</varlistentry>
-
- <varlistentry>
- <term><varname>RapidCommit=</varname></term>
- <listitem>
- <para>Takes a boolean. The DHCPv6 client can obtain configuration parameters from a DHCPv6 server through
- a rapid two-message exchange (solicit and reply). When the rapid commit option is enabled by both
- the DHCPv6 client and the DHCPv6 server, the two-message exchange is used, rather than the default
- four-message exchange (solicit, advertise, request, and reply). The two-message exchange provides
- faster client configuration and is beneficial in environments in which networks are under a heavy load.
- See <ulink url="https://tools.ietf.org/html/rfc3315#section-17.2.1">RFC 3315</ulink> for details.
- Defaults to true.</para>
- </listitem>
- </varlistentry>
</variablelist>
</refsect1>
<varlistentry>
<term><varname>DNSLifetimeSec=</varname></term>
- <listitem><para>Lifetime in seconds for the DNS server addresses listed
- in <varname>DNS=</varname> and search domains listed in
- <varname>Domains=</varname>.</para></listitem>
+ <listitem><para>Lifetime in seconds for the DNS server addresses listed in
+ <varname>DNS=</varname> and search domains listed in <varname>Domains=</varname>. Defaults to
+ 604800 seconds (one week).</para></listitem>
</varlistentry>
</variablelist>
<filename>initrd-root-device.target</filename>,
<filename>initrd-root-fs.target</filename>,
<filename>initrd-usr-fs.target</filename>,
+ <filename>integritysetup-pre.target</filename>,
+ <filename>integritysetup.target</filename>,
<filename>kbrequest.target</filename>,
<filename>kexec.target</filename>,
<filename>local-fs-pre.target</filename>,
<title>Description</title>
<para>The <filename>/etc/veritytab</filename> file describes
- verity integrity protected block devices that are set up during
+ verity protected block devices that are set up during
system boot.</para>
<para>Empty lines and lines starting with the <literal>#</literal>
character are ignored. Each of the remaining lines describes one
- verity integrity protected block device. Fields are delimited by
+ verity protected block device. Fields are delimited by
white space.</para>
<para>Each line is in the form<programlisting><replaceable>volume-name</replaceable> <replaceable>data-device</replaceable> <replaceable>hash-device</replaceable> <replaceable>roothash</replaceable> <replaceable>options</replaceable></programlisting>
<term><option>restart-on-corruption</option></term>
<term><option>panic-on-corruption</option></term>
- <listitem><para>Defines what to do if data integrity problem is detected (data corruption). Without these
+ <listitem><para>Defines what to do if a data verity problem is detected (data corruption). Without these
options kernel fails the IO operation with I/O error. With <literal>--ignore-corruption</literal> option the
corruption is only logged. With <literal>--restart-on-corruption</literal> or
<literal>--panic-on-corruption</literal> the kernel is restarted (panicked) immediately.
<varlistentry>
<term><option>x-initrd.attach</option></term>
- <listitem><para>Setup this verity integrity protected block device in the initramfs, similarly to
+ <listitem><para>Setup this verity protected block device in the initramfs, similarly to
<citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>5</manvolnum></citerefentry>
units marked with <option>x-initrd.mount</option>.</para>
<para>Although it's not necessary to mark the mount entry for the root file system with
<option>x-initrd.mount</option>, <option>x-initrd.attach</option> is still recommended with
- the verity integrity protected block device containing the root file system as otherwise systemd
+ the verity protected block device containing the root file system as otherwise systemd
will attempt to detach the device during the regular system shutdown while it's still in
use. With this option the device will still be detached but later after the root file
system is unmounted.</para>
- <para>All other verity integrity protected block devices that contain file systems mounted in the
+ <para>All other verity protected block devices that contain file systems mounted in the
initramfs should use this option.</para>
</listitem>
</varlistentry>
<title>Examples</title>
<example>
<title>/etc/veritytab example</title>
- <para>Set up two verity integrity protected block devices. One using device blocks, another using files.</para>
+ <para>Set up two verity protected block devices. One using device blocks, another using files.</para>
<programlisting>usr PARTUUID=783e45ae-7aa3-484a-beef-a80ff9c19cbb PARTUUID=21dc1dfe-4c33-8b48-98a9-918a22eb3e37 36e3f740ad502e2c25e2a23d9c7c17bf0fdad2300b7580842d4b7ec1fb0fa263 auto
data /etc/data /etc/hash a5ee4b42f70ae1f46a08a7c92c2e0a20672ad2f514792730f5d49d7606ab8fdf auto
conf.set_quoted('SYSTEMD_HOMEWORK_PATH', rootlibexecdir / 'systemd-homework')
conf.set_quoted('SYSTEMD_IMPORT_FS_PATH', rootlibexecdir / 'systemd-import-fs')
conf.set_quoted('SYSTEMD_IMPORT_PATH', rootlibexecdir / 'systemd-import')
+conf.set_quoted('SYSTEMD_INTEGRITYSETUP_PATH', rootlibexecdir / 'systemd-integritysetup')
conf.set_quoted('SYSTEMD_KBD_MODEL_MAP', pkgdatadir / 'kbd-model-map')
conf.set_quoted('SYSTEMD_LANGUAGE_FALLBACK_MAP', pkgdatadir / 'language-fallback-map')
conf.set_quoted('SYSTEMD_MAKEFS_PATH', rootlibexecdir / 'systemd-makefs')
['mount_setattr', '''#include <sys/mount.h>'''],
['move_mount', '''#include <sys/mount.h>'''],
['open_tree', '''#include <sys/mount.h>'''],
+ ['getdents64', '''#include <dirent.h>'''],
]
have = cc.has_function(ident[0], prefix : ident[1], args : '-D_GNU_SOURCE')
libp11kit],
install_rpath : rootlibexecdir,
install : true)
+
+ executable(
+ 'systemd-integritysetup',
+ ['src/integritysetup/integritysetup.c', 'src/integritysetup/integrity-util.c'],
+ include_directories : includes,
+ link_with : [libshared],
+ dependencies : [libcryptsetup],
+ install_rpath : rootlibexecdir,
+ install : true,
+ install_dir : rootlibexecdir)
+
+ executable(
+ 'systemd-integritysetup-generator',
+ ['src/integritysetup/integritysetup-generator.c', 'src/integritysetup/integrity-util.c'],
+ include_directories : includes,
+ link_with : [libshared],
+ install_rpath : rootlibexecdir,
+ install : true,
+ install_dir : systemgeneratordir)
endif
if conf.get('HAVE_SYSV_COMPAT') == 1
if conf.get('ENABLE_LOCALED') == 1
if conf.get('HAVE_XKBCOMMON') == 1
- # logind will load libxkbcommon.so dynamically on its own
- deps = [libdl]
- extra_includes = [libxkbcommon.get_pkgconfig_variable('includedir')]
+ # logind will load libxkbcommon.so dynamically on its own, but we still
+ # need to specify where the headers are
+ deps = [libdl, libxkbcommon.partial_dependency(compile_args: true)]
else
deps = []
- extra_includes = []
endif
executable(
'systemd-localed',
systemd_localed_sources,
- include_directories : includes + extra_includes,
+ include_directories : includes,
link_with : [libshared],
dependencies : deps,
install_rpath : rootlibexecdir,
--- /dev/null
+/LINGUAS generated
src/boot/efi/boot.c
src/boot/efi/secure-boot.c
+src/boot/efi/stub.c
src/core/dbus-automount.c
src/core/dbus-device.c
src/core/dbus-job.c
local -A VERBS=(
# systemd-efi-options takes an argument, but it is free-form, so we cannot complete it
- [STANDALONE]='help status install update remove is-installed random-seed systemd-efi-options list'
+ [STANDALONE]='help status install update remove is-installed random-seed systemd-efi-options list set-timeout set-timeout-oneshot'
[BOOTENTRY]='set-default set-oneshot'
[BOOLEAN]='reboot-to-firmware'
)
"list:List boot loader entries"
"set-default:Set the default boot loader entry"
"set-oneshot:Set the default boot loader entry only for the next boot"
+ "set-timeout:Set the menu timeout"
+ "set-timeout-oneshot:Set the menu timeout for the next boot only"
)
if (( CURRENT == 1 )); then
_describe -t commands 'bootctl command' _bootctl_cmds || compadd "$@"
if (!sysname)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Requires a subsystem and sysname pair specifying a backlight device.");
- ss = strndupa(argv[2], sysname - argv[2]);
+ ss = strndupa_safe(argv[2], sysname - argv[2]);
sysname++;
#define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t)))
+#define alloca_safe(n) \
+ ({ \
+ size_t _nn_ = n; \
+ assert(_nn_ <= ALLOCA_MAX); \
+ alloca(_nn_ == 0 ? 1 : _nn_); \
+ }) \
+
#define newa(t, n) \
({ \
size_t _n_ = n; \
assert(!size_multiply_overflow(sizeof(t), _n_)); \
- assert(sizeof(t)*_n_ <= ALLOCA_MAX); \
- (t*) alloca((sizeof(t)*_n_) ?: 1); \
+ (t*) alloca_safe(sizeof(t)*_n_); \
})
#define newa0(t, n) \
({ \
size_t _n_ = n; \
assert(!size_multiply_overflow(sizeof(t), _n_)); \
- assert(sizeof(t)*_n_ <= ALLOCA_MAX); \
- (t*) alloca0((sizeof(t)*_n_) ?: 1); \
+ (t*) alloca0((sizeof(t)*_n_)); \
})
#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
#define malloc0(n) (calloc(1, (n) ?: 1))
-#define mfree(memory) \
- ({ \
- free(memory); \
- (typeof(memory)) NULL; \
- })
-
#define free_and_replace(a, b) \
({ \
typeof(a)* _a = &(a); \
({ \
void *_q_; \
size_t _l_ = l; \
- assert(_l_ <= ALLOCA_MAX); \
- _q_ = alloca(_l_ ?: 1); \
+ _q_ = alloca_safe(_l_); \
memcpy_safe(_q_, p, _l_); \
})
({ \
void *_q_; \
size_t _l_ = l; \
- assert(_l_ <= ALLOCA_MAX); \
- _q_ = alloca(_l_ + 1); \
+ _q_ = alloca_safe(_l_ + 1); \
((uint8_t*) _q_)[_l_] = 0; \
memcpy_safe(_q_, p, _l_); \
})
({ \
char *_new_; \
size_t _len_ = n; \
- assert(_len_ <= ALLOCA_MAX); \
- _new_ = alloca(_len_ ?: 1); \
- (void *) memset(_new_, 0, _len_); \
+ _new_ = alloca_safe(_len_); \
+ memset(_new_, 0, _len_); \
})
/* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */
void *_ptr_; \
size_t _mask_ = (align) - 1; \
size_t _size_ = size; \
- assert(_size_ <= ALLOCA_MAX); \
- _ptr_ = alloca((_size_ + _mask_) ?: 1); \
+ _ptr_ = alloca_safe(_size_ + _mask_); \
(void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \
})
void *_new_; \
size_t _xsize_ = (size); \
_new_ = alloca_align(_xsize_, (align)); \
- (void*)memset(_new_, 0, _xsize_); \
+ memset(_new_, 0, _xsize_); \
})
#if HAS_FEATURE_MEMORY_SANITIZER
__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
MALLOC_SIZEOF_SAFE(x)/sizeof((x)[0]), \
VOID_0))
+
+
+/* These are like strdupa()/strndupa(), but honour ALLOCA_MAX */
+#define strdupa_safe(s) \
+ ({ \
+ const char *_t = (s); \
+ (char*) memdupa_suffix0(_t, strlen(_t)); \
+ })
+
+#define strndupa_safe(s, n) \
+ ({ \
+ const char *_t = (s); \
+ (char*) memdupa_suffix0(_t, strnlen(_t, (n))); \
+ })
+
+#include "memory-util.h"
if (n < 3)
return -ENXIO;
- c = strndupa(cgroup, n);
+ c = strndupa_safe(cgroup, n);
c = cg_unescape(c);
if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
return -EINVAL;
}
- name = strndupa(p, t - p);
+ name = strndupa_safe(p, t - p);
STRV_FOREACH(f, *l)
if (env_entry_has_name(*f, name)) {
if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
const char *t;
- t = strndupa(name, k);
+ t = strndupa_safe(name, k);
return getenv(t);
};
if (!e)
return -EINVAL;
- n = strndupa(assignment, e - assignment);
+ n = strndupa_safe(assignment, e - assignment);
/* This is like putenv(), but uses setenv() so that our memory doesn't become part of environ[]. */
if (setenv(n, e + 1, override) < 0)
return !truncated;
}
-int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
+int read_virtual_file_at(
+ int dir_fd,
+ const char *filename,
+ size_t max_size,
+ char **ret_contents,
+ size_t *ret_size) {
+
_cleanup_close_ int fd = -1;
- assert(filename);
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+
+ if (!filename) {
+ if (dir_fd == AT_FDCWD)
+ return -EBADF;
+
+ return read_virtual_file_fd(dir_fd, max_size, ret_contents, ret_size);
+ }
- fd = open(filename, O_RDONLY | O_NOCTTY | O_CLOEXEC);
+ fd = openat(dir_fd, filename, O_RDONLY | O_NOCTTY | O_CLOEXEC);
if (fd < 0)
return -errno;
}
int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size);
-int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size);
+int read_virtual_file_at(int dir_fd, const char *filename, size_t max_size, char **ret_contents, size_t *ret_size);
+static inline int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
+ return read_virtual_file_at(AT_FDCWD, filename, max_size, ret_contents, ret_size);
+}
static inline int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size) {
return read_virtual_file(filename, SIZE_MAX, ret_contents, ret_size);
}
if (!path_is_safe(stop))
return -EINVAL;
- p = strdupa(path);
+ p = strdupa_safe(path);
for (;;) {
char *slash = NULL;
for (size_t i = 0; i < n_input_iovec; i++)
if (memory_startswith(input_iovec[i].iov_base, input_iovec[i].iov_len, "MESSAGE=")) {
- char *m = strndupa(input_iovec[i].iov_base + STRLEN("MESSAGE="),
- input_iovec[i].iov_len - STRLEN("MESSAGE="));
+ char *m;
+
+ m = strndupa_safe((char*) input_iovec[i].iov_base + STRLEN("MESSAGE="),
+ input_iovec[i].iov_len - STRLEN("MESSAGE="));
return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, m);
}
#define _public_ __attribute__((__visibility__("default")))
#define _hidden_ __attribute__((__visibility__("hidden")))
#define _weakref_(x) __attribute__((__weakref__(#x)))
-#define _alignas_(x) __attribute__((__aligned__(__alignof(x))))
+#define _alignas_(x) __attribute__((__aligned__(__alignof__(x))))
#define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
#define _warn_unused_result_ __attribute__((__warn_unused_result__))
# define move_mount missing_move_mount
#endif
+
+/* ======================================================================= */
+
+#if !HAVE_GETDENTS64
+
+static inline ssize_t missing_getdents64(int fd, void *buffer, size_t length) {
+# if defined __NR_getdents64 && __NR_getdents64 >= 0
+ return syscall(__NR_getdents64, fd, buffer, length);
+# else
+ errno = ENOSYS;
+ return -1;
+# endif
+}
+
+# define getdents64 missing_getdents64
+#endif
assert(*e == '/');
/* drop the last component */
- path = strndupa(path, e - path);
+ path = strndupa_safe(path, e - path);
r = is_dir(path, true);
if (r > 0)
return 0;
if (slash[strspn(slash, "/")] != 0) /* Check that the suffix consist only of one or more slashes */
return false;
- copied = strndupa(s, slash - s);
+ copied = strndupa_safe(s, slash - s);
return filename_is_valid(copied);
}
if (s[n] != ':')
return -EINVAL;
- major = strndupa(s, n);
+ major = strndupa_safe(s, n);
r = safe_atou(major, &x);
if (r < 0)
return r;
if (!d)
return -EINVAL;
- i_str = strndupa(s, d - s);
+ i_str = strndupa_safe(s, d - s);
f_str = d + 1;
r = safe_atolu_full(i_str, 10, &i);
k = endswith(e, ":");
if (k) {
- e = strndupa(e, k - e);
+ e = strndupa_safe(e, k - e);
*append = true;
}
if (!pc)
return -EINVAL;
- n = strndupa(p, pc - p);
+ n = strndupa_safe(p, pc - p);
r = safe_atoi(n, &v);
if (r < 0)
return r;
if (dot[1] < '0' || dot[1] > '9')
return -EINVAL;
q = dot[1] - '0';
- n = strndupa(p, dot - p);
+ n = strndupa_safe(p, dot - p);
} else {
q = 0;
- n = strndupa(p, pc - p);
+ n = strndupa_safe(p, pc - p);
}
r = safe_atoi(n, &v);
if (r < 0)
/* We do not support zero or more than two places */
return -EINVAL;
- n = strndupa(p, dot - p);
+ n = strndupa_safe(p, dot - p);
} else {
q = 0;
- n = strndupa(p, pc - p);
+ n = strndupa_safe(p, pc - p);
}
r = safe_atoi(n, &v);
if (r < 0)
p++;
n = strspn(p, DIGITS);
- nr = strndupa(p, n);
+ nr = strndupa_safe(p, n);
return safe_atou64(nr, ret);
}
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
+#include "missing_syscall.h"
#include "mountpoint-util.h"
#include "recurse-dir.h"
#include "sort-util.h"
return strcmp((*a)->d_name, (*b)->d_name);
}
-struct dirent** readdir_all_free(struct dirent **array) {
+static bool ignore_dirent(const struct dirent *de, RecurseDirFlags flags) {
+ assert(de);
- /* Destructor that relies on the fact that the array of dirent structure pointers is NULL
- * terminated */
+ /* Depending on flag either ignore everything starting with ".", or just "." itself and ".." */
- if (!array)
- return NULL;
-
- for (struct dirent **i = array; *i; i++)
- free(*i);
-
- return mfree(array);
+ return FLAGS_SET(flags, RECURSE_DIR_IGNORE_DOT) ?
+ de->d_name[0] == '.' :
+ dot_or_dot_dot(de->d_name);
}
-int readdir_all(DIR *d,
+/* Maximum space one direent structure might require at most */
+#define DIRENT_SIZE_MAX MAX(sizeof(struct dirent), offsetof(struct dirent, d_name) + NAME_MAX + 1)
+
+int readdir_all(int dir_fd,
RecurseDirFlags flags,
- struct dirent ***ret) {
+ DirectoryEntries **ret) {
- _cleanup_(readdir_all_freep) struct dirent **de_array = NULL;
- size_t n_de = 0;
+ _cleanup_free_ DirectoryEntries *de = NULL;
+ DirectoryEntries *nde;
+ size_t add, sz, j;
- assert(d);
+ assert(dir_fd >= 0);
/* Returns an array with pointers to "struct dirent" directory entries, optionally sorted. Free the
* array with readdir_all_freep(). */
+ /* Only if 64bit off_t is enabled struct dirent + struct dirent64 are actually the same. We require
+ * this, and we want them to be interchangable, hence verify that. */
+ assert_cc(_FILE_OFFSET_BITS == 64);
+ assert_cc(sizeof(struct dirent) == sizeof(struct dirent64));
+ assert_cc(offsetof(struct dirent, d_ino) == offsetof(struct dirent64, d_ino));
+ assert_cc(sizeof(((struct dirent*) NULL)->d_ino) == sizeof(((struct dirent64*) NULL)->d_ino));
+ assert_cc(offsetof(struct dirent, d_off) == offsetof(struct dirent64, d_off));
+ assert_cc(sizeof(((struct dirent*) NULL)->d_off) == sizeof(((struct dirent64*) NULL)->d_off));
+ assert_cc(offsetof(struct dirent, d_reclen) == offsetof(struct dirent64, d_reclen));
+ assert_cc(sizeof(((struct dirent*) NULL)->d_reclen) == sizeof(((struct dirent64*) NULL)->d_reclen));
+ assert_cc(offsetof(struct dirent, d_type) == offsetof(struct dirent64, d_type));
+ assert_cc(sizeof(((struct dirent*) NULL)->d_type) == sizeof(((struct dirent64*) NULL)->d_type));
+ assert_cc(offsetof(struct dirent, d_name) == offsetof(struct dirent64, d_name));
+ assert_cc(sizeof(((struct dirent*) NULL)->d_name) == sizeof(((struct dirent64*) NULL)->d_name));
+
+ /* Start with space for up to 8 directory entries. We expect at least 2 ("." + ".."), hence hopefully
+ * 8 will cover most cases comprehensively. (Note that most likely a lot more entries will actually
+ * fit in the buffer, given we calculate maximum file name length here.) */
+ de = malloc(offsetof(DirectoryEntries, buffer) + DIRENT_SIZE_MAX * 8);
+ if (!de)
+ return -ENOMEM;
+
+ de->buffer_size = 0;
for (;;) {
- _cleanup_free_ struct dirent *copy = NULL;
- struct dirent *de;
+ size_t bs;
+ ssize_t n;
- errno = 0;
- de = readdir(d);
- if (!de) {
- if (errno == 0)
- break;
+ bs = MIN(MALLOC_SIZEOF_SAFE(de) - offsetof(DirectoryEntries, buffer), (size_t) SSIZE_MAX);
+ assert(bs > de->buffer_size);
+ n = getdents64(dir_fd, de->buffer + de->buffer_size, bs - de->buffer_size);
+ if (n < 0)
return -errno;
- }
+ if (n == 0)
+ break;
- /* Depending on flag either ignore everything starting with ".", or just "." itself and ".." */
- if (FLAGS_SET(flags, RECURSE_DIR_IGNORE_DOT) ?
- de->d_name[0] == '.' :
- dot_or_dot_dot(de->d_name))
+ de->buffer_size += n;
+
+ if (de->buffer_size < bs - DIRENT_SIZE_MAX) /* Still room for one more entry, then try to
+ * fill it up without growing the structure. */
continue;
- if (n_de >= INT_MAX) /* Make sure we can return the number as 'int' return value */
- return -ERANGE;
+ if (bs >= SSIZE_MAX - offsetof(DirectoryEntries, buffer))
+ return -EFBIG;
+ bs = bs >= (SSIZE_MAX - offsetof(DirectoryEntries, buffer))/2 ? SSIZE_MAX - offsetof(DirectoryEntries, buffer) : bs * 2;
- if (!GREEDY_REALLOC(de_array, n_de+2))
+ nde = realloc(de, bs);
+ if (!nde)
return -ENOMEM;
- copy = memdup(de, de->d_reclen);
- if (!copy)
- return -ENOMEM;
+ de = nde;
+ }
+
+ de->n_entries = 0;
+ for (struct dirent *entry = (struct dirent*) de->buffer;
+ (uint8_t*) entry < de->buffer + de->buffer_size;
+ entry = (struct dirent*) ((uint8_t*) entry + entry->d_reclen)) {
+
+ if (ignore_dirent(entry, flags))
+ continue;
- de_array[n_de++] = TAKE_PTR(copy);
- de_array[n_de] = NULL; /* guarantee array remains NUL terminated */
+ de->n_entries++;
+ }
+
+ sz = ALIGN(offsetof(DirectoryEntries, buffer) + de->buffer_size);
+ add = sizeof(struct dirent*) * de->n_entries;
+ if (add > SIZE_MAX - add)
+ return -ENOMEM;
+
+ nde = realloc(de, sz + add);
+ if (!nde)
+ return -ENOMEM;
+
+ de = nde;
+ de->entries = (struct dirent**) ((uint8_t*) de + ALIGN(offsetof(DirectoryEntries, buffer) + de->buffer_size));
+
+ j = 0;
+ for (struct dirent *entry = (struct dirent*) de->buffer;
+ (uint8_t*) entry < de->buffer + de->buffer_size;
+ entry = (struct dirent*) ((uint8_t*) entry + entry->d_reclen)) {
+
+ if (ignore_dirent(entry, flags))
+ continue;
+
+ de->entries[j++] = entry;
}
if (FLAGS_SET(flags, RECURSE_DIR_SORT))
- typesafe_qsort(de_array, n_de, sort_func);
+ typesafe_qsort(de->entries, de->n_entries, sort_func);
if (ret)
- *ret = TAKE_PTR(de_array);
+ *ret = TAKE_PTR(de);
- return (int) n_de;
+ return 0;
}
int recurse_dir(
- DIR *d,
+ int dir_fd,
const char *path,
unsigned statx_mask,
unsigned n_depth_max,
recurse_dir_func_t func,
void *userdata) {
- _cleanup_(readdir_all_freep) struct dirent **de = NULL;
- int r, n;
+ _cleanup_free_ DirectoryEntries *de = NULL;
+ int r;
- assert(d);
+ assert(dir_fd >= 0);
assert(func);
- /* This is a lot like ftw()/nftw(), but a lot more modern, i.e. built around openat()/statx(), and
- * under the assumption that fds are not as 'expensive' as they used to be. */
+ /* This is a lot like ftw()/nftw(), but a lot more modern, i.e. built around openat()/statx()/O_PATH,
+ * and under the assumption that fds are not as 'expensive' as they used to be. */
if (n_depth_max == 0)
return -EOVERFLOW;
if (n_depth_max == UINT_MAX) /* special marker for "default" */
n_depth_max = DEFAULT_RECURSION_MAX;
- n = readdir_all(d, flags, &de);
- if (n < 0)
- return n;
+ r = readdir_all(dir_fd, flags, &de);
+ if (r < 0)
+ return r;
- for (int i = 0; i < n; i++) {
+ for (size_t i = 0; i < de->n_entries; i++) {
+ _cleanup_close_ int inode_fd = -1, subdir_fd = -1;
_cleanup_free_ char *joined = NULL;
- _cleanup_closedir_ DIR *subdir = NULL;
- _cleanup_close_ int inode_fd = -1;
STRUCT_STATX_DEFINE(sx);
bool sx_valid = false;
const char *p;
/* For each directory entry we'll do one of the following:
*
- * 1) If the entry refers to a directory, we'll open it as O_DIRECTORY 'subdir' and then statx() the opened directory if requested
- * 2) Otherwise and RECURSE_DIR_INODE_FD is set we'll open O_PATH 'inode_fd' and then statx() the opened inode
- * 3) Otherwise we'll statx() the directory entry via the directory we are currently looking at
+ * 1) If the entry refers to a directory, we'll open it as O_DIRECTORY 'subdir_fd' and then statx() the opened directory via that new fd (if requested)
+ * 2) Otherwise, if RECURSE_DIR_INODE_FD is set we'll open it as O_PATH 'inode_fd' and then statx() the opened inode via that new fd (if requested)
+ * 3) Otherwise, we'll statx() the directory entry via the directory fd we are currently looking at (if requested)
*/
if (path) {
- joined = path_join(path, de[i]->d_name);
+ joined = path_join(path, de->entries[i]->d_name);
if (!joined)
return -ENOMEM;
p = joined;
} else
- p = de[i]->d_name;
+ p = de->entries[i]->d_name;
- if (IN_SET(de[i]->d_type, DT_UNKNOWN, DT_DIR)) {
- subdir = xopendirat(dirfd(d), de[i]->d_name, O_NOFOLLOW);
- if (!subdir) {
+ if (IN_SET(de->entries[i]->d_type, DT_UNKNOWN, DT_DIR)) {
+ subdir_fd = openat(dir_fd, de->entries[i]->d_name, O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
+ if (subdir_fd < 0) {
if (errno == ENOENT) /* Vanished by now, go for next file immediately */
continue;
r = func(RECURSE_DIR_SKIP_OPEN_DIR_ERROR_BASE + errno,
p,
- dirfd(d),
+ dir_fd,
-1,
- de[i],
+ de->entries[i],
NULL,
userdata);
if (r == RECURSE_DIR_LEAVE_DIRECTORY)
} else {
/* If we managed to get a DIR* off the inode, it's definitely a directory. */
- de[i]->d_type = DT_DIR;
+ de->entries[i]->d_type = DT_DIR;
if (statx_mask != 0 || (flags & RECURSE_DIR_SAME_MOUNT)) {
- r = statx_fallback(dirfd(subdir), "", AT_EMPTY_PATH, statx_mask, &sx);
+ r = statx_fallback(subdir_fd, "", AT_EMPTY_PATH, statx_mask, &sx);
if (r < 0)
return r;
}
}
- if (!subdir) {
+ if (subdir_fd < 0) {
/* It's not a subdirectory. */
if (flags & RECURSE_DIR_INODE_FD) {
- inode_fd = openat(dirfd(d), de[i]->d_name, O_PATH|O_NOFOLLOW|O_CLOEXEC);
+ inode_fd = openat(dir_fd, de->entries[i]->d_name, O_PATH|O_NOFOLLOW|O_CLOEXEC);
if (inode_fd < 0) {
if (errno == ENOENT) /* Vanished by now, go for next file immediately */
continue;
r = func(RECURSE_DIR_SKIP_OPEN_INODE_ERROR_BASE + errno,
p,
- dirfd(d),
+ dir_fd,
-1,
- de[i],
+ de->entries[i],
NULL,
userdata);
if (r == RECURSE_DIR_LEAVE_DIRECTORY)
* directory fd — which sould be riskless now that we pinned the
* inode. */
- subdir = xopendirat(AT_FDCWD, FORMAT_PROC_FD_PATH(inode_fd), 0);
- if (!subdir)
+ subdir_fd = openat(AT_FDCWD, FORMAT_PROC_FD_PATH(inode_fd), O_DIRECTORY|O_CLOEXEC);
+ if (subdir_fd < 0)
return -errno;
inode_fd = safe_close(inode_fd);
}
- } else if (statx_mask != 0 || (de[i]->d_type == DT_UNKNOWN && (flags & RECURSE_DIR_ENSURE_TYPE))) {
+ } else if (statx_mask != 0 || (de->entries[i]->d_type == DT_UNKNOWN && (flags & RECURSE_DIR_ENSURE_TYPE))) {
- r = statx_fallback(dirfd(d), de[i]->d_name, AT_SYMLINK_NOFOLLOW, statx_mask | STATX_TYPE, &sx);
+ r = statx_fallback(dir_fd, de->entries[i]->d_name, AT_SYMLINK_NOFOLLOW, statx_mask | STATX_TYPE, &sx);
if (r == -ENOENT) /* Vanished by now? Go for next file immediately */
continue;
if (r < 0) {
r = func(RECURSE_DIR_SKIP_STAT_INODE_ERROR_BASE + -r,
p,
- dirfd(d),
+ dir_fd,
-1,
- de[i],
+ de->entries[i],
NULL,
userdata);
if (r == RECURSE_DIR_LEAVE_DIRECTORY)
r = func(RECURSE_DIR_SKIP_STAT_INODE_ERROR_BASE + EISDIR,
p,
- dirfd(d),
+ dir_fd,
-1,
- de[i],
+ de->entries[i],
NULL,
userdata);
if (r == RECURSE_DIR_LEAVE_DIRECTORY)
if (sx_valid) {
/* Copy over the data we acquired through statx() if we acquired any */
if (sx.stx_mask & STATX_TYPE) {
- assert(!!subdir == !!S_ISDIR(sx.stx_mode));
- de[i]->d_type = IFTODT(sx.stx_mode);
+ assert((subdir_fd < 0) == !S_ISDIR(sx.stx_mode));
+ de->entries[i]->d_type = IFTODT(sx.stx_mode);
}
if (sx.stx_mask & STATX_INO)
- de[i]->d_ino = sx.stx_ino;
+ de->entries[i]->d_ino = sx.stx_ino;
}
- if (subdir) {
+ if (subdir_fd >= 0) {
if (FLAGS_SET(flags, RECURSE_DIR_SAME_MOUNT)) {
bool is_mount;
if (sx_valid && FLAGS_SET(sx.stx_attributes_mask, STATX_ATTR_MOUNT_ROOT))
is_mount = FLAGS_SET(sx.stx_attributes, STATX_ATTR_MOUNT_ROOT);
else {
- r = fd_is_mount_point(dirfd(d), de[i]->d_name, 0);
+ r = fd_is_mount_point(dir_fd, de->entries[i]->d_name, 0);
if (r < 0)
log_debug_errno(r, "Failed to determine whether %s is a submount, assuming not: %m", p);
if (is_mount) {
r = func(RECURSE_DIR_SKIP_MOUNT,
p,
- dirfd(d),
- dirfd(subdir),
- de[i],
+ dir_fd,
+ subdir_fd,
+ de->entries[i],
statx_mask != 0 ? &sx : NULL, /* only pass sx if user asked for it */
userdata);
if (r == RECURSE_DIR_LEAVE_DIRECTORY)
r = func(RECURSE_DIR_SKIP_DEPTH,
p,
- dirfd(d),
- dirfd(subdir),
- de[i],
+ dir_fd,
+ subdir_fd,
+ de->entries[i],
statx_mask != 0 ? &sx : NULL, /* only pass sx if user asked for it */
userdata);
if (r == RECURSE_DIR_LEAVE_DIRECTORY)
r = func(RECURSE_DIR_ENTER,
p,
- dirfd(d),
- dirfd(subdir),
- de[i],
+ dir_fd,
+ subdir_fd,
+ de->entries[i],
statx_mask != 0 ? &sx : NULL, /* only pass sx if user asked for it */
userdata);
if (r == RECURSE_DIR_LEAVE_DIRECTORY)
if (r != RECURSE_DIR_CONTINUE)
return r;
- r = recurse_dir(subdir,
+ r = recurse_dir(subdir_fd,
p,
statx_mask,
n_depth_max - 1,
r = func(RECURSE_DIR_LEAVE,
p,
- dirfd(d),
- dirfd(subdir),
- de[i],
+ dir_fd,
+ subdir_fd,
+ de->entries[i],
statx_mask != 0 ? &sx : NULL, /* only pass sx if user asked for it */
userdata);
} else
/* Non-directory inode */
r = func(RECURSE_DIR_ENTRY,
p,
- dirfd(d),
+ dir_fd,
inode_fd,
- de[i],
+ de->entries[i],
statx_mask != 0 ? &sx : NULL, /* only pass sx if user asked for it */
userdata);
recurse_dir_func_t func,
void *userdata) {
- _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_close_ int fd = -1;
+
+ assert(atfd >= 0 || atfd == AT_FDCWD);
+ assert(func);
+
+ if (!path)
+ path = ".";
- d = xopendirat(atfd, path, 0);
- if (!d)
+ fd = openat(atfd, path, O_DIRECTORY|O_CLOEXEC);
+ if (fd < 0)
return -errno;
- return recurse_dir(d, path, statx_mask, n_depth_max, flags, func, userdata);
+ return recurse_dir(fd, path, statx_mask, n_depth_max, flags, func, userdata);
}
RECURSE_DIR_INODE_FD = 1 << 4, /* passes an opened inode fd (O_DIRECTORY fd in case of dirs, O_PATH otherwise) */
} RecurseDirFlags;
-struct dirent** readdir_all_free(struct dirent **array);
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct dirent **, readdir_all_free);
-int readdir_all(DIR *d, RecurseDirFlags flags, struct dirent ***ret);
+typedef struct DirectoryEntries {
+ size_t n_entries;
+ struct dirent** entries;
+ size_t buffer_size;
+ uint8_t buffer[] _alignas_(struct dirent);
+} DirectoryEntries;
-int recurse_dir(DIR *d, const char *path, unsigned statx_mask, unsigned n_depth_max, RecurseDirFlags flags, recurse_dir_func_t func, void *userdata);
+int readdir_all(int dir_fd, RecurseDirFlags flags, DirectoryEntries **ret);
+
+int recurse_dir(int dir_fd, const char *path, unsigned statx_mask, unsigned n_depth_max, RecurseDirFlags flags, recurse_dir_func_t func, void *userdata);
int recurse_dir_at(int atfd, const char *path, unsigned statx_mask, unsigned n_depth_max, RecurseDirFlags flags, recurse_dir_func_t func, void *userdata);
return 0;
}
-static const char *const __signal_table[] = {
+static const char *const static_signal_table[] = {
[SIGHUP] = "HUP",
[SIGINT] = "INT",
[SIGQUIT] = "QUIT",
[SIGSYS] = "SYS"
};
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(static_signal, int);
const char *signal_to_string(int signo) {
static thread_local char buf[STRLEN("RTMIN+") + DECIMAL_STR_MAX(int)];
const char *name;
- name = __signal_to_string(signo);
+ name = static_signal_to_string(signo);
if (name)
return name;
s += 3;
/* Check that the input is a signal name. */
- signo = __signal_from_string(s);
+ signo = static_signal_from_string(s);
if (signo > 0)
return signo;
return -errno;
if (sa.sa.sa_family == AF_UNIX) {
- struct ucred ucred = {};
+ struct ucred ucred = UCRED_INVALID;
/* UNIX connection sockets are anonymous, so let's use
* PID/UID as pretty credentials instead */
}
int socket_get_mtu(int fd, int af, size_t *ret);
+
+/* an initializer for struct ucred that initialized all fields to the invalid value appropriate for each */
+#define UCRED_INVALID { .pid = 0, .uid = UID_INVALID, .gid = GID_INVALID }
/* hey glibc, APIs with callbacks without a user pointer are so useless */
void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
- __compar_d_fn_t compar, void *arg) {
+ comparison_userdata_fn_t compar, void *arg) {
size_t l, u, idx;
const void *p;
int comparison;
#include "macro.h"
+/* This is the same as glibc's internal __compar_d_fn_t type. glibc exports a public comparison_fn_t, for the
+ * external type __compar_fn_t, but doesn't do anything similar for __compar_d_fn_t. Let's hence do that
+ * ourselves, picking a name that is obvious, but likely enough to not clash with glibc's choice of naming if
+ * they should ever add one. */
+typedef int (*comparison_userdata_fn_t)(const void *, const void *, void *);
+
void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
- __compar_d_fn_t compar, void *arg);
+ comparison_userdata_fn_t compar, void *arg);
#define typesafe_bsearch_r(k, b, n, func, userdata) \
({ \
const typeof(b[0]) *_k = k; \
int (*_func_)(const typeof(b[0])*, const typeof(b[0])*, typeof(userdata)) = func; \
- xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_d_fn_t) _func_, userdata); \
+ xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_userdata_fn_t) _func_, userdata); \
})
/**
* that only if nmemb > 0.
*/
static inline void* bsearch_safe(const void *key, const void *base,
- size_t nmemb, size_t size, __compar_fn_t compar) {
+ size_t nmemb, size_t size, comparison_fn_t compar) {
if (nmemb <= 0)
return NULL;
({ \
const typeof(b[0]) *_k = k; \
int (*_func_)(const typeof(b[0])*, const typeof(b[0])*) = func; \
- bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_fn_t) _func_); \
+ bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_fn_t) _func_); \
})
/**
* Normal qsort requires base to be nonnull. Here were require
* that only if nmemb > 0.
*/
-static inline void _qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) {
+static inline void _qsort_safe(void *base, size_t nmemb, size_t size, comparison_fn_t compar) {
if (nmemb <= 1)
return;
#define typesafe_qsort(p, n, func) \
({ \
int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \
- _qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \
+ _qsort_safe((p), (n), sizeof((p)[0]), (comparison_fn_t) _func_); \
})
-static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_d_fn_t compar, void *userdata) {
+static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, comparison_userdata_fn_t compar, void *userdata) {
if (nmemb <= 1)
return;
#define typesafe_qsort_r(p, n, func, userdata) \
({ \
int (*_func_)(const typeof(p[0])*, const typeof(p[0])*, typeof(userdata)) = func; \
- qsort_r_safe((p), (n), sizeof((p)[0]), (__compar_d_fn_t) _func_, userdata); \
+ qsort_r_safe((p), (n), sizeof((p)[0]), (comparison_userdata_fn_t) _func_, userdata); \
})
int cmp_int(const int *a, const int *b);
#undef le64toh
#if __BYTE_ORDER == __LITTLE_ENDIAN
-#define bswap_16_on_le(x) __bswap_16(x)
-#define bswap_32_on_le(x) __bswap_32(x)
-#define bswap_64_on_le(x) __bswap_64(x)
+#define bswap_16_on_le(x) bswap_16(x)
+#define bswap_32_on_le(x) bswap_32(x)
+#define bswap_64_on_le(x) bswap_64(x)
#define bswap_16_on_be(x) (x)
#define bswap_32_on_be(x) (x)
#define bswap_64_on_be(x) (x)
#define bswap_16_on_le(x) (x)
#define bswap_32_on_le(x) (x)
#define bswap_64_on_le(x) (x)
-#define bswap_16_on_be(x) __bswap_16(x)
-#define bswap_32_on_be(x) __bswap_32(x)
-#define bswap_64_on_be(x) __bswap_64(x)
+#define bswap_16_on_be(x) bswap_16(x)
+#define bswap_32_on_be(x) bswap_32(x)
+#define bswap_64_on_be(x) bswap_64(x)
#endif
static inline le16_t htole16(uint16_t value) { return (le16_t __sd_force) bswap_16_on_be(value); }
goto finish;
} else if ((k = endswith(t, " ago"))) {
- t = strndupa(t, k - t);
+ t = strndupa_safe(t, k - t);
r = parse_sec(t, &minus);
if (r < 0)
goto finish;
} else if ((k = endswith(t, " left"))) {
- t = strndupa(t, k - t);
+ t = strndupa_safe(t, k - t);
r = parse_sec(t, &plus);
if (r < 0)
/* See if the timestamp is suffixed with UTC */
utc = endswith_no_case(t, " UTC");
if (utc)
- t = strndupa(t, utc - t);
+ t = strndupa_safe(t, utc - t);
else {
const char *e = NULL;
int j;
if (IN_SET(j, 0, 1)) {
/* Found one of the two timezones specified. */
- t = strndupa(t, e - t - 1);
+ t = strndupa_safe(t, e - t - 1);
dst = j;
tzn = tzname[j];
}
/* Cut off the timezone if we don't need it. */
if (with_tz)
- t = strndupa(t, last_space - t);
+ t = strndupa_safe(t, last_space - t);
shared->return_value = parse_timestamp_impl(t, &shared->usec, with_tz);
return uid == getuid();
}
+
+const char *get_home_root(void) {
+ const char *e;
+
+ /* For debug purposes allow overriding where we look for home dirs */
+ e = secure_getenv("SYSTEMD_HOME_ROOT");
+ if (e && path_is_absolute(e) && path_is_normalized(e))
+ return e;
+
+ return "/home";
+}
int is_this_me(const char *username);
+const char *get_home_root(void);
+
/* A locked *and* invalid password for "struct spwd"'s .sp_pwdp and "struct passwd"'s .pw_passwd field */
#define PASSWORD_LOCKED_AND_INVALID "!*"
bool found_offline = false, found_online = false;
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
+ int r;
d = opendir("/sys/class/power_supply");
if (!d)
return errno == ENOENT ? true : -errno;
FOREACH_DIRENT(de, d, return -errno) {
- _cleanup_close_ int fd = -1, device = -1;
- char contents[6];
- ssize_t n;
+ _cleanup_close_ int device_fd = -1;
+ _cleanup_free_ char *contents = NULL;
+ unsigned v;
- device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (device < 0) {
+ device_fd = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
+ if (device_fd < 0) {
if (IN_SET(errno, ENOENT, ENOTDIR))
continue;
return -errno;
}
- fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0) {
- if (errno == ENOENT)
- continue;
-
- return -errno;
- }
+ r = read_virtual_file_at(device_fd, "type", SIZE_MAX, &contents, NULL);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0)
+ return r;
- n = read(fd, contents, sizeof(contents));
- if (n < 0)
- return -errno;
+ delete_trailing_chars(contents, NEWLINE);
- if (n != 6 || memcmp(contents, "Mains\n", 6))
+ /* We assume every power source is AC, except for batteries. See
+ * https://github.com/torvalds/linux/blob/4eef766b7d4d88f0b984781bc1bcb574a6eafdc7/include/linux/power_supply.h#L176
+ * for defined power source types. Also see:
+ * https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-power */
+ if (streq(contents, "Battery"))
continue;
- safe_close(fd);
- fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0) {
- if (errno == ENOENT)
- continue;
-
- return -errno;
- }
+ contents = mfree(contents);
- n = read(fd, contents, sizeof(contents));
- if (n < 0)
- return -errno;
+ r = read_virtual_file_at(device_fd, "online", SIZE_MAX, &contents, NULL);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0)
+ return r;
- if (n != 2 || contents[1] != '\n')
- return -EIO;
+ delete_trailing_chars(contents, NEWLINE);
- if (contents[0] == '1') {
+ r = safe_atou(contents, &v);
+ if (r < 0)
+ return r;
+ if (v > 0) /* At least 1 and 2 are defined as different types of 'online' */
found_online = true;
- break;
- } else if (contents[0] == '0')
- found_offline = true;
else
- return -EIO;
+ found_offline = true;
}
return found_online || !found_offline;
{ "Parallels", VIRTUALIZATION_PARALLELS },
/* https://wiki.freebsd.org/bhyve */
{ "BHYVE", VIRTUALIZATION_BHYVE },
+ { "Microsoft", VIRTUALIZATION_MICROSOFT },
};
int r;
#endif
}
-static int detect_vm_xen(void) {
-
- /* Check for Dom0 will be executed later in detect_vm_xen_dom0
- The presence of /proc/xen indicates some form of a Xen domain */
- if (access("/proc/xen", F_OK) < 0) {
- log_debug("Virtualization XEN not found, /proc/xen does not exist");
- return VIRTUALIZATION_NONE;
- }
-
- log_debug("Virtualization XEN found (/proc/xen exists)");
- return VIRTUALIZATION_XEN;
-}
-
#define XENFEAT_dom0 11 /* xen/include/public/features.h */
#define PATH_FEATURES "/sys/hypervisor/properties/features"
/* Returns -errno, or 0 for domU, or 1 for dom0 */
}
}
+static int detect_vm_xen(void) {
+ int r;
+
+ /* The presence of /proc/xen indicates some form of a Xen domain */
+ if (access("/proc/xen", F_OK) < 0) {
+ log_debug("Virtualization XEN not found, /proc/xen does not exist");
+ return VIRTUALIZATION_NONE;
+ }
+ log_debug("Virtualization XEN found (/proc/xen exists)");
+
+ /* Ignore the Xen hypervisor if we are in Dom0 */
+ r = detect_vm_xen_dom0();
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return VIRTUALIZATION_NONE;
+
+ return VIRTUALIZATION_XEN;
+}
+
static int detect_vm_hypervisor(void) {
_cleanup_free_ char *hvtype = NULL;
int r;
*
* → First, try to detect Oracle Virtualbox and Amazon EC2 Nitro, even if they use KVM, as well as Xen even if
* it cloaks as Microsoft Hyper-V. Attempt to detect uml at this stage also since it runs as a user-process
- * nested inside other VMs.
+ * nested inside other VMs. Also check for Xen now, because Xen PV mode does not override CPUID when nested
+ * inside another hypervisor.
*
* → Second, try to detect from CPUID, this will report KVM for whatever software is used even if info in DMI is
* overwritten.
else if (r != VIRTUALIZATION_NONE)
goto finish;
+ /* Detect Xen */
+ r = detect_vm_xen();
+ if (r < 0)
+ return r;
+ if (r == VIRTUALIZATION_VM_OTHER)
+ other = true;
+ else if (r != VIRTUALIZATION_NONE)
+ goto finish;
+
/* Detect from CPUID */
r = detect_vm_cpuid();
if (r < 0)
goto finish;
}
- /* x86 xen will most likely be detected by cpuid. If not (most likely
- * because we're not an x86 guest), then we should try the /proc/xen
- * directory next. If that's not found, then we check for the high-level
- * hypervisor sysfs file.
- */
-
- r = detect_vm_xen();
- if (r < 0)
- return r;
- if (r == VIRTUALIZATION_VM_OTHER)
- other = true;
- else if (r != VIRTUALIZATION_NONE)
- goto finish;
-
+ /* Check high-level hypervisor sysfs file */
r = detect_vm_hypervisor();
if (r < 0)
return r;
return r;
finish:
- /* x86 xen Dom0 is detected as XEN in hypervisor and maybe others.
- * In order to detect the Dom0 as not virtualization we need to
- * double-check it */
- if (r == VIRTUALIZATION_XEN) {
- int dom0;
-
- dom0 = detect_vm_xen_dom0();
- if (dom0 < 0)
- return dom0;
- if (dom0 > 0)
- r = VIRTUALIZATION_NONE;
- } else if (r == VIRTUALIZATION_NONE && other)
+ if (r == VIRTUALIZATION_NONE && other)
r = VIRTUALIZATION_VM_OTHER;
cached_found = r;
"Can't parse empty 'tries left' counter from LoaderBootCountPath: %s",
path);
- z = strndupa(e, k);
+ z = strndupa_safe(e, k);
r = safe_atou64(z, &left);
if (r < 0)
return log_error_errno(r, "Failed to parse 'tries left' counter from LoaderBootCountPath: %s", path);
"Can't parse empty 'tries done' counter from LoaderBootCountPath: %s",
path);
- z = strndupa(e, k);
+ z = strndupa_safe(e, k);
r = safe_atou64(z, &done);
if (r < 0)
return log_error_errno(r, "Failed to parse 'tries done' counter from LoaderBootCountPath: %s", path);
" list List boot loader entries\n"
" set-default ID Set default boot loader entry\n"
" set-oneshot ID Set default boot loader entry, for next boot only\n"
+ " set-timeout SECONDS Set the menu timeout\n"
+ " set-timeout-oneshot SECONDS\n"
+ " Set the menu timeout for the next boot only\n"
"\n%3$ssystemd-boot Commands:%4$s\n"
" install Install systemd-boot to the ESP and EFI variables\n"
" update Update systemd-boot in the ESP and EFI variables\n"
return EXIT_SUCCESS;
}
+static int parse_timeout(const char *arg1, char16_t **ret_timeout, size_t *ret_timeout_size) {
+ char utf8[DECIMAL_STR_MAX(usec_t)];
+ char16_t *encoded;
+ usec_t timeout;
+ int r;
+
+ assert(arg1);
+ assert(ret_timeout);
+ assert(ret_timeout_size);
+
+ if (streq(arg1, "menu-force"))
+ timeout = USEC_INFINITY;
+ else if (streq(arg1, "menu-hidden"))
+ timeout = 0;
+ else {
+ r = parse_time(arg1, &timeout, USEC_PER_SEC);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse timeout '%s': %m", arg1);
+ if (timeout != USEC_INFINITY && timeout > UINT32_MAX * USEC_PER_SEC)
+ log_warning("Timeout is too long and will be treated as 'menu-force' instead.");
+ }
+
+ xsprintf(utf8, USEC_FMT, MIN(timeout / USEC_PER_SEC, UINT32_MAX));
+ encoded = utf8_to_utf16(utf8, strlen(utf8));
+ if (!encoded)
+ return log_oom();
+ *ret_timeout = encoded;
+ *ret_timeout_size = char16_strlen(encoded) * 2 + 2;
+ return 0;
+}
+
static int parse_loader_entry_target_arg(const char *arg1, char16_t **ret_target, size_t *ret_target_size) {
int r;
if (streq(arg1, "@current")) {
return 0;
}
-static int verb_set_default(int argc, char *argv[], void *userdata) {
+static int verb_set_efivar(int argc, char *argv[], void *userdata) {
int r;
if (!is_efi_boot())
if (!arg_touch_variables)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "'%s' operation cannot be combined with --touch-variables=no.",
+ "'%s' operation cannot be combined with --no-variables.",
argv[0]);
- const char *variable = streq(argv[0], "set-default") ?
- EFI_LOADER_VARIABLE(LoaderEntryDefault) : EFI_LOADER_VARIABLE(LoaderEntryOneShot);
+ const char *variable;
+ int (* arg_parser)(const char *, char16_t **, size_t *);
+
+ if (streq(argv[0], "set-default")) {
+ variable = EFI_LOADER_VARIABLE(LoaderEntryDefault);
+ arg_parser = parse_loader_entry_target_arg;
+ } else if (streq(argv[0], "set-oneshot")) {
+ variable = EFI_LOADER_VARIABLE(LoaderEntryOneShot);
+ arg_parser = parse_loader_entry_target_arg;
+ } else if (streq(argv[0], "set-timeout")) {
+ variable = EFI_LOADER_VARIABLE(LoaderConfigTimeout);
+ arg_parser = parse_timeout;
+ } else if (streq(argv[0], "set-timeout-oneshot")) {
+ variable = EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot);
+ arg_parser = parse_timeout;
+ } else
+ assert_not_reached();
if (isempty(argv[1])) {
r = efi_set_variable(variable, NULL, 0);
if (r < 0 && r != -ENOENT)
return log_error_errno(r, "Failed to remove EFI variable '%s': %m", variable);
} else {
- _cleanup_free_ char16_t *target = NULL;
- size_t target_size = 0;
+ _cleanup_free_ char16_t *value = NULL;
+ size_t value_size = 0;
- r = parse_loader_entry_target_arg(argv[1], &target, &target_size);
+ r = arg_parser(argv[1], &value, &value_size);
if (r < 0)
return r;
- r = efi_set_variable(variable, target, target_size);
+ r = efi_set_variable(variable, value, value_size);
if (r < 0)
return log_error_errno(r, "Failed to update EFI variable '%s': %m", variable);
}
{ "remove", VERB_ANY, 1, 0, verb_remove },
{ "is-installed", VERB_ANY, 1, 0, verb_is_installed },
{ "list", VERB_ANY, 1, 0, verb_list },
- { "set-default", 2, 2, 0, verb_set_default },
- { "set-oneshot", 2, 2, 0, verb_set_default },
+ { "set-default", 2, 2, 0, verb_set_efivar },
+ { "set-oneshot", 2, 2, 0, verb_set_efivar },
+ { "set-timeout", 2, 2, 0, verb_set_efivar },
+ { "set-timeout-oneshot", 2, 2, 0, verb_set_efivar },
{ "random-seed", VERB_ANY, 1, 0, verb_random_seed },
{ "systemd-efi-options", VERB_ANY, 2, 0, verb_systemd_efi_options },
{ "reboot-to-firmware", VERB_ANY, 2, 0, verb_reboot_to_firmware },
#include "secure-boot.h"
#include "shim.h"
#include "util.h"
+#include "xbootldr.h"
#ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
#define TEXT_ATTR_SWAP(c) EFI_TEXT_ATTR(((c) & 0b11110000) >> 4, (c) & 0b1111)
/* magic string to find in the binary image */
-static const char _used_ _section_(".sdmagic") magic[] = "#### LoaderInfo: systemd-boot " GIT_VERSION " ####";
+_used_ _section_(".sdmagic") static const char magic[] = "#### LoaderInfo: systemd-boot " GIT_VERSION " ####";
enum loader_type {
LOADER_UNDEFINED,
UINTN entry_count;
INTN idx_default;
INTN idx_default_efivar;
- UINTN timeout_sec;
- UINTN timeout_sec_config;
- INTN timeout_sec_efivar;
+ UINT32 timeout_sec; /* Actual timeout used (efi_main() override > efivar > config). */
+ UINT32 timeout_sec_config;
+ UINT32 timeout_sec_efivar;
CHAR16 *entry_default_pattern;
CHAR16 *entry_oneshot;
CHAR16 *options_edit;
RandomSeedMode random_seed_mode;
} Config;
+/* These values have been chosen so that the transitions the user sees could
+ * employ unsigned over-/underflow like this:
+ * efivar unset ↔ force menu ↔ no timeout/skip menu ↔ 1 s ↔ 2 s ↔ … */
+enum {
+ TIMEOUT_MIN = 1,
+ TIMEOUT_MAX = UINT32_MAX - 2U,
+ TIMEOUT_UNSET = UINT32_MAX - 1U,
+ TIMEOUT_MENU_FORCE = UINT32_MAX,
+ TIMEOUT_MENU_HIDDEN = 0,
+ TIMEOUT_TYPE_MAX = UINT32_MAX,
+};
+
static VOID cursor_left(UINTN *cursor, UINTN *first) {
assert(cursor);
assert(first);
return -1;
}
+static CHAR16 *update_timeout_efivar(UINT32 *t, BOOLEAN inc) {
+ assert(t);
+
+ switch (*t) {
+ case TIMEOUT_MAX:
+ *t = inc ? TIMEOUT_MAX : (*t - 1);
+ break;
+ case TIMEOUT_UNSET:
+ *t = inc ? TIMEOUT_MENU_FORCE : TIMEOUT_UNSET;
+ break;
+ case TIMEOUT_MENU_FORCE:
+ *t = inc ? TIMEOUT_MENU_HIDDEN : TIMEOUT_UNSET;
+ break;
+ case TIMEOUT_MENU_HIDDEN:
+ *t = inc ? TIMEOUT_MIN : TIMEOUT_MENU_FORCE;
+ break;
+ default:
+ *t += inc ? 1 : -1;
+ }
+
+ switch (*t) {
+ case TIMEOUT_UNSET:
+ return StrDuplicate(L"Menu timeout defined by configuration file.");
+ case TIMEOUT_MENU_FORCE:
+ return StrDuplicate(L"Timeout disabled, menu will always be shown.");
+ case TIMEOUT_MENU_HIDDEN:
+ return StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
+ default:
+ return PoolPrint(L"Menu timeout set to %u s.", *t);
+ }
+}
+
static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
UINT64 key;
UINTN timeout;
Print(L"\n--- press key ---\n\n");
console_key_read(&key, 0);
- Print(L"timeout: %u\n", config->timeout_sec);
- if (config->timeout_sec_efivar >= 0)
- Print(L"timeout (EFI var): %d\n", config->timeout_sec_efivar);
- Print(L"timeout (config): %u\n", config->timeout_sec_config);
+ Print(L"timeout: %u s\n", config->timeout_sec);
+ if (config->timeout_sec_efivar != TIMEOUT_UNSET)
+ Print(L"timeout (EFI var): %u s\n", config->timeout_sec_efivar);
+ if (config->timeout_sec_config != TIMEOUT_UNSET)
+ Print(L"timeout (config): %u s\n", config->timeout_sec_config);
if (config->entry_default_pattern)
Print(L"default pattern: '%s'\n", config->entry_default_pattern);
Print(L"editor: %s\n", yes_no(config->editor));
BOOLEAN refresh = TRUE, highlight = FALSE;
UINTN x_start = 0, y_start = 0, y_status = 0;
UINTN x_max, y_max;
- CHAR16 **lines = NULL;
+ _cleanup_(strv_freep) CHAR16 **lines = NULL;
_cleanup_freepool_ CHAR16 *clearline = NULL, *status = NULL;
- UINTN timeout_remain = config->timeout_sec;
+ UINT32 timeout_efivar_saved = config->timeout_sec_efivar;
+ UINT32 timeout_remain = config->timeout_sec == TIMEOUT_MENU_FORCE ? 0 : config->timeout_sec;
INT16 idx;
BOOLEAN exit = FALSE, run = TRUE;
INT64 console_mode_initial = ST->ConOut->Mode->Mode, console_mode_efivar_saved = config->console_mode_efivar;
/* Put status line after the entry list, but give it some breathing room. */
y_status = MIN(y_start + MIN(visible_max, config->entry_count) + 4, y_max - 1);
- if (lines) {
- for (UINTN i = 0; i < config->entry_count; i++)
- FreePool(lines[i]);
- FreePool(lines);
- FreePool(clearline);
- }
+ strv_free(lines);
+ FreePool(clearline);
/* menu entries title lines */
- lines = AllocatePool(sizeof(CHAR16 *) * config->entry_count);
+ lines = AllocatePool((config->entry_count + 1) * sizeof(CHAR16 *));
for (UINTN i = 0; i < config->entry_count; i++) {
UINTN j, padding;
lines[i][j] = ' ';
lines[i][line_width] = '\0';
}
+ lines[config->entry_count] = NULL;
clearline = AllocatePool((x_max+1) * sizeof(CHAR16));
for (UINTN i = 0; i < x_max; i++)
if (timeout_remain > 0) {
FreePool(status);
- status = PoolPrint(L"Boot in %d s.", timeout_remain);
+ status = PoolPrint(L"Boot in %u s.", timeout_remain);
}
/* print status at last line of screen */
case KEYPRESS(0, 0, '-'):
case KEYPRESS(0, 0, 'T'):
- if (config->timeout_sec_efivar > 0) {
- config->timeout_sec_efivar--;
- efivar_set_uint_string(
- LOADER_GUID,
- L"LoaderConfigTimeout",
- config->timeout_sec_efivar,
- EFI_VARIABLE_NON_VOLATILE);
- if (config->timeout_sec_efivar > 0)
- status = PoolPrint(L"Menu timeout set to %d s.", config->timeout_sec_efivar);
- else
- status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
- } else if (config->timeout_sec_efivar <= 0){
- config->timeout_sec_efivar = -1;
- efivar_set(
- LOADER_GUID, L"LoaderConfigTimeout", NULL, EFI_VARIABLE_NON_VOLATILE);
- if (config->timeout_sec_config > 0)
- status = PoolPrint(L"Menu timeout of %d s is defined by configuration file.",
- config->timeout_sec_config);
- else
- status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
- }
+ status = update_timeout_efivar(&config->timeout_sec_efivar, FALSE);
break;
case KEYPRESS(0, 0, '+'):
case KEYPRESS(0, 0, 't'):
- if (config->timeout_sec_efivar == -1 && config->timeout_sec_config == 0)
- config->timeout_sec_efivar++;
- config->timeout_sec_efivar++;
- efivar_set_uint_string(
- LOADER_GUID,
- L"LoaderConfigTimeout",
- config->timeout_sec_efivar,
- EFI_VARIABLE_NON_VOLATILE);
- if (config->timeout_sec_efivar > 0)
- status = PoolPrint(L"Menu timeout set to %d s.",
- config->timeout_sec_efivar);
- else
- status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
+ status = update_timeout_efivar(&config->timeout_sec_efivar, TRUE);
break;
case KEYPRESS(0, 0, 'e'):
*chosen_entry = config->entries[idx_highlight];
- /* The user is likely to cycle through several modes before
- * deciding to keep one. Therefore, we update the EFI var after
- * we left the menu to reduce nvram writes. */
+ /* Update EFI vars after we left the menu to reduce NVRAM writes. */
+
if (console_mode_efivar_saved != config->console_mode_efivar) {
if (config->console_mode_efivar == CONSOLE_MODE_KEEP)
efivar_set(LOADER_GUID, L"LoaderConfigConsoleMode", NULL, EFI_VARIABLE_NON_VOLATILE);
config->console_mode_efivar, EFI_VARIABLE_NON_VOLATILE);
}
- for (UINTN i = 0; i < config->entry_count; i++)
- FreePool(lines[i]);
- FreePool(lines);
+ if (timeout_efivar_saved != config->timeout_sec_efivar) {
+ if (config->timeout_sec_efivar == TIMEOUT_UNSET)
+ efivar_set(LOADER_GUID, L"LoaderConfigTimeout", NULL, EFI_VARIABLE_NON_VOLATILE);
+ else
+ efivar_set_uint_string(LOADER_GUID, L"LoaderConfigTimeout",
+ config->timeout_sec_efivar, EFI_VARIABLE_NON_VOLATILE);
+ }
clear_screen(COLOR_NORMAL);
return run;
FreePool(entry);
}
+static inline VOID config_entry_freep(ConfigEntry **entry) {
+ config_entry_free(*entry);
+}
+
static CHAR8 *line_get_key_value(
CHAR8 *content,
const CHAR8 *sep,
while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
if (strcmpa((CHAR8 *)"timeout", key) == 0) {
- _cleanup_freepool_ CHAR16 *s = NULL;
+ if (strcmpa((CHAR8*) "menu-force", value) == 0)
+ config->timeout_sec_config = TIMEOUT_MENU_FORCE;
+ else if (strcmpa((CHAR8*) "menu-hidden", value) == 0)
+ config->timeout_sec_config = TIMEOUT_MENU_HIDDEN;
+ else {
+ _cleanup_freepool_ CHAR16 *s = NULL;
- s = stra_to_str(value);
- config->timeout_sec_config = Atoi(s);
+ s = stra_to_str(value);
+ config->timeout_sec_config = MIN(Atoi(s), TIMEOUT_TYPE_MAX);
+ }
config->timeout_sec = config->timeout_sec_config;
continue;
}
CHAR8 *content,
const CHAR16 *loaded_image_path) {
- ConfigEntry *entry;
+ _cleanup_(config_entry_freep) ConfigEntry *entry = NULL;
CHAR8 *line;
UINTN pos = 0;
CHAR8 *key, *value;
assert(path);
assert(file);
assert(content);
- assert(loaded_image_path);
entry = AllocatePool(sizeof(ConfigEntry));
}
}
- if (entry->type == LOADER_UNDEFINED) {
- config_entry_free(entry);
+ if (entry->type == LOADER_UNDEFINED)
return;
- }
/* check existence */
err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, entry->loader, EFI_FILE_MODE_READ, 0ULL);
- if (EFI_ERROR(err)) {
- config_entry_free(entry);
+ if (EFI_ERROR(err))
return;
- }
uefi_call_wrapper(handle->Close, 1, handle);
/* add initrd= to options */
config_add_entry(config, entry);
config_entry_parse_tries(entry, path, file, L".conf");
+ TAKE_PTR(entry);
}
static VOID config_load_defaults(Config *config, EFI_FILE *root_dir) {
.idx_default_efivar = -1,
.console_mode = CONSOLE_MODE_KEEP,
.console_mode_efivar = CONSOLE_MODE_KEEP,
+ .timeout_sec_config = TIMEOUT_UNSET,
+ .timeout_sec_efivar = TIMEOUT_UNSET,
};
err = file_read(root_dir, L"\\loader\\loader.conf", 0, 0, &content, NULL);
err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigTimeout", &value);
if (!EFI_ERROR(err)) {
- config->timeout_sec_efivar = value > INTN_MAX ? INTN_MAX : value;
- config->timeout_sec = value;
- } else
- config->timeout_sec_efivar = -1;
+ config->timeout_sec_efivar = MIN(value, TIMEOUT_TYPE_MAX);
+ config->timeout_sec = config->timeout_sec_efivar;
+ }
err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigTimeoutOneShot", &value);
if (!EFI_ERROR(err)) {
/* Unset variable now, after all it's "one shot". */
(void) efivar_set(LOADER_GUID, L"LoaderConfigTimeoutOneShot", NULL, EFI_VARIABLE_NON_VOLATILE);
- config->timeout_sec = value;
+ config->timeout_sec = MIN(value, TIMEOUT_TYPE_MAX);
config->force_menu = TRUE; /* force the menu when this is set */
}
assert(config);
assert(device);
assert(root_dir);
- assert(loaded_image_path);
err = open_directory(root_dir, L"\\loader\\entries", &entries_dir);
if (EFI_ERROR(err))
}
}
-#define XBOOTLDR_GUID \
- &(const EFI_GUID) { 0xbc13c2ff, 0x59e6, 0x4262, { 0xa3, 0x52, 0xb2, 0x75, 0xfd, 0x6f, 0x71, 0x72 } }
-
-static EFI_DEVICE_PATH *path_parent(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node) {
- EFI_DEVICE_PATH *parent;
- UINTN len;
-
- assert(path);
- assert(node);
-
- len = (UINT8*) NextDevicePathNode(node) - (UINT8*) path;
- parent = (EFI_DEVICE_PATH*) AllocatePool(len + sizeof(EFI_DEVICE_PATH));
- CopyMem(parent, path, len);
- CopyMem((UINT8*) parent + len, EndDevicePath, sizeof(EFI_DEVICE_PATH));
-
- return parent;
-}
-
static VOID config_load_xbootldr(
Config *config,
EFI_HANDLE *device) {
- EFI_DEVICE_PATH *partition_path, *disk_path, *copy;
- UINT32 found_partition_number = UINT32_MAX;
- UINT64 found_partition_start = UINT64_MAX;
- UINT64 found_partition_size = UINT64_MAX;
- UINT8 found_partition_signature[16] = {};
EFI_HANDLE new_device;
EFI_FILE *root_dir;
- EFI_STATUS r;
+ EFI_STATUS err;
assert(config);
assert(device);
- partition_path = DevicePathFromHandle(device);
- if (!partition_path)
- return;
-
- for (EFI_DEVICE_PATH *node = partition_path; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) {
- EFI_HANDLE disk_handle;
- EFI_BLOCK_IO *block_io;
- EFI_DEVICE_PATH *p;
-
- /* First, Let's look for the SCSI/SATA/USB/… device path node, i.e. one above the media
- * devices */
- if (DevicePathType(node) != MESSAGING_DEVICE_PATH)
- continue;
-
- /* Determine the device path one level up */
- disk_path = path_parent(partition_path, node);
- p = disk_path;
- r = uefi_call_wrapper(BS->LocateDevicePath, 3, &BlockIoProtocol, &p, &disk_handle);
- if (EFI_ERROR(r))
- continue;
-
- r = uefi_call_wrapper(BS->HandleProtocol, 3, disk_handle, &BlockIoProtocol, (VOID **)&block_io);
- if (EFI_ERROR(r))
- continue;
-
- /* Filter out some block devices early. (We only care about block devices that aren't
- * partitions themselves — we look for GPT partition tables to parse after all —, and only
- * those which contain a medium and have at least 2 blocks.) */
- if (block_io->Media->LogicalPartition ||
- !block_io->Media->MediaPresent ||
- block_io->Media->LastBlock <= 1)
- continue;
-
- /* Try both copies of the GPT header, in case one is corrupted */
- for (UINTN nr = 0; nr < 2; nr++) {
- _cleanup_freepool_ EFI_PARTITION_ENTRY* entries = NULL;
- union {
- EFI_PARTITION_TABLE_HEADER gpt_header;
- uint8_t space[((sizeof(EFI_PARTITION_TABLE_HEADER) + 511) / 512) * 512];
- } gpt_header_buffer;
- EFI_PARTITION_TABLE_HEADER *h = &gpt_header_buffer.gpt_header;
- UINT64 where;
- UINTN sz;
- UINT32 crc32, crc32_saved;
-
- if (nr == 0)
- /* Read the first copy at LBA 1 */
- where = 1;
- else
- /* Read the second copy at the very last LBA of this block device */
- where = block_io->Media->LastBlock;
-
- /* Read the GPT header */
- r = uefi_call_wrapper(block_io->ReadBlocks, 5,
- block_io,
- block_io->Media->MediaId,
- where,
- sizeof(gpt_header_buffer), &gpt_header_buffer);
- if (EFI_ERROR(r))
- continue;
-
- /* Some superficial validation of the GPT header */
- if(CompareMem(&h->Header.Signature, "EFI PART", sizeof(h->Header.Signature) != 0))
- continue;
-
- if (h->Header.HeaderSize < 92 ||
- h->Header.HeaderSize > 512)
- continue;
-
- if (h->Header.Revision != 0x00010000U)
- continue;
-
- /* Calculate CRC check */
- crc32_saved = h->Header.CRC32;
- h->Header.CRC32 = 0;
- r = BS->CalculateCrc32(&gpt_header_buffer, h->Header.HeaderSize, &crc32);
- h->Header.CRC32 = crc32_saved;
- if (EFI_ERROR(r) || crc32 != crc32_saved)
- continue;
-
- if (h->MyLBA != where)
- continue;
-
- if (h->SizeOfPartitionEntry < sizeof(EFI_PARTITION_ENTRY))
- continue;
-
- if (h->NumberOfPartitionEntries <= 0 ||
- h->NumberOfPartitionEntries > 1024)
- continue;
-
- if (h->SizeOfPartitionEntry > UINTN_MAX / h->NumberOfPartitionEntries) /* overflow check */
- continue;
-
- /* Now load the GPT entry table */
- sz = ALIGN_TO((UINTN) h->SizeOfPartitionEntry * (UINTN) h->NumberOfPartitionEntries, 512);
- entries = AllocatePool(sz);
-
- r = uefi_call_wrapper(block_io->ReadBlocks, 5,
- block_io,
- block_io->Media->MediaId,
- h->PartitionEntryLBA,
- sz, entries);
- if (EFI_ERROR(r))
- continue;
-
- /* Calculate CRC of entries array, too */
- r = BS->CalculateCrc32(&entries, sz, &crc32);
- if (EFI_ERROR(r) || crc32 != h->PartitionEntryArrayCRC32)
- continue;
-
- for (UINTN i = 0; i < h->NumberOfPartitionEntries; i++) {
- EFI_PARTITION_ENTRY *entry;
-
- entry = (EFI_PARTITION_ENTRY*) ((UINT8*) entries + h->SizeOfPartitionEntry * i);
-
- if (CompareMem(&entry->PartitionTypeGUID, XBOOTLDR_GUID, 16) == 0) {
- UINT64 end;
-
- /* Let's use memcpy(), in case the structs are not aligned (they really should be though) */
- CopyMem(&found_partition_start, &entry->StartingLBA, sizeof(found_partition_start));
- CopyMem(&end, &entry->EndingLBA, sizeof(end));
-
- if (end < found_partition_start) /* Bogus? */
- continue;
-
- found_partition_size = end - found_partition_start + 1;
- CopyMem(found_partition_signature, &entry->UniquePartitionGUID, sizeof(found_partition_signature));
-
- found_partition_number = i + 1;
- goto found;
- }
- }
-
- break; /* This GPT was fully valid, but we didn't find what we are looking for. This
- * means there's no reason to check the second copy of the GPT header */
- }
- }
-
- return; /* Not found */
-
-found:
- copy = DuplicateDevicePath(partition_path);
-
- /* Patch in the data we found */
- for (EFI_DEVICE_PATH *node = copy; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) {
- HARDDRIVE_DEVICE_PATH *hd;
-
- if (DevicePathType(node) != MEDIA_DEVICE_PATH)
- continue;
-
- if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP)
- continue;
-
- hd = (HARDDRIVE_DEVICE_PATH*) node;
- hd->PartitionNumber = found_partition_number;
- hd->PartitionStart = found_partition_start;
- hd->PartitionSize = found_partition_size;
- CopyMem(hd->Signature, found_partition_signature, sizeof(hd->Signature));
- hd->MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
- hd->SignatureType = SIGNATURE_TYPE_GUID;
- }
-
- r = uefi_call_wrapper(BS->LocateDevicePath, 3, &BlockIoProtocol, ©, &new_device);
- if (EFI_ERROR(r))
- return;
-
- root_dir = LibOpenRoot(new_device);
- if (!root_dir)
+ err = xbootldr_open(device, &new_device, &root_dir);
+ if (EFI_ERROR(err))
return;
config_entry_add_linux(config, new_device, root_dir);
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
_cleanup_freepool_ EFI_LOADED_IMAGE *loaded_image = NULL;
_cleanup_(FileHandleClosep) EFI_FILE *root_dir = NULL;
+ _cleanup_(config_free) Config config = {};
CHAR16 *loaded_image_path;
EFI_STATUS err;
- Config config;
UINT64 init_usec;
BOOLEAN menu = FALSE;
}
err = EFI_SUCCESS;
out:
- config_free(&config);
uefi_call_wrapper(BS->CloseProtocol, 4, image, &LoadedImageProtocol, image, NULL);
return err;
}
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Failed to pack cpio trailer: %r");
-#if ENABLE_TPM
err = tpm_log_event(
tpm_pcr,
POINTER_TO_PHYSICAL_ADDRESS(buffer),
tpm_description);
if (EFI_ERROR(err))
log_error_stall(L"Unable to add initrd TPM measurement for PCR %u (%s), ignoring: %r", tpm_pcr, tpm_description, err);
-#endif
*ret_buffer = TAKE_PTR(buffer);
*ret_buffer_size = buffer_size;
return uefi_call_wrapper(BS->InstallConfigurationTable, 2, &EfiDtbTableGuid, PHYSICAL_ADDRESS_TO_POINTER(state->addr));
}
+EFI_STATUS devicetree_install_from_memory(struct devicetree_state *state,
+ const VOID *dtb_buffer, UINTN dtb_length) {
+
+ EFI_STATUS err;
+
+ assert(state);
+ assert(dtb_buffer && dtb_length > 0);
+
+ err = LibGetSystemConfigurationTable(&EfiDtbTableGuid, &state->orig);
+ if (EFI_ERROR(err))
+ return EFI_UNSUPPORTED;
+
+ err = devicetree_allocate(state, dtb_length);
+ if (EFI_ERROR(err))
+ return err;
+
+ CopyMem(PHYSICAL_ADDRESS_TO_POINTER(state->addr), dtb_buffer, dtb_length);
+
+ err = devicetree_fixup(state, dtb_length);
+ if (EFI_ERROR(err))
+ return err;
+
+ return uefi_call_wrapper(BS->InstallConfigurationTable, 2, &EfiDtbTableGuid, PHYSICAL_ADDRESS_TO_POINTER(state->addr));
+}
+
void devicetree_cleanup(struct devicetree_state *state) {
EFI_STATUS err;
};
EFI_STATUS devicetree_install(struct devicetree_state *state, EFI_FILE_HANDLE root_dir, CHAR16 *name);
+EFI_STATUS devicetree_install_from_memory(
+ struct devicetree_state *state, const VOID *dtb_buffer, UINTN dtb_length);
void devicetree_cleanup(struct devicetree_state *state);
EFI_STATUS disk_get_part_uuid(EFI_HANDLE *handle, CHAR16 uuid[static 37]) {
EFI_DEVICE_PATH *device_path;
+ _cleanup_freepool_ EFI_DEVICE_PATH *paths = NULL;
assert(handle);
/* export the device path this image is started from */
device_path = DevicePathFromHandle(handle);
- if (device_path) {
- _cleanup_freepool_ EFI_DEVICE_PATH *paths = NULL;
-
- paths = UnpackDevicePath(device_path);
- for (EFI_DEVICE_PATH *path = paths; !IsDevicePathEnd(path); path = NextDevicePathNode(path)) {
- HARDDRIVE_DEVICE_PATH *drive;
-
- if (DevicePathType(path) != MEDIA_DEVICE_PATH)
- continue;
- if (DevicePathSubType(path) != MEDIA_HARDDRIVE_DP)
- continue;
- drive = (HARDDRIVE_DEVICE_PATH *)path;
- if (drive->SignatureType != SIGNATURE_TYPE_GUID)
- continue;
-
- GuidToString(uuid, (EFI_GUID *)&drive->Signature);
- return EFI_SUCCESS;
- }
+ if (!device_path)
+ return EFI_NOT_FOUND;
+
+ paths = UnpackDevicePath(device_path);
+ for (EFI_DEVICE_PATH *path = paths; !IsDevicePathEnd(path); path = NextDevicePathNode(path)) {
+ HARDDRIVE_DEVICE_PATH *drive;
+
+ if (DevicePathType(path) != MEDIA_DEVICE_PATH)
+ continue;
+ if (DevicePathSubType(path) != MEDIA_HARDDRIVE_DP)
+ continue;
+ drive = (HARDDRIVE_DEVICE_PATH *)path;
+ if (drive->SignatureType != SIGNATURE_TYPE_GUID)
+ continue;
+
+ GuidToString(uuid, (EFI_GUID *)&drive->Signature);
+ return EFI_SUCCESS;
}
return EFI_NOT_FOUND;
#include <efilib.h>
#include "graphics.h"
+#include "missing_efi.h"
#include "util.h"
-#define EFI_CONSOLE_CONTROL_GUID \
- &(const EFI_GUID) { 0xf42f7782, 0x12e, 0x4c12, { 0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21 } }
-
EFI_STATUS graphics_mode(BOOLEAN on) {
-
- struct _EFI_CONSOLE_CONTROL_PROTOCOL;
-
- typedef enum {
- EfiConsoleControlScreenText,
- EfiConsoleControlScreenGraphics,
- EfiConsoleControlScreenMaxValue,
- } EFI_CONSOLE_CONTROL_SCREEN_MODE;
-
- typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE)(
- struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
- EFI_CONSOLE_CONTROL_SCREEN_MODE *Mode,
- BOOLEAN *UgaExists,
- BOOLEAN *StdInLocked
- );
-
- typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE)(
- struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
- EFI_CONSOLE_CONTROL_SCREEN_MODE Mode
- );
-
- typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN)(
- struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
- CHAR16 *Password
- );
-
- typedef struct _EFI_CONSOLE_CONTROL_PROTOCOL {
- EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE GetMode;
- EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode;
- EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN LockStdIn;
- } EFI_CONSOLE_CONTROL_PROTOCOL;
-
EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
EFI_CONSOLE_CONTROL_SCREEN_MODE new;
EFI_CONSOLE_CONTROL_SCREEN_MODE current;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "initrd.h"
+#include "macro-fundamental.h"
+#include "missing_efi.h"
+
+/* extend LoadFileProtocol */
+struct initrd_loader {
+ EFI_LOAD_FILE_PROTOCOL load_file;
+ const VOID *address;
+ UINTN length;
+};
+
+/* static structure for LINUX_INITRD_MEDIA device path
+ see https://github.com/torvalds/linux/blob/v5.13/drivers/firmware/efi/libstub/efi-stub-helper.c
+ */
+static const struct {
+ VENDOR_DEVICE_PATH vendor;
+ EFI_DEVICE_PATH end;
+} _packed_ efi_initrd_device_path = {
+ .vendor = {
+ .Header = {
+ .Type = MEDIA_DEVICE_PATH,
+ .SubType = MEDIA_VENDOR_DP,
+ .Length = { sizeof(efi_initrd_device_path.vendor), 0 }
+ },
+ .Guid = LINUX_INITRD_MEDIA_GUID
+ },
+ .end = {
+ .Type = END_DEVICE_PATH_TYPE,
+ .SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE,
+ .Length = { sizeof(efi_initrd_device_path.end), 0 }
+ }
+};
+
+EFIAPI EFI_STATUS initrd_load_file(
+ EFI_LOAD_FILE_PROTOCOL *this,
+ EFI_DEVICE_PATH *file_path,
+ BOOLEAN boot_policy,
+ UINTN *buffer_size,
+ VOID *buffer) {
+
+ struct initrd_loader *loader;
+
+ if (!this || !buffer_size || !file_path)
+ return EFI_INVALID_PARAMETER;
+ if (boot_policy)
+ return EFI_UNSUPPORTED;
+
+ loader = (struct initrd_loader *) this;
+
+ if (loader->length == 0 || !loader->address)
+ return EFI_NOT_FOUND;
+
+ if (!buffer || *buffer_size < loader->length) {
+ *buffer_size = loader->length;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ CopyMem(buffer, loader->address, loader->length);
+ *buffer_size = loader->length;
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS initrd_register(
+ const VOID *initrd_address,
+ UINTN initrd_length,
+ EFI_HANDLE *ret_initrd_handle) {
+
+ EFI_STATUS err;
+ EFI_DEVICE_PATH *dp = (EFI_DEVICE_PATH *) &efi_initrd_device_path;
+ EFI_HANDLE handle;
+ struct initrd_loader *loader;
+
+ assert(ret_initrd_handle);
+
+ if (!initrd_address || initrd_length == 0)
+ return EFI_SUCCESS;
+
+ /* check if a LINUX_INITRD_MEDIA_GUID DevicePath is already registed.
+ LocateDevicePath checks for the "closest DevicePath" and returns its handle,
+ where as InstallMultipleProtocolInterfaces only maches identical DevicePaths.
+ */
+ err = uefi_call_wrapper(BS->LocateDevicePath, 3, &EfiLoadFile2Protocol, &dp, &handle);
+ if (err != EFI_NOT_FOUND) /* InitrdMedia is already registered */
+ return EFI_ALREADY_STARTED;
+
+ loader = AllocatePool(sizeof(struct initrd_loader));
+ if (!loader)
+ return EFI_OUT_OF_RESOURCES;
+
+ *loader = (struct initrd_loader) {
+ .load_file.LoadFile = initrd_load_file,
+ .address = initrd_address,
+ .length = initrd_length
+ };
+
+ /* create a new handle and register the LoadFile2 protocol with the InitrdMediaPath on it */
+ err = uefi_call_wrapper(
+ BS->InstallMultipleProtocolInterfaces, 8,
+ ret_initrd_handle,
+ &DevicePathProtocol, &efi_initrd_device_path,
+ &EfiLoadFile2Protocol, loader,
+ NULL);
+ if (EFI_ERROR(err))
+ FreePool(loader);
+
+ return err;
+}
+
+EFI_STATUS initrd_unregister(EFI_HANDLE initrd_handle) {
+ EFI_STATUS err;
+ struct initrd_loader *loader;
+
+ if (!initrd_handle)
+ return EFI_SUCCESS;
+
+ /* get the LoadFile2 protocol that we allocated earlier */
+ err = uefi_call_wrapper(
+ BS->OpenProtocol, 6,
+ initrd_handle, &EfiLoadFile2Protocol, (VOID **) &loader,
+ NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR(err))
+ return err;
+
+ /* close the handle */
+ (void) uefi_call_wrapper(
+ BS->CloseProtocol, 4,
+ initrd_handle, &EfiLoadFile2Protocol, NULL, NULL);
+
+ /* uninstall all protocols thus destroying the handle */
+ err = uefi_call_wrapper(
+ BS->UninstallMultipleProtocolInterfaces, 6,
+ initrd_handle,
+ &DevicePathProtocol, &efi_initrd_device_path,
+ &EfiLoadFile2Protocol, loader,
+ NULL);
+ if (EFI_ERROR(err))
+ return err;
+
+ initrd_handle = NULL;
+ FreePool(loader);
+ return EFI_SUCCESS;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <efi.h>
+
+EFI_STATUS initrd_register(
+ const VOID *initrd_address,
+ UINTN initrd_length,
+ EFI_HANDLE *ret_initrd_handle);
+
+EFI_STATUS initrd_unregister(EFI_HANDLE initrd_handle);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Generic Linux boot protocol using the EFI/PE entry point of the kernel. Passes
+ * initrd with the LINUX_INITRD_MEDIA_GUID DevicePath and cmdline with
+ * EFI LoadedImageProtocol.
+ *
+ * This method works for Linux 5.8 and newer on ARM/Aarch64, x86/x68_64 and RISC-V.
+ */
+
#include <efi.h>
#include <efilib.h>
+#include "initrd.h"
#include "linux.h"
+#include "pe.h"
#include "util.h"
-#ifdef __i386__
-#define __regparm0__ __attribute__((regparm(0)))
-#else
-#define __regparm0__
-#endif
+static EFI_LOADED_IMAGE * loaded_image_free(EFI_LOADED_IMAGE *img) {
+ if (!img)
+ return NULL;
+ mfree(img->LoadOptions);
+ return mfree(img);
+}
+
+static EFI_STATUS loaded_image_register(
+ const CHAR8 *cmdline, UINTN cmdline_len,
+ const VOID *linux_buffer, UINTN linux_length,
+ EFI_HANDLE *ret_image) {
+
+ EFI_LOADED_IMAGE *loaded_image = NULL;
+ EFI_STATUS err;
+
+ assert(cmdline || cmdline_len > 0);
+ assert(linux_buffer && linux_length > 0);
+ assert(ret_image);
-typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct boot_params *params) __regparm0__;
+ /* create and install new LoadedImage Protocol */
+ loaded_image = AllocatePool(sizeof(EFI_LOADED_IMAGE));
+ if (!loaded_image)
+ return EFI_OUT_OF_RESOURCES;
-static VOID linux_efi_handover(EFI_HANDLE image, struct boot_params *params) {
- handover_f handover;
- UINTN start = (UINTN)params->hdr.code32_start;
+ /* provide the image base address and size */
+ *loaded_image = (EFI_LOADED_IMAGE) {
+ .ImageBase = (VOID *) linux_buffer,
+ .ImageSize = linux_length
+ };
- assert(params);
+ /* if a cmdline is set convert it to UTF16 */
+ if (cmdline) {
+ loaded_image->LoadOptions = stra_to_str(cmdline);
+ if (!loaded_image->LoadOptions) {
+ loaded_image = loaded_image_free(loaded_image);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ loaded_image->LoadOptionsSize = StrSize(loaded_image->LoadOptions);
+ }
+
+ /* install a new LoadedImage protocol. ret_handle is a new image handle */
+ err = uefi_call_wrapper(BS->InstallMultipleProtocolInterfaces, 4,
+ ret_image,
+ &LoadedImageProtocol, loaded_image,
+ NULL);
+ if (EFI_ERROR(err))
+ loaded_image = loaded_image_free(loaded_image);
-#ifdef __x86_64__
- asm volatile ("cli");
- start += 512;
-#endif
- handover = (handover_f)(start + params->hdr.handover_offset);
- handover(image, ST, params);
+ return err;
}
-EFI_STATUS linux_exec(EFI_HANDLE image,
- CHAR8 *cmdline, UINTN cmdline_len,
- UINTN linux_addr,
- UINTN initrd_addr, UINTN initrd_size) {
+static EFI_STATUS loaded_image_unregister(EFI_HANDLE loaded_image_handle) {
+ EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
+ EFI_STATUS err;
+
+ if (!loaded_image_handle)
+ return EFI_SUCCESS;
+
+ /* get the LoadedImage protocol that we allocated earlier */
+ err = uefi_call_wrapper(
+ BS->OpenProtocol, 6,
+ loaded_image_handle, &LoadedImageProtocol, (VOID **) &loaded_image,
+ NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR(err))
+ return err;
- const struct boot_params *image_params;
- struct boot_params *boot_params;
+ /* close the handle */
+ (void) uefi_call_wrapper(
+ BS->CloseProtocol, 4,
+ loaded_image_handle, &LoadedImageProtocol, NULL, NULL);
+ err = uefi_call_wrapper(BS->UninstallMultipleProtocolInterfaces, 4,
+ loaded_image_handle,
+ &LoadedImageProtocol, loaded_image,
+ NULL);
+ if (EFI_ERROR(err))
+ return err;
+ loaded_image_handle = NULL;
+ loaded_image = loaded_image_free(loaded_image);
+
+ return EFI_SUCCESS;
+}
+
+static inline void cleanup_initrd(EFI_HANDLE *initrd_handle) {
+ (void) initrd_unregister(*initrd_handle);
+ *initrd_handle = NULL;
+}
+
+static inline void cleanup_loaded_image(EFI_HANDLE *loaded_image_handle) {
+ (void) loaded_image_unregister(*loaded_image_handle);
+ *loaded_image_handle = NULL;
+}
+
+/* struct to call cleanup_pages */
+struct pages {
EFI_PHYSICAL_ADDRESS addr;
- UINT8 setup_sectors;
- EFI_STATUS err;
+ UINTN num;
+};
- assert(image);
- assert(cmdline);
+static inline void cleanup_pages(struct pages *p) {
+ if (p->addr == 0)
+ return;
+ (void) uefi_call_wrapper(BS->FreePages, 2, p->addr, p->num);
+}
- image_params = (const struct boot_params *) linux_addr;
+EFI_STATUS linux_exec(
+ EFI_HANDLE image,
+ const CHAR8 *cmdline, UINTN cmdline_len,
+ const VOID *linux_buffer, UINTN linux_length,
+ const VOID *initrd_buffer, UINTN initrd_length) {
+
+ _cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL;
+ _cleanup_(cleanup_loaded_image) EFI_HANDLE loaded_image_handle = NULL;
+ UINT32 kernel_alignment, kernel_size_of_image, kernel_entry_address;
+ EFI_IMAGE_ENTRY_POINT kernel_entry;
+ _cleanup_(cleanup_pages) struct pages kernel = {};
+ VOID *new_buffer;
+ EFI_STATUS err;
- if (image_params->hdr.boot_flag != 0xAA55 ||
- image_params->hdr.header != SETUP_MAGIC ||
- image_params->hdr.version < 0x20b ||
- !image_params->hdr.relocatable_kernel)
- return EFI_LOAD_ERROR;
+ assert(image);
+ assert(cmdline || cmdline_len == 0);
+ assert(linux_buffer && linux_length > 0);
+ assert(initrd_buffer || initrd_length == 0);
- addr = UINT32_MAX; /* Below the 32bit boundary */
- err = uefi_call_wrapper(
- BS->AllocatePages, 4,
- AllocateMaxAddress,
- EfiLoaderData,
- EFI_SIZE_TO_PAGES(0x4000),
- &addr);
+ /* get the necessary fields from the PE header */
+ err = pe_alignment_info(linux_buffer, &kernel_entry_address, &kernel_size_of_image, &kernel_alignment);
if (EFI_ERROR(err))
return err;
+ /* sanity check */
+ assert(kernel_size_of_image >= linux_length);
+
+ /* Linux kernel complains if it's not loaded at a properly aligned memory address. The correct alignment
+ is provided by Linux as the SegmentAlignment in the PeOptionalHeader. Additionally the kernel needs to
+ be in a memory segment thats SizeOfImage (again from PeOptionalHeader) large, so that the Kernel has
+ space for its BSS section. SizeOfImage is always larger than linux_length, which is only the size of
+ Code, (static) Data and Headers.
+
+ Interrestingly only ARM/Aarch64 and RISC-V kernel stubs check these assertions and can even boot (with warnings)
+ if they are not met. x86 and x86_64 kernel stubs don't do checks and fail if the BSS section is too small.
+ */
+ /* allocate SizeOfImage + SectionAlignment because the new_buffer can move up to Alignment-1 bytes */
+ kernel.num = EFI_SIZE_TO_PAGES(ALIGN_TO(kernel_size_of_image, kernel_alignment) + kernel_alignment);
+ err = uefi_call_wrapper(
+ BS->AllocatePages, 4,
+ AllocateAnyPages, EfiLoaderData,
+ kernel.num, &kernel.addr);
+ if (EFI_ERROR(err))
+ return EFI_OUT_OF_RESOURCES;
+ new_buffer = PHYSICAL_ADDRESS_TO_POINTER(ALIGN_TO(kernel.addr, kernel_alignment));
+ CopyMem(new_buffer, linux_buffer, linux_length);
+ /* zero out rest of memory (probably not needed, but BSS section should be 0) */
+ SetMem((UINT8 *)new_buffer + linux_length, kernel_size_of_image - linux_length, 0);
- boot_params = (struct boot_params *) PHYSICAL_ADDRESS_TO_POINTER(addr);
- ZeroMem(boot_params, 0x4000);
- boot_params->hdr = image_params->hdr;
- boot_params->hdr.type_of_loader = 0xff;
- setup_sectors = image_params->hdr.setup_sects > 0 ? image_params->hdr.setup_sects : 4;
- boot_params->hdr.code32_start = (UINT32)linux_addr + (setup_sectors + 1) * 512;
+ /* get the entry point inside the relocated kernel */
+ kernel_entry = (EFI_IMAGE_ENTRY_POINT) ((const UINT8 *)new_buffer + kernel_entry_address);
- if (cmdline) {
- addr = 0xA0000;
-
- err = uefi_call_wrapper(
- BS->AllocatePages, 4,
- AllocateMaxAddress,
- EfiLoaderData,
- EFI_SIZE_TO_PAGES(cmdline_len + 1),
- &addr);
- if (EFI_ERROR(err))
- return err;
-
- CopyMem(PHYSICAL_ADDRESS_TO_POINTER(addr), cmdline, cmdline_len);
- ((CHAR8 *) PHYSICAL_ADDRESS_TO_POINTER(addr))[cmdline_len] = 0;
- boot_params->hdr.cmd_line_ptr = (UINT32) addr;
- }
+ /* register a LoadedImage Protocol in order to pass on the commandline */
+ err = loaded_image_register(cmdline, cmdline_len, new_buffer, linux_length, &loaded_image_handle);
+ if (EFI_ERROR(err))
+ return err;
- boot_params->hdr.ramdisk_image = (UINT32) initrd_addr;
- boot_params->hdr.ramdisk_size = (UINT32) initrd_size;
+ /* register a LINUX_INITRD_MEDIA DevicePath to serve the initrd */
+ err = initrd_register(initrd_buffer, initrd_length, &initrd_handle);
+ if (EFI_ERROR(err))
+ return err;
- linux_efi_handover(image, boot_params);
- return EFI_LOAD_ERROR;
+ /* call the kernel */
+ return uefi_call_wrapper(kernel_entry, 2, loaded_image_handle, ST);
}
#pragma once
#include <efi.h>
-#include "macro-fundamental.h"
-#define SETUP_MAGIC 0x53726448 /* "HdrS" */
-
-struct setup_header {
- UINT8 setup_sects;
- UINT16 root_flags;
- UINT32 syssize;
- UINT16 ram_size;
- UINT16 vid_mode;
- UINT16 root_dev;
- UINT16 boot_flag;
- UINT16 jump;
- UINT32 header;
- UINT16 version;
- UINT32 realmode_swtch;
- UINT16 start_sys_seg;
- UINT16 kernel_version;
- UINT8 type_of_loader;
- UINT8 loadflags;
- UINT16 setup_move_size;
- UINT32 code32_start;
- UINT32 ramdisk_image;
- UINT32 ramdisk_size;
- UINT32 bootsect_kludge;
- UINT16 heap_end_ptr;
- UINT8 ext_loader_ver;
- UINT8 ext_loader_type;
- UINT32 cmd_line_ptr;
- UINT32 initrd_addr_max;
- UINT32 kernel_alignment;
- UINT8 relocatable_kernel;
- UINT8 min_alignment;
- UINT16 xloadflags;
- UINT32 cmdline_size;
- UINT32 hardware_subarch;
- UINT64 hardware_subarch_data;
- UINT32 payload_offset;
- UINT32 payload_length;
- UINT64 setup_data;
- UINT64 pref_address;
- UINT32 init_size;
- UINT32 handover_offset;
-} _packed_;
-
-/* adapted from linux' bootparam.h */
-struct boot_params {
- UINT8 screen_info[64]; // was: struct screen_info
- UINT8 apm_bios_info[20]; // was: struct apm_bios_info
- UINT8 _pad2[4];
- UINT64 tboot_addr;
- UINT8 ist_info[16]; // was: struct ist_info
- UINT8 _pad3[16];
- UINT8 hd0_info[16];
- UINT8 hd1_info[16];
- UINT8 sys_desc_table[16]; // was: struct sys_desc_table
- UINT8 olpc_ofw_header[16]; // was: struct olpc_ofw_header
- UINT32 ext_ramdisk_image;
- UINT32 ext_ramdisk_size;
- UINT32 ext_cmd_line_ptr;
- UINT8 _pad4[116];
- UINT8 edid_info[128]; // was: struct edid_info
- UINT8 efi_info[32]; // was: struct efi_info
- UINT32 alt_mem_k;
- UINT32 scratch;
- UINT8 e820_entries;
- UINT8 eddbuf_entries;
- UINT8 edd_mbr_sig_buf_entries;
- UINT8 kbd_status;
- UINT8 secure_boot;
- UINT8 _pad5[2];
- UINT8 sentinel;
- UINT8 _pad6[1];
- struct setup_header hdr;
- UINT8 _pad7[0x290-0x1f1-sizeof(struct setup_header)];
- UINT32 edd_mbr_sig_buffer[16]; // was: edd_mbr_sig_buffer[EDD_MBR_SIG_MAX]
- UINT8 e820_table[20*128]; // was: struct boot_e820_entry e820_table[E820_MAX_ENTRIES_ZEROPAGE]
- UINT8 _pad8[48];
- UINT8 eddbuf[6*82]; // was: struct edd_info eddbuf[EDDMAXNR]
- UINT8 _pad9[276];
-} _packed_;
-
-EFI_STATUS linux_exec(EFI_HANDLE image,
- CHAR8 *cmdline, UINTN cmdline_size,
- UINTN linux_addr,
- UINTN initrd_addr, UINTN initrd_size);
+EFI_STATUS linux_exec(
+ EFI_HANDLE image,
+ const CHAR8 *cmdline, UINTN cmdline_len,
+ const VOID *linux_buffer, UINTN linux_length,
+ const VOID *initrd_buffer, UINTN initrd_length);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+/*
+ * x86 specific code to for EFI handover boot protocol
+ * Linux kernels version 5.8 and newer support providing the initrd by
+ * LINUX_INITRD_MEDIA_GUID DevicePath. In order to support older kernels too,
+ * this x86 specific linux_exec function passes the initrd by setting the
+ * corresponding fields in the setup_header struct.
+ *
+ * see https://www.kernel.org/doc/html/latest/x86/boot.html
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "initrd.h"
+#include "linux.h"
+#include "macro-fundamental.h"
+#include "util.h"
+
+#define SETUP_MAGIC 0x53726448 /* "HdrS" */
+
+struct setup_header {
+ UINT8 setup_sects;
+ UINT16 root_flags;
+ UINT32 syssize;
+ UINT16 ram_size;
+ UINT16 vid_mode;
+ UINT16 root_dev;
+ UINT16 boot_flag;
+ UINT16 jump;
+ UINT32 header;
+ UINT16 version;
+ UINT32 realmode_swtch;
+ UINT16 start_sys_seg;
+ UINT16 kernel_version;
+ UINT8 type_of_loader;
+ UINT8 loadflags;
+ UINT16 setup_move_size;
+ UINT32 code32_start;
+ UINT32 ramdisk_image;
+ UINT32 ramdisk_size;
+ UINT32 bootsect_kludge;
+ UINT16 heap_end_ptr;
+ UINT8 ext_loader_ver;
+ UINT8 ext_loader_type;
+ UINT32 cmd_line_ptr;
+ UINT32 initrd_addr_max;
+ UINT32 kernel_alignment;
+ UINT8 relocatable_kernel;
+ UINT8 min_alignment;
+ UINT16 xloadflags;
+ UINT32 cmdline_size;
+ UINT32 hardware_subarch;
+ UINT64 hardware_subarch_data;
+ UINT32 payload_offset;
+ UINT32 payload_length;
+ UINT64 setup_data;
+ UINT64 pref_address;
+ UINT32 init_size;
+ UINT32 handover_offset;
+} _packed_;
+
+/* adapted from linux' bootparam.h */
+struct boot_params {
+ UINT8 screen_info[64]; // was: struct screen_info
+ UINT8 apm_bios_info[20]; // was: struct apm_bios_info
+ UINT8 _pad2[4];
+ UINT64 tboot_addr;
+ UINT8 ist_info[16]; // was: struct ist_info
+ UINT8 _pad3[16];
+ UINT8 hd0_info[16];
+ UINT8 hd1_info[16];
+ UINT8 sys_desc_table[16]; // was: struct sys_desc_table
+ UINT8 olpc_ofw_header[16]; // was: struct olpc_ofw_header
+ UINT32 ext_ramdisk_image;
+ UINT32 ext_ramdisk_size;
+ UINT32 ext_cmd_line_ptr;
+ UINT8 _pad4[116];
+ UINT8 edid_info[128]; // was: struct edid_info
+ UINT8 efi_info[32]; // was: struct efi_info
+ UINT32 alt_mem_k;
+ UINT32 scratch;
+ UINT8 e820_entries;
+ UINT8 eddbuf_entries;
+ UINT8 edd_mbr_sig_buf_entries;
+ UINT8 kbd_status;
+ UINT8 secure_boot;
+ UINT8 _pad5[2];
+ UINT8 sentinel;
+ UINT8 _pad6[1];
+ struct setup_header hdr;
+ UINT8 _pad7[0x290-0x1f1-sizeof(struct setup_header)];
+ UINT32 edd_mbr_sig_buffer[16]; // was: edd_mbr_sig_buffer[EDD_MBR_SIG_MAX]
+ UINT8 e820_table[20*128]; // was: struct boot_e820_entry e820_table[E820_MAX_ENTRIES_ZEROPAGE]
+ UINT8 _pad8[48];
+ UINT8 eddbuf[6*82]; // was: struct edd_info eddbuf[EDDMAXNR]
+ UINT8 _pad9[276];
+} _packed_;
+
+#ifdef __i386__
+#define __regparm0__ __attribute__((regparm(0)))
+#else
+#define __regparm0__
+#endif
+
+typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct boot_params *params) __regparm0__;
+
+static VOID linux_efi_handover(EFI_HANDLE image, struct boot_params *params) {
+ handover_f handover;
+ UINTN start = (UINTN)params->hdr.code32_start;
+
+ assert(params);
+
+#ifdef __x86_64__
+ asm volatile ("cli");
+ start += 512;
+#endif
+ handover = (handover_f)(start + params->hdr.handover_offset);
+ handover(image, ST, params);
+}
+
+EFI_STATUS linux_exec(
+ EFI_HANDLE image,
+ const CHAR8 *cmdline, UINTN cmdline_len,
+ const VOID *linux_buffer, UINTN linux_length,
+ const VOID *initrd_buffer, UINTN initrd_length) {
+
+ const struct boot_params *image_params;
+ struct boot_params *boot_params;
+ EFI_HANDLE initrd_handle = NULL;
+ EFI_PHYSICAL_ADDRESS addr;
+ UINT8 setup_sectors;
+ EFI_STATUS err;
+
+ assert(image);
+ assert(cmdline || cmdline_len == 0);
+ assert(linux_buffer);
+ assert(initrd_buffer || initrd_length == 0);
+
+ image_params = (const struct boot_params *) linux_buffer;
+
+ if (image_params->hdr.boot_flag != 0xAA55 ||
+ image_params->hdr.header != SETUP_MAGIC ||
+ image_params->hdr.version < 0x20b ||
+ !image_params->hdr.relocatable_kernel)
+ return EFI_LOAD_ERROR;
+
+ addr = UINT32_MAX; /* Below the 32bit boundary */
+ err = uefi_call_wrapper(
+ BS->AllocatePages, 4,
+ AllocateMaxAddress,
+ EfiLoaderData,
+ EFI_SIZE_TO_PAGES(0x4000),
+ &addr);
+ if (EFI_ERROR(err))
+ return err;
+
+ boot_params = (struct boot_params *) PHYSICAL_ADDRESS_TO_POINTER(addr);
+ ZeroMem(boot_params, 0x4000);
+ boot_params->hdr = image_params->hdr;
+ boot_params->hdr.type_of_loader = 0xff;
+ setup_sectors = image_params->hdr.setup_sects > 0 ? image_params->hdr.setup_sects : 4;
+ boot_params->hdr.code32_start = (UINT32) POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + (setup_sectors + 1) * 512;
+
+ if (cmdline) {
+ addr = 0xA0000;
+
+ err = uefi_call_wrapper(
+ BS->AllocatePages, 4,
+ AllocateMaxAddress,
+ EfiLoaderData,
+ EFI_SIZE_TO_PAGES(cmdline_len + 1),
+ &addr);
+ if (EFI_ERROR(err))
+ return err;
+
+ CopyMem(PHYSICAL_ADDRESS_TO_POINTER(addr), cmdline, cmdline_len);
+ ((CHAR8 *) PHYSICAL_ADDRESS_TO_POINTER(addr))[cmdline_len] = 0;
+ boot_params->hdr.cmd_line_ptr = (UINT32) addr;
+ }
+
+ /* Providing the initrd via LINUX_INITRD_MEDIA_GUID is only supported by Linux 5.8+ (5.7+ on ARM64).
+ Until supported kernels become more established, we continue to set ramdisk in the handover struct.
+ This value is overridden by kernels that support LINUX_INITRD_MEDIA_GUID.
+ If you need to know which protocol was used by the kernel, pass "efi=debug" to the kernel,
+ this will print a line when InitrdMediaGuid was successfully used to load the initrd.
+ */
+ boot_params->hdr.ramdisk_image = (UINT32) POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer);
+ boot_params->hdr.ramdisk_size = (UINT32) initrd_length;
+
+ /* register LINUX_INITRD_MEDIA_GUID */
+ err = initrd_register(initrd_buffer, initrd_length, &initrd_handle);
+ if (EFI_ERROR(err))
+ return err;
+ linux_efi_handover(image, boot_params);
+ (void) initrd_unregister(initrd_handle);
+ initrd_handle = NULL;
+ return EFI_LOAD_ERROR;
+}
shim.h
splash.h
util.h
+ xbootldr.h
'''.split())
common_sources = '''
assert.c
+ devicetree.c
disk.c
graphics.c
measure.c
systemd_boot_sources = '''
boot.c
console.c
- devicetree.c
drivers.c
random-seed.c
shim.c
+ xbootldr.c
'''.split()
stub_sources = '''
- linux.c
+ initrd.c
splash.c
stub.c
cpio.c
'-include', efi_config_h,
'-include', version_h,
]
+ if ['ia32', 'x86_64'].contains(efi_arch)
+ stub_sources += 'linux_x86.c'
+ else
+ stub_sources += 'linux.c'
+ endif
if efi_arch == 'x86_64'
compile_args += ['-mno-red-zone',
'-mno-sse',
EFI_DT_FIXUP Fixup;
};
+#endif
+
+#ifndef EFI_TCG_GUID
+
#define EFI_TCG_GUID \
&(const EFI_GUID) { 0xf541796d, 0xa62e, 0x4954, { 0xa7, 0x75, 0x95, 0x84, 0xf6, 0x1b, 0x9c, 0xdd } }
EFI_TCG_HASH_LOG_EXTEND_EVENT HashLogExtendEvent;
} EFI_TCG;
+#endif
+
+#ifndef EFI_TCG2_GUID
+
#define EFI_TCG2_GUID \
&(const EFI_GUID) { 0x607f766c, 0x7455, 0x42be, { 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f } }
} EFI_TCG2;
#endif
+
+#ifndef EFI_LOAD_FILE2_PROTOCOL_GUID
+#define EFI_LOAD_FILE2_PROTOCOL_GUID \
+ {0x4006c0c1, 0xfcb3, 0x403e, {0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d} }
+#define EfiLoadFile2Protocol ((EFI_GUID)EFI_LOAD_FILE2_PROTOCOL_GUID)
+#endif
+
+#define LINUX_INITRD_MEDIA_GUID \
+ {0x5568e427, 0x68fc, 0x4f3d, {0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68} }
+
+/* UEFI Platform Initialization (Vol2: DXE) */
+#ifndef SECURITY_PROTOCOL_GUID
+
+#define SECURITY_PROTOCOL_GUID \
+ &(const EFI_GUID) { 0xa46423e3, 0x4617, 0x49f1, { 0xb9, 0xff, 0xd1, 0xbf, 0xa9, 0x11, 0x58, 0x39 } }
+#define SECURITY_PROTOCOL2_GUID \
+ &(const EFI_GUID) { 0x94ab2f58, 0x1438, 0x4ef1, { 0x91, 0x52, 0x18, 0x94, 0x1a, 0x3a, 0x0e, 0x68 } }
+
+struct _EFI_SECURITY2_PROTOCOL;
+struct _EFI_SECURITY_PROTOCOL;
+struct _EFI_DEVICE_PATH_PROTOCOL;
+
+typedef struct _EFI_SECURITY2_PROTOCOL EFI_SECURITY2_PROTOCOL;
+typedef struct _EFI_SECURITY_PROTOCOL EFI_SECURITY_PROTOCOL;
+typedef struct _EFI_DEVICE_PATH_PROTOCOL EFI_DEVICE_PATH_PROTOCOL;
+
+typedef EFI_STATUS (EFIAPI *EFI_SECURITY_FILE_AUTHENTICATION_STATE) (
+ const EFI_SECURITY_PROTOCOL *This,
+ UINT32 AuthenticationStatus,
+ const EFI_DEVICE_PATH_PROTOCOL *File
+);
+
+typedef EFI_STATUS (EFIAPI *EFI_SECURITY2_FILE_AUTHENTICATION) (
+ const EFI_SECURITY2_PROTOCOL *This,
+ const EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ VOID *FileBuffer,
+ UINTN FileSize,
+ BOOLEAN BootPolicy
+);
+
+struct _EFI_SECURITY2_PROTOCOL {
+ EFI_SECURITY2_FILE_AUTHENTICATION FileAuthentication;
+};
+
+struct _EFI_SECURITY_PROTOCOL {
+ EFI_SECURITY_FILE_AUTHENTICATION_STATE FileAuthenticationState;
+};
+
+#endif
+
+#ifndef EFI_CONSOLE_CONTROL_GUID
+
+#define EFI_CONSOLE_CONTROL_GUID \
+ &(const EFI_GUID) { 0xf42f7782, 0x12e, 0x4c12, { 0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21 } }
+
+struct _EFI_CONSOLE_CONTROL_PROTOCOL;
+
+typedef enum {
+ EfiConsoleControlScreenText,
+ EfiConsoleControlScreenGraphics,
+ EfiConsoleControlScreenMaxValue,
+} EFI_CONSOLE_CONTROL_SCREEN_MODE;
+
+typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE)(
+ struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
+ EFI_CONSOLE_CONTROL_SCREEN_MODE *Mode,
+ BOOLEAN *UgaExists,
+ BOOLEAN *StdInLocked
+);
+
+typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE)(
+ struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
+ EFI_CONSOLE_CONTROL_SCREEN_MODE Mode
+);
+
+typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN)(
+ struct _EFI_CONSOLE_CONTROL_PROTOCOL *This,
+ CHAR16 *Password
+);
+
+typedef struct _EFI_CONSOLE_CONTROL_PROTOCOL {
+ EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE GetMode;
+ EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode;
+ EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN LockStdIn;
+} EFI_CONSOLE_CONTROL_PROTOCOL;
+
+#endif
UINT16 Characteristics;
} _packed_;
+#define OPTHDR32_MAGIC 0x10B /* PE32 OptionalHeader */
+#define OPTHDR64_MAGIC 0x20B /* PE32+ OptionalHeader */
+
+struct PeOptionalHeader {
+ UINT16 Magic;
+ UINT8 LinkerMajor;
+ UINT8 LinkerMinor;
+ UINT32 SizeOfCode;
+ UINT32 SizeOfInitializedData;
+ UINT32 SizeOfUninitializeData;
+ UINT32 AddressOfEntryPoint;
+ UINT32 BaseOfCode;
+ union {
+ struct { /* PE32 */
+ UINT32 BaseOfData;
+ UINT32 ImageBase32;
+ };
+ UINT64 ImageBase64; /* PE32+ */
+ };
+ UINT32 SectionAlignment;
+ UINT32 FileAlignment;
+ UINT16 MajorOperatingSystemVersion;
+ UINT16 MinorOperatingSystemVersion;
+ UINT16 MajorImageVersion;
+ UINT16 MinorImageVersion;
+ UINT16 MajorSubsystemVersion;
+ UINT16 MinorSubsystemVersion;
+ UINT32 Win32VersionValue;
+ UINT32 SizeOfImage;
+ UINT32 SizeOfHeaders;
+ UINT32 CheckSum;
+ UINT16 Subsystem;
+ UINT16 DllCharacteristics;
+ /* fields with different sizes for 32/64 omitted */
+} _packed_;
+
struct PeFileHeader {
UINT8 Magic[4];
struct CoffFileHeader FileHeader;
- /* OptionalHeader omitted */
+ struct PeOptionalHeader OptionalHeader;
} _packed_;
struct PeSectionHeader {
static inline UINTN section_table_offset(const struct DosFileHeader *dos, const struct PeFileHeader *pe) {
assert(dos);
assert(pe);
- return dos->ExeHeader + sizeof(struct PeFileHeader) + pe->FileHeader.SizeOfOptionalHeader;
+ return dos->ExeHeader + OFFSETOF(struct PeFileHeader, OptionalHeader) + pe->FileHeader.SizeOfOptionalHeader;
}
static VOID locate_sections(
}
}
+EFI_STATUS pe_alignment_info(
+ const VOID *base,
+ UINT32 *ret_entry_point_address,
+ UINT32 *ret_size_of_image,
+ UINT32 *ret_section_alignment) {
+
+ const struct DosFileHeader *dos;
+ const struct PeFileHeader *pe;
+
+ assert(base);
+ assert(ret_entry_point_address);
+ assert(ret_size_of_image);
+ assert(ret_section_alignment);
+
+ dos = (const struct DosFileHeader *) base;
+ if (!verify_dos(dos))
+ return EFI_LOAD_ERROR;
+
+ pe = (const struct PeFileHeader*) ((const UINT8 *)base + dos->ExeHeader);
+ if (!verify_pe(pe))
+ return EFI_LOAD_ERROR;
+
+ *ret_entry_point_address = pe->OptionalHeader.AddressOfEntryPoint;
+
+ if (pe->OptionalHeader.Magic == OPTHDR32_MAGIC) {
+ *ret_size_of_image = pe->OptionalHeader.SizeOfImage;
+ *ret_section_alignment = pe->OptionalHeader.SectionAlignment;
+ } else if (pe->OptionalHeader.Magic == OPTHDR64_MAGIC) {
+ *ret_size_of_image = pe->OptionalHeader.SizeOfImage;
+ *ret_section_alignment = pe->OptionalHeader.SectionAlignment;
+ } else
+ return EFI_UNSUPPORTED;
+ return EFI_SUCCESS;
+}
+
EFI_STATUS pe_memory_locate_sections(
const CHAR8 *base,
const CHAR8 **sections,
const CHAR8 **sections,
UINTN *offsets,
UINTN *sizes);
+
+EFI_STATUS pe_alignment_info(
+ const VOID *base,
+ UINT32 *ret_entry_point_address,
+ UINT32 *ret_size_of_image,
+ UINT32 *ret_section_alignment);
#include <efi.h>
#include <efilib.h>
+#include "missing_efi.h"
#include "util.h"
#include "shim.h"
};
#define SIMPLE_FS_GUID &(const EFI_GUID) SIMPLE_FILE_SYSTEM_PROTOCOL
-#define SECURITY_PROTOCOL_GUID \
- &(const EFI_GUID) { 0xa46423e3, 0x4617, 0x49f1, { 0xb9, 0xff, 0xd1, 0xbf, 0xa9, 0x11, 0x58, 0x39 } }
-#define SECURITY_PROTOCOL2_GUID \
- &(const EFI_GUID) { 0x94ab2f58, 0x1438, 0x4ef1, { 0x91, 0x52, 0x18, 0x94, 0x1a, 0x3a, 0x0e, 0x68 } }
#define SHIM_LOCK_GUID \
&(const EFI_GUID) { 0x605dab50, 0xe046, 0x4300, { 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } }
return shim_lock->shim_verify(data, size) == EFI_SUCCESS;
}
-/*
- * See the UEFI Platform Initialization manual (Vol2: DXE) for this
- */
-struct _EFI_SECURITY2_PROTOCOL;
-struct _EFI_SECURITY_PROTOCOL;
-struct _EFI_DEVICE_PATH_PROTOCOL;
-
-typedef struct _EFI_SECURITY2_PROTOCOL EFI_SECURITY2_PROTOCOL;
-typedef struct _EFI_SECURITY_PROTOCOL EFI_SECURITY_PROTOCOL;
-typedef struct _EFI_DEVICE_PATH_PROTOCOL EFI_DEVICE_PATH_PROTOCOL;
-
-typedef EFI_STATUS (EFIAPI *EFI_SECURITY_FILE_AUTHENTICATION_STATE) (
- const EFI_SECURITY_PROTOCOL *This,
- UINT32 AuthenticationStatus,
- const EFI_DEVICE_PATH_PROTOCOL *File
-);
-
-typedef EFI_STATUS (EFIAPI *EFI_SECURITY2_FILE_AUTHENTICATION) (
- const EFI_SECURITY2_PROTOCOL *This,
- const EFI_DEVICE_PATH_PROTOCOL *DevicePath,
- VOID *FileBuffer,
- UINTN FileSize,
- BOOLEAN BootPolicy
-);
-
-struct _EFI_SECURITY2_PROTOCOL {
- EFI_SECURITY2_FILE_AUTHENTICATION FileAuthentication;
-};
-
-struct _EFI_SECURITY_PROTOCOL {
- EFI_SECURITY_FILE_AUTHENTICATION_STATE FileAuthenticationState;
-};
-
/* Handle to the original authenticator for security1 protocol */
static EFI_SECURITY_FILE_AUTHENTICATION_STATE esfas = NULL;
#include <efilib.h>
#include "cpio.h"
+#include "devicetree.h"
#include "disk.h"
#include "graphics.h"
#include "linux.h"
#include "util.h"
/* magic string to find in the binary image */
-static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-stub " GIT_VERSION " ####";
+_used_ _section_(".sdmagic") static const char magic[] = "#### LoaderInfo: systemd-stub " GIT_VERSION " ####";
static EFI_STATUS combine_initrd(
EFI_PHYSICAL_ADDRESS initrd_base, UINTN initrd_size,
SECTION_LINUX,
SECTION_INITRD,
SECTION_SPLASH,
+ SECTION_DTB,
_SECTION_MAX,
};
[SECTION_LINUX] = (const CHAR8*) ".linux",
[SECTION_INITRD] = (const CHAR8*) ".initrd",
[SECTION_SPLASH] = (const CHAR8*) ".splash",
+ [SECTION_DTB] = (const CHAR8*) ".dtb",
NULL,
};
- UINTN cmdline_len = 0, initrd_size, credential_initrd_size = 0, sysext_initrd_size = 0;
+ UINTN cmdline_len = 0, linux_size, initrd_size, dt_size;
+ UINTN credential_initrd_size = 0, sysext_initrd_size = 0;
_cleanup_freepool_ VOID *credential_initrd = NULL, *sysext_initrd = NULL;
- EFI_PHYSICAL_ADDRESS linux_base, initrd_base;
+ EFI_PHYSICAL_ADDRESS linux_base, initrd_base, dt_base;
+ _cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {};
EFI_LOADED_IMAGE *loaded_image;
UINTN addrs[_SECTION_MAX] = {};
UINTN szs[_SECTION_MAX] = {};
&sysext_initrd,
&sysext_initrd_size);
+ linux_size = szs[SECTION_LINUX];
linux_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_LINUX];
initrd_size = szs[SECTION_INITRD];
initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_INITRD] : 0;
+ dt_size = szs[SECTION_DTB];
+ dt_base = dt_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_DTB] : 0;
+
if (credential_initrd || sysext_initrd) {
/* If we have generated initrds dynamically, let's combine them with the built-in initrd. */
err = combine_initrd(
}
}
- err = linux_exec(image, cmdline, cmdline_len, linux_base, initrd_base, initrd_size);
+ if (dt_size > 0) {
+ err = devicetree_install_from_memory(
+ &dt_state, PHYSICAL_ADDRESS_TO_POINTER(dt_base), dt_size);
+ if (EFI_ERROR(err))
+ log_error_stall(L"Error loading embedded devicetree: %r", err);
+ }
+
+ err = linux_exec(image, cmdline, cmdline_len,
+ PHYSICAL_ADDRESS_TO_POINTER(linux_base), linux_size,
+ PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);
graphics_mode(FALSE);
return log_error_status_stall(err, L"Execution of embedded linux image failed: %r", err);
}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <efi.h>
+#include <efigpt.h>
+#include <efilib.h>
+
+#include "util.h"
+#include "xbootldr.h"
+
+union GptHeaderBuffer {
+ EFI_PARTITION_TABLE_HEADER gpt_header;
+ uint8_t space[((sizeof(EFI_PARTITION_TABLE_HEADER) + 511) / 512) * 512];
+};
+
+static EFI_DEVICE_PATH *path_parent(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node) {
+ EFI_DEVICE_PATH *parent;
+ UINTN len;
+
+ assert(path);
+ assert(node);
+
+ len = (UINT8*) NextDevicePathNode(node) - (UINT8*) path;
+ parent = (EFI_DEVICE_PATH*) AllocatePool(len + sizeof(EFI_DEVICE_PATH));
+ if (!parent)
+ return NULL;
+
+ CopyMem(parent, path, len);
+ CopyMem((UINT8*) parent + len, EndDevicePath, sizeof(EFI_DEVICE_PATH));
+
+ return parent;
+}
+
+static BOOLEAN verify_gpt(union GptHeaderBuffer *gpt_header_buffer, EFI_LBA lba_expected) {
+ EFI_PARTITION_TABLE_HEADER *h;
+ UINT32 crc32, crc32_saved;
+ EFI_STATUS err;
+
+ assert(gpt_header_buffer);
+
+ h = &gpt_header_buffer->gpt_header;
+
+ /* Some superficial validation of the GPT header */
+ if(CompareMem(&h->Header.Signature, "EFI PART", sizeof(h->Header.Signature) != 0))
+ return FALSE;
+
+ if (h->Header.HeaderSize < 92 || h->Header.HeaderSize > 512)
+ return FALSE;
+
+ if (h->Header.Revision != 0x00010000U)
+ return FALSE;
+
+ /* Calculate CRC check */
+ crc32_saved = h->Header.CRC32;
+ h->Header.CRC32 = 0;
+ err = BS->CalculateCrc32(gpt_header_buffer, h->Header.HeaderSize, &crc32);
+ h->Header.CRC32 = crc32_saved;
+ if (EFI_ERROR(err) || crc32 != crc32_saved)
+ return FALSE;
+
+ if (h->MyLBA != lba_expected)
+ return FALSE;
+
+ if (h->SizeOfPartitionEntry < sizeof(EFI_PARTITION_ENTRY))
+ return FALSE;
+
+ if (h->NumberOfPartitionEntries <= 0 || h->NumberOfPartitionEntries > 1024)
+ return FALSE;
+
+ /* overflow check */
+ if (h->SizeOfPartitionEntry > UINTN_MAX / h->NumberOfPartitionEntries)
+ return FALSE;
+
+ return TRUE;
+}
+
+static EFI_STATUS try_gpt(
+ EFI_BLOCK_IO *block_io,
+ EFI_LBA lba,
+ EFI_LBA *ret_backup_lba, /* May be changed even on error! */
+ UINT32 *ret_part_number,
+ UINT64 *ret_part_start,
+ UINT64 *ret_part_size,
+ EFI_GUID *ret_part_uuid) {
+
+ _cleanup_freepool_ EFI_PARTITION_ENTRY *entries = NULL;
+ union GptHeaderBuffer gpt;
+ EFI_STATUS err;
+ UINT32 crc32;
+ UINTN size;
+
+ assert(block_io);
+ assert(ret_part_number);
+ assert(ret_part_start);
+ assert(ret_part_size);
+ assert(ret_part_uuid);
+
+ /* Read the GPT header */
+ err = uefi_call_wrapper(
+ block_io->ReadBlocks, 5,
+ block_io,
+ block_io->Media->MediaId,
+ lba,
+ sizeof(gpt), &gpt);
+ if (EFI_ERROR(err))
+ return err;
+
+ /* Indicate the location of backup LBA even if the rest of the header is corrupt. */
+ if (ret_backup_lba)
+ *ret_backup_lba = gpt.gpt_header.AlternateLBA;
+
+ if (!verify_gpt(&gpt, lba))
+ return EFI_NOT_FOUND;
+
+ /* Now load the GPT entry table */
+ size = ALIGN_TO((UINTN) gpt.gpt_header.SizeOfPartitionEntry * (UINTN) gpt.gpt_header.NumberOfPartitionEntries, 512);
+ entries = AllocatePool(size);
+ if (!entries)
+ return EFI_OUT_OF_RESOURCES;
+
+ err = uefi_call_wrapper(
+ block_io->ReadBlocks, 5,
+ block_io,
+ block_io->Media->MediaId,
+ gpt.gpt_header.PartitionEntryLBA,
+ size, entries);
+ if (EFI_ERROR(err))
+ return err;
+
+ /* Calculate CRC of entries array, too */
+ err = BS->CalculateCrc32(entries, size, &crc32);
+ if (EFI_ERROR(err) || crc32 != gpt.gpt_header.PartitionEntryArrayCRC32)
+ return EFI_CRC_ERROR;
+
+ /* Now we can finally look for xbootloader partitions. */
+ for (UINTN i = 0; i < gpt.gpt_header.NumberOfPartitionEntries; i++) {
+ EFI_PARTITION_ENTRY *entry;
+ EFI_LBA start, end;
+
+ entry = (EFI_PARTITION_ENTRY*) ((UINT8*) entries + gpt.gpt_header.SizeOfPartitionEntry * i);
+
+ if (CompareMem(&entry->PartitionTypeGUID, XBOOTLDR_GUID, sizeof(entry->PartitionTypeGUID)) != 0)
+ continue;
+
+ /* Let's use memcpy(), in case the structs are not aligned (they really should be though) */
+ CopyMem(&start, &entry->StartingLBA, sizeof(start));
+ CopyMem(&end, &entry->EndingLBA, sizeof(end));
+
+ if (end < start) /* Bogus? */
+ continue;
+
+ *ret_part_number = i + 1;
+ *ret_part_start = start;
+ *ret_part_size = end - start + 1;
+ CopyMem(ret_part_uuid, &entry->UniquePartitionGUID, sizeof(*ret_part_uuid));
+
+ return EFI_SUCCESS;
+ }
+
+ /* This GPT was fully valid, but we didn't find what we are looking for. This
+ * means there's no reason to check the second copy of the GPT header */
+ return EFI_NOT_FOUND;
+}
+
+static EFI_STATUS find_device(
+ EFI_HANDLE *device,
+ EFI_DEVICE_PATH **ret_device_path,
+ UINT32 *ret_part_number,
+ UINT64 *ret_part_start,
+ UINT64 *ret_part_size,
+ EFI_GUID *ret_part_uuid) {
+
+ EFI_DEVICE_PATH *partition_path;
+ EFI_STATUS err;
+
+ assert(device);
+ assert(ret_device_path);
+ assert(ret_part_number);
+ assert(ret_part_start);
+ assert(ret_part_size);
+ assert(ret_part_uuid);
+
+ partition_path = DevicePathFromHandle(device);
+ if (!partition_path)
+ return EFI_NOT_FOUND;
+
+ for (EFI_DEVICE_PATH *node = partition_path; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) {
+ _cleanup_freepool_ EFI_DEVICE_PATH *disk_path = NULL;
+ EFI_HANDLE disk_handle;
+ EFI_BLOCK_IO *block_io;
+ EFI_DEVICE_PATH *p;
+
+ /* First, Let's look for the SCSI/SATA/USB/… device path node, i.e. one above the media
+ * devices */
+ if (DevicePathType(node) != MESSAGING_DEVICE_PATH)
+ continue;
+
+ /* Determine the device path one level up */
+ disk_path = p = path_parent(partition_path, node);
+ if (!disk_path)
+ continue;
+
+ err = uefi_call_wrapper(BS->LocateDevicePath, 3, &BlockIoProtocol, &p, &disk_handle);
+ if (EFI_ERROR(err))
+ continue;
+
+ err = uefi_call_wrapper(BS->HandleProtocol, 3, disk_handle, &BlockIoProtocol, (VOID **)&block_io);
+ if (EFI_ERROR(err))
+ continue;
+
+ /* Filter out some block devices early. (We only care about block devices that aren't
+ * partitions themselves — we look for GPT partition tables to parse after all —, and only
+ * those which contain a medium and have at least 2 blocks.) */
+ if (block_io->Media->LogicalPartition ||
+ !block_io->Media->MediaPresent ||
+ block_io->Media->LastBlock <= 1)
+ continue;
+
+ /* Try several copies of the GPT header, in case one is corrupted */
+ EFI_LBA backup_lba = 0;
+ for (UINTN nr = 0; nr < 3; nr++) {
+ EFI_LBA lba;
+
+ /* Read the first copy at LBA 1 and then try the backup GPT header pointed
+ * to by the first header if that one was corrupted. As a last resort,
+ * try the very last LBA of this block device. */
+ if (nr == 0)
+ lba = 1;
+ else if (nr == 1 && backup_lba != 0)
+ lba = backup_lba;
+ else if (nr == 2 && backup_lba != block_io->Media->LastBlock)
+ lba = block_io->Media->LastBlock;
+ else
+ continue;
+
+ err = try_gpt(
+ block_io, lba,
+ nr == 0 ? &backup_lba : NULL, /* Only get backup LBA location from first GPT header. */
+ ret_part_number,
+ ret_part_start,
+ ret_part_size,
+ ret_part_uuid);
+ if (!EFI_ERROR(err)) {
+ *ret_device_path = DuplicateDevicePath(partition_path);
+ if (!*ret_device_path)
+ return EFI_OUT_OF_RESOURCES;
+ return EFI_SUCCESS;
+ }
+
+ /* GPT was valid but no XBOOT loader partition found. */
+ if (err == EFI_NOT_FOUND)
+ break;
+ }
+ }
+
+ /* No xbootloader partition found */
+ return EFI_NOT_FOUND;
+}
+
+EFI_STATUS xbootldr_open(EFI_HANDLE *device, EFI_HANDLE *ret_device, EFI_FILE **ret_root_dir) {
+ _cleanup_freepool_ EFI_DEVICE_PATH *partition_path = NULL;
+ UINT32 part_number = UINT32_MAX;
+ UINT64 part_start = UINT64_MAX, part_size = UINT64_MAX;
+ EFI_HANDLE new_device;
+ EFI_FILE *root_dir;
+ EFI_GUID part_uuid;
+ EFI_STATUS err;
+
+ assert(device);
+ assert(ret_device);
+ assert(ret_root_dir);
+
+ err = find_device(device, &partition_path, &part_number, &part_start, &part_size, &part_uuid);
+ if (EFI_ERROR(err))
+ return err;
+
+ /* Patch in the data we found */
+ for (EFI_DEVICE_PATH *node = partition_path; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) {
+ HARDDRIVE_DEVICE_PATH *hd;
+
+ if (DevicePathType(node) != MEDIA_DEVICE_PATH)
+ continue;
+
+ if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP)
+ continue;
+
+ hd = (HARDDRIVE_DEVICE_PATH*) node;
+ hd->PartitionNumber = part_number;
+ hd->PartitionStart = part_start;
+ hd->PartitionSize = part_size;
+ CopyMem(hd->Signature, &part_uuid, sizeof(hd->Signature));
+ hd->MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
+ hd->SignatureType = SIGNATURE_TYPE_GUID;
+ }
+
+ err = uefi_call_wrapper(BS->LocateDevicePath, 3, &BlockIoProtocol, &partition_path, &new_device);
+ if (EFI_ERROR(err))
+ return err;
+
+ root_dir = LibOpenRoot(new_device);
+ if (!root_dir)
+ return EFI_NOT_FOUND;
+
+ *ret_device = new_device;
+ *ret_root_dir = root_dir;
+ return EFI_SUCCESS;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <efi.h>
+
+#define XBOOTLDR_GUID \
+ &(const EFI_GUID) { 0xbc13c2ff, 0x59e6, 0x4262, { 0xa3, 0x52, 0xb2, 0x75, 0xfd, 0x6f, 0x71, 0x72 } }
+
+EFI_STATUS xbootldr_open(EFI_HANDLE *device, EFI_HANDLE *ret_device, EFI_FILE **ret_root_dir);
assert_se(strv_extend(&expected, "/org/freedesktop/LogControl1") >= 0);
assert_se(strv_extend(&expected, "/org/freedesktop/network1") >= 0);
assert_se(strv_extend(&expected, "/org/freedesktop/network1/network") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/0") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/0/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/1") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/1/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/2") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/2/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/3") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/3/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/4") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/4/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/5") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/5/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/6") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/6/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/7") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/7/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/8") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/8/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/9") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/9/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/10") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/10/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/11") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/11/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/12") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/12/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/13") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/13/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/14") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/14/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/15") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/15/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/16") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/16/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/17") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/17/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/18") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/18/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/19") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/19/hoge") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/20") >= 0);
- assert_se(strv_extend(&expected, "/org/freedesktop/network1/network/20/hoge") >= 0);
+ for (unsigned i = 0; i <= 20; i++) {
+ assert_se(strv_extendf(&expected, "/org/freedesktop/network1/network/%u", i) >= 0);
+ assert_se(strv_extendf(&expected, "/org/freedesktop/network1/network/%u/hoge", i) >= 0);
+ }
strv_sort(expected);
assert_se(strv_equal(l, expected));
assert(where);
l = sizeof(struct autofs_dev_ioctl) + strlen(where) + 1;
- param = alloca(l);
+ param = alloca_safe(l);
init_autofs_dev_ioctl(param);
param->size = l;
offsetof(struct bpf_cgroup_dev_ctx, minor)),
};
- _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
+ _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
int r;
assert(ret);
}
int bpf_devices_apply_policy(
- BPFProgram *prog,
+ BPFProgram **prog,
CGroupDevicePolicy policy,
bool allow_list,
const char *cgroup_path,
/* This will assign *prog_installed if everything goes well. */
- if (!prog)
+ assert(prog);
+ if (!*prog)
goto finish;
const bool deny_everything = policy == CGROUP_DEVICE_POLICY_STRICT && !allow_list;
};
if (!deny_everything) {
- r = bpf_program_add_instructions(prog, post_insn, ELEMENTSOF(post_insn));
+ r = bpf_program_add_instructions(*prog, post_insn, ELEMENTSOF(post_insn));
if (r < 0)
return log_error_errno(r, "Extending device control BPF program failed: %m");
/* Fixup PASS_JUMP_OFF jump offsets. */
- for (size_t off = 0; off < prog->n_instructions; off++) {
- struct bpf_insn *ins = &prog->instructions[off];
+ for (size_t off = 0; off < (*prog)->n_instructions; off++) {
+ struct bpf_insn *ins = &((*prog)->instructions[off]);
if (ins->code == (BPF_JMP | BPF_JA) && ins->off == PASS_JUMP_OFF)
- ins->off = prog->n_instructions - off - 1;
+ ins->off = (*prog)->n_instructions - off - 1;
}
}
- r = bpf_program_add_instructions(prog, exit_insn, ELEMENTSOF(exit_insn));
+ r = bpf_program_add_instructions(*prog, exit_insn, ELEMENTSOF(exit_insn));
if (r < 0)
return log_error_errno(r, "Extending device control BPF program failed: %m");
if (r < 0)
return log_error_errno(r, "Failed to determine cgroup path: %m");
- r = bpf_program_cgroup_attach(prog, BPF_CGROUP_DEVICE, controller_path, BPF_F_ALLOW_MULTI);
+ r = bpf_program_cgroup_attach(*prog, BPF_CGROUP_DEVICE, controller_path, BPF_F_ALLOW_MULTI);
if (r < 0)
return log_error_errno(r, "Attaching device control BPF program to cgroup %s failed: %m",
empty_to_root(cgroup_path));
finish:
/* Unref the old BPF program (which will implicitly detach it) right before attaching the new program. */
if (prog_installed) {
- bpf_program_unref(*prog_installed);
- *prog_installed = bpf_program_ref(prog);
+ bpf_program_free(*prog_installed);
+ *prog_installed = TAKE_PTR(*prog);
}
return 0;
}
BPF_EXIT_INSN()
};
- _cleanup_(bpf_program_unrefp) BPFProgram *program = NULL;
+ _cleanup_(bpf_program_freep) BPFProgram *program = NULL;
static int supported = -1;
int r;
int bpf_devices_cgroup_init(BPFProgram **ret, CGroupDevicePolicy policy, bool allow_list);
int bpf_devices_apply_policy(
- BPFProgram *prog,
+ BPFProgram **prog,
CGroupDevicePolicy policy,
bool allow_list,
const char *cgroup_path,
BPF_MOV64_IMM(BPF_REG_0, 0),
};
- _cleanup_(bpf_program_unrefp) BPFProgram *p = NULL;
+ _cleanup_(bpf_program_freep) BPFProgram *p = NULL;
int accounting_map_fd, r;
bool access_enabled;
* but we reuse the accounting maps. That way the firewall in effect always maps to the actual
* configuration, but we don't flush out the accounting unnecessarily */
- u->ip_bpf_ingress = bpf_program_unref(u->ip_bpf_ingress);
- u->ip_bpf_egress = bpf_program_unref(u->ip_bpf_egress);
+ u->ip_bpf_ingress = bpf_program_free(u->ip_bpf_ingress);
+ u->ip_bpf_egress = bpf_program_free(u->ip_bpf_egress);
u->ipv4_allow_map_fd = safe_close(u->ipv4_allow_map_fd);
u->ipv4_deny_map_fd = safe_close(u->ipv4_deny_map_fd);
set_clear(*set);
STRV_FOREACH(bpf_fs_path, filter_paths) {
- _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
+ _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
int r;
r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &prog);
assert(u);
set_clear(*set_installed);
+ r = set_ensure_allocated(set_installed, &bpf_program_hash_ops);
+ if (r < 0)
+ return log_oom();
- SET_FOREACH(prog, *set) {
+ SET_FOREACH_MOVE(prog, *set_installed, *set) {
r = bpf_program_cgroup_attach(prog, attach_type, path, BPF_F_ALLOW_MULTI);
if (r < 0)
return log_unit_error_errno(u, r, "Attaching custom egress BPF program to cgroup %s failed: %m", path);
-
- /* Remember that these BPF programs are installed now. */
- r = set_ensure_put(set_installed, &bpf_program_hash_ops, prog);
- if (r < 0)
- return log_unit_error_errno(u, r, "Can't add program to BPF program set: %m");
-
- bpf_program_ref(prog);
}
-
return 0;
}
int bpf_firewall_install(Unit *u) {
- _cleanup_(bpf_program_unrefp) BPFProgram *ip_bpf_ingress_uninstall = NULL, *ip_bpf_egress_uninstall = NULL;
+ _cleanup_(bpf_program_freep) BPFProgram *ip_bpf_ingress_uninstall = NULL, *ip_bpf_egress_uninstall = NULL;
_cleanup_free_ char *path = NULL;
CGroupContext *cc;
int r, supported;
/* If we don't have BPF_F_ALLOW_MULTI then unref the old BPF programs (which will implicitly
* detach them) right before attaching the new program, to minimize the time window when we
* don't account for IP traffic. */
- u->ip_bpf_egress_installed = bpf_program_unref(u->ip_bpf_egress_installed);
- u->ip_bpf_ingress_installed = bpf_program_unref(u->ip_bpf_ingress_installed);
+ u->ip_bpf_egress_installed = bpf_program_free(u->ip_bpf_egress_installed);
+ u->ip_bpf_ingress_installed = bpf_program_free(u->ip_bpf_ingress_installed);
}
if (u->ip_bpf_egress) {
return log_unit_error_errno(u, r, "Attaching egress BPF program to cgroup %s failed: %m", path);
/* Remember that this BPF program is installed now. */
- u->ip_bpf_egress_installed = bpf_program_ref(u->ip_bpf_egress);
+ u->ip_bpf_egress_installed = TAKE_PTR(u->ip_bpf_egress);
}
if (u->ip_bpf_ingress) {
if (r < 0)
return log_unit_error_errno(u, r, "Attaching ingress BPF program to cgroup %s failed: %m", path);
- u->ip_bpf_ingress_installed = bpf_program_ref(u->ip_bpf_ingress);
+ u->ip_bpf_ingress_installed = TAKE_PTR(u->ip_bpf_ingress);
}
/* And now, definitely get rid of the old programs, and detach them */
- ip_bpf_egress_uninstall = bpf_program_unref(ip_bpf_egress_uninstall);
- ip_bpf_ingress_uninstall = bpf_program_unref(ip_bpf_ingress_uninstall);
+ ip_bpf_egress_uninstall = bpf_program_free(ip_bpf_egress_uninstall);
+ ip_bpf_ingress_uninstall = bpf_program_free(ip_bpf_ingress_uninstall);
r = attach_custom_bpf_progs(u, path, BPF_CGROUP_INET_EGRESS, &u->ip_bpf_custom_egress, &u->ip_bpf_custom_egress_installed);
if (r < 0)
BPF_EXIT_INSN()
};
- _cleanup_(bpf_program_unrefp) BPFProgram *program = NULL;
+ _cleanup_(bpf_program_freep) BPFProgram *program = NULL;
static int supported = -1;
union bpf_attr attr;
int r;
u->ipv4_deny_map_fd = safe_close(u->ipv4_deny_map_fd);
u->ipv6_deny_map_fd = safe_close(u->ipv6_deny_map_fd);
- u->ip_bpf_ingress = bpf_program_unref(u->ip_bpf_ingress);
- u->ip_bpf_ingress_installed = bpf_program_unref(u->ip_bpf_ingress_installed);
- u->ip_bpf_egress = bpf_program_unref(u->ip_bpf_egress);
- u->ip_bpf_egress_installed = bpf_program_unref(u->ip_bpf_egress_installed);
+ u->ip_bpf_ingress = bpf_program_free(u->ip_bpf_ingress);
+ u->ip_bpf_ingress_installed = bpf_program_free(u->ip_bpf_ingress_installed);
+ u->ip_bpf_egress = bpf_program_free(u->ip_bpf_egress);
+ u->ip_bpf_egress_installed = bpf_program_free(u->ip_bpf_egress_installed);
u->ip_bpf_custom_ingress = set_free(u->ip_bpf_custom_ingress);
u->ip_bpf_custom_egress = set_free(u->ip_bpf_custom_egress);
DEFINE_PRIVATE_HASH_OPS_FULL(bpf_foreign_by_key_hash_ops,
BPFForeignKey, bpf_foreign_key_hash_func, bpf_foreign_key_compare_func, free,
- BPFProgram, bpf_program_unref);
+ BPFProgram, bpf_program_free);
static int attach_programs(Unit *u, const char *path, Hashmap* foreign_by_key, uint32_t attach_flags) {
const BPFForeignKey *key;
Unit *u,
enum bpf_attach_type attach_type,
const char *bpffs_path) {
- _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
+ _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
_cleanup_free_ BPFForeignKey *key = NULL;
uint32_t prog_id;
int r;
-# SPDX-License-Identifier: LGPL-2.1+
+# SPDX-License-Identifier: LGPL-2.1-or-later
if conf.get('BPF_FRAMEWORK') == 1
restrict_fs_skel_h = custom_target(
}
static int cgroup_apply_devices(Unit *u) {
- _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
+ _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
const char *path;
CGroupContext *c;
CGroupDeviceAllow *a;
policy = CGROUP_DEVICE_POLICY_STRICT;
}
- r = bpf_devices_apply_policy(prog, policy, any, path, &u->bpf_device_control_installed);
+ r = bpf_devices_apply_policy(&prog, policy, any, path, &u->bpf_device_control_installed);
if (r < 0) {
static bool warned = false;
u->cgroup_realized_mask = 0;
u->cgroup_enabled_mask = 0;
- u->bpf_device_control_installed = bpf_program_unref(u->bpf_device_control_installed);
+ u->bpf_device_control_installed = bpf_program_free(u->bpf_device_control_installed);
}
int unit_search_main_pid(Unit *u, pid_t *ret) {
if (u)
return u;
- p = strdupa(cgroup);
+ p = strdupa_safe(cgroup);
for (;;) {
char *e;
if (soft) {
const char *n;
- n = strndupa(suffix, soft - suffix);
+ n = strndupa_safe(suffix, soft - suffix);
ri = rlimit_from_string(n);
if (ri >= 0)
name = strjoina("Limit", n);
else
*p = v;
- char *n = strndupa(name, strlen(name) - 4);
+ char *n = strndupa_safe(name, strlen(name) - 4);
unit_write_settingf(u, flags, name, "%sSec=%s", n, FORMAT_TIMESPAN(v, USEC_PER_MSEC));
}
}
}
- if (context->utmp_id)
+ if (context->utmp_id) {
+ const char *line = context->tty_path ?
+ (path_startswith(context->tty_path, "/dev/") ?: context->tty_path) :
+ NULL;
utmp_put_init_process(context->utmp_id, getpid_cached(), getsid(0),
- context->tty_path,
+ line,
context->utmp_mode == EXEC_UTMP_INIT ? INIT_PROCESS :
context->utmp_mode == EXEC_UTMP_LOGIN ? LOGIN_PROCESS :
USER_PROCESS,
username);
+ }
if (uid_is_valid(uid)) {
r = chown_terminal(STDIN_FILENO, uid);
assert(fds);
n = strcspn(v, " ");
- id = strndupa(v, n);
+ id = strndupa_safe(v, n);
if (v[n] != ' ')
goto finalize;
p = v + n + 1;
char *buf;
n = strcspn(v, " ");
- buf = strndupa(v, n);
+ buf = strndupa_safe(v, n);
r = safe_atoi(buf, &netns_fdpair[0]);
if (r < 0)
char *buf;
n = strcspn(v, " ");
- buf = strndupa(v, n);
+ buf = strndupa_safe(v, n);
r = safe_atoi(buf, &netns_fdpair[1]);
if (r < 0)
char *buf;
n = strcspn(v, " ");
- buf = strndupa(v, n);
+ buf = strndupa_safe(v, n);
r = safe_atoi(buf, &ipcns_fdpair[0]);
if (r < 0)
char *buf;
n = strcspn(v, " ");
- buf = strndupa(v, n);
+ buf = strndupa_safe(v, n);
r = safe_atoi(buf, &ipcns_fdpair[1]);
if (r < 0)
s->parameters_fragment.priority_set = true;
return 0;
}
+
+int config_parse_watchdog_sec(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ usec_t *usec = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ /* This is called for {Runtime,Reboot,KExec}WatchdogSec= where "default" maps to
+ * USEC_INFINITY internally. */
+
+ if (streq(rvalue, "default"))
+ *usec = USEC_INFINITY;
+ else if (streq(rvalue, "off"))
+ *usec = 0;
+ else
+ return config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata);
+
+ return 0;
+}
CONFIG_PARSER_PROTOTYPE(config_parse_bpf_foreign_program);
CONFIG_PARSER_PROTOTYPE(config_parse_cgroup_socket_bind);
CONFIG_PARSER_PROTOTYPE(config_parse_restrict_network_interfaces);
+CONFIG_PARSER_PROTOTYPE(config_parse_watchdog_sec);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
(void) parse_path_argument(value, false, &arg_watchdog_device);
+ } else if (proc_cmdline_key_streq(key, "systemd.watchdog_sec")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ if (streq(value, "default"))
+ arg_runtime_watchdog = USEC_INFINITY;
+ else if (streq(value, "off"))
+ arg_runtime_watchdog = 0;
+ else {
+ r = parse_sec(value, &arg_runtime_watchdog);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to parse systemd.watchdog_sec= argument '%s', ignoring: %m", value);
+ return 0;
+ }
+ }
+
+ arg_kexec_watchdog = arg_reboot_watchdog = arg_runtime_watchdog;
+
} else if (proc_cmdline_key_streq(key, "systemd.clock_usec")) {
if (proc_cmdline_value_missing(key, value))
{ "Manager", "NUMAPolicy", config_parse_numa_policy, 0, &arg_numa_policy.type },
{ "Manager", "NUMAMask", config_parse_numa_mask, 0, &arg_numa_policy },
{ "Manager", "JoinControllers", config_parse_warn_compat, DISABLED_CONFIGURATION, NULL },
- { "Manager", "RuntimeWatchdogSec", config_parse_sec, 0, &arg_runtime_watchdog },
- { "Manager", "RebootWatchdogSec", config_parse_sec, 0, &arg_reboot_watchdog },
- { "Manager", "ShutdownWatchdogSec", config_parse_sec, 0, &arg_reboot_watchdog }, /* obsolete alias */
- { "Manager", "KExecWatchdogSec", config_parse_sec, 0, &arg_kexec_watchdog },
+ { "Manager", "RuntimeWatchdogSec", config_parse_watchdog_sec, 0, &arg_runtime_watchdog },
+ { "Manager", "RebootWatchdogSec", config_parse_watchdog_sec, 0, &arg_reboot_watchdog },
+ { "Manager", "ShutdownWatchdogSec", config_parse_watchdog_sec, 0, &arg_reboot_watchdog }, /* obsolete alias */
+ { "Manager", "KExecWatchdogSec", config_parse_watchdog_sec, 0, &arg_kexec_watchdog },
{ "Manager", "WatchdogDevice", config_parse_path, 0, &arg_watchdog_device },
{ "Manager", "CapabilityBoundingSet", config_parse_capability_set, 0, &arg_capability_bounding_set },
{ "Manager", "NoNewPrivileges", config_parse_bool, 0, &arg_no_new_privs },
};
_cleanup_strv_free_ char **env_block = NULL;
+ usec_t watchdog_timer = 0;
size_t pos = 7;
int r;
- usec_t watchdog_timer = 0;
assert(shutdown_verb);
assert(!command_line[pos]);
else if (streq(shutdown_verb, "kexec"))
watchdog_timer = arg_kexec_watchdog;
- if (timestamp_is_set(watchdog_timer)) {
- /* If we reboot or kexec let's set the shutdown watchdog and tell the shutdown binary to
- * repeatedly ping it */
- r = watchdog_setup(watchdog_timer);
- watchdog_close(r < 0);
+ /* If we reboot or kexec let's set the shutdown watchdog and tell the
+ * shutdown binary to repeatedly ping it */
+ r = watchdog_setup(watchdog_timer);
+ watchdog_close(r < 0);
- /* Tell the binary how often to ping, ignore failure */
- (void) strv_extendf(&env_block, "WATCHDOG_USEC="USEC_FMT, watchdog_timer);
+ /* Tell the binary how often to ping, ignore failure */
+ (void) strv_extendf(&env_block, "WATCHDOG_USEC="USEC_FMT, watchdog_timer);
- if (arg_watchdog_device)
- (void) strv_extendf(&env_block, "WATCHDOG_DEVICE=%s", arg_watchdog_device);
- } else
- watchdog_close(true);
+ if (arg_watchdog_device)
+ (void) strv_extendf(&env_block, "WATCHDOG_DEVICE=%s", arg_watchdog_device);
/* Avoid the creation of new processes forked by the kernel; at this
* point, we will not listen to the signals anyway */
return;
if (t == WATCHDOG_RUNTIME)
- if (!timestamp_is_set(m->watchdog_overridden[WATCHDOG_RUNTIME])) {
- if (timestamp_is_set(timeout))
- (void) watchdog_setup(timeout);
- else
- watchdog_close(true);
- }
+ if (!timestamp_is_set(m->watchdog_overridden[WATCHDOG_RUNTIME]))
+ (void) watchdog_setup(timeout);
m->watchdog[t] = timeout;
}
if (t == WATCHDOG_RUNTIME) {
usec_t usec = timestamp_is_set(timeout) ? timeout : m->watchdog[t];
- if (timestamp_is_set(usec))
- (void) watchdog_setup(usec);
- else
- watchdog_close(true);
+ (void) watchdog_setup(usec);
}
m->watchdog_overridden[t] = timeout;
DISABLE_WARNING_FORMAT_NONLITERAL;
log_internalv(LOG_AUTH | callback_type_to_priority(type),
- 0, PROJECT_FILE, __LINE__, __FUNCTION__,
+ 0, PROJECT_FILE, __LINE__, __func__,
fmt2, ap);
REENABLE_WARNING;
va_end(ap);
#CPUAffinity=
#NUMAPolicy=default
#NUMAMask=
-#RuntimeWatchdogSec=0
+#RuntimeWatchdogSec=off
#RebootWatchdogSec=10min
-#KExecWatchdogSec=0
+#KExecWatchdogSec=off
#WatchdogDevice=
#CapabilityBoundingSet=
#NoNewPrivileges=no
(void) bpf_program_serialize_attachment(f, fds, "ip-bpf-ingress-installed", u->ip_bpf_ingress_installed);
(void) bpf_program_serialize_attachment(f, fds, "ip-bpf-egress-installed", u->ip_bpf_egress_installed);
+ (void) bpf_program_serialize_attachment(f, fds, "bpf-device-control-installed", u->bpf_device_control_installed);
(void) bpf_program_serialize_attachment_set(f, fds, "ip-bpf-custom-ingress-installed", u->ip_bpf_custom_ingress_installed);
(void) bpf_program_serialize_attachment_set(f, fds, "ip-bpf-custom-egress-installed", u->ip_bpf_custom_egress_installed);
} else if (streq(l, "ip-bpf-egress-installed")) {
(void) bpf_program_deserialize_attachment(v, fds, &u->ip_bpf_egress_installed);
continue;
+ } else if (streq(l, "bpf-device-control-installed")) {
+ (void) bpf_program_deserialize_attachment(v, fds, &u->bpf_device_control_installed);
+ continue;
} else if (streq(l, "ip-bpf-custom-ingress-installed")) {
(void) bpf_program_deserialize_attachment_set(v, fds, &u->ip_bpf_custom_ingress_installed);
hashmap_free(u->bpf_foreign_by_key);
- bpf_program_unref(u->bpf_device_control_installed);
+ bpf_program_free(u->bpf_device_control_installed);
#if BPF_FRAMEWORK
bpf_link_free(u->restrict_ifaces_ingress_bpf_link);
if (!e)
return -EINVAL;
- u = strndupa(p, e-p);
+ u = strndupa_safe(p, e - p);
return parse_uid(u, uid);
}
assert(file);
assert(j);
+ (void) sd_journal_set_data_threshold(j, 0);
+
SD_JOURNAL_FOREACH_DATA(j, d, l) {
RETRIEVE(d, l, "MESSAGE_ID", mid);
RETRIEVE(d, l, "COREDUMP_PID", pid);
_cleanup_strv_free_erase_ char **pins = NULL;
_cleanup_free_ void *loaded_salt = NULL;
+ bool device_exists = false;
const char *salt;
size_t salt_size;
char *e;
-ENOANO, /* needs pin */
-ENOLCK)) /* pin incorrect */
return r;
- }
- pins = strv_free_erase(pins);
+ 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
+ * the device is not plugged in, which is confusing. */
+
+ r = fido2_have_device(device);
+ if (r < 0)
+ return r;
+ if (r == 0) /* no device found, return EAGAIN so that caller will wait/watch udev */
+ return -EAGAIN;
+
+ device_exists = true; /* now we know for sure, a device exists, no need to ask again */
+ }
+
+ 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)
return log_error_errno(r, "Failed to ask for user password: %m");
#include "cryptsetup-util.h"
#include "device-util.h"
#include "efi-loader.h"
+#include "env-util.h"
#include "escape.h"
#include "fileio.h"
#include "fs-util.h"
static bool arg_tpm2_device_auto = false;
static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
static bool arg_headless = false;
+static usec_t arg_token_timeout_usec = 30*USEC_PER_SEC;
STATIC_DESTRUCTOR_REGISTER(arg_cipher, freep);
STATIC_DESTRUCTOR_REGISTER(arg_hash, freep);
} else if (streq(option, "headless"))
arg_headless = true;
- else if (!streq(option, "x-initrd.attach"))
+ else if ((val = startswith(option, "token-timeout="))) {
+
+ r = parse_sec_fix_0(val, &arg_token_timeout_usec);
+ if (r < 0) {
+ log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
+ return 0;
+ }
+
+ } else if (!streq(option, "x-initrd.attach"))
log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
return 0;
return s;
}
-static int make_security_device_monitor(sd_event *event, sd_device_monitor **ret) {
+static int make_security_device_monitor(
+ sd_event **ret_event,
+ sd_device_monitor **ret_monitor) {
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
int r;
- assert(ret);
+ assert(ret_event);
+ assert(ret_monitor);
+
+ /* Waits for a device with "security-device" tag to show up in udev */
+
+ r = sd_event_default(&event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate event loop: %m");
+
+ r = sd_event_add_time_relative(event, NULL, CLOCK_MONOTONIC, arg_token_timeout_usec, USEC_PER_SEC, NULL, INT_TO_PTR(-ETIMEDOUT));
+ if (r < 0)
+ return log_error_errno(r, "Failed to install timeout event source: %m");
r = sd_device_monitor_new(&monitor);
if (r < 0)
if (r < 0)
return log_error_errno(r, "Failed to start device monitor: %m");
- *ret = TAKE_PTR(monitor);
+ *ret_event = TAKE_PTR(event);
+ *ret_monitor = TAKE_PTR(monitor);
return 0;
}
+static int run_security_device_monitor(
+ sd_event *event,
+ sd_device_monitor *monitor) {
+ bool processed = false;
+ int r;
+
+ assert(event);
+ assert(monitor);
+
+ /* Runs the event loop for the device monitor until either something happens, or the time-out is
+ * hit. */
+
+ for (;;) {
+ int x;
+
+ r = sd_event_get_exit_code(event, &x);
+ if (r < 0) {
+ if (r != -ENODATA)
+ return log_error_errno(r, "Failed to query exit code from event loop: %m");
+
+ /* On ENODATA we aren't told to exit yet. */
+ } else {
+ assert(x == -ETIMEDOUT);
+ return log_notice_errno(SYNTHETIC_ERRNO(EAGAIN),
+ "Timed out waiting for security device, aborting security device based authentication attempt.");
+ }
+
+ /* Wait for one event, and then eat all subsequent events until there are no further ones */
+ r = sd_event_run(event, processed ? 0 : UINT64_MAX);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
+ if (r == 0) /* no events queued anymore */
+ return 0;
+
+ processed = true;
+ }
+}
+
static bool libcryptsetup_plugins_support(void) {
#if HAVE_LIBCRYPTSETUP_PLUGINS
- return crypt_token_external_path() != NULL;
+ int r;
+
+ /* Permit a way to disable libcryptsetup token module support, for debugging purposes. */
+ r = getenv_bool("SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE");
+ if (r < 0 && r != -ENXIO)
+ log_debug_errno(r, "Failed to parse $SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE env var: %m");
+ if (r == 0)
+ return false;
+
+ return crypt_token_external_path();
#else
return false;
#endif
void *usrptr,
uint32_t activation_flags) {
- int r = -EOPNOTSUPP;
#if HAVE_LIBCRYPTSETUP_PLUGINS
- char **p;
- _cleanup_strv_free_erase_ char **pins = NULL;
AskPasswordFlags flags = ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_ACCEPT_CACHED;
+ _cleanup_strv_free_erase_ char **pins = NULL;
+ char **p;
+ int r;
r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, NULL, 0, usrptr, activation_flags);
if (r > 0) /* returns unlocked keyslot id on success */
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, flags, &pins);
- if (r < 0)
- return r;
-
- STRV_FOREACH(p, pins) {
- r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, *p, strlen(*p), usrptr, activation_flags);
- if (r > 0) /* returns unlocked keyslot id on success */
- r = 0;
- if (r != -ENOANO) /* needs pin or pin is wrong */
- return r;
- }
-
- flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
for (;;) {
pins = strv_free_erase(pins);
r = ask_password_auto("Please enter security token PIN:", "drive-harddisk", NULL, "fido2-pin", "cryptsetup.fido2-pin", until, flags, &pins);
if (r != -ENOANO) /* needs pin or pin is wrong */
return r;
}
+
+ flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
}
-#endif
return r;
+#else
+ return -EOPNOTSUPP;
+#endif
}
static int attach_luks_or_plain_or_bitlk_by_fido2(
return log_oom();
for (;;) {
- bool processed = false;
-
if (use_libcryptsetup_plugin && !arg_fido2_cid) {
r = attach_luks2_by_fido2(cd, name, until, arg_headless, arg_fido2_device, flags);
if (IN_SET(r, -ENOTUNIQ, -ENXIO, -ENOENT))
assert(!event);
- r = sd_event_default(&event);
- if (r < 0)
- return log_error_errno(r, "Failed to allocate event loop: %m");
-
- r = make_security_device_monitor(event, &monitor);
+ r = make_security_device_monitor(&event, &monitor);
if (r < 0)
return r;
continue;
}
- for (;;) {
- /* Wait for one event, and then eat all subsequent events until there are no
- * further ones */
- r = sd_event_run(event, processed ? 0 : UINT64_MAX);
- if (r < 0)
- return log_error_errno(r, "Failed to run event loop: %m");
- if (r == 0)
- break;
-
- processed = true;
- }
+ r = run_security_device_monitor(event, monitor);
+ if (r < 0)
+ return r;
log_debug("Got one or more potentially relevant udev events, rescanning FIDO2...");
}
bool headless,
uint32_t flags) {
- int r = -EOPNOTSUPP;
#if HAVE_LIBCRYPTSETUP_PLUGINS
- if (!crypt_get_type(cd) || strcmp(crypt_get_type(cd), CRYPT_LUKS2))
+ int r;
+
+ if (!streq_ptr(crypt_get_type(cd), CRYPT_LUKS2))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Automatic PKCS#11 metadata requires LUKS2 device.");
systemd_pkcs11_plugin_params params = {
r = crypt_activate_by_token_pin(cd, name, "systemd-pkcs11", CRYPT_ANY_TOKEN, NULL, 0, ¶ms, flags);
if (r > 0) /* returns unlocked keyslot id on success */
r = 0;
-#endif
+
return r;
+#else
+ return -EOPNOTSUPP;
+#endif
}
static int attach_luks_or_plain_or_bitlk_by_pkcs11(
return log_oom();
for (;;) {
- bool processed = false;
-
if (use_libcryptsetup_plugin && arg_pkcs11_uri_auto)
r = attach_luks2_by_pkcs11(cd, name, friendly, until, arg_headless, flags);
else {
assert(!event);
- r = sd_event_default(&event);
- if (r < 0)
- return log_error_errno(r, "Failed to allocate event loop: %m");
-
- r = make_security_device_monitor(event, &monitor);
+ r = make_security_device_monitor(&event, &monitor);
if (r < 0)
return r;
continue;
}
- for (;;) {
- /* Wait for one event, and then eat all subsequent events until there are no
- * further ones */
- r = sd_event_run(event, processed ? 0 : UINT64_MAX);
- if (r < 0)
- return log_error_errno(r, "Failed to run event loop: %m");
- if (r == 0)
- break;
-
- processed = true;
- }
+ r = run_security_device_monitor(event, monitor);
+ if (r < 0)
+ return r;
log_debug("Got one or more potentially relevant udev events, rescanning PKCS#11...");
}
return 0;
}
-static int make_tpm2_device_monitor(sd_event *event, sd_device_monitor **ret) {
+static int make_tpm2_device_monitor(
+ sd_event **ret_event,
+ sd_device_monitor **ret_monitor) {
+
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
int r;
- assert(ret);
+ assert(ret_event);
+ assert(ret_monitor);
+
+ r = sd_event_default(&event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate event loop: %m");
+
+ r = sd_event_add_time_relative(event, NULL, CLOCK_MONOTONIC, arg_token_timeout_usec, USEC_PER_SEC, NULL, INT_TO_PTR(-ETIMEDOUT));
+ if (r < 0)
+ return log_error_errno(r, "Failed to install timeout event source: %m");
r = sd_device_monitor_new(&monitor);
if (r < 0)
if (r < 0)
return log_error_errno(r, "Failed to start device monitor: %m");
- *ret = TAKE_PTR(monitor);
+ *ret_event = TAKE_PTR(event);
+ *ret_monitor = TAKE_PTR(monitor);
return 0;
}
return log_oom();
for (;;) {
- bool processed = false;
-
if (key_file || key_data) {
/* If key data is specified, use that */
return log_notice_errno(SYNTHETIC_ERRNO(EAGAIN),
"No TPM2 hardware discovered and EFI bios indicates no support for it either, assuming TPM2-less system, falling back to traditional unocking.");
- r = sd_event_default(&event);
- if (r < 0)
- return log_error_errno(r, "Failed to allocate event loop: %m");
-
- r = make_tpm2_device_monitor(event, &monitor);
+ r = make_tpm2_device_monitor(&event, &monitor);
if (r < 0)
return r;
continue;
}
- for (;;) {
- /* Wait for one event, and then eat all subsequent events until there are no
- * further ones */
- r = sd_event_run(event, processed ? 0 : UINT64_MAX);
- if (r < 0)
- return log_error_errno(r, "Failed to run event loop: %m");
- if (r == 0)
- break;
-
- processed = true;
- }
+ r = run_security_device_monitor(event, monitor);
+ if (r < 0)
+ return r;
log_debug("Got one or more potentially relevant udev events, rescanning for TPM2...");
}
#endif
#define memcpy(a, b, c) CopyMem((a), (b), (c))
+ #define free(a) FreePool(a)
#endif
#if defined(static_assert)
* @x: a string literal.
*/
#define STRLEN(x) (sizeof(""x"") - sizeof(typeof(x[0])))
+
+#define mfree(memory) \
+ ({ \
+ free(memory); \
+ (typeof(memory)) NULL; \
+ })
+++ /dev/null
-"\xfe"
-"\x00"
-"\x01"
-"\xf1"
-"\xd0"
-"\xf1\xd0\x00\x01"
" --storage=STORAGE Storage type to use (luks, fscrypt, directory,\n"
" subvolume, cifs)\n"
" --image-path=PATH Path to image file/directory\n"
+ " --drop-caches=BOOL Whether to automatically drop caches on logout\n"
"\n%4$sLUKS Storage User Record Properties:%5$s\n"
" --fs-type=TYPE File system type to use in case of luks\n"
" storage (btrfs, ext4, xfs)\n"
ARG_RECOVERY_KEY,
ARG_AND_RESIZE,
ARG_AND_CHANGE_PASSWORD,
+ ARG_DROP_CACHES,
};
static const struct option options[] = {
{ "recovery-key", required_argument, NULL, ARG_RECOVERY_KEY },
{ "and-resize", required_argument, NULL, ARG_AND_RESIZE },
{ "and-change-password", required_argument, NULL, ARG_AND_CHANGE_PASSWORD },
+ { "drop-caches", required_argument, NULL, ARG_DROP_CACHES },
{}
};
arg_and_change_password = true;
break;
+ case ARG_DROP_CACHES: {
+ bool drop_caches;
+
+ if (isempty(optarg)) {
+ r = drop_from_identity("dropCaches");
+ if (r < 0)
+ return r;
+ }
+
+ r = parse_boolean_argument("--drop-caches=", optarg, &drop_caches);
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field_boolean(&arg_identity_extra, "dropCaches", r);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set drop caches field: %m");
+
+ break;
+ }
+
case '?':
return -EINVAL;
#include "user-record.h"
#include "user-util.h"
+/* Retry to deactivate home directories again and again every 15s until it works */
+#define RETRY_DEACTIVATE_USEC (15U * USEC_PER_SEC)
+
#define HOME_USERS_MAX 500
#define PENDING_OPERATIONS_MAX 100
.worker_stdout_fd = -1,
.sysfs = TAKE_PTR(ns),
.signed_locally = -1,
+ .pin_fd = -1,
+ .luks_lock_fd = -1,
};
r = hashmap_put(m->homes_by_name, home->user_name, home);
h->current_operation = operation_unref(h->current_operation);
+ safe_close(h->pin_fd);
+ safe_close(h->luks_lock_fd);
+
+ h->retry_deactivate_event_source = sd_event_source_disable_unref(h->retry_deactivate_event_source);
+
return mfree(h);
}
return 0;
}
+static void home_unpin(Home *h) {
+ assert(h);
+
+ if (h->pin_fd < 0)
+ return;
+
+ h->pin_fd = safe_close(h->pin_fd);
+ log_debug("Successfully closed pin fd on home for %s.", h->user_name);
+}
+
+static void home_pin(Home *h) {
+ const char *path;
+
+ assert(h);
+
+ if (h->pin_fd >= 0) /* Already pinned? */
+ return;
+
+ path = user_record_home_directory(h->record);
+ if (!path) {
+ log_warning("No home directory path to pin for %s, ignoring.", h->user_name);
+ return;
+ }
+
+ h->pin_fd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
+ if (h->pin_fd < 0) {
+ log_warning_errno(errno, "Couldn't open home directory '%s' for pinning, ignoring: %m", path);
+ return;
+ }
+
+ log_debug("Successfully pinned home directory '%s'.", path);
+}
+
+static void home_update_pin_fd(Home *h, HomeState state) {
+ assert(h);
+
+ if (state < 0)
+ state = home_get_state(h);
+
+ return HOME_STATE_SHALL_PIN(state) ? home_pin(h) : home_unpin(h);
+}
+
+static void home_maybe_close_luks_lock_fd(Home *h, HomeState state) {
+ assert(h);
+
+ if (h->luks_lock_fd < 0)
+ return;
+
+ if (state < 0)
+ state = home_get_state(h);
+
+ /* Keep the lock as long as the home dir is active or has some operation going */
+ if (HOME_STATE_IS_EXECUTING_OPERATION(state) || HOME_STATE_IS_ACTIVE(state) || state == HOME_LOCKED)
+ return;
+
+ h->luks_lock_fd = safe_close(h->luks_lock_fd);
+ log_debug("Successfully closed LUKS backing file lock for %s.", h->user_name);
+}
+
+static void home_maybe_stop_retry_deactivate(Home *h, HomeState state) {
+ assert(h);
+
+ /* Free the deactivation retry event source if we won't need it anymore. Specifically, we'll free the
+ * event source whenever the home directory is already deactivated (and we thus where successful) or
+ * if we start executing an operation that indicates that the home directory is going to be used or
+ * operated on again. Also, if the home is referenced again stop the timer */
+
+ if (HOME_STATE_MAY_RETRY_DEACTIVATE(state) &&
+ !h->ref_event_source_dont_suspend &&
+ !h->ref_event_source_please_suspend)
+ return;
+
+ h->retry_deactivate_event_source = sd_event_source_disable_unref(h->retry_deactivate_event_source);
+}
+
+static int home_deactivate_internal(Home *h, bool force, sd_bus_error *error);
+static void home_start_retry_deactivate(Home *h);
+
+static int home_on_retry_deactivate(sd_event_source *s, uint64_t usec, void *userdata) {
+ Home *h = userdata;
+ HomeState state;
+
+ assert(s);
+ assert(h);
+
+ /* 15s after the last attempt to deactivate the home directory passed. Let's try it one more time. */
+
+ h->retry_deactivate_event_source = sd_event_source_disable_unref(h->retry_deactivate_event_source);
+
+ state = home_get_state(h);
+ if (!HOME_STATE_MAY_RETRY_DEACTIVATE(state))
+ return 0;
+
+ if (IN_SET(state, HOME_ACTIVE, HOME_LINGERING)) {
+ log_info("Again trying to deactivate home directory.");
+
+ /* If we are not executing any operation, let's start deactivating now. Note that this will
+ * restart our timer again, we are gonna be called again if this doesn't work. */
+ (void) home_deactivate_internal(h, /* force= */ false, NULL);
+ } else
+ /* if we are executing an operation (specifically, area already running a deactivation
+ * operation), then simply reque the timer, so that we retry again. */
+ home_start_retry_deactivate(h);
+
+ return 0;
+}
+
+static void home_start_retry_deactivate(Home *h) {
+ int r;
+
+ assert(h);
+ assert(h->manager);
+
+ /* Alrady allocated? */
+ if (h->retry_deactivate_event_source)
+ return;
+
+ /* If the home directory is being used now don't start the timer */
+ if (h->ref_event_source_dont_suspend || h->ref_event_source_please_suspend)
+ return;
+
+ r = sd_event_add_time_relative(
+ h->manager->event,
+ &h->retry_deactivate_event_source,
+ CLOCK_MONOTONIC,
+ RETRY_DEACTIVATE_USEC,
+ 1*USEC_PER_MINUTE,
+ home_on_retry_deactivate,
+ h);
+ if (r < 0)
+ return (void) log_warning_errno(r, "Failed to install retry-deactivate event source, ignoring: %m");
+
+ (void) sd_event_source_set_description(h->retry_deactivate_event_source, "retry-deactivate");
+}
+
static void home_set_state(Home *h, HomeState state) {
HomeState old_state, new_state;
home_state_to_string(old_state),
home_state_to_string(new_state));
+ home_update_pin_fd(h, new_state);
+ home_maybe_close_luks_lock_fd(h, new_state);
+ home_maybe_stop_retry_deactivate(h, new_state);
+
if (HOME_STATE_IS_EXECUTING_OPERATION(old_state) && !HOME_STATE_IS_EXECUTING_OPERATION(new_state)) {
/* If we just finished executing some operation, process the queue of pending operations. And
* enqueue it for GC too. */
return sd_bus_error_setf(error, BUS_ERROR_NO_DISK_SPACE, "Not enough disk space for home %s", h->user_name);
case -EKEYREVOKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_CANT_AUTHENTICATE, "Home %s has no password or other authentication mechanism defined.", h->user_name);
+ case -EADDRINUSE:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_IN_USE, "Home %s is currently being used elsewhere.", h->user_name);
}
return 0;
_exit(EXIT_FAILURE);
}
+ /* If we haven't locked the device yet, ask for a lock to be taken and be passed back to us via sd_notify(). */
+ if (setenv("SYSTEMD_LUKS_LOCK", one_zero(h->luks_lock_fd < 0), 1) < 0) {
+ log_error_errno(errno, "Failed to set $SYSTEMD_LUKS_LOCK: %m");
+ _exit(EXIT_FAILURE);
+ }
+
if (h->manager->default_storage >= 0)
if (setenv("SYSTEMD_HOME_DEFAULT_STORAGE", user_storage_to_string(h->manager->default_storage), 1) < 0) {
log_error_errno(errno, "Failed to set $SYSTEMD_HOME_DEFAULT_STORAGE: %m");
case HOME_INACTIVE:
case HOME_DIRTY:
case HOME_ACTIVE:
+ case HOME_LINGERING:
case HOME_LOCKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_ALREADY_FIXATED, "Home %s is already fixated.", h->user_name);
case HOME_UNFIXATED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
case HOME_ACTIVE:
return sd_bus_error_setf(error, BUS_ERROR_HOME_ALREADY_ACTIVE, "Home %s is already active.", h->user_name);
+ case HOME_LINGERING:
+ /* If we are lingering, i.e. active but are supposed to be deactivated, then cancel this
+ * timer if the user explicitly asks us to be active */
+ h->retry_deactivate_event_source = sd_event_source_disable_unref(h->retry_deactivate_event_source);
+ return 0;
case HOME_LOCKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
case HOME_INACTIVE:
case HOME_INACTIVE:
case HOME_DIRTY:
case HOME_ACTIVE:
+ case HOME_LINGERING:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
if (r < 0)
return r;
- return home_authenticate_internal(h, secret, state == HOME_ACTIVE ? HOME_AUTHENTICATING_WHILE_ACTIVE : HOME_AUTHENTICATING, error);
+ return home_authenticate_internal(h, secret, HOME_STATE_IS_ACTIVE(state) ? HOME_AUTHENTICATING_WHILE_ACTIVE : HOME_AUTHENTICATING, error);
}
static int home_deactivate_internal(Home *h, bool force, sd_bus_error *error) {
assert(h);
+ home_unpin(h); /* unpin so that we can deactivate */
+
r = home_start_work(h, force ? "deactivate-force" : "deactivate", h->record, NULL);
if (r < 0)
- return r;
+ /* Operation failed before it even started, reacquire pin fd, if state still dictates so */
+ home_update_pin_fd(h, _HOME_STATE_INVALID);
+ else {
+ home_set_state(h, HOME_DEACTIVATING);
+ r = 0;
+ }
- home_set_state(h, HOME_DEACTIVATING);
- return 0;
+ /* Let's start a timer to retry deactivation in 15. We'll stop the timer once we manage to deactivate
+ * the home directory again, or we we start any other operation. */
+ home_start_retry_deactivate(h);
+
+ return r;
}
int home_deactivate(Home *h, bool force, sd_bus_error *error) {
case HOME_LOCKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
case HOME_ACTIVE:
+ case HOME_LINGERING:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
case HOME_ABSENT:
break;
case HOME_ACTIVE:
+ case HOME_LINGERING:
case HOME_LOCKED:
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
case HOME_DIRTY:
break;
case HOME_ACTIVE:
+ case HOME_LINGERING:
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
}
case HOME_INACTIVE:
case HOME_DIRTY:
case HOME_ACTIVE:
+ case HOME_LINGERING:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
if (r < 0)
return r;
- home_set_state(h, state == HOME_ACTIVE ? HOME_UPDATING_WHILE_ACTIVE : HOME_UPDATING);
+ home_set_state(h, HOME_STATE_IS_ACTIVE(state) ? HOME_UPDATING_WHILE_ACTIVE : HOME_UPDATING);
return 0;
}
case HOME_INACTIVE:
case HOME_DIRTY:
case HOME_ACTIVE:
+ case HOME_LINGERING:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
if (r < 0)
return r;
- home_set_state(h, state == HOME_ACTIVE ? HOME_RESIZING_WHILE_ACTIVE : HOME_RESIZING);
+ home_set_state(h, HOME_STATE_IS_ACTIVE(state) ? HOME_RESIZING_WHILE_ACTIVE : HOME_RESIZING);
return 0;
}
case HOME_INACTIVE:
case HOME_DIRTY:
case HOME_ACTIVE:
+ case HOME_LINGERING:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
if (r < 0)
return r;
- home_set_state(h, state == HOME_ACTIVE ? HOME_PASSWD_WHILE_ACTIVE : HOME_PASSWD);
+ home_set_state(h, HOME_STATE_IS_ACTIVE(state) ? HOME_PASSWD_WHILE_ACTIVE : HOME_PASSWD);
return 0;
}
case HOME_DIRTY:
break;
case HOME_ACTIVE:
+ case HOME_LINGERING:
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
}
case HOME_LOCKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is already locked.", h->user_name);
case HOME_ACTIVE:
+ case HOME_LINGERING:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
case HOME_ABSENT:
case HOME_INACTIVE:
case HOME_ACTIVE:
+ case HOME_LINGERING:
case HOME_DIRTY:
return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_LOCKED, "Home %s is not locked.", h->user_name);
case HOME_LOCKED:
/* Otherwise, let's see if the home directory is mounted. If so, we assume for sure the home
* directory is active */
if (user_record_test_home_directory(h->record) == USER_TEST_MOUNTED)
- return HOME_ACTIVE;
+ return h->retry_deactivate_event_source ? HOME_LINGERING : HOME_ACTIVE;
/* And if we see the image being gone, we report this as absent */
r = user_record_test_image_path(h->record);
return HOME_INACTIVE;
}
-void home_process_notify(Home *h, char **l) {
+void home_process_notify(Home *h, char **l, int fd) {
+ _cleanup_close_ int taken_fd = TAKE_FD(fd);
const char *e;
int error;
int r;
assert(h);
- e = strv_env_get(l, "ERRNO");
- if (!e) {
- log_debug("Got notify message lacking ERRNO= field, ignoring.");
+ e = strv_env_get(l, "SYSTEMD_LUKS_LOCK_FD");
+ if (e) {
+ r = parse_boolean(e);
+ if (r < 0)
+ return (void) log_debug_errno(r, "Failed to parse SYSTEMD_LUKS_LOCK_FD value: %m");
+ if (r > 0) {
+ if (taken_fd < 0)
+ return (void) log_debug("Got notify message with SYSTEMD_LUKS_LOCK_FD=1 but no fd passed, ignoring: %m");
+
+ safe_close(h->luks_lock_fd);
+ h->luks_lock_fd = TAKE_FD(taken_fd);
+
+ log_debug("Successfully acquired LUKS lock fd from worker.");
+
+ /* Immediately check if we actually want to keep it */
+ home_maybe_close_luks_lock_fd(h, _HOME_STATE_INVALID);
+ } else {
+ if (taken_fd >= 0)
+ return (void) log_debug("Got notify message with SYSTEMD_LUKS_LOCK_FD=0 but fd passed, ignoring: %m");
+
+ h->luks_lock_fd = safe_close(h->luks_lock_fd);
+ }
+
return;
}
+ e = strv_env_get(l, "ERRNO");
+ if (!e)
+ return (void) log_debug("Got notify message lacking both ERRNO= and SYSTEMD_LUKS_LOCK_FD= field, ignoring.");
+
r = safe_atoi(e, &error);
- if (r < 0) {
- log_debug_errno(r, "Failed to parse received error number, ignoring: %s", e);
- return;
- }
- if (error <= 0) {
- log_debug("Error number is out of range: %i", error);
- return;
- }
+ if (r < 0)
+ return (void) log_debug_errno(r, "Failed to parse received error number, ignoring: %s", e);
+ if (error <= 0)
+ return (void) log_debug("Error number is out of range: %i", error);
h->worker_error_code = error;
}
break;
case HOME_ACTIVE:
+ case HOME_LINGERING:
for_state = HOME_AUTHENTICATING_FOR_ACQUIRE;
call = home_authenticate_internal;
break;
break;
case HOME_ACTIVE:
+ case HOME_LINGERING:
r = home_deactivate_internal(h, false, &error);
break;
break;
case HOME_ACTIVE:
+ case HOME_LINGERING:
log_info("Locking home %s.", h->user_name);
r = home_lock(h, &error);
break;
break;
case HOME_ACTIVE:
+ case HOME_LINGERING:
log_info("Deactivating home %s.", h->user_name);
r = home_deactivate_internal(h, false, &error);
break;
break;
case HOME_ACTIVE:
+ case HOME_LINGERING:
r = home_deactivate_internal(h, false, &error);
if (r < 0)
log_warning_errno(r, "Failed to deactivate %s, ignoring: %s", h->user_name, bus_error_message(&error, r));
case HOME_ACTIVE:
case HOME_LOCKED:
+ case HOME_LINGERING:
r = home_deactivate_internal(h, true, &error);
if (r < 0)
log_warning_errno(r, "Failed to forcibly deactivate %s, ignoring: %s", h->user_name, bus_error_message(&error, r));
[HOME_ACTIVATING_FOR_ACQUIRE] = "activating-for-acquire",
[HOME_DEACTIVATING] = "deactivating",
[HOME_ACTIVE] = "active",
+ [HOME_LINGERING] = "lingering",
[HOME_LOCKING] = "locking",
[HOME_LOCKED] = "locked",
[HOME_UNLOCKING] = "unlocking",
HOME_ACTIVATING_FOR_ACQUIRE, /* activating because Acquire() was called */
HOME_DEACTIVATING,
HOME_ACTIVE, /* logged in right now */
+ HOME_LINGERING, /* not logged in anymore, but we didn't manage to deactivate (because some process keeps it busy?) but we'll keep trying */
HOME_LOCKING,
HOME_LOCKED,
HOME_UNLOCKING,
static inline bool HOME_STATE_IS_ACTIVE(HomeState state) {
return IN_SET(state,
HOME_ACTIVE,
+ HOME_LINGERING,
HOME_UPDATING_WHILE_ACTIVE,
HOME_RESIZING_WHILE_ACTIVE,
HOME_PASSWD_WHILE_ACTIVE,
HOME_AUTHENTICATING_FOR_ACQUIRE);
}
+static inline bool HOME_STATE_SHALL_PIN(HomeState state) {
+ /* Like HOME_STATE_IS_ACTIVE() – but HOME_LINGERING is missing! */
+ return IN_SET(state,
+ HOME_ACTIVE,
+ HOME_UPDATING_WHILE_ACTIVE,
+ HOME_RESIZING_WHILE_ACTIVE,
+ HOME_PASSWD_WHILE_ACTIVE,
+ HOME_AUTHENTICATING_WHILE_ACTIVE,
+ HOME_AUTHENTICATING_FOR_ACQUIRE);
+}
+
+static inline bool HOME_STATE_MAY_RETRY_DEACTIVATE(HomeState state) {
+ /* Indicates when to leave the deactivate retry timer active */
+ return IN_SET(state,
+ HOME_ACTIVE,
+ HOME_LINGERING,
+ HOME_DEACTIVATING,
+ HOME_LOCKING,
+ HOME_UNLOCKING,
+ HOME_UNLOCKING_FOR_ACQUIRE,
+ HOME_UPDATING_WHILE_ACTIVE,
+ HOME_RESIZING_WHILE_ACTIVE,
+ HOME_PASSWD_WHILE_ACTIVE,
+ HOME_AUTHENTICATING_WHILE_ACTIVE,
+ HOME_AUTHENTICATING_FOR_ACQUIRE);
+}
+
struct Home {
Manager *manager;
char *user_name;
/* Used to coalesce bus PropertiesChanged events */
sd_event_source *deferred_change_event_source;
+
+ /* An fd to the top-level home directory we keep while logged in, to keep the dir busy */
+ int pin_fd;
+
+ /* A time event used to repeatedly try to unmount home dir after use if it didn't work on first try */
+ sd_event_source *retry_deactivate_event_source;
+
+ /* An fd that locks the backing file of LUKS home dirs with a BSD lock. */
+ int luks_lock_fd;
};
int home_new(Manager *m, UserRecord *hr, const char *sysfs, Home **ret);
HomeState home_get_state(Home *h);
-void home_process_notify(Home *h, char **l);
+void home_process_notify(Home *h, char **l, int fd);
int home_killall(Home *h);
m->inotify_event_source = sd_event_source_disable_unref(m->inotify_event_source);
m->scan_slash_home = false;
- if (statfs("/home/", &sfs) < 0) {
+ if (statfs(get_home_root(), &sfs) < 0) {
log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
- "Failed to statfs() /home/ directory, disabling automatic scanning.");
+ "Failed to statfs() %s directory, disabling automatic scanning.", get_home_root());
return;
}
if (is_network_fs(&sfs)) {
- log_info("/home/ is a network file system, disabling automatic scanning.");
+ log_info("%s is a network file system, disabling automatic scanning.", get_home_root());
return;
}
if (is_fs_type(&sfs, AUTOFS_SUPER_MAGIC)) {
- log_info("/home/ is on autofs, disabling automatic scanning.");
+ log_info("%s is on autofs, disabling automatic scanning.", get_home_root());
return;
}
m->scan_slash_home = true;
- r = sd_event_add_inotify(m->event, &m->inotify_event_source, "/home/",
+ r = sd_event_add_inotify(m->event, &m->inotify_event_source, get_home_root(),
IN_CREATE|IN_CLOSE_WRITE|IN_DELETE_SELF|IN_MOVE_SELF|IN_ONLYDIR|IN_MOVED_TO|IN_MOVED_FROM|IN_DELETE,
on_home_inotify, m);
if (r < 0)
log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to create inotify watch on /home/, ignoring.");
+ "Failed to create inotify watch on %s, ignoring.", get_home_root());
(void) sd_event_source_set_description(m->inotify_event_source, "home-inotify");
+
+ log_info("Watching %s.", get_home_root());
}
static int on_home_inotify(sd_event_source *s, const struct inotify_event *event, void *userdata) {
+ _cleanup_free_ char *j = NULL;
Manager *m = userdata;
const char *e, *n;
if ((event->mask & (IN_Q_OVERFLOW|IN_MOVE_SELF|IN_DELETE_SELF|IN_IGNORED|IN_UNMOUNT)) != 0) {
if (FLAGS_SET(event->mask, IN_Q_OVERFLOW))
- log_debug("/home/ inotify queue overflow, rescanning.");
+ log_debug("%s inotify queue overflow, rescanning.", get_home_root());
else if (FLAGS_SET(event->mask, IN_MOVE_SELF))
- log_info("/home/ moved or renamed, recreating watch and rescanning.");
+ log_info("%s moved or renamed, recreating watch and rescanning.", get_home_root());
else if (FLAGS_SET(event->mask, IN_DELETE_SELF))
- log_info("/home/ deleted, recreating watch and rescanning.");
+ log_info("%s deleted, recreating watch and rescanning.", get_home_root());
else if (FLAGS_SET(event->mask, IN_UNMOUNT))
- log_info("/home/ unmounted, recreating watch and rescanning.");
+ log_info("%s unmounted, recreating watch and rescanning.", get_home_root());
else if (FLAGS_SET(event->mask, IN_IGNORED))
- log_info("/home/ watch invalidated, recreating watch and rescanning.");
+ log_info("%s watch invalidated, recreating watch and rescanning.", get_home_root());
manager_watch_home(m);
(void) manager_gc_images(m);
if (!e)
return 0;
- n = strndupa(event->name, e - event->name);
+ n = strndupa_safe(event->name, e - event->name);
if (!suitable_user_name(n))
return 0;
+ j = path_join(get_home_root(), event->name);
+ if (!j)
+ return log_oom();
+
if ((event->mask & (IN_CREATE|IN_CLOSE_WRITE|IN_MOVED_TO)) != 0) {
if (FLAGS_SET(event->mask, IN_CREATE))
- log_debug("/home/%s has been created, having a look.", event->name);
+ log_debug("%s has been created, having a look.", j);
else if (FLAGS_SET(event->mask, IN_CLOSE_WRITE))
- log_debug("/home/%s has been modified, having a look.", event->name);
+ log_debug("%s has been modified, having a look.", j);
else if (FLAGS_SET(event->mask, IN_MOVED_TO))
- log_debug("/home/%s has been moved in, having a look.", event->name);
+ log_debug("%s has been moved in, having a look.", j);
- (void) manager_assess_image(m, -1, "/home/", event->name);
+ (void) manager_assess_image(m, -1, get_home_root(), event->name);
(void) bus_manager_emit_auto_login_changed(m);
}
Home *h;
if (FLAGS_SET(event->mask, IN_DELETE))
- log_debug("/home/%s has been deleted, revalidating.", event->name);
+ log_debug("%s has been deleted, revalidating.", j);
else if (FLAGS_SET(event->mask, IN_CLOSE_WRITE))
- log_debug("/home/%s has been closed after writing, revalidating.", event->name);
+ log_debug("%s has been closed after writing, revalidating.", j);
else if (FLAGS_SET(event->mask, IN_MOVED_FROM))
- log_debug("/home/%s has been moved away, revalidating.", event->name);
+ log_debug("%s has been moved away, revalidating.", j);
h = hashmap_get(m->homes_by_name, n);
if (h) {
* comprehensive, but should cover most cases. Note that in an ideal world every user would be
* registered in NSS and avoid our own UID range, but for all other cases, it's a good idea to be
* paranoid and check quota if we can. */
- FOREACH_STRING(where, "/home/", "/tmp/", "/var/", "/var/mail/", "/var/tmp/", "/var/spool/") {
+ FOREACH_STRING(where, get_home_root(), "/tmp/", "/var/", "/var/mail/", "/var/tmp/", "/var/spool/") {
struct dqblk req;
struct stat st;
if (!m->scan_slash_home)
return 0;
- d = opendir("/home/");
+ d = opendir(get_home_root());
if (!d)
return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
- "Failed to open /home/: %m");
+ "Failed to open %s: %m", get_home_root());
- FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read /home/ directory: %m"))
- (void) manager_assess_image(m, dirfd(d), "/home", de->d_name);
+ FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read %s directory: %m", get_home_root()))
+ (void) manager_assess_image(m, dirfd(d), get_home_root(), de->d_name);
return 0;
}
return 0;
}
-static ssize_t read_datagram(int fd, struct ucred *ret_sender, void **ret) {
+static ssize_t read_datagram(
+ int fd,
+ struct ucred *ret_sender,
+ void **ret,
+ int *ret_passed_fd) {
+
+ CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(int))) control;
_cleanup_free_ void *buffer = NULL;
+ _cleanup_close_ int passed_fd = -1;
+ struct ucred *sender = NULL;
+ struct cmsghdr *cmsg;
+ struct msghdr mh;
+ struct iovec iov;
ssize_t n, m;
assert(fd >= 0);
assert(ret_sender);
assert(ret);
+ assert(ret_passed_fd);
n = next_datagram_size_fd(fd);
if (n < 0)
if (!buffer)
return -ENOMEM;
- if (ret_sender) {
- CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
- bool found_ucred = false;
- struct cmsghdr *cmsg;
- struct msghdr mh;
- struct iovec iov;
+ /* Pass one extra byte, as a size check */
+ iov = IOVEC_MAKE(buffer, n + 1);
- /* Pass one extra byte, as a size check */
- iov = IOVEC_MAKE(buffer, n + 1);
-
- mh = (struct msghdr) {
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- };
+ mh = (struct msghdr) {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
- m = recvmsg_safe(fd, &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
- if (m < 0)
- return m;
+ m = recvmsg_safe(fd, &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
+ if (m < 0)
+ return m;
+ /* Ensure the size matches what we determined before */
+ if (m != n) {
cmsg_close_all(&mh);
+ return -EMSGSIZE;
+ }
- /* Ensure the size matches what we determined before */
- if (m != n)
- return -EMSGSIZE;
+ CMSG_FOREACH(cmsg, &mh) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDENTIALS &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
+ assert(!sender);
+ sender = (struct ucred*) CMSG_DATA(cmsg);
+ }
- CMSG_FOREACH(cmsg, &mh)
- if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_CREDENTIALS &&
- cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
- memcpy(ret_sender, CMSG_DATA(cmsg), sizeof(struct ucred));
- found_ucred = true;
+ if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
+ cmsg_close_all(&mh);
+ return -EMSGSIZE;
}
- if (!found_ucred)
- *ret_sender = (struct ucred) {
- .pid = 0,
- .uid = UID_INVALID,
- .gid = GID_INVALID,
- };
- } else {
- m = recv(fd, buffer, n + 1, MSG_DONTWAIT);
- if (m < 0)
- return -errno;
-
- /* Ensure the size matches what we determined before */
- if (m != n)
- return -EMSGSIZE;
+ assert(passed_fd < 0);
+ passed_fd = *(int*) CMSG_DATA(cmsg);
+ }
}
+ if (sender)
+ *ret_sender = *sender;
+ else
+ *ret_sender = (struct ucred) UCRED_INVALID;
+
+ *ret_passed_fd = TAKE_FD(passed_fd);
+
/* For safety reasons: let's always NUL terminate. */
((char*) buffer)[n] = 0;
*ret = TAKE_PTR(buffer);
static int on_notify_socket(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_strv_free_ char **l = NULL;
_cleanup_free_ void *datagram = NULL;
- struct ucred sender;
+ _cleanup_close_ int passed_fd = -1;
+ struct ucred sender = UCRED_INVALID;
Manager *m = userdata;
ssize_t n;
Home *h;
assert(s);
assert(m);
- n = read_datagram(fd, &sender, &datagram);
+ n = read_datagram(fd, &sender, &datagram, &passed_fd);
if (IN_SET(n, -EAGAIN, -EINTR))
return 0;
if (n < 0)
if (!l)
return log_oom();
- home_process_notify(h, l);
+ home_process_notify(h, l, TAKE_FD(passed_fd));
return 0;
}
#include "strv.h"
#include "tmpfile-util.h"
-int home_prepare_cifs(
+int home_setup_cifs(
UserRecord *h,
- bool already_activated,
+ HomeSetupFlags flags,
HomeSetup *setup) {
assert(h);
assert(setup);
assert(user_record_storage(h) == USER_CIFS);
- if (already_activated)
+ if (FLAGS_SET(flags, HOME_SETUP_ALREADY_ACTIVATED))
setup->root_fd = open(user_record_home_directory(h), O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
else {
bool mounted = false;
int home_activate_cifs(
UserRecord *h,
+ HomeSetup *setup,
PasswordCache *cache,
UserRecord **ret_home) {
- _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
_cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
const char *hdo, *hd;
int r;
assert(h);
assert(user_record_storage(h) == USER_CIFS);
+ assert(setup);
assert(ret_home);
if (!h->cifs_service)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks CIFS service, refusing.");
assert_se(hdo = user_record_home_directory(h));
- hd = strdupa(hdo); /* copy the string out, since it might change later in the home record object */
+ hd = strdupa_safe(hdo); /* copy the string out, since it might change later in the home record object */
- r = home_prepare_cifs(h, false, &setup);
+ r = home_setup_cifs(h, 0, setup);
if (r < 0)
return r;
- r = home_refresh(h, &setup, NULL, cache, NULL, &new_home);
+ r = home_refresh(h, setup, NULL, cache, NULL, &new_home);
if (r < 0)
return r;
- setup.root_fd = safe_close(setup.root_fd);
+ setup->root_fd = safe_close(setup->root_fd);
r = home_move_mount(NULL, hd);
if (r < 0)
return r;
- setup.undo_mount = false;
+ setup->undo_mount = false;
log_info("Everything completed.");
return 1;
}
-int home_create_cifs(UserRecord *h, UserRecord **ret_home) {
- _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+int home_create_cifs(UserRecord *h, HomeSetup *setup, UserRecord **ret_home) {
_cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
_cleanup_(closedirp) DIR *d = NULL;
_cleanup_close_ int copy = -1;
assert(h);
assert(user_record_storage(h) == USER_CIFS);
+ assert(setup);
assert(ret_home);
if (!h->cifs_service)
return log_error_errno(errno, "Unable to detect whether /sbin/mount.cifs exists: %m");
}
- r = home_prepare_cifs(h, false, &setup);
+ r = home_setup_cifs(h, 0, setup);
if (r < 0)
return r;
- copy = fcntl(setup.root_fd, F_DUPFD_CLOEXEC, 3);
+ copy = fcntl(setup->root_fd, F_DUPFD_CLOEXEC, 3);
if (copy < 0)
return -errno;
if (errno != 0)
return log_error_errno(errno, "Failed to detect if CIFS directory is empty: %m");
- r = home_populate(h, setup.root_fd);
+ r = home_populate(h, setup->root_fd);
if (r < 0)
return r;
- r = home_sync_and_statfs(setup.root_fd, NULL);
+ r = home_sync_and_statfs(setup->root_fd, NULL);
if (r < 0)
return r;
#include "homework.h"
#include "user-record.h"
-int home_prepare_cifs(UserRecord *h, bool already_activated, HomeSetup *setup);
+int home_setup_cifs(UserRecord *h, HomeSetupFlags flags, HomeSetup *setup);
-int home_activate_cifs(UserRecord *h, PasswordCache *cache, UserRecord **ret_home);
+int home_activate_cifs(UserRecord *h, HomeSetup *setup, PasswordCache *cache, UserRecord **ret_home);
-int home_create_cifs(UserRecord *h, UserRecord **ret_home);
+int home_create_cifs(UserRecord *h, HomeSetup *setup, UserRecord **ret_home);
#include "tmpfile-util.h"
#include "umask-util.h"
-int home_prepare_directory(UserRecord *h, bool already_activated, HomeSetup *setup) {
+int home_setup_directory(UserRecord *h, HomeSetup *setup) {
assert(h);
assert(setup);
int home_activate_directory(
UserRecord *h,
+ HomeSetup *setup,
PasswordCache *cache,
UserRecord **ret_home) {
_cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *header_home = NULL;
- _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
const char *hdo, *hd, *ipo, *ip;
int r;
assert(h);
assert(IN_SET(user_record_storage(h), USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT));
+ assert(setup);
assert(ret_home);
assert_se(ipo = user_record_image_path(h));
- ip = strdupa(ipo); /* copy out, since reconciliation might cause changing of the field */
+ ip = strdupa_safe(ipo); /* copy out, since reconciliation might cause changing of the field */
assert_se(hdo = user_record_home_directory(h));
- hd = strdupa(hdo);
+ hd = strdupa_safe(hdo);
- r = home_prepare(h, false, cache, &setup, &header_home);
+ r = home_setup(h, 0, cache, setup, &header_home);
if (r < 0)
return r;
- r = home_refresh(h, &setup, header_home, cache, NULL, &new_home);
+ r = home_refresh(h, setup, header_home, cache, NULL, &new_home);
if (r < 0)
return r;
- setup.root_fd = safe_close(setup.root_fd);
+ setup->root_fd = safe_close(setup->root_fd);
/* Create mount point to mount over if necessary */
if (!path_equal(ip, hd))
int home_resize_directory(
UserRecord *h,
- bool already_activated,
+ HomeSetupFlags flags,
PasswordCache *cache,
HomeSetup *setup,
UserRecord **ret_home) {
assert(ret_home);
assert(IN_SET(user_record_storage(h), USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT));
- r = home_prepare(h, already_activated, cache, setup, NULL);
+ r = home_setup(h, flags, cache, setup, NULL);
if (r < 0)
return r;
if (r < 0)
return r;
- r = home_setup_undo(setup);
+ r = home_setup_done(setup);
if (r < 0)
return r;
#include "homework.h"
#include "user-record.h"
-int home_prepare_directory(UserRecord *h, bool already_activated, HomeSetup *setup);
-int home_activate_directory(UserRecord *h, PasswordCache *cache, UserRecord **ret_home);
+int home_setup_directory(UserRecord *h, HomeSetup *setup);
+int home_activate_directory(UserRecord *h, HomeSetup *setup, PasswordCache *cache, UserRecord **ret_home);
int home_create_directory_or_subvolume(UserRecord *h, UserRecord **ret_home);
-int home_resize_directory(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_home);
+int home_resize_directory(UserRecord *h, HomeSetupFlags flags, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_home);
return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Failed to set up home directory with provided passwords.");
}
-int home_prepare_fscrypt(
+int home_setup_fscrypt(
UserRecord *h,
- bool already_activated,
- PasswordCache *cache,
+ const PasswordCache *cache,
HomeSetup *setup) {
_cleanup_(erase_and_freep) void *volume_key = NULL;
int home_passwd_fscrypt(
UserRecord *h,
HomeSetup *setup,
- PasswordCache *cache, /* the passwords acquired via PKCS#11/FIDO2 security tokens */
+ const PasswordCache *cache, /* the passwords acquired via PKCS#11/FIDO2 security tokens */
char **effective_passwords /* new passwords */) {
_cleanup_(erase_and_freep) void *volume_key = NULL;
#include "homework.h"
#include "user-record.h"
-int home_prepare_fscrypt(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup);
+int home_setup_fscrypt(UserRecord *h, const PasswordCache *cache, HomeSetup *setup);
int home_create_fscrypt(UserRecord *h, char **effective_passwords, UserRecord **ret_home);
-int home_passwd_fscrypt(UserRecord *h, HomeSetup *setup, PasswordCache *cache, char **effective_passwords);
+int home_passwd_fscrypt(UserRecord *h, HomeSetup *setup, const PasswordCache *cache, char **effective_passwords);
#include <sys/mount.h>
#include <sys/xattr.h>
+#if HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
+#include "sd-daemon.h"
+
#include "blkid-util.h"
#include "blockdev-util.h"
#include "btrfs-util.h"
#include "chattr-util.h"
#include "dm-util.h"
+#include "env-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
return log_oom();
r = -ENOKEY;
- FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, passwords) {
+ FOREACH_POINTER(list,
+ cache ? cache->pkcs11_passwords : NULL,
+ cache ? cache->fido2_passwords : NULL,
+ passwords) {
r = luks_try_passwords(cd, list, vk, &vks);
if (r != -ENOKEY)
break;
static int luks_open(
const char *dm_name,
char **passwords,
- PasswordCache *cache,
+ const PasswordCache *cache,
struct crypt_device **ret,
sd_id128_t *ret_found_uuid,
void **ret_volume_key,
return log_oom();
r = -ENOKEY;
- FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, passwords) {
+ FOREACH_POINTER(list,
+ cache ? cache->pkcs11_passwords : NULL,
+ cache ? cache->fido2_passwords : NULL,
+ passwords) {
r = luks_try_passwords(cd, list, vk, &vks);
if (r != -ENOKEY)
break;
e = strchr(cipher_mode, '-');
if (e)
- cipher_mode = strndupa(cipher_mode, e - cipher_mode);
+ cipher_mode = strndupa_safe(cipher_mode, e - cipher_mode);
r = sym_crypt_get_volume_key_size(cd);
if (r <= 0)
return run_fallocate(backing_fd, NULL);
}
-int home_prepare_luks(
+static int lock_image_fd(int image_fd, const char *ip) {
+ int r;
+
+ /* If the $SYSTEMD_LUKS_LOCK environment variable is set we'll take an exclusive BSD lock on the
+ * image file, and send it to our parent. homed will keep it open to ensure no other instance of
+ * homed (across the network or such) will also mount the file. */
+
+ r = getenv_bool("SYSTEMD_LUKS_LOCK");
+ if (r == -ENXIO)
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse $SYSTEMD_LUKS_LOCK environment variable: %m");
+ if (r > 0) {
+ struct stat st;
+
+ if (fstat(image_fd, &st) < 0)
+ return log_error_errno(errno, "Failed to stat image file: %m");
+ if (S_ISBLK(st.st_mode)) {
+ /* Locking block devices doesn't really make sense, as this might interfear with
+ * udev's workings, and these locks aren't network propagated anyway, hence not what
+ * we are after here. */
+ log_debug("Not locking image file '%s', since it's a block device.", ip);
+ return 0;
+ }
+ r = stat_verify_regular(&st);
+ if (r < 0)
+ return log_error_errno(r, "Image file to lock is not a regular file: %m");
+
+ if (flock(image_fd, LOCK_EX|LOCK_NB) < 0) {
+
+ if (errno == EWOULDBLOCK)
+ log_error_errno(errno, "Image file '%s' already locked, can't use.", ip);
+ else
+ log_error_errno(errno, "Failed to lock image file '%s': %m", ip);
+
+ return errno != EWOULDBLOCK ? -errno : -EADDRINUSE; /* Make error recognizable */
+ }
+
+ log_info("Successfully locked image file '%s'.", ip);
+
+ /* Now send it to our parent to keep safe while the home dir is active */
+ r = sd_pid_notify_with_fds(0, false, "SYSTEMD_LUKS_LOCK_FD=1", &image_fd, 1);
+ if (r < 0)
+ log_warning_errno(r, "Failed to send LUKS lock fd to parent, ignoring: %m");
+ }
+
+ return 0;
+}
+
+static int open_image_file(
+ UserRecord *h,
+ const char *force_image_path,
+ struct stat *ret_stat) {
+
+ _cleanup_close_ int image_fd = -1;
+ struct stat st;
+ const char *ip;
+ int r;
+
+ ip = force_image_path ?: user_record_image_path(h);
+
+ image_fd = open(ip, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (image_fd < 0)
+ return log_error_errno(errno, "Failed to open image file %s: %m", ip);
+
+ if (fstat(image_fd, &st) < 0)
+ return log_error_errno(errno, "Failed to fstat() image file: %m");
+ if (!S_ISREG(st.st_mode) && !S_ISBLK(st.st_mode))
+ return log_error_errno(
+ S_ISDIR(st.st_mode) ? SYNTHETIC_ERRNO(EISDIR) : SYNTHETIC_ERRNO(EBADFD),
+ "Image file %s is not a regular file or block device: %m", ip);
+
+ r = lock_image_fd(image_fd, ip);
+ if (r < 0)
+ return r;
+
+ if (ret_stat)
+ *ret_stat = st;
+
+ return TAKE_FD(image_fd);
+}
+
+int home_setup_luks(
UserRecord *h,
- bool already_activated,
+ HomeSetupFlags flags,
const char *force_image_path,
PasswordCache *cache,
HomeSetup *setup,
_cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
_cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
_cleanup_(erase_and_freep) void *volume_key = NULL;
- _cleanup_close_ int root_fd = -1, image_fd = -1;
+ _cleanup_close_ int opened_image_fd = -1, root_fd = -1;
bool dm_activated = false, mounted = false;
size_t volume_key_size = 0;
bool marked_dirty = false;
uint64_t offset, size;
- int r;
+ int r, image_fd = -1;
assert(h);
assert(setup);
if (r < 0)
return r;
- if (already_activated) {
+ if (FLAGS_SET(flags, HOME_SETUP_ALREADY_ACTIVATED)) {
struct loop_info64 info;
const char *n;
offset *= 512U;
}
} else {
+#if HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
+#endif
+
offset = info.lo_offset;
size = info.lo_sizelimit;
}
root_fd = open(user_record_home_directory(h), O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
if (root_fd < 0) {
- r = log_error_errno(r, "Failed to open home directory: %m");
+ r = log_error_errno(errno, "Failed to open home directory: %m");
goto fail;
}
} else {
if (!subdir)
return log_oom();
- image_fd = open(ip, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
- if (image_fd < 0)
- return log_error_errno(errno, "Failed to open image file %s: %m", ip);
+ /* Reuse the image fd if it has already been opened by an earlier step */
+ if (setup->image_fd < 0) {
+ opened_image_fd = open_image_file(h, force_image_path, &st);
+ if (opened_image_fd < 0)
+ return opened_image_fd;
- if (fstat(image_fd, &st) < 0)
- return log_error_errno(errno, "Failed to fstat() image file: %m");
- if (!S_ISREG(st.st_mode) && !S_ISBLK(st.st_mode))
- return log_error_errno(
- S_ISDIR(st.st_mode) ? SYNTHETIC_ERRNO(EISDIR) : SYNTHETIC_ERRNO(EBADFD),
- "Image file %s is not a regular file or block device: %m", ip);
+ image_fd = opened_image_fd;
+ } else
+ image_fd = setup->image_fd;
r = luks_validate(image_fd, user_record_user_name_and_realm(h), h->partition_uuid, &found_partition_uuid, &offset, &size);
if (r < 0)
root_fd = open(subdir, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
if (root_fd < 0) {
- r = log_error_errno(r, "Failed to open home directory: %m");
+ r = log_error_errno(errno, "Failed to open home directory: %m");
goto fail;
}
if (user_record_luks_discard(h))
(void) run_fitrim(root_fd);
- setup->image_fd = TAKE_FD(image_fd);
+ /* And now, fill in everything */
+ if (opened_image_fd >= 0) {
+ safe_close(setup->image_fd);
+ setup->image_fd = TAKE_FD(opened_image_fd);
+ }
+
setup->do_offline_fallocate = !(setup->do_offline_fitrim = user_record_luks_offline_discard(h));
setup->do_mark_clean = marked_dirty;
}
int home_activate_luks(
UserRecord *h,
+ HomeSetup *setup,
PasswordCache *cache,
UserRecord **ret_home) {
_cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *luks_home_record = NULL;
- _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
uint64_t host_size, encrypted_size;
const char *hdo, *hd;
struct statfs sfs;
assert(h);
assert(user_record_storage(h) == USER_LUKS);
+ assert(setup);
assert(ret_home);
r = dlopen_cryptsetup();
return r;
assert_se(hdo = user_record_home_directory(h));
- hd = strdupa(hdo); /* copy the string out, since it might change later in the home record object */
+ hd = strdupa_safe(hdo); /* copy the string out, since it might change later in the home record object */
- r = make_dm_names(h->user_name, &setup.dm_name, &setup.dm_node);
+ r = home_get_state_luks(h, setup);
if (r < 0)
return r;
+ if (r > 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Device mapper device %s already exists, refusing.", setup->dm_node);
- r = access(setup.dm_node, F_OK);
- if (r < 0) {
- if (errno != ENOENT)
- return log_error_errno(errno, "Failed to determine whether %s exists: %m", setup.dm_node);
- } else
- return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Device mapper device %s already exists, refusing.", setup.dm_node);
-
- r = home_prepare_luks(
+ r = home_setup_luks(
h,
- false,
+ 0,
NULL,
cache,
- &setup,
+ setup,
&luks_home_record);
if (r < 0)
return r;
- r = block_get_size_by_fd(setup.loop->fd, &host_size);
+ r = block_get_size_by_fd(setup->loop->fd, &host_size);
if (r < 0)
return log_error_errno(r, "Failed to get loopback block device size: %m");
- r = block_get_size_by_path(setup.dm_node, &encrypted_size);
+ r = block_get_size_by_path(setup->dm_node, &encrypted_size);
if (r < 0)
return log_error_errno(r, "Failed to get LUKS block device size: %m");
r = home_refresh(
h,
- &setup,
+ setup,
luks_home_record,
cache,
&sfs,
if (r < 0)
return r;
- r = home_extend_embedded_identity(new_home, h, &setup);
+ r = home_extend_embedded_identity(new_home, h, setup);
if (r < 0)
return r;
- setup.root_fd = safe_close(setup.root_fd);
+ setup->root_fd = safe_close(setup->root_fd);
r = home_move_mount(user_record_user_name_and_realm(h), hd);
if (r < 0)
return r;
- setup.undo_mount = false;
- setup.do_offline_fitrim = false;
+ setup->undo_mount = false;
+ setup->do_offline_fitrim = false;
- loop_device_relinquish(setup.loop);
+ loop_device_relinquish(setup->loop);
- r = sym_crypt_deactivate_by_name(NULL, setup.dm_name, CRYPT_DEACTIVATE_DEFERRED);
+ r = sym_crypt_deactivate_by_name(NULL, setup->dm_name, CRYPT_DEACTIVATE_DEFERRED);
if (r < 0)
log_warning_errno(r, "Failed to relinquish DM device, ignoring: %m");
- setup.undo_dm = false;
- setup.do_offline_fallocate = false;
- setup.do_mark_clean = false;
+ setup->undo_dm = false;
+ setup->do_offline_fallocate = false;
+ setup->do_mark_clean = false;
log_info("Everything completed.");
STRV_FOREACH(pp, effective_passwords) {
- if (strv_contains(cache->pkcs11_passwords, *pp) ||
- strv_contains(cache->fido2_passwords, *pp)) {
+ if (password_cache_contains(cache, *pp)) { /* is this a fido2 or pkcs11 password? */
log_debug("Using minimal PBKDF for slot %i", slot);
r = sym_crypt_set_pbkdf_type(cd, &minimal_pbkdf);
} else {
int home_create_luks(
UserRecord *h,
- PasswordCache *cache,
+ const PasswordCache *cache,
char **effective_passwords,
UserRecord **ret_home) {
return r;
}
-int home_validate_update_luks(UserRecord *h, HomeSetup *setup) {
+int home_get_state_luks(UserRecord *h, HomeSetup *setup) {
_cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
int r;
int home_resize_luks(
UserRecord *h,
- bool already_activated,
+ HomeSetupFlags flags,
PasswordCache *cache,
HomeSetup *setup,
UserRecord **ret_home) {
uint64_t old_image_size, new_image_size, old_fs_size, new_fs_size, crypto_offset, new_partition_size;
_cleanup_(user_record_unrefp) UserRecord *header_home = NULL, *embedded_home = NULL, *new_home = NULL;
_cleanup_(fdisk_unref_tablep) struct fdisk_table *table = NULL;
+ _cleanup_close_ int opened_image_fd = -1;
_cleanup_free_ char *whole_disk = NULL;
- _cleanup_close_ int image_fd = -1;
+ int r, resize_type, image_fd = -1;
sd_id128_t disk_uuid;
const char *ip, *ipo;
struct statfs sfs;
struct stat st;
- int r, resize_type;
assert(h);
assert(user_record_storage(h) == USER_LUKS);
return r;
assert_se(ipo = user_record_image_path(h));
- ip = strdupa(ipo); /* copy out since original might change later in home record object */
+ ip = strdupa_safe(ipo); /* copy out since original might change later in home record object */
- image_fd = open(ip, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
- if (image_fd < 0)
- return log_error_errno(errno, "Failed to open image file %s: %m", ip);
+ if (setup->image_fd < 0) {
+ setup->image_fd = open_image_file(h, NULL, &st);
+ if (setup->image_fd < 0)
+ return setup->image_fd;
+ } else {
+ if (fstat(setup->image_fd, &st) < 0)
+ return log_error_errno(errno, "Failed to stat image file %s: %m", ip);
+ }
+
+ image_fd = setup->image_fd;
- if (fstat(image_fd, &st) < 0)
- return log_error_errno(errno, "Failed to stat image file %s: %m", ip);
if (S_ISBLK(st.st_mode)) {
dev_t parent;
if (r < 0)
return log_error_errno(r, "Failed to derive whole disk path for %s: %m", ip);
- safe_close(image_fd);
-
- image_fd = open(whole_disk, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
- if (image_fd < 0)
+ opened_image_fd = open(whole_disk, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (opened_image_fd < 0)
return log_error_errno(errno, "Failed to open whole block device %s: %m", whole_disk);
+ image_fd = opened_image_fd;
+
if (fstat(image_fd, &st) < 0)
return log_error_errno(errno, "Failed to stat whole block device %s: %m", whole_disk);
if (!S_ISBLK(st.st_mode))
new_image_size = new_image_size_rounded;
}
- r = home_prepare_luks(h, already_activated, whole_disk, cache, setup, &header_home);
+ r = home_setup_luks(h, flags, whole_disk, cache, setup, &header_home);
if (r < 0)
return r;
resize_type = can_resize_fs(setup->root_fd, old_fs_size, new_fs_size);
if (resize_type < 0)
return resize_type;
- if (resize_type == CAN_RESIZE_OFFLINE && already_activated)
+ if (resize_type == CAN_RESIZE_OFFLINE && FLAGS_SET(flags, HOME_SETUP_ALREADY_ACTIVATED))
return log_error_errno(SYNTHETIC_ERRNO(ETXTBSY), "File systems of this type can only be resized offline, but is currently online.");
log_info("Ready to resize image size %s → %s, partition size %s → %s, file system size %s → %s.",
if (r > 0)
log_info("Growing of partition completed.");
- if (ioctl(image_fd, BLKRRPART, 0) < 0)
+ if (S_ISBLK(st.st_mode) && ioctl(image_fd, BLKRRPART, 0) < 0)
log_debug_errno(errno, "BLKRRPART failed on block device, ignoring: %m");
/* Tell LUKS about the new bigger size too */
}
/* Now resize the file system */
- if (resize_type == CAN_RESIZE_ONLINE)
+ if (resize_type == CAN_RESIZE_ONLINE) {
r = resize_fs(setup->root_fd, new_fs_size, NULL);
- else
+ if (r < 0)
+ return log_error_errno(r, "Failed to resize file system: %m");
+ } else {
r = ext4_offline_resize_fs(setup, new_fs_size, user_record_luks_discard(h), user_record_mount_flags(h));
- if (r < 0)
- return log_error_errno(r, "Failed to resize file system: %m");
+ if (r < 0)
+ return r;
+ }
log_info("File system resizing completed.");
if (r > 0)
log_info("Shrinking of partition completed.");
- if (ioctl(image_fd, BLKRRPART, 0) < 0)
+ if (S_ISBLK(st.st_mode) && ioctl(image_fd, BLKRRPART, 0) < 0)
log_debug_errno(errno, "BLKRRPART failed on block device, ignoring: %m");
} else {
r = home_store_embedded_identity(new_home, setup->root_fd, h->uid, embedded_home);
if (r < 0)
return r;
- r = home_setup_undo(setup);
+ r = home_setup_done(setup);
if (r < 0)
return r;
int home_passwd_luks(
UserRecord *h,
HomeSetup *setup,
- PasswordCache *cache, /* the passwords acquired via PKCS#11/FIDO2 security tokens */
+ const PasswordCache *cache, /* the passwords acquired via PKCS#11/FIDO2 security tokens */
char **effective_passwords /* new passwords */) {
size_t volume_key_size, max_key_slots, n_effective;
return log_oom();
r = -ENOKEY;
- FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, h->password) {
+ FOREACH_POINTER(list,
+ cache ? cache->pkcs11_passwords : NULL,
+ cache ? cache->fido2_passwords : NULL,
+ h->password) {
+
r = luks_try_passwords(setup->crypt_device, list, volume_key, &volume_key_size);
if (r != -ENOKEY)
break;
continue;
}
- if (strv_contains(cache->pkcs11_passwords, effective_passwords[i]) ||
- strv_contains(cache->fido2_passwords, effective_passwords[i])) {
+ if (password_cache_contains(cache, effective_passwords[i])) { /* Is this a FIDO2 or PKCS#11 password? */
log_debug("Using minimal PBKDF for slot %zu", i);
r = sym_crypt_set_pbkdf_type(setup->crypt_device, &minimal_pbkdf);
} else {
return -ENOKEY;
}
-int home_unlock_luks(UserRecord *h, PasswordCache *cache) {
+int home_unlock_luks(UserRecord *h, const PasswordCache *cache) {
_cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
_cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
char **list;
cryptsetup_enable_logging(cd);
r = -ENOKEY;
- FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, h->password) {
+ FOREACH_POINTER(list,
+ cache ? cache->pkcs11_passwords : NULL,
+ cache ? cache->fido2_passwords : NULL,
+ h->password) {
r = luks_try_resume(cd, dm_name, list);
if (r != -ENOKEY)
break;
#include "homework.h"
#include "user-record.h"
-int home_prepare_luks(UserRecord *h, bool already_activated, const char *force_image_path, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_luks_home);
+int home_setup_luks(UserRecord *h, HomeSetupFlags flags, const char *force_image_path, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_luks_home);
-int home_activate_luks(UserRecord *h, PasswordCache *cache, UserRecord **ret_home);
+int home_activate_luks(UserRecord *h, HomeSetup *setup, PasswordCache *cache, UserRecord **ret_home);
int home_deactivate_luks(UserRecord *h);
int home_trim_luks(UserRecord *h);
int home_store_header_identity_luks(UserRecord *h, HomeSetup *setup, UserRecord *old_home);
-int home_create_luks(UserRecord *h, PasswordCache *cache, char **effective_passwords, UserRecord **ret_home);
+int home_create_luks(UserRecord *h, const PasswordCache *cache, char **effective_passwords, UserRecord **ret_home);
-int home_validate_update_luks(UserRecord *h, HomeSetup *setup);
+int home_get_state_luks(UserRecord *h, HomeSetup *setup);
-int home_resize_luks(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_home);
+int home_resize_luks(UserRecord *h, HomeSetupFlags flags, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_home);
-int home_passwd_luks(UserRecord *h, HomeSetup *setup, PasswordCache *cache, char **effective_passwords);
+int home_passwd_luks(UserRecord *h, HomeSetup *setup, const PasswordCache *cache, char **effective_passwords);
int home_lock_luks(UserRecord *h);
-int home_unlock_luks(UserRecord *h, PasswordCache *cache);
+int home_unlock_luks(UserRecord *h, const PasswordCache *cache);
static inline uint64_t luks_volume_key_size_convert(struct crypt_device *cd) {
int k;
#include "rm-rf.h"
#include "stat-util.h"
#include "strv.h"
+#include "sync-util.h"
#include "tmpfile-util.h"
#include "user-util.h"
#include "virt.h"
return 0;
}
-int home_setup_undo(HomeSetup *setup) {
+static void drop_caches_now(void) {
+ int r;
+
+ /* Drop file system caches now. See https://www.kernel.org/doc/Documentation/sysctl/vm.txt for
+ * details. We write "2" into /proc/sys/vm/drop_caches to ensure dentries/inodes are flushed, but not
+ * more. */
+
+ r = write_string_file("/proc/sys/vm/drop_caches", "2\n", WRITE_STRING_FILE_DISABLE_BUFFER);
+ if (r < 0)
+ log_warning_errno(r, "Failed to drop caches, ignoring: %m");
+ else
+ log_debug("Dropped caches.");
+}
+
+int home_setup_done(HomeSetup *setup) {
int r = 0, q;
assert(setup);
r = q;
}
+ if (syncfs(setup->root_fd) < 0)
+ log_debug_errno(errno, "Failed to synchronize home directory, ignoring: %m");
+
setup->root_fd = safe_close(setup->root_fd);
}
setup->crypt_device = NULL;
}
- explicit_bzero_safe(setup->volume_key, setup->volume_key_size);
- setup->volume_key = mfree(setup->volume_key);
+ setup->volume_key = erase_and_free(setup->volume_key);
setup->volume_key_size = 0;
+ if (setup->do_drop_caches)
+ drop_caches_now();
+
return r;
}
-int home_prepare(
+int home_setup(
UserRecord *h,
- bool already_activated,
+ HomeSetupFlags flags,
PasswordCache *cache,
HomeSetup *setup,
UserRecord **ret_header_home) {
/* Makes a home directory accessible (through the root_fd file descriptor, not by path!). */
+ if (!FLAGS_SET(flags, HOME_SETUP_ALREADY_ACTIVATED)) /* If we set up the directory, we should also drop caches once we are done */
+ setup->do_drop_caches = setup->do_drop_caches || user_record_drop_caches(h);
+
switch (user_record_storage(h)) {
case USER_LUKS:
- return home_prepare_luks(h, already_activated, NULL, cache, setup, ret_header_home);
+ return home_setup_luks(h, flags, NULL, cache, setup, ret_header_home);
case USER_SUBVOLUME:
case USER_DIRECTORY:
- r = home_prepare_directory(h, already_activated, setup);
+ r = home_setup_directory(h, setup);
break;
case USER_FSCRYPT:
- r = home_prepare_fscrypt(h, already_activated, cache, setup);
+ r = home_setup_fscrypt(h, cache, setup);
break;
case USER_CIFS:
- r = home_prepare_cifs(h, already_activated, setup);
+ r = home_setup_cifs(h, flags, setup);
break;
default:
}
static int home_activate(UserRecord *h, UserRecord **ret_home) {
- _cleanup_(password_cache_free) PasswordCache cache = {};
+ _cleanup_(home_setup_done) HomeSetup setup = HOME_SETUP_INIT;
_cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
+ _cleanup_(password_cache_free) PasswordCache cache = {};
int r;
assert(h);
switch (user_record_storage(h)) {
case USER_LUKS:
- r = home_activate_luks(h, &cache, &new_home);
+ r = home_activate_luks(h, &setup, &cache, &new_home);
if (r < 0)
return r;
case USER_SUBVOLUME:
case USER_DIRECTORY:
case USER_FSCRYPT:
- r = home_activate_directory(h, &cache, &new_home);
+ r = home_activate_directory(h, &setup, &cache, &new_home);
if (r < 0)
return r;
break;
case USER_CIFS:
- r = home_activate_cifs(h, &cache, &new_home);
+ r = home_activate_cifs(h, &setup, &cache, &new_home);
if (r < 0)
return r;
return r;
}
+ /* Sync explicitly, so that the drop caches logic below can work as documented */
+ r = syncfs_path(AT_FDCWD, user_record_home_directory(h));
+ if (r < 0)
+ log_debug_errno(r, "Failed to synchronize home directory, ignoring: %m");
+ else
+ log_info("Syncing completed.");
+
if (umount2(user_record_home_directory(h), UMOUNT_NOFOLLOW | (force ? MNT_FORCE|MNT_DETACH : 0)) < 0)
return log_error_errno(errno, "Failed to unmount %s: %m", user_record_home_directory(h));
if (!done)
return log_error_errno(SYNTHETIC_ERRNO(ENOEXEC), "Home is not active.");
+ if (user_record_drop_caches(h))
+ drop_caches_now();
+
log_info("Everything completed.");
return 0;
}
if (r < 0)
return log_error_errno(r, "Failed to determine whether we are in a container: %m");
if (r == 0) {
- r = path_is_encrypted("/home");
+ r = path_is_encrypted(get_home_root());
if (r > 0)
- log_info("/home is encrypted, not using '%s' storage, in order to avoid double encryption.", user_storage_to_string(USER_LUKS));
+ log_info("%s is encrypted, not using '%s' storage, in order to avoid double encryption.", get_home_root(), user_storage_to_string(USER_LUKS));
else {
if (r < 0)
- log_warning_errno(r, "Failed to determine if /home is encrypted, ignoring: %m");
+ log_warning_errno(r, "Failed to determine if %s is encrypted, ignoring: %m", get_home_root());
r = dlopen_cryptsetup();
if (r < 0)
} else
log_info("Running in container, not using '%s' storage.", user_storage_to_string(USER_LUKS));
- r = path_is_fs_type("/home", BTRFS_SUPER_MAGIC);
+ r = path_is_fs_type(get_home_root(), BTRFS_SUPER_MAGIC);
if (r < 0)
- log_warning_errno(r, "Failed to determine file system of /home, ignoring: %m");
+ log_warning_errno(r, "Failed to determine file system of %s, ignoring: %m", get_home_root());
if (r > 0) {
- log_info("/home is on btrfs, using '%s' as storage.", user_storage_to_string(USER_SUBVOLUME));
+ log_info("%s is on btrfs, using '%s' as storage.", get_home_root(), user_storage_to_string(USER_SUBVOLUME));
*ret = USER_SUBVOLUME;
} else {
- log_info("/home is on simple file system, using '%s' as storage.", user_storage_to_string(USER_DIRECTORY));
+ log_info("%s is on simple file system, using '%s' as storage.", get_home_root(), user_storage_to_string(USER_DIRECTORY));
*ret = USER_DIRECTORY;
}
static int home_create(UserRecord *h, UserRecord **ret_home) {
_cleanup_(strv_free_erasep) char **effective_passwords = NULL;
+ _cleanup_(home_setup_done) HomeSetup setup = HOME_SETUP_INIT;
_cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
_cleanup_(password_cache_free) PasswordCache cache = {};
UserStorage new_storage = _USER_STORAGE_INVALID;
break;
case USER_CIFS:
- r = home_create_cifs(h, &new_home);
+ r = home_create_cifs(h, &setup, &new_home);
break;
default:
if (unlink(ip) < 0) {
if (errno != ENOENT)
return log_error_errno(errno, "Failed to remove %s: %m", ip);
- } else
+ } else {
+ _cleanup_free_ char *parent = NULL;
+
deleted = true;
+ r = path_extract_directory(ip, &parent);
+ if (r < 0)
+ log_debug_errno(r, "Failed to determine parent directory of '%s': %m", ip);
+ else {
+ r = fsync_path_at(AT_FDCWD, parent);
+ if (r < 0)
+ log_debug_errno(r, "Failed to synchronize disk after deleting '%s', ignoring: %m", ip);
+ }
+ }
+
} else if (S_ISBLK(st.st_mode))
log_info("Not removing file system on block device %s.", ip);
else
case USER_FSCRYPT:
assert(ip);
- r = rm_rf(ip, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+ r = rm_rf(ip, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME|REMOVE_SYNCFS);
if (r < 0) {
if (r != -ENOENT)
return log_warning_errno(r, "Failed to remove %s: %m", ip);
deleted = true;
}
- if (deleted)
+ if (deleted) {
+ if (user_record_drop_caches(h))
+ drop_caches_now();
+
log_info("Everything completed.");
- else
+ } else
return log_notice_errno(SYNTHETIC_ERRNO(EALREADY),
"Nothing to remove.");
return 0;
}
-static int home_validate_update(UserRecord *h, HomeSetup *setup) {
+static int home_validate_update(UserRecord *h, HomeSetup *setup, HomeSetupFlags *flags) {
bool has_mount = false;
int r;
break;
case USER_LUKS: {
- r = home_validate_update_luks(h, setup);
+ r = home_get_state_luks(h, setup);
if (r < 0)
return r;
if ((r > 0) != has_mount)
assert_not_reached();
}
+ if (flags)
+ SET_FLAG(*flags, HOME_SETUP_ALREADY_ACTIVATED, has_mount);
+
return has_mount; /* return true if the home record is already active */
}
static int home_update(UserRecord *h, UserRecord **ret) {
_cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *header_home = NULL, *embedded_home = NULL;
- _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+ _cleanup_(home_setup_done) HomeSetup setup = HOME_SETUP_INIT;
_cleanup_(password_cache_free) PasswordCache cache = {};
- bool already_activated = false;
+ HomeSetupFlags flags = 0;
int r;
assert(h);
return r;
assert(r > 0); /* Insist that a password was verified */
- r = home_validate_update(h, &setup);
+ r = home_validate_update(h, &setup, &flags);
if (r < 0)
return r;
- already_activated = r > 0;
-
- r = home_prepare(h, already_activated, &cache, &setup, &header_home);
+ r = home_setup(h, flags, &cache, &setup, &header_home);
if (r < 0)
return r;
if (r < 0)
return r;
- r = home_setup_undo(&setup);
+ r = home_setup_done(&setup);
if (r < 0)
return r;
}
static int home_resize(UserRecord *h, UserRecord **ret) {
- _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+ _cleanup_(home_setup_done) HomeSetup setup = HOME_SETUP_INIT;
_cleanup_(password_cache_free) PasswordCache cache = {};
- bool already_activated = false;
+ HomeSetupFlags flags = 0;
int r;
assert(h);
return r;
assert(r > 0); /* Insist that a password was verified */
- r = home_validate_update(h, &setup);
+ r = home_validate_update(h, &setup, &flags);
if (r < 0)
return r;
- already_activated = r > 0;
-
switch (user_record_storage(h)) {
case USER_LUKS:
- return home_resize_luks(h, already_activated, &cache, &setup, ret);
+ return home_resize_luks(h, flags, &cache, &setup, ret);
case USER_DIRECTORY:
case USER_SUBVOLUME:
case USER_FSCRYPT:
- return home_resize_directory(h, already_activated, &cache, &setup, ret);
+ return home_resize_directory(h, flags, &cache, &setup, ret);
default:
return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Resizing home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
static int home_passwd(UserRecord *h, UserRecord **ret_home) {
_cleanup_(user_record_unrefp) UserRecord *header_home = NULL, *embedded_home = NULL, *new_home = NULL;
_cleanup_(strv_free_erasep) char **effective_passwords = NULL;
- _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+ _cleanup_(home_setup_done) HomeSetup setup = HOME_SETUP_INIT;
_cleanup_(password_cache_free) PasswordCache cache = {};
- bool already_activated = false;
+ HomeSetupFlags flags = 0;
int r;
assert(h);
if (r < 0)
return r;
- r = home_validate_update(h, &setup);
+ r = home_validate_update(h, &setup, &flags);
if (r < 0)
return r;
- already_activated = r > 0;
-
- r = home_prepare(h, already_activated, &cache, &setup, &header_home);
+ r = home_setup(h, flags, &cache, &setup, &header_home);
if (r < 0)
return r;
if (r < 0)
return r;
- r = home_setup_undo(&setup);
+ r = home_setup_done(&setup);
if (r < 0)
return r;
static int home_inspect(UserRecord *h, UserRecord **ret_home) {
_cleanup_(user_record_unrefp) UserRecord *header_home = NULL, *new_home = NULL;
- _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+ _cleanup_(home_setup_done) HomeSetup setup = HOME_SETUP_INIT;
_cleanup_(password_cache_free) PasswordCache cache = {};
- bool already_activated = false;
+ HomeSetupFlags flags = 0;
int r;
assert(h);
if (r < 0)
return r;
- r = home_validate_update(h, &setup);
+ r = home_validate_update(h, &setup, &flags);
if (r < 0)
return r;
- already_activated = r > 0;
-
- r = home_prepare(h, already_activated, &cache, &setup, &header_home);
+ r = home_setup(h, flags, &cache, &setup, &header_home);
if (r < 0)
return r;
if (r < 0)
return r;
- r = home_setup_undo(&setup);
+ r = home_setup_done(&setup);
if (r < 0)
return r;
* ENOEXEC → file system is currently not active
* ENOSPC → not enough disk space for operation
* EKEYREVOKED → user record has not suitable hashed password or pkcs#11 entry, we cannot authenticate
+ * EADDRINUSE → home image is already used elsewhere (lock taken)
*/
if (streq(argv[1], "activate"))
#include "sd-id128.h"
#include "loop-util.h"
+#include "strv.h"
#include "user-record.h"
#include "user-record-util.h"
bool do_offline_fitrim;
bool do_offline_fallocate;
bool do_mark_clean;
+ bool do_drop_caches;
uint64_t partition_offset;
uint64_t partition_size;
} HomeSetup;
typedef struct PasswordCache {
- /* Decoding passwords from security tokens is expensive and typically requires user interaction, hence cache any we already figured out. */
+ /* Decoding passwords from security tokens is expensive and typically requires user interaction,
+ * hence cache any we already figured out. */
char **pkcs11_passwords;
char **fido2_passwords;
} PasswordCache;
void password_cache_free(PasswordCache *cache);
+static inline bool password_cache_contains(const PasswordCache *cache, const char *p) {
+ if (!cache)
+ return false;
+
+ return strv_contains(cache->pkcs11_passwords, p) || strv_contains(cache->fido2_passwords, p);
+}
+
#define HOME_SETUP_INIT \
{ \
.root_fd = -1, \
.partition_size = UINT64_MAX, \
}
-int home_setup_undo(HomeSetup *setup);
+/* Various flags for the operation of setting up a home directory */
+typedef enum HomeSetupFlags {
+ HOME_SETUP_ALREADY_ACTIVATED = 1 << 0, /* Open an already activated home, rather than activate it afresh */
+} HomeSetupFlags;
+
+int home_setup_done(HomeSetup *setup);
-int home_prepare(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_header_home);
+int home_setup(UserRecord *h, HomeSetupFlags flags, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_header_home);
int home_refresh(UserRecord *h, HomeSetup *setup, UserRecord *header_home, PasswordCache *cache, struct statfs *ret_statfs, UserRecord **ret_new_home);
if (!ip)
return -ENOMEM;
- hd = path_join("/home/", user_name);
+ hd = path_join(get_home_root(), user_name);
if (!hd)
return -ENOMEM;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "integrity-util.h"
+
+#include "extract-word.h"
+#include "fileio.h"
+#include "path-util.h"
+#include "percent-util.h"
+
+
+static int supported_integrity_algorithm(char *user_supplied) {
+ if (!STR_IN_SET(user_supplied, "crc32", "crc32c", "sha1", "sha256", "hmac-sha256"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported integrity algorithm (%s)", user_supplied);
+ return 0;
+}
+
+int parse_integrity_options(
+ const char *options,
+ uint32_t *ret_activate_flags,
+ int *ret_percent,
+ usec_t *ret_commit_time,
+ char **ret_data_device,
+ char **ret_integrity_alg) {
+ int r;
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ char *val;
+
+ r = extract_first_word(&options, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS | EXTRACT_UNESCAPE_SEPARATORS);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse options: %m");
+ if (r == 0)
+ break;
+ else if (streq(word, "allow-discards")) {
+ if (ret_activate_flags)
+ *ret_activate_flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
+ } else if ((val = startswith(word, "journal-watermark="))) {
+ r = parse_percent(val);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse journal-watermark value or value out of range (%s)", val);
+ if (ret_percent)
+ *ret_percent = r;
+ } else if ((val = startswith(word, "journal-commit-time="))) {
+ usec_t tmp_commit_time;
+ r = parse_sec(val, &tmp_commit_time);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse journal-commit-time value (%s)", val);
+ if (ret_commit_time)
+ *ret_commit_time = tmp_commit_time;
+ } else if ((val = startswith(word, "data-device="))) {
+ r = free_and_strdup(ret_data_device, val);
+ if (r < 0)
+ return log_oom();
+ } else if ((val = startswith(word, "integrity-algorithm="))) {
+ r = free_and_strdup(ret_integrity_alg, val);
+ if (r < 0)
+ return log_oom();
+ r = supported_integrity_algorithm(*ret_integrity_alg);
+ if (r < 0)
+ return r;
+ } else
+ log_warning("Encountered unknown option '%s', ignoring.", word);
+ }
+
+ return r;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdint.h>
+
+#include "cryptsetup-util.h"
+#include "time-util.h"
+
+
+int parse_integrity_options(
+ const char *options,
+ uint32_t *ret_activate_flags,
+ int *ret_percent,
+ usec_t *ret_commit_time,
+ char **ret_data_device,
+ char **ret_integrity_alg);
+
+#define DM_HMAC_256 "hmac(sha256)"
+#define DM_MAX_KEY_SIZE 4096 /* Maximum size of key allowed for dm-integrity */
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fstab-util.h"
+#include "generator.h"
+#include "hexdecoct.h"
+#include "id128-util.h"
+#include "integrity-util.h"
+#include "main-func.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "proc-cmdline.h"
+#include "specifier.h"
+#include "string-util.h"
+#include "unit-name.h"
+
+static const char *arg_dest = NULL;
+static const char *arg_integritytab = NULL;
+static char *arg_options = NULL;
+STATIC_DESTRUCTOR_REGISTER(arg_options, freep);
+
+static int create_disk(
+ const char *name,
+ const char *device,
+ const char *key_file,
+ const char *options) {
+
+ _cleanup_free_ char *n = NULL, *dd = NULL, *e = NULL, *name_escaped = NULL, *key_file_escaped = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+ char *dmname = NULL;
+
+ assert(name);
+ assert(device);
+
+ name_escaped = specifier_escape(name);
+ if (!name_escaped)
+ return log_oom();
+
+ e = unit_name_escape(name);
+ if (!e)
+ return log_oom();
+
+ r = unit_name_build("systemd-integritysetup", e, ".service", &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
+
+ r = unit_name_from_path(device, ".device", &dd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
+
+ r = generator_open_unit_file(arg_dest, NULL, n, &f);
+ if (r < 0)
+ return r;
+
+ if (key_file) {
+ if (!path_is_absolute(key_file))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "key file not absolute file path %s", key_file);
+
+ key_file_escaped = specifier_escape(key_file);
+ if (!key_file_escaped)
+ return log_oom();
+ }
+
+ if (options) {
+ r = parse_integrity_options(options, NULL, NULL, NULL, NULL, NULL);
+ if (r < 0)
+ return r;
+ }
+
+ fprintf(f,
+ "[Unit]\n"
+ "Description=Integrity Setup for %%I\n"
+ "Documentation=man:integritytab(5) man:systemd-integritysetup-generator(8) man:systemd-integritysetup@.service(8)\n"
+ "SourcePath=%s\n"
+ "DefaultDependencies=no\n"
+ "IgnoreOnIsolate=true\n"
+ "After=integritysetup-pre.target systemd-udevd-kernel.socket\n"
+ "Before=blockdev@dev-mapper-%%i.target\n"
+ "Wants=blockdev@dev-mapper-%%i.target\n"
+ "Conflicts=umount.target\n"
+ "Before=integritysetup.target\n"
+ "BindsTo=%s\n"
+ "After=%s\n"
+ "Before=umount.target\n",
+ arg_integritytab,
+ dd, dd);
+
+ fprintf(f,
+ "\n"
+ "[Service]\n"
+ "Type=oneshot\n"
+ "RemainAfterExit=yes\n"
+ "TimeoutSec=0\n"
+ "ExecStart=" ROOTLIBEXECDIR "/systemd-integritysetup attach '%s' '%s' '%s' '%s'\n"
+ "ExecStop=" ROOTLIBEXECDIR "/systemd-integritysetup detach '%s'\n",
+ name_escaped, device, empty_to_dash(key_file_escaped), empty_to_dash(options),
+ name_escaped);
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write unit file %s: %m", n);
+
+ r = generator_add_symlink(arg_dest, "integritysetup.target", "requires", n);
+ if (r < 0)
+ return r;
+
+ dmname = strjoina("dev-mapper-", e, ".device");
+ return generator_add_symlink(arg_dest, dmname, "requires", n);
+}
+
+static int add_integritytab_devices(void) {
+ _cleanup_fclose_ FILE *f = NULL;
+ unsigned integritytab_line = 0;
+ int r;
+
+ r = fopen_unlocked(arg_integritytab, "re", &f);
+ if (r < 0) {
+ if (errno != ENOENT)
+ log_error_errno(errno, "Failed to open %s: %m", arg_integritytab);
+ return 0;
+ }
+
+ for (;;) {
+ _cleanup_free_ char *line = NULL, *name = NULL, *device_id = NULL, *device_path = NULL, *key_file = NULL, *options = NULL;
+ char *l;
+
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read %s: %m", arg_integritytab);
+ if (r == 0)
+ break;
+
+ integritytab_line++;
+
+ l = strstrip(line);
+ if (!l)
+ continue;
+
+ if (IN_SET(l[0], 0, '#'))
+ continue;
+
+ /* The key file and the options are optional */
+ r = sscanf(l, "%ms %ms %ms %ms", &name, &device_id, &key_file, &options);
+ if (!IN_SET(r, 2, 3, 4)) {
+ log_error("Failed to parse %s:%u, ignoring.", l, integritytab_line);
+ continue;
+ }
+
+ device_path = fstab_node_to_udev_node(device_id);
+ if (!device_path) {
+ log_error("Failed to find device %s:%u, ignoring.", device_id, integritytab_line);
+ continue;
+ }
+
+ r = create_disk(name, device_path, empty_or_dash_to_null(key_file), empty_or_dash_to_null(options));
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int run(const char *dest, const char *dest_early, const char *dest_late) {
+ assert_se(arg_dest = dest);
+
+ arg_integritytab = getenv("SYSTEMD_INTEGRITYTAB") ?: "/etc/integritytab";
+
+ return add_integritytab_devices();
+}
+
+DEFINE_MAIN_GENERATOR_FUNCTION(run);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
+#include "alloc-util.h"
+#include "cryptsetup-util.h"
+#include "fileio.h"
+#include "hexdecoct.h"
+#include "integrity-util.h"
+#include "log.h"
+#include "main-func.h"
+#include "memory-util.h"
+#include "path-util.h"
+#include "parse-util.h"
+#include "pretty-print.h"
+#include "string-util.h"
+#include "terminal-util.h"
+
+static uint32_t arg_activate_flags;
+static int arg_percent;
+static usec_t arg_commit_time;
+static char *arg_existing_data_device;
+static char *arg_integrity_algorithm;
+
+STATIC_DESTRUCTOR_REGISTER(arg_existing_data_device, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_integrity_algorithm, freep);
+
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-integritysetup@.service", "8", &link);
+ if (r < 0)
+ return log_oom();
+
+ printf("%s attach VOLUME DEVICE [HMAC_KEY_FILE|-] [OPTIONS]\n"
+ "%s detach VOLUME\n\n"
+ "Attach or detach an integrity protected block device.\n"
+ "\nSee the %s for details.\n",
+ program_invocation_short_name,
+ program_invocation_short_name,
+ link);
+
+ return 0;
+}
+
+static int load_key_file(
+ const char *key_file,
+ void **ret_key_file_contents,
+ size_t *ret_key_file_size) {
+ int r;
+ _cleanup_(erase_and_freep) char *tmp_key_file_contents = NULL;
+ size_t tmp_key_file_size;
+
+ if (!path_is_absolute(key_file))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "key file not absolute path: %s", key_file);
+
+ r = read_full_file_full(
+ AT_FDCWD, key_file, UINT64_MAX, DM_MAX_KEY_SIZE,
+ READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET|READ_FULL_FILE_FAIL_WHEN_LARGER,
+ NULL,
+ &tmp_key_file_contents, &tmp_key_file_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to process key file: %m");
+
+ if (ret_key_file_contents && ret_key_file_size) {
+ *ret_key_file_contents = TAKE_PTR(tmp_key_file_contents);
+ *ret_key_file_size = tmp_key_file_size;
+ }
+
+ return 0;
+}
+
+static const char *integrity_algorithm_select(const void *key_file_buf) {
+ /* To keep a bit of sanity for end users, the subset of integrity
+ algorithms we support will match what is used in integritysetup */
+ if (arg_integrity_algorithm) {
+ if (streq("hmac-sha256", arg_integrity_algorithm))
+ return DM_HMAC_256;
+ return arg_integrity_algorithm;
+ } else if (key_file_buf)
+ return DM_HMAC_256;
+ return "crc32c";
+}
+
+static int run(int argc, char *argv[]) {
+ _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+ int r;
+ char *action, *volume;
+
+ if (argc <= 1 ||
+ strv_contains(strv_skip(argv, 1), "--help") ||
+ strv_contains(strv_skip(argv, 1), "-h") ||
+ streq(argv[1], "help"))
+ return help();
+
+ if (argc < 3)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires at least two arguments.");
+
+ action = argv[1];
+ volume = argv[2];
+
+ log_setup();
+
+ cryptsetup_enable_logging(NULL);
+
+ umask(0022);
+
+ if (streq(action, "attach")) {
+ /* attach name device optional_key_file optional_options */
+
+ crypt_status_info status;
+ _cleanup_(erase_and_freep) void *key_buf = NULL;
+ const char *device, *key_file, *options;
+ size_t key_buf_size = 0;
+
+ if (argc < 4)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "attach requires at least three arguments.");
+
+ if (argc > 6)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "attach has a maximum of five arguments.");
+
+ device = argv[3];
+ key_file = (argc > 4) ? empty_or_dash_to_null(argv[4]) : NULL;
+ options = (argc > 5) ? empty_or_dash_to_null(argv[5]) : NULL;
+
+ if (key_file) {
+ r = load_key_file(key_file, &key_buf, &key_buf_size);
+ if (r < 0)
+ return r;
+ }
+
+ if (options) {
+ r = parse_integrity_options(options, &arg_activate_flags, &arg_percent,
+ &arg_commit_time, &arg_existing_data_device, &arg_integrity_algorithm);
+ if (r < 0)
+ return r;
+ }
+
+ r = crypt_init(&cd, device);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open integrity device %s: %m", device);
+
+ cryptsetup_enable_logging(cd);
+
+ status = crypt_status(cd, volume);
+ if (IN_SET(status, CRYPT_ACTIVE, CRYPT_BUSY)) {
+ log_info("Volume %s already active.", volume);
+ return 0;
+ }
+
+ if (!isempty(arg_existing_data_device)) {
+ r = crypt_init_data_device(&cd, device, arg_existing_data_device);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add separate data device: %m");
+ }
+
+ r = crypt_load(cd,
+ CRYPT_INTEGRITY,
+ &(struct crypt_params_integrity) {
+ .journal_watermark = arg_percent,
+ .journal_commit_time = DIV_ROUND_UP(arg_commit_time, USEC_PER_SEC),
+ .integrity = integrity_algorithm_select(key_buf),
+ });
+ if (r < 0)
+ return log_error_errno(r, "Failed to load integrity superblock: %m");
+
+ r = crypt_activate_by_volume_key(cd, volume, key_buf, key_buf_size, arg_activate_flags);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set up integrity device: %m");
+
+ } else if (streq(action, "detach")) {
+
+ if (argc > 3)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "detach has a maximum of two arguments.");
+
+ r = crypt_init_by_name(&cd, volume);
+ if (r == -ENODEV)
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "crypt_init_by_name() failed: %m");
+
+ cryptsetup_enable_logging(cd);
+
+ r = crypt_deactivate(cd, volume);
+ if (r < 0)
+ return log_error_errno(r, "Failed to deactivate: %m");
+
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown verb %s.", action);
+
+ return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
else
url = strjoina(arg_url, "/entries");
} else
- url = strdupa(arg_url);
+ url = strdupa_safe(arg_url);
log_info("Spawning curl %s...", url);
fd = spawn_curl(url);
if (!hostname)
hostname = arg_url;
- hostname = strndupa(hostname, strcspn(hostname, "/:"));
+ hostname = strndupa_safe(hostname, strcspn(hostname, "/:"));
r = journal_remote_add_source(s, fd, (char *) hostname, false);
if (r < 0)
char *t;
size_t x;
- t = strdupa(url);
+ t = strdupa_safe(url);
x = strlen(t);
while (x > 0 && t[x - 1] == '/')
t[x - 1] = '\0';
} else if (strlen(x) >= SD_ID128_STRING_MAX - 1) {
char *t;
- t = strndupa(x, SD_ID128_STRING_MAX - 1);
+ t = strndupa_safe(x, SD_ID128_STRING_MAX - 1);
r = sd_id128_from_string(t, &id);
if (r >= 0)
x += SD_ID128_STRING_MAX - 1;
return log_oom();
mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
- mpk = alloca(mpk_size);
+ mpk = alloca_safe(mpk_size);
seed_size = FSPRG_RECOMMENDED_SEEDLEN;
- seed = alloca(seed_size);
+ seed = alloca_safe(seed_size);
state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
- state = alloca(state_size);
+ state = alloca_safe(state_size);
log_info("Generating seed...");
r = genuine_random_bytes(seed, seed_size, RANDOM_BLOCK);
#include "syslog-util.h"
#include "tmpfile-util.h"
#include "unit-name.h"
+#include "user-util.h"
#define STDOUT_STREAMS_MAX 4096
*stream = (StdoutStream) {
.fd = -1,
.priority = LOG_INFO,
+ .ucred = UCRED_INVALID,
};
xsprintf(stream->id_field, "_STREAM_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id));
}
if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) {
- struct ucred u;
+ struct ucred u = UCRED_INVALID;
- r = getpeercred(fd, &u);
+ (void) getpeercred(fd, &u);
/* By closing fd here we make sure that the client won't wait too long for journald to
* gather all the data it adds to the error message to find out that the connection has
*/
fd = safe_close(fd);
- server_driver_message(s, r < 0 ? 0 : u.pid, NULL, LOG_MESSAGE("Too many stdout streams, refusing connection."), NULL);
+ server_driver_message(s, u.pid, NULL, LOG_MESSAGE("Too many stdout streams, refusing connection."), NULL);
return 0;
}
if (legacy_unstable_byteorder)
/* for historical reasons (a bug), the bits were swapped and thus
* the result was endianness dependent. Preserve that behavior. */
- id32 = __bswap_32(id32);
+ id32 = bswap_32(id32);
else
/* the fixed behavior returns a stable byte order. Since LE is expected
* to be more common, swap the bytes on LE to give the same as legacy
typedef struct sd_dhcp6_client sd_dhcp6_client;
+bool dhcp6_option_can_request(uint16_t option);
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
size_t optlen, const void *optval);
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia);
#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
+bool dhcp6_option_can_request(uint16_t option) {
+ /* See Client ORO field in
+ * https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-2 */
+
+ switch (option) {
+ case SD_DHCP6_OPTION_CLIENTID:
+ case SD_DHCP6_OPTION_SERVERID:
+ case SD_DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_TA:
+ case SD_DHCP6_OPTION_IAADDR:
+ case SD_DHCP6_OPTION_ORO:
+ case SD_DHCP6_OPTION_PREFERENCE:
+ case SD_DHCP6_OPTION_ELAPSED_TIME:
+ case SD_DHCP6_OPTION_RELAY_MSG:
+ case SD_DHCP6_OPTION_AUTH:
+ case SD_DHCP6_OPTION_UNICAST:
+ case SD_DHCP6_OPTION_STATUS_CODE:
+ case SD_DHCP6_OPTION_RAPID_COMMIT:
+ case SD_DHCP6_OPTION_USER_CLASS:
+ case SD_DHCP6_OPTION_VENDOR_CLASS:
+ return false;
+ case SD_DHCP6_OPTION_VENDOR_OPTS:
+ return true;
+ case SD_DHCP6_OPTION_INTERFACE_ID:
+ case SD_DHCP6_OPTION_RECONF_MSG:
+ case SD_DHCP6_OPTION_RECONF_ACCEPT:
+ return false;
+ case SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME:
+ case SD_DHCP6_OPTION_SIP_SERVER_ADDRESS:
+ case SD_DHCP6_OPTION_DNS_SERVERS:
+ case SD_DHCP6_OPTION_DOMAIN_LIST:
+ return true;
+ case SD_DHCP6_OPTION_IA_PD:
+ case SD_DHCP6_OPTION_IA_PD_PREFIX:
+ return false;
+ case SD_DHCP6_OPTION_NIS_SERVERS:
+ case SD_DHCP6_OPTION_NISP_SERVERS:
+ case SD_DHCP6_OPTION_NIS_DOMAIN_NAME:
+ case SD_DHCP6_OPTION_NISP_DOMAIN_NAME:
+ case SD_DHCP6_OPTION_SNTP_SERVERS:
+ case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
+ case SD_DHCP6_OPTION_BCMCS_SERVER_D:
+ case SD_DHCP6_OPTION_BCMCS_SERVER_A:
+ case SD_DHCP6_OPTION_GEOCONF_CIVIC:
+ return true;
+ case SD_DHCP6_OPTION_REMOTE_ID:
+ case SD_DHCP6_OPTION_SUBSCRIBER_ID:
+ return false;
+ case SD_DHCP6_OPTION_CLIENT_FQDN:
+ case SD_DHCP6_OPTION_PANA_AGENT:
+ case SD_DHCP6_OPTION_NEW_POSIX_TIMEZONE:
+ case SD_DHCP6_OPTION_NEW_TZDB_TIMEZONE:
+ return true;
+ case SD_DHCP6_OPTION_ERO:
+ case SD_DHCP6_OPTION_LQ_QUERY:
+ case SD_DHCP6_OPTION_CLIENT_DATA:
+ case SD_DHCP6_OPTION_CLT_TIME:
+ case SD_DHCP6_OPTION_LQ_RELAY_DATA:
+ case SD_DHCP6_OPTION_LQ_CLIENT_LINK:
+ return false;
+ case SD_DHCP6_OPTION_MIP6_HNIDF:
+ case SD_DHCP6_OPTION_MIP6_VDINF:
+ case SD_DHCP6_OPTION_V6_LOST:
+ case SD_DHCP6_OPTION_CAPWAP_AC_V6:
+ return true;
+ case SD_DHCP6_OPTION_RELAY_ID:
+ return false;
+ case SD_DHCP6_OPTION_IPV6_ADDRESS_MOS:
+ case SD_DHCP6_OPTION_IPV6_FQDN_MOS:
+ case SD_DHCP6_OPTION_NTP_SERVER:
+ case SD_DHCP6_OPTION_V6_ACCESS_DOMAIN:
+ case SD_DHCP6_OPTION_SIP_UA_CS_LIST:
+ case SD_DHCP6_OPTION_BOOTFILE_URL:
+ case SD_DHCP6_OPTION_BOOTFILE_PARAM:
+ return true;
+ case SD_DHCP6_OPTION_CLIENT_ARCH_TYPE:
+ return false;
+ case SD_DHCP6_OPTION_NII:
+ case SD_DHCP6_OPTION_GEOLOCATION:
+ case SD_DHCP6_OPTION_AFTR_NAME:
+ case SD_DHCP6_OPTION_ERP_LOCAL_DOMAIN_NAME:
+ return true;
+ case SD_DHCP6_OPTION_RSOO:
+ return false;
+ case SD_DHCP6_OPTION_PD_EXCLUDE:
+ return true;
+ case SD_DHCP6_OPTION_VSS:
+ return false;
+ case SD_DHCP6_OPTION_MIP6_IDINF:
+ case SD_DHCP6_OPTION_MIP6_UDINF:
+ case SD_DHCP6_OPTION_MIP6_HNP:
+ case SD_DHCP6_OPTION_MIP6_HAA:
+ case SD_DHCP6_OPTION_MIP6_HAF:
+ case SD_DHCP6_OPTION_RDNSS_SELECTION:
+ case SD_DHCP6_OPTION_KRB_PRINCIPAL_NAME:
+ case SD_DHCP6_OPTION_KRB_REALM_NAME:
+ case SD_DHCP6_OPTION_KRB_DEFAULT_REALM_NAME:
+ case SD_DHCP6_OPTION_KRB_KDC:
+ return true;
+ case SD_DHCP6_OPTION_CLIENT_LINKLAYER_ADDR:
+ case SD_DHCP6_OPTION_LINK_ADDRESS:
+ case SD_DHCP6_OPTION_RADIUS:
+ return false;
+ case SD_DHCP6_OPTION_SOL_MAX_RT:
+ case SD_DHCP6_OPTION_INF_MAX_RT:
+ case SD_DHCP6_OPTION_ADDRSEL:
+ case SD_DHCP6_OPTION_ADDRSEL_TABLE:
+ case SD_DHCP6_OPTION_V6_PCP_SERVER:
+ return true;
+ case SD_DHCP6_OPTION_DHCPV4_MSG:
+ return false;
+ case SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER:
+ return true;
+ case SD_DHCP6_OPTION_S46_RULE:
+ return false;
+ case SD_DHCP6_OPTION_S46_BR:
+ return true;
+ case SD_DHCP6_OPTION_S46_DMR:
+ case SD_DHCP6_OPTION_S46_V4V6BIND:
+ case SD_DHCP6_OPTION_S46_PORTPARAMS:
+ return false;
+ case SD_DHCP6_OPTION_S46_CONT_MAPE:
+ case SD_DHCP6_OPTION_S46_CONT_MAPT:
+ case SD_DHCP6_OPTION_S46_CONT_LW:
+ case SD_DHCP6_OPTION_4RD:
+ case SD_DHCP6_OPTION_4RD_MAP_RULE:
+ case SD_DHCP6_OPTION_4RD_NON_MAP_RULE:
+ return true;
+ case SD_DHCP6_OPTION_LQ_BASE_TIME:
+ case SD_DHCP6_OPTION_LQ_START_TIME:
+ case SD_DHCP6_OPTION_LQ_END_TIME:
+ return false;
+ case SD_DHCP6_OPTION_CAPTIVE_PORTAL:
+ case SD_DHCP6_OPTION_MPL_PARAMETERS:
+ return true;
+ case SD_DHCP6_OPTION_ANI_ATT:
+ case SD_DHCP6_OPTION_ANI_NETWORK_NAME:
+ case SD_DHCP6_OPTION_ANI_AP_NAME:
+ case SD_DHCP6_OPTION_ANI_AP_BSSID:
+ case SD_DHCP6_OPTION_ANI_OPERATOR_ID:
+ case SD_DHCP6_OPTION_ANI_OPERATOR_REALM:
+ return false;
+ case SD_DHCP6_OPTION_S46_PRIORITY:
+ return true;
+ case SD_DHCP6_OPTION_MUD_URL_V6:
+ return false;
+ case SD_DHCP6_OPTION_V6_PREFIX64:
+ return true;
+ case SD_DHCP6_OPTION_F_BINDING_STATUS:
+ case SD_DHCP6_OPTION_F_CONNECT_FLAGS:
+ case SD_DHCP6_OPTION_F_DNS_REMOVAL_INFO:
+ case SD_DHCP6_OPTION_F_DNS_HOST_NAME:
+ case SD_DHCP6_OPTION_F_DNS_ZONE_NAME:
+ case SD_DHCP6_OPTION_F_DNS_FLAGS:
+ case SD_DHCP6_OPTION_F_EXPIRATION_TIME:
+ case SD_DHCP6_OPTION_F_MAX_UNACKED_BNDUPD:
+ case SD_DHCP6_OPTION_F_MCLT:
+ case SD_DHCP6_OPTION_F_PARTNER_LIFETIME:
+ case SD_DHCP6_OPTION_F_PARTNER_LIFETIME_SENT:
+ case SD_DHCP6_OPTION_F_PARTNER_DOWN_TIME:
+ case SD_DHCP6_OPTION_F_PARTNER_RAW_CLT_TIME:
+ case SD_DHCP6_OPTION_F_PROTOCOL_VERSION:
+ case SD_DHCP6_OPTION_F_KEEPALIVE_TIME:
+ case SD_DHCP6_OPTION_F_RECONFIGURE_DATA:
+ case SD_DHCP6_OPTION_F_RELATIONSHIP_NAME:
+ case SD_DHCP6_OPTION_F_SERVER_FLAGS:
+ case SD_DHCP6_OPTION_F_SERVER_STATE:
+ case SD_DHCP6_OPTION_F_START_TIME_OF_STATE:
+ case SD_DHCP6_OPTION_F_STATE_EXPIRATION_TIME:
+ case SD_DHCP6_OPTION_RELAY_PORT:
+ return false;
+ case SD_DHCP6_OPTION_V6_SZTP_REDIRECT:
+ case SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX:
+ return true;
+ case SD_DHCP6_OPTION_IA_LL:
+ case SD_DHCP6_OPTION_LLADDR:
+ case SD_DHCP6_OPTION_SLAP_QUAD:
+ return false;
+ case SD_DHCP6_OPTION_V6_DOTS_RI:
+ case SD_DHCP6_OPTION_V6_DOTS_ADDRESS:
+ case SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) {
DHCP6Option *option;
if (dns_name_is_single_label(fqdn))
r--;
- r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_FQDN, 1 + r, buffer);
+ r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_CLIENT_FQDN, 1 + r, buffer);
return r;
}
Copyright © 2014 Intel Corporation. All rights reserved.
***/
+#include <errno.h>
#include <netinet/ip6.h>
#include <netinet/udp.h>
DHCP6_PORT_CLIENT = 546,
};
-#define DHCP6_INF_TIMEOUT 1 * USEC_PER_SEC
-#define DHCP6_INF_MAX_RT 120 * USEC_PER_SEC
-#define DHCP6_SOL_MAX_DELAY 1 * USEC_PER_SEC
-#define DHCP6_SOL_TIMEOUT 1 * USEC_PER_SEC
-#define DHCP6_SOL_MAX_RT 120 * USEC_PER_SEC
-#define DHCP6_REQ_TIMEOUT 1 * USEC_PER_SEC
-#define DHCP6_REQ_MAX_RT 120 * USEC_PER_SEC
+#define DHCP6_INF_TIMEOUT (1 * USEC_PER_SEC)
+#define DHCP6_INF_MAX_RT (120 * USEC_PER_SEC)
+#define DHCP6_SOL_MAX_DELAY (1 * USEC_PER_SEC)
+#define DHCP6_SOL_TIMEOUT (1 * USEC_PER_SEC)
+#define DHCP6_SOL_MAX_RT (120 * USEC_PER_SEC)
+#define DHCP6_REQ_TIMEOUT (1 * USEC_PER_SEC)
+#define DHCP6_REQ_MAX_RT (120 * USEC_PER_SEC)
#define DHCP6_REQ_MAX_RC 10
-#define DHCP6_REN_TIMEOUT 10 * USEC_PER_SEC
-#define DHCP6_REN_MAX_RT 600 * USEC_PER_SEC
-#define DHCP6_REB_TIMEOUT 10 * USEC_PER_SEC
-#define DHCP6_REB_MAX_RT 600 * USEC_PER_SEC
+#define DHCP6_REN_TIMEOUT (10 * USEC_PER_SEC)
+#define DHCP6_REN_MAX_RT (600 * USEC_PER_SEC)
+#define DHCP6_REB_TIMEOUT (10 * USEC_PER_SEC)
+#define DHCP6_REB_MAX_RT (600 * USEC_PER_SEC)
-enum DHCP6State {
- DHCP6_STATE_STOPPED = 0,
- DHCP6_STATE_INFORMATION_REQUEST = 1,
- DHCP6_STATE_SOLICITATION = 2,
- DHCP6_STATE_REQUEST = 3,
- DHCP6_STATE_BOUND = 4,
- DHCP6_STATE_RENEW = 5,
- DHCP6_STATE_REBIND = 6,
-};
+typedef enum DHCP6State {
+ DHCP6_STATE_STOPPED,
+ DHCP6_STATE_INFORMATION_REQUEST,
+ DHCP6_STATE_SOLICITATION,
+ DHCP6_STATE_REQUEST,
+ DHCP6_STATE_BOUND,
+ DHCP6_STATE_RENEW,
+ DHCP6_STATE_REBIND,
+ _DHCP6_STATE_MAX,
+ _DHCP6_STATE_INVALID = -EINVAL,
+} DHCP6State;
-enum {
- DHCP6_SOLICIT = 1,
- DHCP6_ADVERTISE = 2,
- DHCP6_REQUEST = 3,
- DHCP6_CONFIRM = 4,
- DHCP6_RENEW = 5,
- DHCP6_REBIND = 6,
- DHCP6_REPLY = 7,
- DHCP6_RELEASE = 8,
- DHCP6_DECLINE = 9,
- DHCP6_RECONFIGURE = 10,
- DHCP6_INFORMATION_REQUEST = 11,
- DHCP6_RELAY_FORW = 12,
- DHCP6_RELAY_REPL = 13,
- _DHCP6_MESSAGE_MAX = 14,
-};
+/* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-1 */
+typedef enum DHCP6MessageType {
+ DHCP6_MESSAGE_SOLICIT = 1, /* RFC 8415 */
+ DHCP6_MESSAGE_ADVERTISE = 2, /* RFC 8415 */
+ DHCP6_MESSAGE_REQUEST = 3, /* RFC 8415 */
+ DHCP6_MESSAGE_CONFIRM = 4, /* RFC 8415 */
+ DHCP6_MESSAGE_RENEW = 5, /* RFC 8415 */
+ DHCP6_MESSAGE_REBIND = 6, /* RFC 8415 */
+ DHCP6_MESSAGE_REPLY = 7, /* RFC 8415 */
+ DHCP6_MESSAGE_RELEASE = 8, /* RFC 8415 */
+ DHCP6_MESSAGE_DECLINE = 9, /* RFC 8415 */
+ DHCP6_MESSAGE_RECONFIGURE = 10, /* RFC 8415 */
+ DHCP6_MESSAGE_INFORMATION_REQUEST = 11, /* RFC 8415 */
+ DHCP6_MESSAGE_RELAY_FORWARD = 12, /* RFC 8415 */
+ DHCP6_MESSAGE_RELAY_REPLY = 13, /* RFC 8415 */
+ DHCP6_MESSAGE_LEASE_QUERY = 14, /* RFC 5007 */
+ DHCP6_MESSAGE_LEASE_QUERY_REPLY = 15, /* RFC 5007 */
+ DHCP6_MESSAGE_LEASE_QUERY_DONE = 16, /* RFC 5460 */
+ DHCP6_MESSAGE_LEASE_QUERY_DATA = 17, /* RFC 5460 */
+ DHCP6_MESSAGE_RECONFIGURE_REQUEST = 18, /* RFC 6977 */
+ DHCP6_MESSAGE_RECONFIGURE_REPLY = 19, /* RFC 6977 */
+ DHCP6_MESSAGE_DHCPV4_QUERY = 20, /* RFC 7341 */
+ DHCP6_MESSAGE_DHCPV4_RESPONSE = 21, /* RFC 7341 */
+ DHCP6_MESSAGE_ACTIVE_LEASE_QUERY = 22, /* RFC 7653 */
+ DHCP6_MESSAGE_START_TLS = 23, /* RFC 7653 */
+ DHCP6_MESSAGE_BINDING_UPDATE = 24, /* RFC 8156 */
+ DHCP6_MESSAGE_BINDING_REPLY = 25, /* RFC 8156 */
+ DHCP6_MESSAGE_POOL_REQUEST = 26, /* RFC 8156 */
+ DHCP6_MESSAGE_POOL_RESPONSE = 27, /* RFC 8156 */
+ DHCP6_MESSAGE_UPDATE_REQUEST = 28, /* RFC 8156 */
+ DHCP6_MESSAGE_UPDATE_REQUEST_ALL = 29, /* RFC 8156 */
+ DHCP6_MESSAGE_UPDATE_DONE = 30, /* RFC 8156 */
+ DHCP6_MESSAGE_CONNECT = 31, /* RFC 8156 */
+ DHCP6_MESSAGE_CONNECT_REPLY = 32, /* RFC 8156 */
+ DHCP6_MESSAGE_DISCONNECT = 33, /* RFC 8156 */
+ DHCP6_MESSAGE_STATE = 34, /* RFC 8156 */
+ DHCP6_MESSAGE_CONTACT = 35, /* RFC 8156 */
+ _DHCP6_MESSAGE_TYPE_MAX,
+ _DHCP6_MESSAGE_TYPE_INVALID = -EINVAL,
+} DHCP6MessageType;
-enum {
+typedef enum DHCP6NTPSubOption {
DHCP6_NTP_SUBOPTION_SRV_ADDR = 1,
DHCP6_NTP_SUBOPTION_MC_ADDR = 2,
DHCP6_NTP_SUBOPTION_SRV_FQDN = 3,
-};
+} DHCP6NTPSubOption;
/*
* RFC 8415, RFC 5007 and RFC 7653 status codes:
* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-5
*/
-enum {
+typedef enum DHCP6Status {
DHCP6_STATUS_SUCCESS = 0,
DHCP6_STATUS_UNSPEC_FAIL = 1,
DHCP6_STATUS_NO_ADDRS_AVAIL = 2,
DHCP6_STATUS_SERVER_SHUTTING_DOWN = 20,
DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED = 21,
DHCP6_STATUS_EXCESSIVE_TIME_SKEW = 22,
- _DHCP6_STATUS_MAX = 23,
-};
+ _DHCP6_STATUS_MAX,
+ _DHCP6_STATUS_INVALID = -EINVAL,
+} DHCP6Status;
-enum {
- DHCP6_FQDN_FLAG_S = (1 << 0),
- DHCP6_FQDN_FLAG_O = (1 << 1),
- DHCP6_FQDN_FLAG_N = (1 << 2),
-};
+typedef enum DHCP6FQDNFlag {
+ DHCP6_FQDN_FLAG_S = 1 << 0,
+ DHCP6_FQDN_FLAG_O = 1 << 1,
+ DHCP6_FQDN_FLAG_N = 1 << 2,
+} DHCP6FQDNFlag;
#define IRT_MINIMUM (600 * USEC_PER_SEC)
/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
-enum {
- DHCP6_REQUEST_IA_NA = 1,
- DHCP6_REQUEST_IA_TA = 2, /* currently not used */
- DHCP6_REQUEST_IA_PD = 4,
-};
+typedef enum DHCP6RequestIA {
+ DHCP6_REQUEST_IA_NA = 1 << 0,
+ DHCP6_REQUEST_IA_TA = 1 << 1, /* currently not used */
+ DHCP6_REQUEST_IA_PD = 1 << 2,
+} DHCP6RequestIA;
struct sd_dhcp6_client {
unsigned n_ref;
- enum DHCP6State state;
+ DHCP6State state;
sd_event *event;
int event_priority;
int ifindex;
DHCP6IA ia_pd;
sd_event_source *timeout_t1;
sd_event_source *timeout_t2;
- unsigned request;
+ DHCP6RequestIA request_ia;
be32_t transaction_id;
usec_t transaction_start;
struct sd_dhcp6_lease *lease;
SD_DHCP6_OPTION_SNTP_SERVERS,
};
-const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
- [DHCP6_SOLICIT] = "SOLICIT",
- [DHCP6_ADVERTISE] = "ADVERTISE",
- [DHCP6_REQUEST] = "REQUEST",
- [DHCP6_CONFIRM] = "CONFIRM",
- [DHCP6_RENEW] = "RENEW",
- [DHCP6_REBIND] = "REBIND",
- [DHCP6_REPLY] = "REPLY",
- [DHCP6_RELEASE] = "RELEASE",
- [DHCP6_DECLINE] = "DECLINE",
- [DHCP6_RECONFIGURE] = "RECONFIGURE",
- [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
- [DHCP6_RELAY_FORW] = "RELAY-FORW",
- [DHCP6_RELAY_REPL] = "RELAY-REPL",
+const char * dhcp6_message_type_table[_DHCP6_MESSAGE_TYPE_MAX] = {
+ [DHCP6_MESSAGE_SOLICIT] = "Solicit",
+ [DHCP6_MESSAGE_ADVERTISE] = "Advertise",
+ [DHCP6_MESSAGE_REQUEST] = "Request",
+ [DHCP6_MESSAGE_CONFIRM] = "Confirm",
+ [DHCP6_MESSAGE_RENEW] = "Renew",
+ [DHCP6_MESSAGE_REBIND] = "Rebind",
+ [DHCP6_MESSAGE_REPLY] = "Reply",
+ [DHCP6_MESSAGE_RELEASE] = "Release",
+ [DHCP6_MESSAGE_DECLINE] = "Decline",
+ [DHCP6_MESSAGE_RECONFIGURE] = "Reconfigure",
+ [DHCP6_MESSAGE_INFORMATION_REQUEST] = "Information Request",
+ [DHCP6_MESSAGE_RELAY_FORWARD] = "Relay Forward",
+ [DHCP6_MESSAGE_RELAY_REPLY] = "Relay Reply",
+ [DHCP6_MESSAGE_LEASE_QUERY] = "Lease Query",
+ [DHCP6_MESSAGE_LEASE_QUERY_REPLY] = "Lease Query Reply",
+ [DHCP6_MESSAGE_LEASE_QUERY_DONE] = "Lease Query Done",
+ [DHCP6_MESSAGE_LEASE_QUERY_DATA] = "Lease Query Data",
+ [DHCP6_MESSAGE_RECONFIGURE_REQUEST] = "Reconfigure Request",
+ [DHCP6_MESSAGE_RECONFIGURE_REPLY] = "Reconfigure Reply",
+ [DHCP6_MESSAGE_DHCPV4_QUERY] = "DHCPv4 Query",
+ [DHCP6_MESSAGE_DHCPV4_RESPONSE] = "DHCPv4 Response",
+ [DHCP6_MESSAGE_ACTIVE_LEASE_QUERY] = "Active Lease Query",
+ [DHCP6_MESSAGE_START_TLS] = "Start TLS",
+ [DHCP6_MESSAGE_BINDING_UPDATE] = "Binding Update",
+ [DHCP6_MESSAGE_BINDING_REPLY] = "Binding Reply",
+ [DHCP6_MESSAGE_POOL_REQUEST] = "Pool Request",
+ [DHCP6_MESSAGE_POOL_RESPONSE] = "Pool Response",
+ [DHCP6_MESSAGE_UPDATE_REQUEST] = "Update Request",
+ [DHCP6_MESSAGE_UPDATE_REQUEST_ALL] = "Update Request All",
+ [DHCP6_MESSAGE_UPDATE_DONE] = "Update Done",
+ [DHCP6_MESSAGE_CONNECT] = "Connect",
+ [DHCP6_MESSAGE_CONNECT_REPLY] = "Connect Reply",
+ [DHCP6_MESSAGE_DISCONNECT] = "Disconnect",
+ [DHCP6_MESSAGE_STATE] = "State",
+ [DHCP6_MESSAGE_CONTACT] = "Contact",
};
DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
#define DHCP6_CLIENT_DONT_DESTROY(client) \
_cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
-static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
+static int client_start(sd_dhcp6_client *client, DHCP6State state);
int sd_dhcp6_client_set_callback(
sd_dhcp6_client *client,
assert_return(client, -EINVAL);
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
- if (option <= 0 || option >= UINT8_MAX)
+ if (!dhcp6_option_can_request(option))
return -EINVAL;
for (t = 0; t < client->req_opts_len; t++)
assert_return(client, -EINVAL);
assert_return(delegation, -EINVAL);
- *delegation = FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD);
+ *delegation = FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD);
return 0;
}
int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) {
assert_return(client, -EINVAL);
- SET_FLAG(client->request, DHCP6_REQUEST_IA_PD, delegation);
+ SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_PD, delegation);
return 0;
}
assert_return(client, -EINVAL);
assert_return(request, -EINVAL);
- *request = FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA);
+ *request = FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA);
return 0;
}
int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) {
assert_return(client, -EINVAL);
- SET_FLAG(client->request, DHCP6_REQUEST_IA_NA, request);
+ SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_NA, request);
return 0;
}
switch(client->state) {
case DHCP6_STATE_INFORMATION_REQUEST:
- message->type = DHCP6_INFORMATION_REQUEST;
+ message->type = DHCP6_MESSAGE_INFORMATION_REQUEST;
if (client->mudurl) {
r = dhcp6_option_append(&opt, &optlen,
- SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
+ SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
client->mudurl);
if (r < 0)
return r;
break;
case DHCP6_STATE_SOLICITATION:
- message->type = DHCP6_SOLICIT;
+ message->type = DHCP6_MESSAGE_SOLICIT;
r = dhcp6_option_append(&opt, &optlen,
SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
if (r < 0)
return r;
- if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
+ if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA)) {
r = dhcp6_option_append_ia(&opt, &optlen,
&client->ia_na);
if (r < 0)
if (client->mudurl) {
r = dhcp6_option_append(&opt, &optlen,
- SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
+ SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
client->mudurl);
if (r < 0)
return r;
return r;
}
- if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
+ if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(&opt, &optlen, &client->ia_pd, &client->hint_pd_prefix);
if (r < 0)
return r;
case DHCP6_STATE_RENEW:
if (client->state == DHCP6_STATE_REQUEST)
- message->type = DHCP6_REQUEST;
+ message->type = DHCP6_MESSAGE_REQUEST;
else
- message->type = DHCP6_RENEW;
+ message->type = DHCP6_MESSAGE_RENEW;
r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID,
client->lease->serverid_len,
if (r < 0)
return r;
- if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
+ if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
r = dhcp6_option_append_ia(&opt, &optlen,
&client->lease->ia);
if (r < 0)
if (client->mudurl) {
r = dhcp6_option_append(&opt, &optlen,
- SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
+ SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
client->mudurl);
if (r < 0)
return r;
return r;
}
- if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
+ if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL);
if (r < 0)
return r;
break;
case DHCP6_STATE_REBIND:
- message->type = DHCP6_REBIND;
+ message->type = DHCP6_MESSAGE_REBIND;
- if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
+ if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA)) {
r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
if (r < 0)
return r;
if (client->mudurl) {
r = dhcp6_option_append(&opt, &optlen,
- SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
+ SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
client->mudurl);
if (r < 0)
return r;
return r;
}
- if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
+ if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL);
if (r < 0)
return r;
case DHCP6_STATE_STOPPED:
case DHCP6_STATE_BOUND:
return -EINVAL;
+ default:
+ assert_not_reached();
}
r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO,
static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) {
sd_dhcp6_client *client = userdata;
DHCP6_CLIENT_DONT_DESTROY(client);
- enum DHCP6State state;
+ DHCP6State state;
assert(s);
assert(client);
case DHCP6_STATE_STOPPED:
case DHCP6_STATE_BOUND:
return 0;
+ default:
+ assert_not_reached();
}
if (max_retransmit_count > 0 &&
client->retransmit_time += init_retransmit_time / 10;
} else {
- if (max_retransmit_time > 0 &&
- client->retransmit_time > max_retransmit_time / 2)
+ assert(max_retransmit_time > 0);
+ if (client->retransmit_time > max_retransmit_time / 2)
client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
else
client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
break;
- case SD_DHCP6_OPTION_FQDN:
+ case SD_DHCP6_OPTION_CLIENT_FQDN:
r = dhcp6_lease_set_fqdn(lease, optval, optlen);
if (r < 0)
return r;
assert(client);
assert(reply);
- if (reply->type != DHCP6_REPLY)
+ if (reply->type != DHCP6_MESSAGE_REPLY)
return 0;
r = dhcp6_lease_new(&lease);
uint8_t pref_advertise = 0, pref_lease = 0;
int r;
- if (advertise->type != DHCP6_ADVERTISE)
+ if (advertise->type != DHCP6_MESSAGE_ADVERTISE)
return 0;
r = dhcp6_lease_new(&lease);
return 0;
}
- switch(message->type) {
- case DHCP6_SOLICIT:
- case DHCP6_REQUEST:
- case DHCP6_CONFIRM:
- case DHCP6_RENEW:
- case DHCP6_REBIND:
- case DHCP6_RELEASE:
- case DHCP6_DECLINE:
- case DHCP6_INFORMATION_REQUEST:
- case DHCP6_RELAY_FORW:
- case DHCP6_RELAY_REPL:
- return 0;
-
- case DHCP6_ADVERTISE:
- case DHCP6_REPLY:
- case DHCP6_RECONFIGURE:
- break;
-
- default:
- log_dhcp6_client(client, "Unknown message type %d", message->type);
+ if (!IN_SET(message->type, DHCP6_MESSAGE_ADVERTISE, DHCP6_MESSAGE_REPLY, DHCP6_MESSAGE_RECONFIGURE)) {
+ const char *type_str = dhcp6_message_type_to_string(message->type);
+ if (type_str)
+ log_dhcp6_client(client, "Received unexpected %s message, ignoring.", type_str);
+ else
+ log_dhcp6_client(client, "Received unsupported message type %u, ignoring.", message->type);
return 0;
}
case DHCP6_STATE_STOPPED:
return 0;
+ default:
+ assert_not_reached();
}
log_dhcp6_client(client, "Recv %s",
assert_return(client, -EINVAL);
assert_return(client->lease, -EINVAL);
- if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
+ if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
*lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1);
*lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2);
return 0;
}
- if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
+ if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
*lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1);
*lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2);
return -ENOMSG;
}
-static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
+static int client_start(sd_dhcp6_client *client, DHCP6State state) {
int r;
usec_t timeout, time_now;
uint32_t lifetime_t1, lifetime_t2;
client->state = state;
return 0;
+ default:
+ assert_not_reached();
}
client->transaction_id = random_u32() & htobe32(0x00ffffff);
}
int sd_dhcp6_client_start(sd_dhcp6_client *client) {
- enum DHCP6State state = DHCP6_STATE_SOLICITATION;
+ DHCP6State state = DHCP6_STATE_SOLICITATION;
int r;
assert_return(client, -EINVAL);
if (client->state != DHCP6_STATE_STOPPED)
return -EBUSY;
- if (!client->information_request && !client->request)
+ if (!client->information_request && client->request_ia == 0)
return -EINVAL;
r = client_reset(client);
.ia_na.type = SD_DHCP6_OPTION_IA_NA,
.ia_pd.type = SD_DHCP6_OPTION_IA_PD,
.ifindex = -1,
- .request = DHCP6_REQUEST_IA_NA,
+ .request_ia = DHCP6_REQUEST_IA_NA,
.fd = -1,
.req_opts_len = ELEMENTSOF(default_req_opts),
.hint_pd_prefix.iapdprefix.lifetime_preferred = (be32_t) -1,
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_route_prefix, sd_radv_route_prefix, mfree);
-_public_ int sd_radv_prefix_set_route_prefix(sd_radv_route_prefix *p, const struct in6_addr *in6_addr,
+_public_ int sd_radv_route_prefix_set_prefix(sd_radv_route_prefix *p, const struct in6_addr *in6_addr,
unsigned char prefixlen) {
assert_return(p, -EINVAL);
assert_return(in6_addr, -EINVAL);
sd_dhcp_client *client;
if (verbose)
- printf("* %s\n", __FUNCTION__);
+ printf("* %s\n", __func__);
/* Initialize client without Anonymize settings. */
r = sd_dhcp_client_new(&client, false);
sd_dhcp_client *client;
if (verbose)
- printf("* %s\n", __FUNCTION__);
+ printf("* %s\n", __func__);
/* Initialize client with Anonymize settings. */
r = sd_dhcp_client_new(&client, true);
};
if (verbose)
- printf("* %s\n", __FUNCTION__);
+ printf("* %s\n", __func__);
assert_se(dhcp_packet_checksum((uint8_t*)&buf, 20) == be16toh(0x78ae));
}
#if __BYTE_ORDER == __LITTLE_ENDIAN
assert_se(iaid == iaid_legacy);
#else
- assert_se(iaid == __bswap_32(iaid_legacy));
+ assert_se(iaid == bswap_32(iaid_legacy));
#endif
}
int res, r;
if (verbose)
- printf("* %s\n", __FUNCTION__);
+ printf("* %s\n", __func__);
r = sd_dhcp_client_new(&client, false);
assert_se(r >= 0);
int res, r;
if (verbose)
- printf("* %s\n", __FUNCTION__);
+ printf("* %s\n", __func__);
r = sd_dhcp_client_new(&client, false);
assert_se(r >= 0);
}
static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen) {
- assert(*descpos >= 0);
+ assert_se(*descpos >= 0);
while (*descpos < *desclen) {
switch(descoption[*descpos]) {
assert_se(sd_dhcp6_client_set_fqdn(client, "~host") == -EINVAL);
assert_se(sd_dhcp6_client_set_fqdn(client, "~host.domain") == -EINVAL);
- assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_CLIENTID) == 0);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_CLIENTID) == -EINVAL);
assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVERS) == -EEXIST);
assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_NTP_SERVER) == -EEXIST);
assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_SNTP_SERVERS) == -EEXIST);
assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DOMAIN_LIST) == -EEXIST);
- assert_se(sd_dhcp6_client_set_request_option(client, 10) == 0);
+ assert_se(sd_dhcp6_client_set_request_option(client, 10) == -EINVAL);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_NIS_SERVERS) == 0);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_NISP_SERVERS) == 0);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_NIS_SERVERS) == -EEXIST);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_NISP_SERVERS) == -EEXIST);
assert_se(sd_dhcp6_client_set_information_request(client, 1) >= 0);
v = 0;
assert_se(dhcp6_lease_new(&lease) >= 0);
- assert_se(advertise->type == DHCP6_ADVERTISE);
+ assert_se(advertise->type == DHCP6_MESSAGE_ADVERTISE);
assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) ==
0x0fb4e5);
log_debug("/* %s */", __func__);
reply.transaction_id = request->transaction_id;
- reply.type = DHCP6_REPLY;
+ reply.type = DHCP6_MESSAGE_REPLY;
memcpy(msg_reply, &reply.transaction_id, 4);
log_debug("/* %s */", __func__);
- assert_se(request->type == DHCP6_REQUEST);
+ assert_se(request->type == DHCP6_MESSAGE_REQUEST);
assert_se(dhcp6_lease_new(&lease) >= 0);
len -= sizeof(DHCP6Message);
assert_se(optlen == 2);
break;
- case SD_DHCP6_OPTION_FQDN:
+ case SD_DHCP6_OPTION_CLIENT_FQDN:
assert_se(!found_fqdn);
found_fqdn = true;
log_debug("/* %s */", __func__);
advertise.transaction_id = solicit->transaction_id;
- advertise.type = DHCP6_ADVERTISE;
+ advertise.type = DHCP6_MESSAGE_ADVERTISE;
memcpy(msg_advertise, &advertise.transaction_id, 4);
log_debug("/* %s */", __func__);
- assert_se(solicit->type == DHCP6_SOLICIT);
+ assert_se(solicit->type == DHCP6_MESSAGE_SOLICIT);
len -= sizeof(DHCP6Message);
break;
- case SD_DHCP6_OPTION_FQDN:
+ case SD_DHCP6_OPTION_CLIENT_FQDN:
assert_se(!found_fqdn);
found_fqdn = true;
log_debug("/* %s */", __func__);
- assert_se(information_request->type == DHCP6_INFORMATION_REQUEST);
+ assert_se(information_request->type == DHCP6_MESSAGE_INFORMATION_REQUEST);
assert_se(dhcp6_lease_new(&lease) >= 0);
len -= sizeof(DHCP6Message);
.ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}};
if (verbose)
- printf("* %s\n", __FUNCTION__);
+ printf("* %s\n", __func__);
assert_se(sd_ipv4ll_new(&ll) == 0);
assert_se(ll);
.ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}};
if (verbose)
- printf("* %s\n", __FUNCTION__);
+ printf("* %s\n", __func__);
assert_se(sd_ipv4ll_new(&ll) == 0);
assert_se(sd_ipv4ll_start(ll) == -EINVAL);
static void test_radv_prefix(void) {
sd_radv_prefix *p;
- printf("* %s\n", __FUNCTION__);
+ printf("* %s\n", __func__);
assert_se(sd_radv_prefix_new(&p) >= 0);
static void test_radv(void) {
sd_radv *ra;
- printf("* %s\n", __FUNCTION__);
+ printf("* %s\n", __func__);
assert_se(sd_radv_new(&ra) >= 0);
assert_se(ra);
sd_radv *ra;
unsigned i;
- printf("* %s\n", __FUNCTION__);
+ printf("* %s\n", __func__);
assert_se(socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) >= 0);
sd_ndisc *nd;
if (verbose)
- printf("* %s\n", __FUNCTION__);
+ printf("* %s\n", __func__);
send_ra_function = send_ra;
sd_ndisc *nd;
if (verbose)
- printf("* %s\n", __FUNCTION__);
+ printf("* %s\n", __func__);
send_ra_function = test_timeout_value;
SD_BUS_ERROR_MAP(BUS_ERROR_TOO_MANY_OPERATIONS, ENOBUFS),
SD_BUS_ERROR_MAP(BUS_ERROR_AUTHENTICATION_LIMIT_HIT, ETOOMANYREFS),
SD_BUS_ERROR_MAP(BUS_ERROR_HOME_CANT_AUTHENTICATE, EKEYREVOKED),
+ SD_BUS_ERROR_MAP(BUS_ERROR_HOME_IN_USE, EADDRINUSE),
SD_BUS_ERROR_MAP_END
};
#define BUS_ERROR_TOO_MANY_OPERATIONS "org.freedesktop.home1.TooManyOperations"
#define BUS_ERROR_AUTHENTICATION_LIMIT_HIT "org.freedesktop.home1.AuthenticationLimitHit"
#define BUS_ERROR_HOME_CANT_AUTHENTICATE "org.freedesktop.home1.HomeCantAuthenticate"
+#define BUS_ERROR_HOME_IN_USE "org.freedesktop.home1.HomeInUse"
BUS_ERROR_MAP_ELF_USE(bus_common_errors);
if (r < 0)
return r;
- types = strndupa(c->signature + c->index, l);
+ types = strndupa_safe(c->signature + c->index, l);
}
switch (*types) {
e = strrchr(path, '/');
assert(e);
- p = strndupa(path, MAX(1, e - path));
+ p = strndupa_safe(path, MAX(1, e - path));
parent = bus_node_allocate(bus, p);
if (!parent)
}
/* Make sure the path is NUL terminated */
- p = strndupa(b->sockaddr.un.sun_path, sizeof(b->sockaddr.un.sun_path));
+ p = strndupa_safe(b->sockaddr.un.sun_path,
+ sizeof(b->sockaddr.un.sun_path));
/* Make sure the path is absolute */
r = path_make_absolute_cwd(p, &absolute);
.original_pid = getpid_cached(),
.n_groups = SIZE_MAX,
.close_on_exit = true,
+ .ucred = UCRED_INVALID,
};
/* We guarantee that wqueue always has space for at least one entry */
rbracket = strchr(host, ']');
if (!rbracket)
return -EINVAL;
- t = strndupa(host + 1, rbracket - host - 1);
+ t = strndupa_safe(host + 1, rbracket - host - 1);
e = bus_address_escape(t);
if (!e)
return -ENOMEM;
t = strchr(p, '/');
if (t) {
- p = strndupa(p, t - p);
+ p = strndupa_safe(p, t - p);
got_forward_slash = true;
}
if (!e) {
char *t;
- t = strndupa(host, strcspn(host, ":/"));
+ t = strndupa_safe(host, strcspn(host, ":/"));
e = bus_address_escape(t);
if (!e)
r = bus_set_address_system_remote(b, host);
log_info("\"%s\" → %d, \"%s\"", host, r, strna(r >= 0 ? b->address : NULL));
if (result < 0 || expected) {
- assert(r == result);
+ assert_se(r == result);
if (r >= 0)
assert_se(streq(b->address, expected));
}
return 1;
}
+static int enumerator3_callback(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+ _cleanup_strv_free_ char **v = NULL;
+
+ if (!object_path_startswith("/value/b", path))
+ return 1;
+
+ for (unsigned i = 0; i < 30; i++)
+ assert_se(strv_extendf(&v, "/value/b/%u", i) >= 0);
+
+ *nodes = TAKE_PTR(v);
+ return 1;
+}
+
static void *server(void *p) {
struct context *c = p;
sd_bus *bus = NULL;
assert_se(sd_bus_add_fallback_vtable(bus, NULL, "/value", "org.freedesktop.systemd.ValueTest", vtable2, NULL, UINT_TO_PTR(20)) >= 0);
assert_se(sd_bus_add_node_enumerator(bus, NULL, "/value", enumerator_callback, NULL) >= 0);
assert_se(sd_bus_add_node_enumerator(bus, NULL, "/value/a", enumerator2_callback, NULL) >= 0);
+ assert_se(sd_bus_add_node_enumerator(bus, NULL, "/value/b", enumerator3_callback, NULL) >= 0);
assert_se(sd_bus_add_object_manager(bus, NULL, "/value") >= 0);
assert_se(sd_bus_add_object_manager(bus, NULL, "/value/a") >= 0);
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_strv_free_ char **lines = NULL;
const char *s;
int r;
assert_se(r >= 0);
fputs(s, stdout);
+ assert_se(lines = strv_split_newlines(s));
+ assert_se(strv_contains(lines, " <node name=\"x\"/>"));
+ assert_se(strv_contains(lines, " <node name=\"y\"/>"));
+ assert_se(strv_contains(lines, " <node name=\"z\"/>"));
+ lines = strv_free(lines);
+
+ reply = sd_bus_message_unref(reply);
+
+ r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/b", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, NULL);
+ assert_se(r >= 0);
+
+ r = sd_bus_message_read(reply, "s", &s);
+ assert_se(r >= 0);
+ fputs(s, stdout);
+
+ assert_se(lines = strv_split_newlines(s));
+ for (unsigned i = 0; i < 30; i++) {
+ _cleanup_free_ char *n = NULL;
+
+ assert_se(asprintf(&n, " <node name=\"%u\"/>", i) >= 0);
+ assert_se(strv_contains(lines, n));
+ }
+ lines = strv_free(lines);
+
reply = sd_bus_message_unref(reply);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", NULL);
}
if (fd < 0) {
- sock = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT);
+ sock = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT);
if (sock < 0)
return log_debug_errno(errno, "sd-device-monitor: Failed to create socket: %m");
}
netns = ioctl(m->sock, SIOCGSKNS);
if (netns < 0)
- log_debug_errno(errno, "sd-device-monitor: Unable to get network namespace of udev netlink socket, unable to determine if we are in host netns: %m");
+ log_debug_errno(errno, "sd-device-monitor: Unable to get network namespace of udev netlink socket, unable to determine if we are in host netns, ignoring: %m");
else {
struct stat a, b;
if (ERRNO_IS_PRIVILEGE(errno))
/* If we can't access PID1's netns info due to permissions, it's fine, this is a
* safety check only after all. */
- log_debug_errno(errno, "sd-device-monitor: No permission to stat PID1's netns, unable to determine if we are in host netns: %m");
+ log_debug_errno(errno, "sd-device-monitor: No permission to stat PID1's netns, unable to determine if we are in host netns, ignoring: %m");
else
- log_debug_errno(errno, "sd-device-monitor: Failed to stat PID1's netns: %m");
+ log_debug_errno(errno, "sd-device-monitor: Failed to stat PID1's netns, ignoring: %m");
} else if (a.st_dev != b.st_dev || a.st_ino != b.st_ino)
log_debug("sd-device-monitor: Netlink socket we listen on is not from host netns, we won't see device events.");
}
/* translate sysname back to sysfs filename */
- name = strdupa(sysname);
+ name = strdupa_safe(sysname);
for (size_t i = 0; name[i]; i++)
if (name[i] == '/')
name[i] = '!';
static void maybe_exit(sd_event_source *s, struct inotify_context *c) {
unsigned n;
- assert(s);
- assert(c);
+ assert_se(s);
+ assert_se(c);
if (!c->delete_self_handler_called)
return;
} else {
f->fsprg_state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
f->fsprg_state = malloc(f->fsprg_state_size);
-
if (!f->fsprg_state)
return -ENOMEM;
}
log_debug("Seeking FSPRG key to %"PRIu64".", goal);
- msk = alloca(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR));
+ msk = alloca_safe(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR));
FSPRG_GenMK(msk, NULL, f->fsprg_seed, f->fsprg_seed_size, FSPRG_RECOMMENDED_SECPAR);
FSPRG_Seek(f->fsprg_state, goal, msk, f->fsprg_seed, f->fsprg_seed_size);
return 0;
return r;
#endif
- /* alloca() can't take 0, hence let's allocate at least one */
- items = newa(EntryItem, MAX(1u, n_iovec));
+ items = newa(EntryItem, n_iovec);
for (unsigned i = 0; i < n_iovec; i++) {
uint64_t p;
boot_id = &o->entry.boot_id;
n = journal_file_entry_n_items(o);
- /* alloca() can't take 0, hence let's allocate at least one */
- items = newa(EntryItem, MAX(1u, n));
+ items = newa(EntryItem, n);
for (uint64_t i = 0; i < n; i++) {
uint64_t l, h;
/* Allocate large buffer to accommodate big message */
if (len >= LINE_MAX) {
- buffer = alloca(len + 9);
+ buffer = alloca_safe(len + 9);
memcpy(buffer, "MESSAGE=", 8);
assert_se(vsnprintf(buffer + 8, len + 1, format, ap) == len);
}
/* Allocate large buffer to accommodate big message */
if (len >= LINE_MAX) {
- buffer = alloca(len + 9);
+ buffer = alloca_safe(len + 9);
memcpy(buffer, "MESSAGE=", 8);
assert_se(vsnprintf(buffer + 8, len + 1, format, ap) == len);
}
if (!log_namespace_name_valid(e + 1))
return false;
- k = strndupa(fn, e - fn);
+ k = strndupa_safe(fn, e - fn);
r = sd_id128_from_string(k, &id);
} else
r = sd_id128_from_string(fn, &id);
if (!streq(e + 1, namespace))
return false;
- k = strndupa(fn, e - fn);
+ k = strndupa_safe(fn, e - fn);
return id128_is_valid(k);
}
if (!e)
return id128_is_valid(de->d_name); /* No namespace */
- n = strndupa(de->d_name, e - de->d_name);
+ n = strndupa_safe(de->d_name, e - de->d_name);
if (!id128_is_valid(n))
return false;
compressed = compressed2 = malloc(BUFSIZE_2);
assert_se(compressed2);
r = compress(data, data_len, compressed, BUFSIZE_2, &csize);
- assert(r == 0);
+ assert_se(r == 0);
}
assert_se(r == 0);
}
_public_ int sd_peer_get_session(int fd, char **session) {
- struct ucred ucred = {};
+ struct ucred ucred = UCRED_INVALID;
int r;
assert_return(fd >= 0, -EBADF);
int socket_open(int family) {
int fd;
- fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, family);
+ fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, family);
if (fd < 0)
return -errno;
};
static struct test_async_object *test_async_object_free(struct test_async_object *t) {
- assert(t);
+ assert_se(t);
free(t->ifname);
return mfree(t);
static void test_async_object_destroy(void *userdata) {
struct test_async_object *t = userdata;
- assert(userdata);
+ assert_se(userdata);
log_info("%s: n_ref=%u", __func__, t->n_ref);
test_async_object_unref(t);
uint16_t id;
uint8_t cmd;
- assert(genl);
- assert(m);
+ assert_se(genl);
+ assert_se(m);
assert_se(sd_genl_message_get_family_name(genl, m, &name) >= 0);
assert_se(streq(name, CTRL_GENL_NAME));
*/
char *l, *v = NULL, *converted;
- l = strndupa(c->x11_layout, strcspn(c->x11_layout, ","));
+ l = strndupa_safe(c->x11_layout, strcspn(c->x11_layout, ","));
if (c->x11_variant)
- v = strndupa(c->x11_variant, strcspn(c->x11_variant, ","));
+ v = strndupa_safe(c->x11_variant,
+ strcspn(c->x11_variant, ","));
r = find_converted_keymap(l, v, &converted);
if (r < 0)
return r;
fmt = strjoina("libxkbcommon: ", format);
DISABLE_WARNING_FORMAT_NONLITERAL;
- log_internalv(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, fmt, args);
+ log_internalv(LOG_DEBUG, 0, PROJECT_FILE, __LINE__, __func__, fmt, args);
REENABLE_WARNING;
}
host_basename = basename(host_path);
container_basename = basename(container_path);
- t = strdupa(container_path);
+ t = strdupa_safe(container_path);
container_dirname = dirname(t);
hostfd = open_parent(host_path, O_CLOEXEC, 0);
if (!p)
mtu = value;
else
- mtu = strndupa(value, p - value);
+ mtu = strndupa_safe(value, p - value);
r = network_set_mtu(context, ifname, family, mtu);
if (r < 0)
if (q[1] != ':')
return -EINVAL;
- buf = strndupa(p + 1, q - p - 1);
+ buf = strndupa_safe(p + 1, q - p - 1);
p = q + 2;
} else {
q = strchr(p, ':');
if (!q)
return -EINVAL;
- buf = strndupa(p, q - p);
+ buf = strndupa_safe(p, q - p);
p = q + 1;
}
if (!p)
return -EINVAL;
- q = strndupa(*value, p - *value);
+ q = strndupa_safe(*value, p - *value);
r = safe_atou8(q, ret);
if (r < 0)
return r;
return -EINVAL;
if (p != value) {
- hostname = strndupa(value, p - value);
+ hostname = strndupa_safe(value, p - value);
if (!hostname_is_valid(hostname, 0))
return -EINVAL;
}
if (!p)
return -EINVAL;
- ifname = strndupa(value, p - value);
+ ifname = strndupa_safe(value, p - value);
value = p + 1;
if (!p)
dhcp_type = value;
else
- dhcp_type = strndupa(value, p - value);
+ dhcp_type = strndupa_safe(value, p - value);
r = network_set_dhcp_type(context, ifname, dhcp_type);
if (r < 0)
if (r < 0)
return r;
} else {
- dns = strndupa(value, p - value);
+ dns = strndupa_safe(value, p - value);
r = network_set_dns(context, ifname, dns);
if (r < 0)
return r;
if (!p)
return -EINVAL;
- ifname = strndupa(value, p - value);
+ ifname = strndupa_safe(value, p - value);
value = p + 1;
p = strchr(value, ':');
if (!p)
dhcp_type = value;
else
- dhcp_type = strndupa(value, p - value);
+ dhcp_type = strndupa_safe(value, p - value);
r = network_set_dhcp_type(context, ifname, dhcp_type);
if (r < 0)
if (p[1] != ':')
return -EINVAL;
- buf = strndupa(value + 1, p - value - 1);
+ buf = strndupa_safe(value + 1, p - value - 1);
value = p + 2;
family = AF_INET6;
} else {
if (!p)
return -EINVAL;
- buf = strndupa(value, p - value);
+ buf = strndupa_safe(value, p - value);
value = p + 1;
family = AF_INET;
}
if (!p)
return -EINVAL;
- name = strndupa(value, p - value);
+ name = strndupa_safe(value, p - value);
netdev = netdev_get(context, name);
if (!netdev) {
if (!p)
return -EINVAL;
- name = strndupa(value, p - value);
+ name = strndupa_safe(value, p - value);
netdev = netdev_get(context, name);
if (!netdev) {
if (!p)
return -EINVAL;
- name = strndupa(value, p - value);
+ name = strndupa_safe(value, p - value);
netdev = netdev_get(context, name);
if (!netdev) {
if (!p)
slaves = value;
else
- slaves = strndupa(value, p - value);
+ slaves = strndupa_safe(value, p - value);
if (isempty(slaves))
return -EINVAL;
if (!p)
return -EINVAL;
- name = strndupa(value, p - value);
+ name = strndupa_safe(value, p - value);
r = ether_addr_from_string(p + 1, &mac);
if (r < 0)
return sd_dhcp6_lease_get_pd(lease, &pd_prefix, &pd_prefix_len, &lifetime_preferred, &lifetime_valid) >= 0;
}
-static int dhcp6_pd_get_link_by_prefix(Manager *manager, const struct in6_addr *prefix, Link **ret) {
- union in_addr_union u;
- Link *link;
+static void link_remove_dhcp6_pd_prefix(Link *link, const struct in6_addr *prefix) {
+ void *key;
- assert(manager);
+ assert(link);
+ assert(link->manager);
assert(prefix);
- u.in6 = *prefix;
+ if (hashmap_get(link->manager->links_by_dhcp6_pd_prefix, prefix) != link)
+ return;
- HASHMAP_FOREACH(link, manager->links_by_index) {
- if (!link_dhcp6_pd_is_enabled(link))
- continue;
+ hashmap_remove2(link->manager->links_by_dhcp6_pd_prefix, prefix, &key);
+ free(key);
+}
+
+static int link_add_dhcp6_pd_prefix(Link *link, const struct in6_addr *prefix) {
+ _cleanup_free_ struct in6_addr *copy = NULL;
+ int r;
- if (link->network->dhcp6_pd_assign) {
- Address *address;
+ assert(link);
+ assert(prefix);
- SET_FOREACH(address, link->addresses) {
- if (address->source != NETWORK_CONFIG_SOURCE_DHCP6PD)
- continue;
- assert(address->family == AF_INET6);
+ copy = newdup(struct in6_addr, prefix, 1);
+ if (!copy)
+ return -ENOMEM;
- if (in_addr_prefix_covers(AF_INET6, &u, 64, &address->in_addr) > 0) {
- if (ret)
- *ret = link;
- return 0;
- }
- }
- } else {
- Route *route;
-
- SET_FOREACH(route, link->routes) {
- if (route->source != NETWORK_CONFIG_SOURCE_DHCP6PD)
- continue;
- assert(route->family == AF_INET6);
-
- if (in6_addr_equal(&route->dst.in6, prefix)) {
- if (ret)
- *ret = link;
- return 0;
- }
- }
- }
- }
+ r = hashmap_ensure_put(&link->manager->links_by_dhcp6_pd_prefix, &in6_addr_hash_ops_free, copy, link);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ TAKE_PTR(copy);
- return -ENOENT;
+ return 0;
}
-static int dhcp6_pd_get_assigned_prefix(Link *link, const struct in6_addr *pd_prefix, uint8_t pd_prefix_len, struct in6_addr *ret) {
- union in_addr_union u;
+static int link_get_by_dhcp6_pd_prefix(Manager *manager, const struct in6_addr *prefix, Link **ret) {
+ Link *link;
+
+ assert(manager);
+ assert(prefix);
+
+ link = hashmap_get(manager->links_by_dhcp6_pd_prefix, prefix);
+ if (!link)
+ return -ENODEV;
+ if (ret)
+ *ret = link;
+ return 0;
+}
+
+static int dhcp6_pd_get_assigned_prefix(Link *link, const struct in6_addr *pd_prefix, uint8_t pd_prefix_len, struct in6_addr *ret) {
assert(link);
assert(pd_prefix);
if (!link_dhcp6_pd_is_enabled(link))
return -ENOENT;
- u.in6 = *pd_prefix;
-
if (link->network->dhcp6_pd_assign) {
Address *address;
continue;
assert(address->family == AF_INET6);
- if (in_addr_prefix_covers(AF_INET6, &u, pd_prefix_len, &address->in_addr) <= 0)
+ if (in6_addr_prefix_covers(pd_prefix, pd_prefix_len, &address->in_addr.in6) <= 0)
continue;
if (ret) {
- union in_addr_union prefix = address->in_addr;
+ struct in6_addr prefix = address->in_addr.in6;
- in_addr_mask(AF_INET6, &prefix, 64);
- *ret = prefix.in6;
+ in6_addr_mask(&prefix, 64);
+ *ret = prefix;
}
return 0;
}
continue;
assert(route->family == AF_INET6);
- if (in_addr_prefix_covers(AF_INET6, &u, pd_prefix_len, &route->dst) > 0) {
+ if (in6_addr_prefix_covers(pd_prefix, pd_prefix_len, &route->dst.in6) > 0) {
if (ret)
*ret = route->dst.in6;
return 0;
if (link->radv)
(void) sd_radv_remove_prefix(link->radv, &route->dst.in6, 64);
+ link_remove_dhcp6_pd_prefix(link, &route->dst.in6);
+
k = route_remove(route);
if (k < 0)
r = k;
}
SET_FOREACH(address, link->addresses) {
+ struct in6_addr prefix;
+
if (address->source != NETWORK_CONFIG_SOURCE_DHCP6PD)
continue;
if (only_marked && !address_is_marked(address))
continue;
- if (link->radv) {
- union in_addr_union prefix = address->in_addr;
+ prefix = address->in_addr.in6;
+ in6_addr_mask(&prefix, 64);
- in_addr_mask(AF_INET6, &prefix, 64);
- (void) sd_radv_remove_prefix(link->radv, &prefix.in6, 64);
- }
+ if (link->radv)
+ (void) sd_radv_remove_prefix(link->radv, &prefix, 64);
+
+ link_remove_dhcp6_pd_prefix(link, &prefix);
k = address_remove(address);
if (k < 0)
}
static int dhcp6_pd_check_ready(Link *link) {
- bool has_ready = false;
- Address *address;
int r;
assert(link);
+ assert(link->network);
if (link->dhcp6_pd_messages > 0) {
log_link_debug(link, "%s(): DHCPv6PD addresses and routes are not set.", __func__);
return 0;
}
- SET_FOREACH(address, link->addresses) {
- if (address->source != NETWORK_CONFIG_SOURCE_DHCP6PD)
- continue;
- if (address_is_ready(address)) {
- has_ready = true;
- break;
+ if (link->network->dhcp6_pd_assign) {
+ bool has_ready = false;
+ Address *address;
+
+ SET_FOREACH(address, link->addresses) {
+ if (address->source != NETWORK_CONFIG_SOURCE_DHCP6PD)
+ continue;
+ if (address_is_ready(address)) {
+ has_ready = true;
+ break;
+ }
}
- }
- if (!has_ready) {
- SET_FOREACH(address, link->addresses)
- if (address->source == NETWORK_CONFIG_SOURCE_DHCP6PD)
- address->callback = dhcp6_pd_address_ready_callback;
+ if (!has_ready) {
+ SET_FOREACH(address, link->addresses)
+ if (address->source == NETWORK_CONFIG_SOURCE_DHCP6PD)
+ address->callback = dhcp6_pd_address_ready_callback;
- log_link_debug(link, "%s(): no DHCPv6PD address is ready.", __func__);
- return 0;
+ log_link_debug(link, "%s(): no DHCPv6PD address is ready.", __func__);
+ return 0;
+ }
}
link->dhcp6_pd_configured = true;
if (r < 0)
return r;
- return 0;
+ return link_add_dhcp6_pd_prefix(link, prefix);
}
static bool link_has_preferred_subnet_id(Link *link) {
/* Verify that the prefix we did calculate fits in the pd prefix.
* This should not fail as we checked the prefix size beforehand */
- assert_se(in_addr_prefix_covers(AF_INET6, (const union in_addr_union*) pd_prefix, pd_prefix_len, &prefix) > 0);
+ assert_se(in6_addr_prefix_covers(pd_prefix, pd_prefix_len, &prefix.in6) > 0);
- if (dhcp6_pd_get_link_by_prefix(link->manager, &prefix.in6, &assigned_link) >= 0 &&
+ if (link_get_by_dhcp6_pd_prefix(link->manager, &prefix.in6, &assigned_link) >= 0 &&
assigned_link != link) {
_cleanup_free_ char *assigned_buf = NULL;
for (uint64_t n = 0; n < n_prefixes; n++) {
/* If we do not have an allocation preference just iterate
* through the address space and return the first free prefix. */
- if (dhcp6_pd_get_link_by_prefix(link->manager, &prefix.in6, &assigned_link) < 0 ||
+ if (link_get_by_dhcp6_pd_prefix(link->manager, &prefix.in6, &assigned_link) < 0 ||
assigned_link == link) {
*ret = prefix.in6;
return 0;
return log_link_warning_errno(link, SYNTHETIC_ERRNO(ERANGE), "Couldn't find a suitable prefix. Ran out of address space.");
}
-static void dhcp6_pd_prefix_distribute(Link *dhcp6_link,
- const struct in6_addr *pd_prefix,
- uint8_t pd_prefix_len,
- uint32_t lifetime_preferred,
- uint32_t lifetime_valid,
- bool assign_preferred_subnet_id) {
+static int dhcp6_pd_prefix_distribute(
+ Link *dhcp6_link,
+ const struct in6_addr *pd_prefix,
+ uint8_t pd_prefix_len,
+ uint32_t lifetime_preferred,
+ uint32_t lifetime_valid,
+ bool assign_preferred_subnet_id) {
Link *link;
int r;
assert(pd_prefix_len <= 64);
HASHMAP_FOREACH(link, dhcp6_link->manager->links_by_index) {
- _cleanup_free_ char *assigned_buf = NULL;
+ _cleanup_free_ char *buf = NULL;
struct in6_addr assigned_prefix;
- if (link == dhcp6_link)
+ if (!link_dhcp6_pd_is_enabled(link))
continue;
- if (!link_dhcp6_pd_is_enabled(link))
+ if (link == dhcp6_link && !link->network->dhcp6_pd_assign)
continue;
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
if (assign_preferred_subnet_id != link_has_preferred_subnet_id(link))
continue;
- r = dhcp6_pd_get_assigned_prefix(link, pd_prefix, pd_prefix_len, &assigned_prefix);
- if (r < 0) {
- r = dhcp6_get_preferred_delegated_prefix(link, pd_prefix, pd_prefix_len, &assigned_prefix);
- if (r < 0)
- continue;
- }
+ if (dhcp6_pd_get_assigned_prefix(link, pd_prefix, pd_prefix_len, &assigned_prefix) < 0 &&
+ dhcp6_get_preferred_delegated_prefix(link, pd_prefix, pd_prefix_len, &assigned_prefix) < 0)
+ continue;
- (void) in6_addr_to_string(&assigned_prefix, &assigned_buf);
+ (void) in6_addr_prefix_to_string(&assigned_prefix, 64, &buf);
r = dhcp6_pd_assign_prefix(link, &assigned_prefix, lifetime_preferred, lifetime_valid);
if (r < 0) {
- log_link_error_errno(link, r, "Unable to assign/update prefix %s/64: %m",
- strna(assigned_buf));
+ log_link_warning_errno(link, r, "Failed to assign/update prefix %s: %m", strna(buf));
+ if (link == dhcp6_link)
+ return r;
+
link_enter_failed(link);
- } else
- log_link_debug(link, "Assigned prefix %s/64", strna(assigned_buf));
+ continue;
+ }
+
+ log_link_debug(link, "Assigned prefix %s", strna(buf));
}
+
+ return 0;
}
static int dhcp6_pd_prepare(Link *link) {
for (sd_dhcp6_lease_reset_pd_prefix_iter(dhcp6_link->dhcp6_lease);;) {
uint32_t lifetime_preferred, lifetime_valid;
struct in6_addr pd_prefix;
- union in_addr_union prefix;
uint8_t pd_prefix_len;
r = sd_dhcp6_lease_get_pd(dhcp6_link->dhcp6_lease, &pd_prefix, &pd_prefix_len,
assert(pd_prefix_len <= 64);
- prefix.in6 = pd_prefix;
- r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len);
+ /* Mask prefix for safety. */
+ r = in6_addr_mask(&pd_prefix, pd_prefix_len);
if (r < 0)
return log_link_error_errno(dhcp6_link, r, "Failed to mask DHCPv6 PD prefix: %m");
uint64_t n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
_cleanup_free_ char *buf = NULL;
- (void) in6_addr_prefix_to_string(&prefix.in6, pd_prefix_len, &buf);
+ (void) in6_addr_prefix_to_string(&pd_prefix, pd_prefix_len, &buf);
log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s",
n_prefixes, strna(buf));
}
- dhcp6_pd_prefix_distribute(dhcp6_link,
- &prefix.in6,
- pd_prefix_len,
- lifetime_preferred,
- lifetime_valid,
- true);
-
- dhcp6_pd_prefix_distribute(dhcp6_link,
- &prefix.in6,
- pd_prefix_len,
- lifetime_preferred,
- lifetime_valid,
- false);
+ r = dhcp6_pd_prefix_distribute(dhcp6_link,
+ &pd_prefix,
+ pd_prefix_len,
+ lifetime_preferred,
+ lifetime_valid,
+ true);
+ if (r < 0)
+ return r;
+
+ r = dhcp6_pd_prefix_distribute(dhcp6_link,
+ &pd_prefix,
+ pd_prefix_len,
+ lifetime_preferred,
+ lifetime_valid,
+ false);
+ if (r < 0)
+ return r;
}
HASHMAP_FOREACH(link, dhcp6_link->manager->links_by_index) {
if (r < 0)
return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set ifindex: %m");
- if (link->network->dhcp6_rapid_commit) {
- r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT);
- if (r < 0)
- return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set request flag for rapid commit: %m");
- }
-
if (link->network->dhcp6_mudurl) {
r = sd_dhcp6_client_set_request_mud_url(client, link->network->dhcp6_mudurl);
if (r < 0)
if (!address_exists(a))
continue;
if (IN_SET(a->source,
- NETWORK_CONFIG_SOURCE_IPV4LL, NETWORK_CONFIG_SOURCE_DHCP4,
- NETWORK_CONFIG_SOURCE_DHCP6, NETWORK_CONFIG_SOURCE_NDISC)) {
+ NETWORK_CONFIG_SOURCE_IPV4LL,
+ NETWORK_CONFIG_SOURCE_DHCP4,
+ NETWORK_CONFIG_SOURCE_DHCP6,
+ NETWORK_CONFIG_SOURCE_DHCP6PD,
+ NETWORK_CONFIG_SOURCE_NDISC)) {
has_dynamic_address = true;
break;
}
if ((link_ipv4ll_enabled(link) || link_dhcp4_enabled(link) || link_dhcp6_with_address_enabled(link) ||
(link_dhcp6_pd_is_enabled(link) && link->network->dhcp6_pd_assign)) && !has_dynamic_address)
/* When DHCP[46] or IPv4LL is enabled, at least one address is acquired by them. */
- return (void) log_link_debug(link, "%s(): DHCPv4, DHCPv6 or IPv4LL is enabled but no dynamic address is assigned yet.", __func__);
+ return (void) log_link_debug(link, "%s(): DHCPv4, DHCPv6, DHCPv6PD or IPv4LL is enabled but no dynamic address is assigned yet.", __func__);
/* Ignore NDisc when ConfigureWithoutCarrier= is enabled, as IPv6AcceptRA= is enabled by default. */
if (link_ipv4ll_enabled(link) || link_dhcp4_enabled(link) ||
m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref);
m->links_by_name = hashmap_free(m->links_by_name);
m->links_by_hw_addr = hashmap_free(m->links_by_hw_addr);
+ m->links_by_dhcp6_pd_prefix = hashmap_free(m->links_by_dhcp6_pd_prefix);
m->links_by_index = hashmap_free_with_destructor(m->links_by_index, link_unref);
m->networks = ordered_hashmap_free_with_destructor(m->networks, network_unref);
Hashmap *links_by_index;
Hashmap *links_by_name;
Hashmap *links_by_hw_addr;
+ Hashmap *links_by_dhcp6_pd_prefix;
Hashmap *netdevs;
OrderedHashmap *networks;
OrderedSet *address_pools;
case RTM_NEWNEIGH:
if (neighbor) {
neighbor_enter_configured(neighbor);
- log_neighbor_debug(tmp, "Received remembered", link);
+ log_neighbor_debug(neighbor, "Received remembered", link);
} else {
neighbor_enter_configured(tmp);
log_neighbor_debug(tmp, "Remembering", link);
DHCPv6.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp6_use_hostname)
DHCPv6.UseDomains, config_parse_dhcp_use_domains, AF_INET6, 0
DHCPv6.UseNTP, config_parse_dhcp_use_ntp, AF_INET6, 0
-DHCPv6.RapidCommit, config_parse_bool, 0, offsetof(Network, dhcp6_rapid_commit)
DHCPv6.MUDURL, config_parse_mud_url, 0, offsetof(Network, dhcp6_mudurl)
DHCPv6.RequestOptions, config_parse_dhcp_request_options, AF_INET6, 0
DHCPv6.UserClass, config_parse_dhcp_user_or_vendor_class, AF_INET6, offsetof(Network, dhcp6_user_class)
IPv6SendRA.DNSLifetimeSec, config_parse_sec, 0, offsetof(Network, router_dns_lifetime_usec)
IPv6SendRA.UplinkInterface, config_parse_uplink, 0, 0
IPv6Prefix.Prefix, config_parse_prefix, 0, 0
-IPv6Prefix.OnLink, config_parse_prefix_flags, 0, 0
-IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags, 0, 0
+IPv6Prefix.OnLink, config_parse_prefix_boolean, 0, 0
+IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_boolean, 0, 0
IPv6Prefix.ValidLifetimeSec, config_parse_prefix_lifetime, 0, 0
IPv6Prefix.PreferredLifetimeSec, config_parse_prefix_lifetime, 0, 0
-IPv6Prefix.Assign, config_parse_prefix_assign, 0, 0
+IPv6Prefix.Assign, config_parse_prefix_boolean, 0, 0
IPv6Prefix.RouteMetric, config_parse_prefix_metric, 0, 0
IPv6Prefix.Token, config_parse_prefix_token, 0, 0
IPv6RoutePrefix.Route, config_parse_route_prefix, 0, 0
DHCP.RouteTable, config_parse_dhcp_or_ra_route_table, (RTPROT_DHCP<<16) | AF_UNSPEC, 0
DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
DHCP.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port)
-DHCP.RapidCommit, config_parse_bool, 0, offsetof(Network, dhcp6_rapid_commit)
+DHCP.RapidCommit, config_parse_warn_compat, DISABLED_LEGACY, 0
DHCP.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information)
DHCPv4.UseDomainName, config_parse_dhcp_use_domains, AF_INET, 0
DHCPv4.CriticalConnection, config_parse_tristate, 0, offsetof(Network, dhcp_critical)
DHCPv6.RouteMetric, config_parse_dhcp_or_ra_route_metric, AF_INET6, 0
+DHCPv6.RapidCommit, config_parse_warn_compat, DISABLED_LEGACY, 0
IPv6AcceptRA.DenyList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_deny_listed_prefix)
IPv6AcceptRA.BlackList, config_parse_in_addr_prefixes, AF_INET6, offsetof(Network, ndisc_deny_listed_prefix)
TrafficControlQueueingDiscipline.Parent, config_parse_qdisc_parent, _QDISC_KIND_INVALID, 0
.dhcp6_use_dns = true,
.dhcp6_use_hostname = true,
.dhcp6_use_ntp = true,
- .dhcp6_rapid_commit = true,
.dhcp6_duid.type = _DUID_TYPE_INVALID,
.dhcp6_pd = -1,
.dhcp_server_emit_timezone = true,
.router_lifetime_usec = 30 * USEC_PER_MINUTE,
+ .router_dns_lifetime_usec = 7 * USEC_PER_DAY,
.router_emit_dns = true,
.router_emit_domains = true,
bool dhcp6_use_hostname;
bool dhcp6_use_ntp;
bool dhcp6_use_ntp_set;
- bool dhcp6_rapid_commit;
bool dhcp6_route_table;
bool dhcp6_route_table_set;
bool dhcp6_route_table_set_explicitly;
}
network_config_section_free(prefix->section);
- sd_radv_prefix_unref(prefix->radv_prefix);
set_free(prefix->tokens);
return mfree(prefix);
DEFINE_NETWORK_SECTION_FUNCTIONS(Prefix, prefix_free);
-static int prefix_new(Prefix **ret) {
- _cleanup_(prefix_freep) Prefix *prefix = NULL;
-
- prefix = new0(Prefix, 1);
- if (!prefix)
- return -ENOMEM;
-
- if (sd_radv_prefix_new(&prefix->radv_prefix) < 0)
- return -ENOMEM;
-
- *ret = TAKE_PTR(prefix);
-
- return 0;
-}
-
static int prefix_new_static(Network *network, const char *filename, unsigned section_line, Prefix **ret) {
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
_cleanup_(prefix_freep) Prefix *prefix = NULL;
return 0;
}
- r = prefix_new(&prefix);
- if (r < 0)
- return r;
+ prefix = new(Prefix, 1);
+ if (!prefix)
+ return -ENOMEM;
- prefix->network = network;
- prefix->section = TAKE_PTR(n);
+ *prefix = (Prefix) {
+ .network = network,
+ .section = TAKE_PTR(n),
+
+ .preferred_lifetime = 7 * USEC_PER_DAY,
+ .valid_lifetime = 30 * USEC_PER_DAY,
+ .onlink = true,
+ .address_auto_configuration = true,
+ };
r = hashmap_ensure_put(&network->prefixes_by_section, &network_config_hash_ops, prefix->section, prefix);
if (r < 0)
return r;
*ret = TAKE_PTR(prefix);
-
return 0;
}
}
network_config_section_free(prefix->section);
- sd_radv_route_prefix_unref(prefix->radv_route_prefix);
return mfree(prefix);
}
DEFINE_NETWORK_SECTION_FUNCTIONS(RoutePrefix, route_prefix_free);
-static int route_prefix_new(RoutePrefix **ret) {
- _cleanup_(route_prefix_freep) RoutePrefix *prefix = NULL;
-
- prefix = new0(RoutePrefix, 1);
- if (!prefix)
- return -ENOMEM;
-
- if (sd_radv_route_prefix_new(&prefix->radv_route_prefix) < 0)
- return -ENOMEM;
-
- *ret = TAKE_PTR(prefix);
-
- return 0;
-}
-
static int route_prefix_new_static(Network *network, const char *filename, unsigned section_line, RoutePrefix **ret) {
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
_cleanup_(route_prefix_freep) RoutePrefix *prefix = NULL;
return 0;
}
- r = route_prefix_new(&prefix);
- if (r < 0)
- return r;
+ prefix = new(RoutePrefix, 1);
+ if (!prefix)
+ return -ENOMEM;
- prefix->network = network;
- prefix->section = TAKE_PTR(n);
+ *prefix = (RoutePrefix) {
+ .network = network,
+ .section = TAKE_PTR(n),
+
+ .lifetime = 7 * USEC_PER_DAY,
+ };
r = hashmap_ensure_put(&network->route_prefixes_by_section, &network_config_hash_ops, prefix->section, prefix);
if (r < 0)
return r;
*ret = TAKE_PTR(prefix);
-
return 0;
}
HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
_cleanup_set_free_ Set *addresses = NULL;
- struct in6_addr prefix, *a;
- uint8_t prefixlen;
+ struct in6_addr *a;
if (!p->assign)
continue;
- r = sd_radv_prefix_get_prefix(p->radv_prefix, &prefix, &prefixlen);
- if (r < 0)
- return r;
-
/* radv_generate_addresses() below requires the prefix length <= 64. */
- if (prefixlen > 64) {
- _cleanup_free_ char *str = NULL;
-
- (void) in6_addr_prefix_to_string(&prefix, prefixlen, &str);
- log_link_debug(link,
- "Prefix is longer than 64, refusing to assign an address in %s.",
- strna(str));
+ if (p->prefixlen > 64)
continue;
- }
- r = radv_generate_addresses(link, p->tokens, &prefix, prefixlen, &addresses);
+ r = radv_generate_addresses(link, p->tokens, &p->prefix, p->prefixlen, &addresses);
if (r < 0)
return r;
address->source = NETWORK_CONFIG_SOURCE_STATIC;
address->family = AF_INET6;
address->in_addr.in6 = *a;
- address->prefixlen = prefixlen;
+ address->prefixlen = p->prefixlen;
address->route_metric = p->route_metric;
r = link_request_static_address(link, TAKE_PTR(address), true);
return 0;
}
+static uint32_t usec_to_lifetime(usec_t usec) {
+ uint64_t t;
+
+ if (usec == USEC_INFINITY)
+ return UINT32_MAX;
+
+ t = DIV_ROUND_UP(usec, USEC_PER_SEC);
+ if (t >= UINT32_MAX)
+ return UINT32_MAX;
+
+ return (uint32_t) t;
+}
+
+static int radv_set_prefix(Link *link, Prefix *prefix) {
+ _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
+ int r;
+
+ assert(link);
+ assert(link->radv);
+ assert(prefix);
+
+ r = sd_radv_prefix_new(&p);
+ if (r < 0)
+ return r;
+
+ r = sd_radv_prefix_set_prefix(p, &prefix->prefix, prefix->prefixlen);
+ if (r < 0)
+ return r;
+
+ r = sd_radv_prefix_set_preferred_lifetime(p, usec_to_lifetime(prefix->preferred_lifetime));
+ if (r < 0)
+ return r;
+
+ r = sd_radv_prefix_set_valid_lifetime(p, usec_to_lifetime(prefix->valid_lifetime));
+ if (r < 0)
+ return r;
+
+ r = sd_radv_prefix_set_onlink(p, prefix->onlink);
+ if (r < 0)
+ return r;
+
+ r = sd_radv_prefix_set_address_autoconfiguration(p, prefix->address_auto_configuration);
+ if (r < 0)
+ return r;
+
+ return sd_radv_add_prefix(link->radv, p, false);
+}
+
+static int radv_set_route_prefix(Link *link, RoutePrefix *prefix) {
+ _cleanup_(sd_radv_route_prefix_unrefp) sd_radv_route_prefix *p = NULL;
+ int r;
+
+ assert(link);
+ assert(link->radv);
+ assert(prefix);
+
+ r = sd_radv_route_prefix_new(&p);
+ if (r < 0)
+ return r;
+
+ r = sd_radv_route_prefix_set_prefix(p, &prefix->prefix, prefix->prefixlen);
+ if (r < 0)
+ return r;
+
+ r = sd_radv_route_prefix_set_lifetime(p, usec_to_lifetime(prefix->lifetime));
+ if (r < 0)
+ return r;
+
+ return sd_radv_add_route_prefix(link->radv, p, false);
+}
+
static int network_get_ipv6_dns(Network *network, struct in6_addr **ret_addresses, size_t *ret_size) {
_cleanup_free_ struct in6_addr *addresses = NULL;
size_t n_addresses = 0;
static int radv_set_dns(Link *link, Link *uplink) {
_cleanup_free_ struct in6_addr *dns = NULL;
- usec_t lifetime_usec;
size_t n_dns;
int r;
*(p++) = link->network->router_dns[i];
n_dns = p - dns;
- lifetime_usec = link->network->router_dns_lifetime_usec;
goto set_dns;
}
- lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC;
-
r = network_get_ipv6_dns(link->network, &dns, &n_dns);
if (r > 0)
goto set_dns;
set_dns:
return sd_radv_set_rdnss(link->radv,
- DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC),
+ usec_to_lifetime(link->network->router_dns_lifetime_usec),
dns, n_dns);
}
static int radv_set_domains(Link *link, Link *uplink) {
- OrderedSet *search_domains;
- usec_t lifetime_usec;
_cleanup_free_ char **s = NULL; /* just free() because the strings are owned by the set */
+ OrderedSet *search_domains;
if (!link->network->router_emit_domains)
return 0;
search_domains = link->network->router_search_domains;
- lifetime_usec = link->network->router_dns_lifetime_usec;
if (search_domains)
goto set_domains;
- lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC;
-
search_domains = link->network->search_domains;
if (search_domains)
goto set_domains;
return log_oom();
return sd_radv_set_dnssl(link->radv,
- DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC),
+ usec_to_lifetime(link->network->router_dns_lifetime_usec),
s);
}
}
HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
- r = sd_radv_add_prefix(link->radv, p->radv_prefix, false);
- if (r == -EEXIST)
- continue;
- if (r == -ENOEXEC) {
- log_link_warning_errno(link, r, "[IPv6Prefix] section configured without Prefix= setting, ignoring section.");
- continue;
- }
- if (r < 0)
+ r = radv_set_prefix(link, p);
+ if (r < 0 && r != -EEXIST)
return r;
}
HASHMAP_FOREACH(q, link->network->route_prefixes_by_section) {
- r = sd_radv_add_route_prefix(link->radv, q->radv_route_prefix, false);
- if (r == -EEXIST)
- continue;
- if (r < 0)
+ r = radv_set_route_prefix(link, q);
+ if (r < 0 && r != -EEXIST)
return r;
}
return 0;
}
+static int prefix_section_verify(Prefix *p) {
+ assert(p);
+
+ if (section_is_invalid(p->section))
+ return -EINVAL;
+
+ if (in6_addr_is_null(&p->prefix))
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: [IPv6Prefix] section without Prefix= field configured, "
+ "or specified prefix is the null address. "
+ "Ignoring [IPv6Prefix] section from line %u.",
+ p->section->filename, p->section->line);
+
+ if (p->prefixlen < 3 || p->prefixlen > 128)
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: Invalid prefix length %u is specified in [IPv6Prefix] section. "
+ "Valid range is 3…128. Ignoring [IPv6Prefix] section from line %u.",
+ p->section->filename, p->prefixlen, p->section->line);
+
+ if (p->prefixlen > 64) {
+ _cleanup_free_ char *str = NULL;
+
+ if (p->assign)
+ (void) in6_addr_prefix_to_string(&p->prefix, p->prefixlen, &str);
+
+ log_info("%s: Unusual prefix length %u (> 64) is specified in [IPv6Prefix] section from line %u%s%s.",
+ p->section->filename, p->prefixlen, p->section->line,
+ p->assign ? ", refusing to assign an address in " : "",
+ p->assign ? strna(str) : "");
+
+ p->assign = false;
+ }
+
+ if (p->valid_lifetime == 0)
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: The valid lifetime of prefix cannot be zero. "
+ "Ignoring [IPv6Prefix] section from line %u.",
+ p->section->filename, p->section->line);
+
+ if (p->preferred_lifetime > p->valid_lifetime)
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: The preferred lifetime %s is longer than the valid lifetime %s. "
+ "Ignoring [IPv6Prefix] section from line %u.",
+ p->section->filename,
+ FORMAT_TIMESPAN(p->preferred_lifetime, USEC_PER_SEC),
+ FORMAT_TIMESPAN(p->valid_lifetime, USEC_PER_SEC),
+ p->section->line);
+
+ return 0;
+}
+
void network_drop_invalid_prefixes(Network *network) {
- Prefix *prefix;
+ Prefix *p;
assert(network);
- HASHMAP_FOREACH(prefix, network->prefixes_by_section)
- if (section_is_invalid(prefix->section))
- prefix_free(prefix);
+ HASHMAP_FOREACH(p, network->prefixes_by_section)
+ if (prefix_section_verify(p) < 0)
+ prefix_free(p);
+}
+
+static int route_prefix_section_verify(RoutePrefix *p) {
+ if (section_is_invalid(p->section))
+ return -EINVAL;
+
+ if (p->prefixlen > 128)
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: Invalid prefix length %u is specified in [IPv6RoutePrefix] section. "
+ "Valid range is 0…128. Ignoring [IPv6RoutePrefix] section from line %u.",
+ p->section->filename, p->prefixlen, p->section->line);
+
+ if (p->lifetime == 0)
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: The lifetime of route cannot be zero. "
+ "Ignoring [IPv6RoutePrefix] section from line %u.",
+ p->section->filename, p->section->line);
+
+ return 0;
}
void network_drop_invalid_route_prefixes(Network *network) {
- RoutePrefix *prefix;
+ RoutePrefix *p;
assert(network);
- HASHMAP_FOREACH(prefix, network->route_prefixes_by_section)
- if (section_is_invalid(prefix->section))
- route_prefix_free(prefix);
+ HASHMAP_FOREACH(p, network->route_prefixes_by_section)
+ if (route_prefix_section_verify(p) < 0)
+ route_prefix_free(p);
}
int config_parse_prefix(
void *data,
void *userdata) {
- Network *network = userdata;
_cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
- uint8_t prefixlen = 64;
- union in_addr_union in6addr;
+ Network *network = userdata;
+ union in_addr_union a;
int r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
- assert(data);
+ assert(userdata);
r = prefix_new_static(network, filename, section_line, &p);
if (r < 0)
return log_oom();
- r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen);
+ r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &p->prefixlen);
if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r, "Prefix is invalid, ignoring assignment: %s", rvalue);
- return 0;
- }
-
- r = sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen);
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set radv prefix, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Prefix is invalid, ignoring assignment: %s", rvalue);
return 0;
}
- p = NULL;
+ (void) in6_addr_mask(&a.in6, p->prefixlen);
+ p->prefix = a.in6;
+ TAKE_PTR(p);
return 0;
}
-int config_parse_prefix_flags(
+int config_parse_prefix_boolean(
const char *unit,
const char *filename,
unsigned line,
void *data,
void *userdata) {
- Network *network = userdata;
_cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
+ Network *network = userdata;
int r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
- assert(data);
+ assert(userdata);
r = prefix_new_static(network, filename, section_line, &p);
if (r < 0)
r = parse_boolean(rvalue);
if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
return 0;
}
if (streq(lvalue, "OnLink"))
- r = sd_radv_prefix_set_onlink(p->radv_prefix, r);
+ p->onlink = r;
else if (streq(lvalue, "AddressAutoconfiguration"))
- r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, r);
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set %s=, ignoring assignment: %m", lvalue);
- return 0;
- }
-
- p = NULL;
+ p->address_auto_configuration = r;
+ else if (streq(lvalue, "Assign"))
+ p->assign = r;
+ else
+ assert_not_reached();
+ TAKE_PTR(p);
return 0;
}
void *data,
void *userdata) {
- Network *network = userdata;
_cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
+ Network *network = userdata;
usec_t usec;
int r;
assert(section);
assert(lvalue);
assert(rvalue);
- assert(data);
+ assert(userdata);
r = prefix_new_static(network, filename, section_line, &p);
if (r < 0)
r = parse_sec(rvalue, &usec);
if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue);
- return 0;
- }
-
- /* a value of 0xffffffff represents infinity */
- if (streq(lvalue, "PreferredLifetimeSec"))
- r = sd_radv_prefix_set_preferred_lifetime(p->radv_prefix,
- DIV_ROUND_UP(usec, USEC_PER_SEC));
- else if (streq(lvalue, "ValidLifetimeSec"))
- r = sd_radv_prefix_set_valid_lifetime(p->radv_prefix,
- DIV_ROUND_UP(usec, USEC_PER_SEC));
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set %s=, ignoring assignment: %m", lvalue);
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Lifetime is invalid, ignoring assignment: %s", rvalue);
return 0;
}
- p = NULL;
-
- return 0;
-}
-
-int config_parse_prefix_assign(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Network *network = userdata;
- _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
- int r;
-
- assert(filename);
- assert(section);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- r = prefix_new_static(network, filename, section_line, &p);
- if (r < 0)
- return log_oom();
-
- r = parse_boolean(rvalue);
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "Failed to parse %s=, ignoring assignment: %s",
- lvalue, rvalue);
+ if (usec != USEC_INFINITY && DIV_ROUND_UP(usec, USEC_PER_SEC) >= UINT32_MAX) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Lifetime is too long, ignoring assignment: %s", rvalue);
return 0;
}
- p->assign = r;
- p = NULL;
+ if (streq(lvalue, "PreferredLifetimeSec"))
+ p->preferred_lifetime = usec;
+ else if (streq(lvalue, "ValidLifetimeSec"))
+ p->valid_lifetime = usec;
+ else
+ assert_not_reached();
+ TAKE_PTR(p);
return 0;
}
void *data,
void *userdata) {
- Network *network = userdata;
_cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
+ Network *network = userdata;
int r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
- assert(data);
+ assert(userdata);
r = prefix_new_static(network, filename, section_line, &p);
if (r < 0)
}
TAKE_PTR(p);
-
return 0;
}
void *data,
void *userdata) {
- Network *network = userdata;
_cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
- uint8_t prefixlen = 64;
- union in_addr_union in6addr;
+ Network *network = userdata;
+ union in_addr_union a;
int r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
- assert(data);
+ assert(userdata);
r = route_prefix_new_static(network, filename, section_line, &p);
if (r < 0)
return log_oom();
- r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen);
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r, "Route prefix is invalid, ignoring assignment: %s", rvalue);
- return 0;
- }
-
- r = sd_radv_prefix_set_route_prefix(p->radv_route_prefix, &in6addr.in6, prefixlen);
+ r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &p->prefixlen);
if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set route prefix, ignoring assignment: %m");
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Route prefix is invalid, ignoring assignment: %s", rvalue);
return 0;
}
- p = NULL;
+ (void) in6_addr_mask(&a.in6, p->prefixlen);
+ p->prefix = a.in6;
+ TAKE_PTR(p);
return 0;
}
void *data,
void *userdata) {
- Network *network = userdata;
_cleanup_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL;
+ Network *network = userdata;
usec_t usec;
int r;
assert(section);
assert(lvalue);
assert(rvalue);
- assert(data);
+ assert(userdata);
r = route_prefix_new_static(network, filename, section_line, &p);
if (r < 0)
return 0;
}
- /* a value of 0xffffffff represents infinity */
- r = sd_radv_route_prefix_set_lifetime(p->radv_route_prefix, DIV_ROUND_UP(usec, USEC_PER_SEC));
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "Failed to set route lifetime, ignoring assignment: %m");
+ if (usec != USEC_INFINITY && DIV_ROUND_UP(usec, USEC_PER_SEC) >= UINT32_MAX) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Lifetime is too long, ignoring assignment: %s", rvalue);
return 0;
}
- p = NULL;
+ p->lifetime = usec;
+ TAKE_PTR(p);
return 0;
}
Network *network;
NetworkConfigSection *section;
- sd_radv_prefix *radv_prefix;
+ struct in6_addr prefix;
+ uint8_t prefixlen;
+ usec_t preferred_lifetime;
+ usec_t valid_lifetime;
+
+ bool onlink;
+ bool address_auto_configuration;
bool assign;
uint32_t route_metric;
Network *network;
NetworkConfigSection *section;
- sd_radv_route_prefix *radv_route_prefix;
+ struct in6_addr prefix;
+ uint8_t prefixlen;
+ usec_t lifetime;
} RoutePrefix;
Prefix *prefix_free(Prefix *prefix);
CONFIG_PARSER_PROTOTYPE(config_parse_router_prefix_delegation);
CONFIG_PARSER_PROTOTYPE(config_parse_router_preference);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix);
-CONFIG_PARSER_PROTOTYPE(config_parse_prefix_flags);
+CONFIG_PARSER_PROTOTYPE(config_parse_prefix_boolean);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_lifetime);
-CONFIG_PARSER_PROTOTYPE(config_parse_prefix_assign);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_metric);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_token);
CONFIG_PARSER_PROTOTYPE(config_parse_radv_dns);
test_table(bond_primary_reselect, NETDEV_BOND_PRIMARY_RESELECT);
test_table(bond_xmit_hash_policy, NETDEV_BOND_XMIT_HASH_POLICY);
test_table(dhcp6_message_status, DHCP6_STATUS);
- test_table_sparse(dhcp6_message_type, DHCP6_MESSAGE); /* enum starts from 1 */
+ test_table_sparse(dhcp6_message_type, DHCP6_MESSAGE_TYPE); /* enum starts from 1 */
test_table(dhcp_use_domains, DHCP_USE_DOMAINS);
test_table(duplex, DUP);
test_table(ip6tnl_mode, NETDEV_IP6_TNL_MODE);
assert(send_fd >= 0);
- fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
+ fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
if (fd < 0)
return log_error_errno(errno, "Failed to allocate container netlink: %m");
range = strchr(rvalue, ':');
if (range) {
- shift = strndupa(rvalue, range - rvalue);
+ shift = strndupa_safe(rvalue, range - rvalue);
range++;
r = safe_atou32(range, &rn);
/* Create another cgroup below this one for the pids we forked off. We need this to be managed
* by the test so that pid1 doesn't delete it before we can read the xattrs. */
cgroup = path_join(cgroup_root, "oomdkilltest");
- assert(cgroup);
+ assert_se(cgroup);
assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, cgroup) >= 0);
/* If we don't have permissions to set xattrs we're likely in a userns or missing capabilities */
at = strchr(name, '@');
assert(at);
- prefix = strndupa(name, at + 1 - name);
+ prefix = strndupa_safe(name, at + 1 - name);
joined = strjoina(prefix, "*", at + 1);
r = sd_bus_message_append_strv(m, STRV_MAKE(joined));
q = strchr(p, '?');
if (q) {
- n = strndupa(p, q - p);
+ n = strndupa_safe(p, q - p);
q++;
for (;;) {
if (r < 0)
return log_error_errno(r, "Invalid port \"%s\".", port + 1);
- address = strndupa(address, port - address);
+ address = strndupa_safe(address, port - address);
}
r = asprintf(&full, "_%u._%s.%s",
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(!hashmap_get(hosts.by_name, "within.comment"));
- assert(!hashmap_get(hosts.by_name, "within.comment2"));
- assert(!hashmap_get(hosts.by_name, "within.comment3"));
- assert(!hashmap_get(hosts.by_name, "#"));
-
- assert(!hashmap_get(hosts.by_name, "short.address"));
- assert(!hashmap_get(hosts.by_name, "long.address"));
- assert(!hashmap_get(hosts.by_name, "multi.colon"));
+ assert_se(!hashmap_get(hosts.by_name, "within.comment"));
+ assert_se(!hashmap_get(hosts.by_name, "within.comment2"));
+ assert_se(!hashmap_get(hosts.by_name, "within.comment3"));
+ assert_se(!hashmap_get(hosts.by_name, "#"));
+
+ assert_se(!hashmap_get(hosts.by_name, "short.address"));
+ assert_se(!hashmap_get(hosts.by_name, "long.address"));
+ assert_se(!hashmap_get(hosts.by_name, "multi.colon"));
assert_se(!set_contains(hosts.no_address, "short.address"));
assert_se(!set_contains(hosts.no_address, "long.address"));
assert_se(!set_contains(hosts.no_address, "multi.colon"));
DEFINE_STRING_TABLE_LOOKUP(bpf_cgroup_attach_type, int);
-DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(bpf_program_hash_ops, void, trivial_hash_func, trivial_compare_func, bpf_program_unref);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(bpf_program_hash_ops, void, trivial_hash_func, trivial_compare_func, bpf_program_free);
+
+BPFProgram *bpf_program_free(BPFProgram *p) {
+ if (!p)
+ return NULL;
+ /* Unfortunately, the kernel currently doesn't implicitly detach BPF programs from their cgroups when the last
+ * fd to the BPF program is closed. This has nasty side-effects since this means that abnormally terminated
+ * programs that attached one of their BPF programs to a cgroup will leave this program pinned for good with
+ * zero chance of recovery, until the cgroup is removed. This is particularly problematic if the cgroup in
+ * question is the root cgroup (or any other cgroup belonging to a service that cannot be restarted during
+ * operation, such as dbus), as the memory for the BPF program can only be reclaimed through a reboot. To
+ * counter this, we track closely to which cgroup a program was attached to and will detach it on our own
+ * whenever we close the BPF fd. */
+ (void) bpf_program_cgroup_detach(p);
+
+ safe_close(p->kernel_fd);
+ free(p->instructions);
+ free(p->attached_path);
+
+ return mfree(p);
+}
/* struct bpf_prog_info info must be initialized since its value is both input and output
* for BPF_OBJ_GET_INFO_BY_FD syscall. */
}
int bpf_program_new(uint32_t prog_type, BPFProgram **ret) {
- _cleanup_(bpf_program_unrefp) BPFProgram *p = NULL;
+ _cleanup_(bpf_program_freep) BPFProgram *p = NULL;
p = new(BPFProgram, 1);
if (!p)
return -ENOMEM;
*p = (BPFProgram) {
- .n_ref = 1,
.prog_type = prog_type,
.kernel_fd = -1,
};
}
int bpf_program_new_from_bpffs_path(const char *path, BPFProgram **ret) {
- _cleanup_(bpf_program_unrefp) BPFProgram *p = NULL;
+ _cleanup_(bpf_program_freep) BPFProgram *p = NULL;
struct bpf_prog_info info = {};
int r;
*p = (BPFProgram) {
.prog_type = BPF_PROG_TYPE_UNSPEC,
- .n_ref = 1,
.kernel_fd = -1,
};
return 0;
}
-static BPFProgram *bpf_program_free(BPFProgram *p) {
- assert(p);
-
- /* Unfortunately, the kernel currently doesn't implicitly detach BPF programs from their cgroups when the last
- * fd to the BPF program is closed. This has nasty side-effects since this means that abnormally terminated
- * programs that attached one of their BPF programs to a cgroup will leave this programs pinned for good with
- * zero chance of recovery, until the cgroup is removed. This is particularly problematic if the cgroup in
- * question is the root cgroup (or any other cgroup belonging to a service that cannot be restarted during
- * operation, such as dbus), as the memory for the BPF program can only be reclaimed through a reboot. To
- * counter this, we track closely to which cgroup a program was attached to and will detach it on our own
- * whenever we close the BPF fd. */
- (void) bpf_program_cgroup_detach(p);
-
- safe_close(p->kernel_fd);
- free(p->instructions);
- free(p->attached_path);
-
- return mfree(p);
-}
-
-DEFINE_TRIVIAL_REF_UNREF_FUNC(BPFProgram, bpf_program, bpf_program_free);
int bpf_program_add_instructions(BPFProgram *p, const struct bpf_insn *instructions, size_t count) {
int bpf_program_deserialize_attachment(const char *v, FDSet *fds, BPFProgram **bpfp) {
_cleanup_free_ char *sfd = NULL, *sat = NULL, *unescaped = NULL;
- _cleanup_(bpf_program_unrefp) BPFProgram *p = NULL;
+ _cleanup_(bpf_program_freep) BPFProgram *p = NULL;
_cleanup_close_ int fd = -1;
ssize_t l;
int ifd, at, r;
return -ENOMEM;
*p = (BPFProgram) {
- .n_ref = 1,
.kernel_fd = TAKE_FD(fd),
.prog_type = BPF_PROG_TYPE_UNSPEC,
.attached_path = TAKE_PTR(unescaped),
};
if (*bpfp)
- bpf_program_unref(*bpfp);
+ bpf_program_free(*bpfp);
*bpfp = TAKE_PTR(p);
return 0;
* we attach it, but it might happen that we operate with programs that aren't loaded or aren't attached, or
* where we don't have the code. */
struct BPFProgram {
- unsigned n_ref;
-
/* The loaded BPF program, if loaded */
int kernel_fd;
uint32_t prog_type;
int bpf_program_new(uint32_t prog_type, BPFProgram **ret);
int bpf_program_new_from_bpffs_path(const char *path, BPFProgram **ret);
-BPFProgram *bpf_program_ref(BPFProgram *p);
-BPFProgram *bpf_program_unref(BPFProgram *p);
+BPFProgram *bpf_program_free(BPFProgram *p);
int bpf_program_add_instructions(BPFProgram *p, const struct bpf_insn *insn, size_t count);
int bpf_program_load_kernel(BPFProgram *p, char *log_buf, size_t log_size);
int bpf_cgroup_attach_type_from_string(const char *str) _pure_;
const char *bpf_cgroup_attach_type_to_string(int attach_type) _const_;
-DEFINE_TRIVIAL_CLEANUP_FUNC(BPFProgram*, bpf_program_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(BPFProgram*, bpf_program_free);
int z;
/* Chop off "Soft" suffix */
- s = is_soft ? strndupa(property, is_soft - property) : property;
+ s = is_soft ? strndupa_safe(property, is_soft - property) : property;
/* Skip over any prefix, such as "Default" */
assert_se(p = strstr(s, "Limit"));
if (!e)
return -EINVAL;
- pp = strndupa(path, e - path);
+ pp = strndupa_safe(path, e - path);
r = add_cgroup(cgroups, pp, false, &parent);
if (r < 0)
e = strchr(eq, ' ');
if (e) {
- path = strndupa(eq, e - eq);
+ path = strndupa_safe(eq, e - eq);
rwm = e+1;
}
"Failed to parse %s value %s.",
field, eq);
- path = strndupa(eq, e - eq);
+ path = strndupa_safe(eq, e - eq);
bandwidth = e+1;
if (streq(bandwidth, "infinity"))
"Failed to parse %s value %s.",
field, eq);
- path = strndupa(eq, e - eq);
+ path = strndupa_safe(eq, e - eq);
weight = e+1;
r = safe_atou64(weight, &u);
"Failed to parse %s value %s.",
field, eq);
- path = strndupa(eq, e - eq);
+ path = strndupa_safe(eq, e - eq);
target = e+1;
r = parse_sec(target, &usec);
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Not an assignment: %s", assignment);
- field = strndupa(assignment, eq - assignment);
+ field = strndupa_safe(assignment, eq - assignment);
eq++;
switch (t) {
#include "stdio-util.h"
#include "string-util.h"
#include "user-util.h"
+#include "virt.h"
+
+static int cg_any_controller_used_for_v1(void) {
+ _cleanup_free_ char *buf = NULL;
+ _cleanup_strv_free_ char **lines = NULL;
+ char **line;
+ int r;
+
+ r = read_full_virtual_file("/proc/cgroups", &buf, NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Could not read /proc/cgroups, ignoring: %m");
+
+ r = strv_split_newlines_full(&lines, buf, 0);
+ if (r < 0)
+ return r;
+
+ /* The intention of this is to check if the fully unified cgroup tree setup is possible, meaning all
+ * enabled kernel cgroup controllers are currently not in use by cgroup1. For reference:
+ * https://systemd.io/CGROUP_DELEGATION/#three-different-tree-setups-
+ *
+ * Note that this is typically only useful to check inside a container where we don't know what
+ * cgroup tree setup is in use by the host; if the host is using legacy or hybrid, we can't use
+ * unified since some or all controllers would be missing. This is not the best way to detect this,
+ * as whatever container manager created our container should have mounted /sys/fs/cgroup
+ * appropriately, but in case that wasn't done, we try to detect if it's possible for us to use
+ * unified cgroups. */
+ STRV_FOREACH(line, lines) {
+ _cleanup_free_ char *name = NULL, *hierarchy_id = NULL, *num = NULL, *enabled = NULL;
+
+ /* Skip header line */
+ if (startswith(*line, "#"))
+ continue;
+
+ const char *p = *line;
+ r = extract_many_words(&p, NULL, 0, &name, &hierarchy_id, &num, &enabled, NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Error parsing /proc/cgroups line, ignoring: %m");
+ else if (r < 4) {
+ log_debug("Invalid /proc/cgroups line, ignoring.");
+ continue;
+ }
+
+ /* Ignore disabled controllers. */
+ if (streq(enabled, "0"))
+ continue;
+
+ /* Ignore controllers we don't care about. */
+ if (cgroup_controller_from_string(name) < 0)
+ continue;
+
+ /* Since the unified cgroup doesn't use multiple hierarchies, if any controller has a
+ * non-zero hierarchy_id that means it's in use already in a legacy (or hybrid) cgroup v1
+ * hierarchy, and can't be used in a unified cgroup. */
+ if (!streq(hierarchy_id, "0")) {
+ log_debug("Cgroup controller %s in use by legacy v1 hierarchy.", name);
+ return 1;
+ }
+ }
+
+ return 0;
+}
bool cg_is_unified_wanted(void) {
static thread_local int wanted = -1;
if (r > 0 && streq_ptr(c, "all"))
return (wanted = true);
+ /* If any controller is in use as v1, don't use unified. */
+ if (cg_any_controller_used_for_v1() > 0)
+ return (wanted = false);
+
return (wanted = is_default);
}
(void) sd_device_get_sysname(device, &sn);
log_device_debug(device,
- "Waiting for device '%s' to initialize for %s.", strna(sn), FORMAT_TIMESPAN(left, 0));
+ "Will wait up to %s for '%s' to initialize…", FORMAT_TIMESPAN(left, 0), strna(sn));
}
if (left != USEC_INFINITY)
}
/* Found it! Now generate the new name */
- prefix = strndupa(name, saved_before - name);
+ prefix = strndupa_safe(name, saved_before - name);
r = dns_name_concat(prefix, new_suffix, 0, ret);
if (r < 0)
if (memchr(label, 0, n))
return false;
- s = strndupa(label, n);
+ s = strndupa_safe(label, n);
return dns_service_name_is_valid(s);
}
static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_usettings **ret) {
struct ecmd {
struct ethtool_link_settings req;
- __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
+ uint32_t link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
} ecmd = {
.req.cmd = ETHTOOL_GLINKSETTINGS,
};
static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
struct {
struct ethtool_link_settings req;
- __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
+ uint32_t link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
} ecmd = {};
unsigned offset;
mr->rangesize = 1;
/* Create a search mask entry */
- mask = alloca(sz);
+ mask = alloca_safe(sz);
memset(mask, 0xFF, sz);
if (add) {
if (!journal_field_valid(line, sep - line, true)) {
char buf[64], *t;
- t = strndupa(line, sep - line);
+ t = strndupa_safe(line, sep - line);
log_debug("Ignoring invalid field: \"%s\"",
cellescape(buf, sizeof buf, t));
if (!journal_field_valid(line, n - 1, true)) {
char buf[64], *t;
- t = strndupa(line, n - 1);
+ t = strndupa_safe(line, n - 1);
log_debug("Ignoring invalid field: \"%s\"",
cellescape(buf, sizeof buf, t));
#include "memory-util.h"
#include "random-util.h"
#include "strv.h"
+#include "unistd.h"
static void *libfido2_dl = NULL;
"FIDO2 tokens not supported on this build.");
#endif
}
+
+int fido2_have_device(const char *device) {
+#if HAVE_LIBFIDO2
+ size_t allocated = 64, found = 0;
+ fido_dev_info_t *di = NULL;
+ int r;
+
+ /* Return == 0 if not devices are found, > 0 if at least one is found */
+
+ r = dlopen_libfido2();
+ if (r < 0)
+ return log_error_errno(r, "FIDO2 support is not installed.");
+
+ if (device) {
+ if (access(device, F_OK) < 0) {
+ if (errno == ENOENT)
+ return 0;
+
+ return log_error_errno(errno, "Failed to determine whether device '%s' exists: %m", device);
+ }
+
+ return 1;
+ }
+
+ di = sym_fido_dev_info_new(allocated);
+ if (!di)
+ return log_oom();
+
+ r = sym_fido_dev_info_manifest(di, allocated, &found);
+ if (r == FIDO_ERR_INTERNAL) {
+ /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
+ r = 0;
+ goto finish;
+ }
+ if (r != FIDO_OK) {
+ r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r));
+ goto finish;
+ }
+
+ r = found;
+
+finish:
+ sym_fido_dev_info_free(&di, allocated);
+ return r;
+#else
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "FIDO2 tokens not supported on this build.");
+#endif
+}
int fido2_list_devices(void);
int fido2_find_device_auto(char **ret);
+
+int fido2_have_device(const char *device);
if (!fields)
return 1;
- s = strndupa(name, n);
+ s = strndupa_safe(name, n);
return set_contains(fields, s);
}
if (!journal_field_valid(data, fieldlen, true))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid field.");
- name = strndupa(data, fieldlen);
+ name = strndupa_safe(data, fieldlen);
if (output_fields && !set_contains(output_fields, name))
return 0;
l = strlen(database);
req_size = offsetof(struct nscdInvalidateRequest, dbname) + l + 1;
- req = alloca(req_size);
+ req = alloca_safe(req_size);
*req = (struct nscdInvalidateRequest) {
.version = 2,
.type = 10,
if (e) {
char *page = NULL, *section = NULL;
- page = strndupa(desc, e - desc);
- section = strndupa(e + 1, desc + k - e - 2);
+ page = strndupa_safe(desc, e - desc);
+ section = strndupa_safe(e + 1, desc + k - e - 2);
args[1] = section;
args[2] = page;
ret = r;
}
+ if (FLAGS_SET(flags, REMOVE_SYNCFS) && syncfs(dirfd(d)) < 0 && ret >= 0)
+ ret = -errno;
+
return ret;
}
REMOVE_MISSING_OK = 1 << 4, /* If the top-level directory is missing, ignore the ENOENT for it */
REMOVE_CHMOD = 1 << 5, /* chmod() for write access if we cannot delete or access something */
REMOVE_CHMOD_RESTORE = 1 << 6, /* Restore the old mode before returning */
+ REMOVE_SYNCFS = 1 << 7, /* syncfs() the root of the specified directory after removing everything in it */
} RemoveFlags;
int unlinkat_harder(int dfd, const char *filename, int unlink_flags, RemoveFlags remove_flags);
if (un->sun_path[0] == 0)
goto skipped;
- path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
+ path = strndupa_safe(un->sun_path,
+ addrlen - offsetof(struct sockaddr_un, sun_path));
/* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
mac_selinux_maybe_reload();
param = strchr(device, ':');
if (param) {
- driver = strndupa(device, param - device);
+ driver = strndupa_safe(device, param - device);
param++;
} else {
driver = "device";
char *b;
uid_t end;
- b = strndupa(s, t - s);
+ b = strndupa_safe(s, t - s);
r = parse_uid(b, &start);
if (r < 0)
return r;
if (hr->password_change_now >= 0)
printf("Pas. Ch. Now: %s\n", yes_no(hr->password_change_now));
+ if (hr->drop_caches >= 0 || user_record_drop_caches(hr))
+ printf(" Drop Caches: %s\n", yes_no(user_record_drop_caches(hr)));
+
if (!strv_isempty(hr->ssh_authorized_keys))
printf("SSH Pub. Key: %zu\n", strv_length(hr->ssh_authorized_keys));
.pkcs11_protected_authentication_path_permitted = -1,
.fido2_user_presence_permitted = -1,
.fido2_user_verification_permitted = -1,
+ .drop_caches = -1,
};
return h;
{ "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 },
+ { "dropCaches", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, drop_caches), 0 },
{ "rateLimitIntervalUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_interval_usec), 0 },
{ "rateLimitBurst", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_burst), 0 },
{ "enforcePasswordPolicy", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, enforce_password_policy), 0 },
return 0;
}
- z = strjoin("/home/", user_name_and_realm, suffix);
+ z = strjoin(get_home_root(), "/", user_name_and_realm, suffix);
if (!z)
return -ENOMEM;
- *ret = z;
+ *ret = path_simplify(z);
return 1;
}
return 0;
if (!h->home_directory && !h->home_directory_auto) {
- h->home_directory_auto = path_join("/home/", h->user_name);
+ h->home_directory_auto = path_join(get_home_root(), h->user_name);
if (!h->home_directory_auto)
return json_log_oom(h->json, json_flags);
}
{ "luksUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, luks_uuid), 0 },
{ "fileSystemUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, file_system_uuid), 0 },
{ "luksDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_discard), 0 },
- { "luksOfflineDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_offline_discard), 0 },
+ { "luksOfflineDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_offline_discard), 0 },
{ "luksCipher", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher), JSON_SAFE },
{ "luksCipherMode", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher_mode), JSON_SAFE },
{ "luksVolumeKeySize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_volume_key_size), 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 },
+ { "dropCaches", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, drop_caches), 0 },
{ "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, service), JSON_SAFE },
{ "rateLimitIntervalUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_interval_usec), 0 },
{ "rateLimitBurst", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_burst), 0 },
return !strv_isempty(h->hashed_password);
}
+bool user_record_drop_caches(UserRecord *h) {
+ assert(h);
+
+ if (h->drop_caches >= 0)
+ return h->drop_caches;
+
+ /* By default drop caches on fscrypt, not otherwise. */
+ return user_record_storage(h) == USER_FSCRYPT;
+}
+
uint64_t user_record_ratelimit_next_try(UserRecord *h) {
assert(h);
int removable;
int enforce_password_policy;
int auto_login;
+ int drop_caches;
uint64_t stop_delay_usec; /* How long to leave systemd --user around on log-out */
int kill_processes; /* Whether to kill user processes forcibly on log-out */
usec_t user_record_ratelimit_interval_usec(UserRecord *h);
uint64_t user_record_ratelimit_burst(UserRecord *h);
bool user_record_can_authenticate(UserRecord *h);
+bool user_record_drop_caches(UserRecord *h);
int user_record_build_image_path(UserStorage storage, const char *user_name_and_realm, char **ret);
.state = _VARLINK_STATE_INVALID,
- .ucred.uid = UID_INVALID,
- .ucred.gid = GID_INVALID,
+ .ucred = UCRED_INVALID,
.timestamp = USEC_INFINITY,
.timeout = VARLINK_DEFAULT_TIMEOUT_USEC
return 1;
}
-static int count_connection(VarlinkServer *server, struct ucred *ucred) {
+static int count_connection(VarlinkServer *server, const struct ucred *ucred) {
unsigned c;
int r;
int varlink_server_add_connection(VarlinkServer *server, int fd, Varlink **ret) {
_cleanup_(varlink_unrefp) Varlink *v = NULL;
+ struct ucred ucred = UCRED_INVALID;
bool ucred_acquired;
- struct ucred ucred;
int r;
assert_return(server, -EINVAL);
#include "errno-util.h"
#include "fd-util.h"
#include "log.h"
+#include "path-util.h"
#include "string-util.h"
#include "time-util.h"
#include "watchdog.h"
static int watchdog_fd = -1;
-static char *watchdog_device = NULL;
-static usec_t watchdog_timeout = USEC_INFINITY;
+static char *watchdog_device;
+static usec_t watchdog_timeout; /* 0 → close device and USEC_INFINITY → don't change timeout */
static usec_t watchdog_last_ping = USEC_INFINITY;
+static int watchdog_set_enable(bool enable) {
+ int flags = enable ? WDIOS_ENABLECARD : WDIOS_DISABLECARD;
+
+ assert(watchdog_fd >= 0);
+
+ if (ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags) < 0) {
+ if (!enable)
+ return log_warning_errno(errno, "Failed to disable hardware watchdog, ignoring: %m");
+
+ /* ENOTTY means the watchdog is always enabled so we're fine */
+ log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno,
+ "Failed to enable hardware watchdog, ignoring: %m");
+ if (!ERRNO_IS_NOT_SUPPORTED(errno))
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int watchdog_get_timeout(void) {
+ int sec = 0;
+
+ assert(watchdog_fd >= 0);
+
+ if (ioctl(watchdog_fd, WDIOC_GETTIMEOUT, &sec) < 0)
+ return -errno;
+
+ assert(sec > 0);
+ watchdog_timeout = sec * USEC_PER_SEC;
+
+ return 0;
+}
+
+static int watchdog_set_timeout(void) {
+ usec_t t;
+ int sec;
+
+ assert(watchdog_fd >= 0);
+ assert(timestamp_is_set(watchdog_timeout));
+
+ t = DIV_ROUND_UP(watchdog_timeout, USEC_PER_SEC);
+ sec = MIN(t, (usec_t) INT_MAX); /* Saturate */
+
+ if (ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec) < 0)
+ return -errno;
+
+ assert(sec > 0);/* buggy driver ? */
+ watchdog_timeout = sec * USEC_PER_SEC;
+
+ return 0;
+}
+
+static int watchdog_ping_now(void) {
+ assert(watchdog_fd >= 0);
+
+ if (ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0) < 0)
+ return log_warning_errno(errno, "Failed to ping hardware watchdog, ignoring: %m");
+
+ watchdog_last_ping = now(clock_boottime_or_monotonic());
+
+ return 0;
+}
+
static int update_timeout(void) {
+ int r;
+
+ assert(watchdog_timeout > 0);
+
if (watchdog_fd < 0)
return 0;
- if (watchdog_timeout == USEC_INFINITY)
- return 0;
- if (watchdog_timeout == 0) {
- int flags;
+ if (watchdog_timeout != USEC_INFINITY) {
+ r = watchdog_set_timeout();
+ if (r < 0) {
+ if (!ERRNO_IS_NOT_SUPPORTED(r))
+ return log_error_errno(r, "Failed to set timeout to %s: %m",
+ FORMAT_TIMESPAN(watchdog_timeout, 0));
- flags = WDIOS_DISABLECARD;
- if (ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags) < 0)
- return log_warning_errno(errno, "Failed to disable hardware watchdog, ignoring: %m");
- } else {
- int sec, flags;
- usec_t t;
-
- t = DIV_ROUND_UP(watchdog_timeout, USEC_PER_SEC);
- sec = MIN(t, (usec_t) INT_MAX); /* Saturate */
- if (ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec) < 0)
- return log_warning_errno(errno, "Failed to set timeout to %is, ignoring: %m", sec);
-
- /* Just in case the driver is buggy */
- assert(sec > 0);
-
- /* watchdog_timeout stores the actual timeout used by the HW */
- watchdog_timeout = sec * USEC_PER_SEC;
- log_info("Set hardware watchdog to %s.", FORMAT_TIMESPAN(watchdog_timeout, 0));
-
- flags = WDIOS_ENABLECARD;
- if (ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags) < 0) {
- /* ENOTTY means the watchdog is always enabled so we're fine */
- log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno,
- "Failed to enable hardware watchdog, ignoring: %m");
- if (!ERRNO_IS_NOT_SUPPORTED(errno))
- return -errno;
+ log_info("Modifying watchdog timeout is not supported, reusing the programmed timeout.");
+ watchdog_timeout = USEC_INFINITY;
}
+ }
- if (ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0) < 0)
- return log_warning_errno(errno, "Failed to ping hardware watchdog, ignoring: %m");
-
- watchdog_last_ping = now(clock_boottime_or_monotonic());
+ if (watchdog_timeout == USEC_INFINITY) {
+ r = watchdog_get_timeout();
+ if (r < 0)
+ return log_error_errno(r, "Failed to query watchdog HW timeout: %m");
}
- return 0;
+ r = watchdog_set_enable(true);
+ if (r < 0)
+ return r;
+
+ log_info("Watchdog running with a timeout of %s.", FORMAT_TIMESPAN(watchdog_timeout, 0));
+
+ return watchdog_ping_now();
}
static int open_watchdog(void) {
struct watchdog_info ident;
const char *fn;
+ int r;
if (watchdog_fd >= 0)
return 0;
- fn = watchdog_device ?: "/dev/watchdog";
+ /* Let's prefer new-style /dev/watchdog0 (i.e. kernel 3.5+) over classic /dev/watchdog. The former
+ * has the benefit that we can easily find the matching directory in sysfs from it, as the relevant
+ * sysfs attributes can only be found via /sys/dev/char/<major>:<minor> if the new-style device
+ * major/minor is used, not the old-style. */
+ fn = !watchdog_device || path_equal(watchdog_device, "/dev/watchdog") ?
+ "/dev/watchdog0" : watchdog_device;
+
watchdog_fd = open(fn, O_WRONLY|O_CLOEXEC);
if (watchdog_fd < 0)
return log_debug_errno(errno, "Failed to open watchdog device %s, ignoring: %m", fn);
ident.firmware_version,
fn);
- return update_timeout();
+ r = update_timeout();
+ if (r < 0)
+ watchdog_close(true);
+
+ return r;
}
int watchdog_set_device(const char *path) {
}
int watchdog_setup(usec_t timeout) {
+ usec_t previous_timeout;
+ int r;
+
+ /* timeout=0 closes the device whereas passing timeout=USEC_INFINITY
+ * opens it (if needed) without configuring any particular timeout and
+ * thus reuses the programmed value (therefore it's a nop if the device
+ * is already opened).
+ */
+
+ if (timeout == 0) {
+ watchdog_close(true);
+ return 0;
+ }
+
+ /* Let's shortcut duplicated requests */
+ if (watchdog_fd >= 0 && (timeout == watchdog_timeout || timeout == USEC_INFINITY))
+ return 0;
/* Initialize the watchdog timeout with the caller value. This value is
* going to be updated by update_timeout() with the closest value
* supported by the driver */
+ previous_timeout = watchdog_timeout;
watchdog_timeout = timeout;
- /* If we didn't open the watchdog yet and didn't get any explicit
- * timeout value set, don't do anything */
- if (watchdog_fd < 0 && watchdog_timeout == USEC_INFINITY)
- return 0;
-
if (watchdog_fd < 0)
return open_watchdog();
- return update_timeout();
+ r = update_timeout();
+ if (r < 0)
+ watchdog_timeout = previous_timeout;
+
+ return r;
}
usec_t watchdog_runtime_wait(void) {
int watchdog_ping(void) {
usec_t ntime;
- if (!timestamp_is_set(watchdog_timeout))
+ if (watchdog_timeout == 0)
return 0;
if (watchdog_fd < 0)
return 0;
}
- if (ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0) < 0)
- return log_warning_errno(errno, "Failed to ping hardware watchdog, ignoring: %m");
-
- watchdog_last_ping = ntime;
- return 0;
+ return watchdog_ping_now();
}
void watchdog_close(bool disarm) {
+
+ /* Once closed, pinging the device becomes a NOP and we request a new
+ * call to watchdog_setup() to open the device again. */
+ watchdog_timeout = 0;
+
if (watchdog_fd < 0)
return;
if (disarm) {
- int flags;
-
- /* Explicitly disarm it */
- flags = WDIOS_DISABLECARD;
- if (ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags) < 0)
- log_warning_errno(errno, "Failed to disable hardware watchdog, ignoring: %m");
+ (void) watchdog_set_enable(false);
/* To be sure, use magic close logic, too */
for (;;) {
}
watchdog_fd = safe_close(watchdog_fd);
-
- /* Once closed, pinging the device becomes a NOP and we request a new
- * call to watchdog_setup() to open the device again. */
- watchdog_timeout = USEC_INFINITY;
}
#include <sys/types.h>
#include <unistd.h>
+#if HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
#include "sd-device.h"
#include "alloc-util.h"
return -EBUSY; /* propagate original error */
}
+#if HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
+#endif
+
if (FLAGS_SET(info.lo_flags, LO_FLAGS_AUTOCLEAR)) /* someone else already set LO_FLAGS_AUTOCLEAR for us? fine by us */
return -EBUSY; /* propagate original error */
return 1;
}
+#if HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
+#endif
+
/* Linux makes LOOP_CLR_FD succeed whenever LO_FLAGS_AUTOCLEAR is set without actually doing
* anything. Very confusing. Let's hence not claim we did anything in this case. */
if (FLAGS_SET(info.lo_flags, LO_FLAGS_AUTOCLEAR))
service = strrchr(arg_remote_host, ':');
if (service) {
- node = strndupa(arg_remote_host, service - arg_remote_host);
+ node = strndupa_safe(arg_remote_host,
+ service - arg_remote_host);
service++;
} else {
node = arg_remote_host;
SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST = 13,
};
+/* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-2 */
enum {
- SD_DHCP6_OPTION_CLIENTID = 1,
- SD_DHCP6_OPTION_SERVERID = 2,
- SD_DHCP6_OPTION_IA_NA = 3,
- SD_DHCP6_OPTION_IA_TA = 4,
- SD_DHCP6_OPTION_IAADDR = 5,
- SD_DHCP6_OPTION_ORO = 6,
- SD_DHCP6_OPTION_PREFERENCE = 7,
- SD_DHCP6_OPTION_ELAPSED_TIME = 8,
- SD_DHCP6_OPTION_RELAY_MSG = 9,
+ SD_DHCP6_OPTION_CLIENTID = 1, /* RFC 8415 */
+ SD_DHCP6_OPTION_SERVERID = 2, /* RFC 8415 */
+ SD_DHCP6_OPTION_IA_NA = 3, /* RFC 8415 */
+ SD_DHCP6_OPTION_IA_TA = 4, /* RFC 8415 */
+ SD_DHCP6_OPTION_IAADDR = 5, /* RFC 8415 */
+ SD_DHCP6_OPTION_ORO = 6, /* RFC 8415 */
+ SD_DHCP6_OPTION_PREFERENCE = 7, /* RFC 8415 */
+ SD_DHCP6_OPTION_ELAPSED_TIME = 8, /* RFC 8415 */
+ SD_DHCP6_OPTION_RELAY_MSG = 9, /* RFC 8415 */
/* option code 10 is unassigned */
- SD_DHCP6_OPTION_AUTH = 11,
- SD_DHCP6_OPTION_UNICAST = 12,
- SD_DHCP6_OPTION_STATUS_CODE = 13,
- SD_DHCP6_OPTION_RAPID_COMMIT = 14,
- SD_DHCP6_OPTION_USER_CLASS = 15,
- SD_DHCP6_OPTION_VENDOR_CLASS = 16,
- SD_DHCP6_OPTION_VENDOR_OPTS = 17,
- SD_DHCP6_OPTION_INTERFACE_ID = 18,
- SD_DHCP6_OPTION_RECONF_MSG = 19,
- SD_DHCP6_OPTION_RECONF_ACCEPT = 20,
-
+ SD_DHCP6_OPTION_AUTH = 11, /* RFC 8415 */
+ SD_DHCP6_OPTION_UNICAST = 12, /* RFC 8415 */
+ SD_DHCP6_OPTION_STATUS_CODE = 13, /* RFC 8415 */
+ SD_DHCP6_OPTION_RAPID_COMMIT = 14, /* RFC 8415 */
+ SD_DHCP6_OPTION_USER_CLASS = 15, /* RFC 8415 */
+ SD_DHCP6_OPTION_VENDOR_CLASS = 16, /* RFC 8415 */
+ SD_DHCP6_OPTION_VENDOR_OPTS = 17, /* RFC 8415 */
+ SD_DHCP6_OPTION_INTERFACE_ID = 18, /* RFC 8415 */
+ SD_DHCP6_OPTION_RECONF_MSG = 19, /* RFC 8415 */
+ SD_DHCP6_OPTION_RECONF_ACCEPT = 20, /* RFC 8415 */
+ SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME = 21, /* RFC 3319 */
+ SD_DHCP6_OPTION_SIP_SERVER_ADDRESS = 22, /* RFC 3319 */
SD_DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */
SD_DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */
- SD_DHCP6_OPTION_IA_PD = 25, /* RFC 3633, prefix delegation */
- SD_DHCP6_OPTION_IA_PD_PREFIX = 26, /* RFC 3633, prefix delegation */
-
+ SD_DHCP6_OPTION_IA_PD = 25, /* RFC 3633, RFC 8415 */
+ SD_DHCP6_OPTION_IA_PD_PREFIX = 26, /* RFC 3633, RFC 8415 */
+ SD_DHCP6_OPTION_NIS_SERVERS = 27, /* RFC 3898 */
+ SD_DHCP6_OPTION_NISP_SERVERS = 28, /* RFC 3898 */
+ SD_DHCP6_OPTION_NIS_DOMAIN_NAME = 29, /* RFC 3898 */
+ SD_DHCP6_OPTION_NISP_DOMAIN_NAME = 30, /* RFC 3898 */
SD_DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075, deprecated */
- SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME = 32, /* RFC 8415, sec. 21.23 */
-
+ SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME = 32, /* RFC 4242, 8415, sec. 21.23 */
+ SD_DHCP6_OPTION_BCMCS_SERVER_D = 33, /* RFC 4280 */
+ SD_DHCP6_OPTION_BCMCS_SERVER_A = 34, /* RFC 4280 */
/* option code 35 is unassigned */
-
- SD_DHCP6_OPTION_FQDN = 39, /* RFC 4704 */
-
+ SD_DHCP6_OPTION_GEOCONF_CIVIC = 36, /* RFC 4776 */
+ SD_DHCP6_OPTION_REMOTE_ID = 37, /* RFC 4649 */
+ SD_DHCP6_OPTION_SUBSCRIBER_ID = 38, /* RFC 4580 */
+ SD_DHCP6_OPTION_CLIENT_FQDN = 39, /* RFC 4704 */
+ SD_DHCP6_OPTION_PANA_AGENT = 40, /* RFC 5192 */
+ SD_DHCP6_OPTION_NEW_POSIX_TIMEZONE = 41, /* RFC 4833 */
+ SD_DHCP6_OPTION_NEW_TZDB_TIMEZONE = 42, /* RFC 4833 */
+ SD_DHCP6_OPTION_ERO = 43, /* RFC 4994 */
+ SD_DHCP6_OPTION_LQ_QUERY = 44, /* RFC 5007 */
+ SD_DHCP6_OPTION_CLIENT_DATA = 45, /* RFC 5007 */
+ SD_DHCP6_OPTION_CLT_TIME = 46, /* RFC 5007 */
+ SD_DHCP6_OPTION_LQ_RELAY_DATA = 47, /* RFC 5007 */
+ SD_DHCP6_OPTION_LQ_CLIENT_LINK = 48, /* RFC 5007 */
+ SD_DHCP6_OPTION_MIP6_HNIDF = 49, /* RFC 6610 */
+ SD_DHCP6_OPTION_MIP6_VDINF = 50, /* RFC 6610 */
+ SD_DHCP6_OPTION_V6_LOST = 51, /* RFC 5223 */
+ SD_DHCP6_OPTION_CAPWAP_AC_V6 = 52, /* RFC 5417 */
+ SD_DHCP6_OPTION_RELAY_ID = 53, /* RFC 5460 */
+ SD_DHCP6_OPTION_IPV6_ADDRESS_MOS = 54, /* RFC 5678 */
+ SD_DHCP6_OPTION_IPV6_FQDN_MOS = 55, /* RFC 5678 */
SD_DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */
- SD_DHCP6_OPTION_MUD_URL = 112, /* RFC 8250 */
-
- /* option codes 89-142 are unassigned */
+ SD_DHCP6_OPTION_V6_ACCESS_DOMAIN = 57, /* RFC 5986 */
+ SD_DHCP6_OPTION_SIP_UA_CS_LIST = 58, /* RFC 6011 */
+ SD_DHCP6_OPTION_BOOTFILE_URL = 59, /* RFC 5970 */
+ SD_DHCP6_OPTION_BOOTFILE_PARAM = 60, /* RFC 5970 */
+ SD_DHCP6_OPTION_CLIENT_ARCH_TYPE = 61, /* RFC 5970 */
+ SD_DHCP6_OPTION_NII = 62, /* RFC 5970 */
+ SD_DHCP6_OPTION_GEOLOCATION = 63, /* RFC 6225 */
+ SD_DHCP6_OPTION_AFTR_NAME = 64, /* RFC 6334 */
+ SD_DHCP6_OPTION_ERP_LOCAL_DOMAIN_NAME = 65, /* RFC 6440 */
+ SD_DHCP6_OPTION_RSOO = 66, /* RFC 6422 */
+ SD_DHCP6_OPTION_PD_EXCLUDE = 67, /* RFC 6603 */
+ SD_DHCP6_OPTION_VSS = 68, /* RFC 6607 */
+ SD_DHCP6_OPTION_MIP6_IDINF = 69, /* RFC 6610 */
+ SD_DHCP6_OPTION_MIP6_UDINF = 70, /* RFC 6610 */
+ SD_DHCP6_OPTION_MIP6_HNP = 71, /* RFC 6610 */
+ SD_DHCP6_OPTION_MIP6_HAA = 72, /* RFC 6610 */
+ SD_DHCP6_OPTION_MIP6_HAF = 73, /* RFC 6610 */
+ SD_DHCP6_OPTION_RDNSS_SELECTION = 74, /* RFC 6731 */
+ SD_DHCP6_OPTION_KRB_PRINCIPAL_NAME = 75, /* RFC 6784 */
+ SD_DHCP6_OPTION_KRB_REALM_NAME = 76, /* RFC 6784 */
+ SD_DHCP6_OPTION_KRB_DEFAULT_REALM_NAME = 77, /* RFC 6784 */
+ SD_DHCP6_OPTION_KRB_KDC = 78, /* RFC 6784 */
+ SD_DHCP6_OPTION_CLIENT_LINKLAYER_ADDR = 79, /* RFC 6939 */
+ SD_DHCP6_OPTION_LINK_ADDRESS = 80, /* RFC 6977 */
+ SD_DHCP6_OPTION_RADIUS = 81, /* RFC 7037 */
+ SD_DHCP6_OPTION_SOL_MAX_RT = 82, /* RFC 7083, RFC 8415 */
+ SD_DHCP6_OPTION_INF_MAX_RT = 83, /* RFC 7083, RFC 8415 */
+ SD_DHCP6_OPTION_ADDRSEL = 84, /* RFC 7078 */
+ SD_DHCP6_OPTION_ADDRSEL_TABLE = 85, /* RFC 7078 */
+ SD_DHCP6_OPTION_V6_PCP_SERVER = 86, /* RFC 7291 */
+ SD_DHCP6_OPTION_DHCPV4_MSG = 87, /* RFC 7341 */
+ SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER = 88, /* RFC 7341 */
+ SD_DHCP6_OPTION_S46_RULE = 89, /* RFC 7598 */
+ SD_DHCP6_OPTION_S46_BR = 90, /* RFC 7598, RFC 8539 */
+ SD_DHCP6_OPTION_S46_DMR = 91, /* RFC 7598 */
+ SD_DHCP6_OPTION_S46_V4V6BIND = 92, /* RFC 7598 */
+ SD_DHCP6_OPTION_S46_PORTPARAMS = 93, /* RFC 7598 */
+ SD_DHCP6_OPTION_S46_CONT_MAPE = 94, /* RFC 7598 */
+ SD_DHCP6_OPTION_S46_CONT_MAPT = 95, /* RFC 7598 */
+ SD_DHCP6_OPTION_S46_CONT_LW = 96, /* RFC 7598 */
+ SD_DHCP6_OPTION_4RD = 97, /* RFC 7600 */
+ SD_DHCP6_OPTION_4RD_MAP_RULE = 98, /* RFC 7600 */
+ SD_DHCP6_OPTION_4RD_NON_MAP_RULE = 99, /* RFC 7600 */
+ SD_DHCP6_OPTION_LQ_BASE_TIME = 100, /* RFC 7653 */
+ SD_DHCP6_OPTION_LQ_START_TIME = 101, /* RFC 7653 */
+ SD_DHCP6_OPTION_LQ_END_TIME = 102, /* RFC 7653 */
+ SD_DHCP6_OPTION_CAPTIVE_PORTAL = 103, /* RFC 8910 */
+ SD_DHCP6_OPTION_MPL_PARAMETERS = 104, /* RFC 7774 */
+ SD_DHCP6_OPTION_ANI_ATT = 105, /* RFC 7839 */
+ SD_DHCP6_OPTION_ANI_NETWORK_NAME = 106, /* RFC 7839 */
+ SD_DHCP6_OPTION_ANI_AP_NAME = 107, /* RFC 7839 */
+ SD_DHCP6_OPTION_ANI_AP_BSSID = 108, /* RFC 7839 */
+ SD_DHCP6_OPTION_ANI_OPERATOR_ID = 109, /* RFC 7839 */
+ SD_DHCP6_OPTION_ANI_OPERATOR_REALM = 110, /* RFC 7839 */
+ SD_DHCP6_OPTION_S46_PRIORITY = 111, /* RFC 8026 */
+ SD_DHCP6_OPTION_MUD_URL_V6 = 112, /* RFC 8520 */
+ SD_DHCP6_OPTION_V6_PREFIX64 = 113, /* RFC 8115 */
+ SD_DHCP6_OPTION_F_BINDING_STATUS = 114, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_CONNECT_FLAGS = 115, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_DNS_REMOVAL_INFO = 116, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_DNS_HOST_NAME = 117, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_DNS_ZONE_NAME = 118, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_DNS_FLAGS = 119, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_EXPIRATION_TIME = 120, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_MAX_UNACKED_BNDUPD = 121, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_MCLT = 122, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_PARTNER_LIFETIME = 123, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_PARTNER_LIFETIME_SENT = 124, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_PARTNER_DOWN_TIME = 125, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_PARTNER_RAW_CLT_TIME = 126, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_PROTOCOL_VERSION = 127, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_KEEPALIVE_TIME = 128, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_RECONFIGURE_DATA = 129, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_RELATIONSHIP_NAME = 130, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_SERVER_FLAGS = 131, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_SERVER_STATE = 132, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_START_TIME_OF_STATE = 133, /* RFC 8156 */
+ SD_DHCP6_OPTION_F_STATE_EXPIRATION_TIME = 134, /* RFC 8156 */
+ SD_DHCP6_OPTION_RELAY_PORT = 135, /* RFC 8357 */
+ SD_DHCP6_OPTION_V6_SZTP_REDIRECT = 136, /* RFC 8572 */
+ SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX = 137, /* RFC 8539 */
+ SD_DHCP6_OPTION_IA_LL = 138, /* RFC 8947 */
+ SD_DHCP6_OPTION_LLADDR = 139, /* RFC 8947 */
+ SD_DHCP6_OPTION_SLAP_QUAD = 140, /* RFC 8948 */
+ SD_DHCP6_OPTION_V6_DOTS_RI = 141, /* RFC 8973 */
+ SD_DHCP6_OPTION_V6_DOTS_ADDRESS = 142, /* RFC 8973 */
+ SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF = 143, /* RFC 6153 */
/* option codes 144-65535 are unassigned */
};
sd_radv_route_prefix *sd_radv_route_prefix_ref(sd_radv_route_prefix *ra);
sd_radv_route_prefix *sd_radv_route_prefix_unref(sd_radv_route_prefix *ra);
-int sd_radv_prefix_set_route_prefix(sd_radv_route_prefix *p, const struct in6_addr *in6_addr, unsigned char prefixlen);
+int sd_radv_route_prefix_set_prefix(sd_radv_route_prefix *p, const struct in6_addr *in6_addr, unsigned char prefixlen);
int sd_radv_route_prefix_set_lifetime(sd_radv_route_prefix *p, uint32_t valid_lifetime);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv, sd_radv_unref);
}
/* Strip ".sh" suffix from file name for comparison */
- filename_no_sh = strdupa(filename);
+ filename_no_sh = strdupa_safe(filename);
e = endswith(filename_no_sh, ".sh");
if (e) {
*e = '\0';
test_setup_logging(LOG_DEBUG);
p = test_acpi_fpdt();
- assert(p >= 0);
+ assert_se(p >= 0);
q = test_efi_loader();
- assert(q >= 0);
+ assert_se(q >= 0);
r = test_boot_timestamps();
- assert(r >= 0);
+ assert_se(r >= 0);
if (p == 0 && q == 0 && r == 0)
return log_tests_skipped("access to firmware variables not possible");
#include "tests.h"
static void test_policy_closed(const char *cgroup_path, BPFProgram **installed_prog) {
- _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
+ _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
unsigned wrong = 0;
int r;
r = bpf_devices_allow_list_static(prog, cgroup_path);
assert_se(r >= 0);
- r = bpf_devices_apply_policy(prog, CGROUP_DEVICE_POLICY_CLOSED, true, cgroup_path, installed_prog);
+ r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_CLOSED, true, cgroup_path, installed_prog);
assert_se(r >= 0);
const char *s;
}
static void test_policy_strict(const char *cgroup_path, BPFProgram **installed_prog) {
- _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
+ _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
unsigned wrong = 0;
int r;
r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/zero", "w");
assert_se(r >= 0);
- r = bpf_devices_apply_policy(prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
+ r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
assert_se(r >= 0);
{
}
static void test_policy_allow_list_major(const char *pattern, const char *cgroup_path, BPFProgram **installed_prog) {
- _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
+ _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
unsigned wrong = 0;
int r;
r = bpf_devices_allow_list_major(prog, cgroup_path, pattern, 'c', "rw");
assert_se(r >= 0);
- r = bpf_devices_apply_policy(prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
+ r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
assert_se(r >= 0);
/* /dev/null, /dev/full have major==1, /dev/tty has major==5 */
}
static void test_policy_allow_list_major_star(char type, const char *cgroup_path, BPFProgram **installed_prog) {
- _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
+ _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
unsigned wrong = 0;
int r;
r = bpf_devices_allow_list_major(prog, cgroup_path, "*", type, "rw");
assert_se(r >= 0);
- r = bpf_devices_apply_policy(prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
+ r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
assert_se(r >= 0);
{
}
static void test_policy_empty(bool add_mismatched, const char *cgroup_path, BPFProgram **installed_prog) {
- _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
+ _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
unsigned wrong = 0;
int r;
assert_se(r < 0);
}
- r = bpf_devices_apply_policy(prog, CGROUP_DEVICE_POLICY_STRICT, false, cgroup_path, installed_prog);
+ r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_STRICT, false, cgroup_path, installed_prog);
assert_se(r >= 0);
{
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, cgroup, NULL, &controller_path);
assert_se(r >= 0);
- _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
+ _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
test_policy_closed(cgroup, &prog);
test_policy_strict(cgroup, &prog);
_cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
CGroupContext *cc = NULL;
- _cleanup_(bpf_program_unrefp) BPFProgram *p = NULL;
+ _cleanup_(bpf_program_freep) BPFProgram *p = NULL;
_cleanup_(manager_freep) Manager *m = NULL;
Unit *u;
char log_buf[65535];
assert_se(runtime_dir = setup_fake_runtime_dir());
r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &p);
- assert(r == 0);
+ assert_se(r == 0);
r = bpf_program_add_instructions(p, exit_insn, ELEMENTSOF(exit_insn));
- assert(r == 0);
+ assert_se(r == 0);
if (getuid() != 0)
return log_tests_skipped("not running as root");
log_notice("BPF firewalling (though without BPF_F_ALLOW_MULTI) supported. Good.");
r = bpf_program_load_kernel(p, log_buf, ELEMENTSOF(log_buf));
- assert(r >= 0);
+ assert_se(r >= 0);
if (test_custom_filter) {
zero(attr);
}
}
- p = bpf_program_unref(p);
+ p = bpf_program_free(p);
/* The simple tests succeeded. Now let's try full unit-based use-case. */
return log_tests_skipped("Kernel doesn't support the necessary bpf bits (masked out via seccomp?)");
assert_se(r >= 0);
- assert(u->ip_bpf_ingress);
- assert(u->ip_bpf_egress);
+ assert_se(u->ip_bpf_ingress);
+ assert_se(u->ip_bpf_egress);
r = bpf_program_load_kernel(u->ip_bpf_ingress, log_buf, ELEMENTSOF(log_buf));
log_notice("%s", log_buf);
log_notice("-------");
- assert(r >= 0);
+ assert_se(r >= 0);
r = bpf_program_load_kernel(u->ip_bpf_egress, log_buf, ELEMENTSOF(log_buf));
log_notice("%s", log_buf);
log_notice("-------");
- assert(r >= 0);
+ assert_se(r >= 0);
assert_se(unit_start(u) >= 0);
assert_se(paths_ret);
for (size_t i = 0; i < test_suite_size; i++) {
- _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
+ _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
_cleanup_free_ char *str = NULL;
r = bpf_foreign_test_to_string(test_suite[i].attach_type, test_suite[i].bpffs_path, &str);
}
r = sd_bus_request_name_async(bus, &slot, "org.freedesktop.systemd.test-bus-util", 0, callback, &n_called);
- assert(r == 1);
+ assert_se(r == 1);
assert_se(sd_bus_slot_get_destroy_callback(slot, NULL) == 0);
assert_se(sd_bus_slot_get_destroy_callback(slot, &t) == 0);
assert_se(t == destroy_callback);
/* Force cleanup so we can look at n_called */
- assert(n_called == 0);
+ assert_se(n_called == 0);
sd_bus_slot_unref(slot);
- assert(n_called == 1);
+ assert_se(n_called == 1);
}
int main(int argc, char **argv) {
old_tz = getenv("TZ");
if (old_tz)
- old_tz = strdupa(old_tz);
+ old_tz = strdupa_safe(old_tz);
if (!isempty(new_tz))
new_tz = strjoina(":", new_tz);
if (expect != USEC_INFINITY)
assert_se(r >= 0 && u == expect);
else
- assert(r == -ENOENT);
+ assert_se(r == -ENOENT);
calendar_spec_free(c);
assert_se(!capability_update_inherited_set(caps, set));
assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
- assert(fv == CAP_SET);
+ assert_se(fv == CAP_SET);
cap_free(caps);
}
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "no_such_attr"), vals3) == -ENXIO);
assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "no_such_attr"), vals3) == 1);
- assert(vals3[0] && !vals3[1]);
+ assert_se(vals3[0] && !vals3[1]);
free(vals3[0]);
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "usage_usec"), vals3) == -ENXIO);
assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "usage_usec"), vals3) == 1);
- assert(vals3[0] && !vals3[1]);
+ assert_se(vals3[0] && !vals3[1]);
free(vals3[0]);
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat",
int c;
- assert(argc >= 0);
- assert(argv);
+ assert_se(argc >= 0);
+ assert_se(argv);
while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0)
switch (c) {
else {
log_info("→ %s", p);
if (arg_open)
- assert(fd >= 0);
+ assert_se(fd >= 0);
else
- assert(fd == -1);
+ assert_se(fd == -1);
}
}
free(gid);
ngroups_max = sysconf(_SC_NGROUPS_MAX);
- assert(ngroups_max > 0);
+ assert_se(ngroups_max > 0);
gids = newa(gid_t, ngroups_max);
ngroups = getgroups(ngroups_max, gids);
- assert(ngroups >= 0);
+ assert_se(ngroups >= 0);
max_gid = getgid();
for (i = 0; i < ngroups; i++) {
assert_se(allocated <= sizeof expected);
assert_se(allocated >= DIV_ROUND_UP(201u, 8u)); /* We need at least 201 bits for our mask */
- assert(memcmp(array, expected, allocated) == 0);
+ assert_se(memcmp(array, expected, allocated) == 0);
assert_se(cpu_set_from_dbus(array, allocated, &c2) == 0);
assert_se(c2.set);
int r;
r = cpus_in_affinity_mask();
- assert(r > 0);
+ assert_se(r > 0);
log_info("cpus_in_affinity_mask: %d", r);
}
log_info("%02d: <%s>", i, q);
if (i > 0)
assert_se(endswith(q, "."));
- assert(strlen(q) <= i);
- assert(strlen(q) + 3 >= strlen(t));
+ assert_se(strlen(q) <= i);
+ assert_se(strlen(q) + 3 >= strlen(t));
}
}
/* noop handlers, just check that arguments are passed correctly */
static int ignore_stdout_func(int fd, void *arg) {
- assert(fd >= 0);
- assert(arg == &here);
+ assert_se(fd >= 0);
+ assert_se(arg == &here);
safe_close(fd);
return 0;
}
static int ignore_stdout_func2(int fd, void *arg) {
- assert(fd >= 0);
- assert(arg == &here2);
+ assert_se(fd >= 0);
+ assert_se(arg == &here2);
safe_close(fd);
return 0;
}
static int ignore_stdout_func3(int fd, void *arg) {
- assert(fd >= 0);
- assert(arg == &here3);
+ assert_se(fd >= 0);
+ assert_se(arg == &here3);
safe_close(fd);
return 0;
struct passwd *p;
struct group *g;
- assert(name);
+ assert_se(name);
p = getpwnam(name);
if (!p ||
char buf[4096];
ssize_t l;
- assert(s);
- assert(fd >= 0);
+ assert_se(s);
+ assert_se(fd >= 0);
l = read(fd, buf, sizeof(buf) - 1);
if (l < 0) {
static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
pid_t *pid = userdata;
- assert(pid);
+ assert_se(pid);
(void) kill(*pid, SIGKILL);
static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
int ret = -EIO;
- assert(si);
+ assert_se(si);
if (si->si_code == CLD_EXITED)
ret = si->si_status;
pid_t pid;
int r;
- assert(exec);
- assert(ret);
+ assert_se(exec);
+ assert_se(ret);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
_cleanup_strv_free_ char **libraries = NULL, **libraries_test = NULL;
int r;
- assert(user_runtime_unit_dir);
+ assert_se(user_runtime_unit_dir);
r = find_executable("touch", &fullpath_touch);
if (r < 0) {
"zzzz=${foobar:-${nothing}}\n"
"zzzzz=${nothing:+${nothing}}\n"
, WRITE_STRING_FILE_AVOID_NEWLINE);
- assert(r >= 0);
+ assert_se(r >= 0);
r = merge_env_file(&a, NULL, t);
assert_se(r >= 0);
"#\n"
"\n\n" /* empty line */
, WRITE_STRING_FILE_AVOID_NEWLINE);
- assert(r >= 0);
+ assert_se(r >= 0);
r = merge_env_file(&a, NULL, t);
assert_se(r >= 0);
}
static union in_addr_union *parse_addr(const char *str, union in_addr_union *u) {
- assert(str);
- assert(u);
+ assert_se(str);
+ assert_se(u);
assert_se(in_addr_from_string(AF_INET, str, u) >= 0);
return u;
}
if (ignore)
return false;
}
- assert(r >= 0);
+ assert_se(r >= 0);
assert_se(fw_add_masquerade(&ctx, true, AF_INET, parse_addr("10.0.2.0", &u), 28) >= 0);
assert_se(fw_add_masquerade(&ctx, false, AF_INET, parse_addr("10.0.2.0", &u), 28) >= 0);
hashmap_put(m, key, (void*) (const char*) "my dummy val");
HASHMAP_FOREACH_KEY(s, key, m) {
- assert(s);
+ assert_se(s);
if (!key_found[0] && streq(key, "key 1"))
key_found[0] = true;
else if (!key_found[1] && streq(key, "key 2"))
l = strlen(s);
assert_se(hex = hexmem(mem, len));
- answer = strndupa(strempty(s), l);
+ answer = strndupa_safe(strempty(s), l);
assert_se(streq(delete_chars(answer, WHITESPACE), hex));
}
}
if (retval == 0) {
char *str;
- str = strndupa(mem, len);
+ str = strndupa_safe(mem, len);
assert_se(streq(str, ans));
}
}
int fd;
fd = mkostemp_safe(path);
- assert(fd > 0);
+ assert_se(fd > 0);
close(fd);
/* simple hostname */
log_info("/* %s */", __func__);
- s = strdupa("foobar");
+ s = strdupa_safe("foobar");
assert_se(streq(hostname_cleanup(s), "foobar"));
- s = strdupa("foobar.com");
+ s = strdupa_safe("foobar.com");
assert_se(streq(hostname_cleanup(s), "foobar.com"));
- s = strdupa("foobar.com.");
+ s = strdupa_safe("foobar.com.");
assert_se(streq(hostname_cleanup(s), "foobar.com"));
- s = strdupa("foo-bar.-com-.");
+ s = strdupa_safe("foo-bar.-com-.");
assert_se(streq(hostname_cleanup(s), "foo-bar.com"));
- s = strdupa("foo-bar-.-com-.");
+ s = strdupa_safe("foo-bar-.-com-.");
assert_se(streq(hostname_cleanup(s), "foo-bar--com"));
- s = strdupa("--foo-bar.-com");
+ s = strdupa_safe("--foo-bar.-com");
assert_se(streq(hostname_cleanup(s), "foo-bar.com"));
- s = strdupa("fooBAR");
+ s = strdupa_safe("fooBAR");
assert_se(streq(hostname_cleanup(s), "fooBAR"));
- s = strdupa("fooBAR.com");
+ s = strdupa_safe("fooBAR.com");
assert_se(streq(hostname_cleanup(s), "fooBAR.com"));
- s = strdupa("fooBAR.");
+ s = strdupa_safe("fooBAR.");
assert_se(streq(hostname_cleanup(s), "fooBAR"));
- s = strdupa("fooBAR.com.");
+ s = strdupa_safe("fooBAR.com.");
assert_se(streq(hostname_cleanup(s), "fooBAR.com"));
- s = strdupa("fööbar");
+ s = strdupa_safe("fööbar");
assert_se(streq(hostname_cleanup(s), "fbar"));
- s = strdupa("");
+ s = strdupa_safe("");
assert_se(isempty(hostname_cleanup(s)));
- s = strdupa(".");
+ s = strdupa_safe(".");
assert_se(isempty(hostname_cleanup(s)));
- s = strdupa("..");
+ s = strdupa_safe("..");
assert_se(isempty(hostname_cleanup(s)));
- s = strdupa("foobar.");
+ s = strdupa_safe("foobar.");
assert_se(streq(hostname_cleanup(s), "foobar"));
- s = strdupa(".foobar");
+ s = strdupa_safe(".foobar");
assert_se(streq(hostname_cleanup(s), "foobar"));
- s = strdupa("foo..bar");
+ s = strdupa_safe("foo..bar");
assert_se(streq(hostname_cleanup(s), "foo.bar"));
- s = strdupa("foo.bar..");
+ s = strdupa_safe("foo.bar..");
assert_se(streq(hostname_cleanup(s), "foo.bar"));
- s = strdupa("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+ s = strdupa_safe("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
assert_se(streq(hostname_cleanup(s), "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
- s = strdupa("xxxx........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+ s = strdupa_safe("xxxx........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
assert_se(streq(hostname_cleanup(s), "xxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
}
else
assert_not_reached();
}
- assert(!p && !q);
+ assert_se(!p && !q);
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
else
assert_not_reached();
}
- assert(!p && !q);
+ assert_se(!p && !q);
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
else
assert_not_reached();
}
- assert(!p && !q);
+ assert_se(!p && !q);
unit_file_changes_free(changes, n_changes);
changes = NULL; n_changes = 0;
UnitFileChange *changes = NULL;
size_t n_changes = 0;
- assert(root);
+ assert_se(root);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", NULL) == -ENOENT);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "yy.service", NULL) == -ENOENT);
i->name, alias, r, expected,
alias2 ? " [" : "", strempty(alias2),
alias2 ? "]" : "");
- assert(r == expected);
+ assert_se(r == expected);
/* This is test for "instance propagation". This propagation matters mostly for WantedBy= and
* RequiredBy= settings, and less so for Alias=. The only case where it should happen is when we have
* an Alias=alias@.service an instantiated template template@instance. In that case the instance name
* should be propagated into the alias as alias@instance. */
- assert(streq_ptr(alias2, updated_name));
+ assert_se(streq_ptr(alias2, updated_name));
}
static void test_verify_alias(void) {
log_info("RELATIVE_SOURCE_PATH: %s", RELATIVE_SOURCE_PATH);
log_info("PROJECT_FILE: %s", PROJECT_FILE);
- assert(startswith(__FILE__, RELATIVE_SOURCE_PATH "/"));
+ assert_se(startswith(__FILE__, RELATIVE_SOURCE_PATH "/"));
}
static void test_log_struct(void) {
k++;
}
- assert(k == 3);
+ assert_se(k == 3);
FOREACH_POINTER(i, &b) {
- assert(k == 3);
- assert(i == &b);
+ assert_se(k == 3);
+ assert_se(i == &b);
k = 4;
}
- assert(k == 4);
+ assert_se(k == 4);
FOREACH_POINTER(i, NULL, &c, NULL, &b, NULL, &a, NULL) {
switch (k) {
k++;
}
- assert(k == 11);
+ assert_se(k == 11);
}
static void test_align_to(void) {
_cleanup_free_ char *z = NULL;
int r;
- assert(t);
+ assert_se(t);
r = normalize_recovery_key(t, &z);
assert_se(expected ?
k++;
}
log_info(" %s", *p);
- assert(u[k]); /* If NULL, we didn't find a matching entry */
+ assert_se(u[k]); /* If NULL, we didn't find a matching entry */
k++;
}
STRV_FOREACH(p, u + k)
static void test_path_simplify_one(const char *in, const char *out) {
char *p;
- p = strdupa(in);
+ p = strdupa_safe(in);
path_simplify(p);
log_debug("/* test_path_simplify(%s) → %s (expected: %s) */", in, p, out);
assert_se(streq(p, out));
assert_se(LOADAVG_INT_SIDE(rp.avg300) == 1);
assert_se(LOADAVG_DECIMAL_SIDE(rp.avg300) == 11);
assert_se(rp.total == 58761459);
- assert(read_resource_pressure(path, PRESSURE_TYPE_FULL, &rp) == 0);
+ assert_se(read_resource_pressure(path, PRESSURE_TYPE_FULL, &rp) == 0);
assert_se(LOADAVG_INT_SIDE(rp.avg10) == 0);
assert_se(LOADAVG_DECIMAL_SIDE(rp.avg10) == 23);
assert_se(LOADAVG_INT_SIDE(rp.avg60) == 0);
assert_se(LOADAVG_INT_SIDE(rp.avg300) == 1);
assert_se(LOADAVG_DECIMAL_SIDE(rp.avg300) == 11);
assert_se(rp.total == 58761459);
- assert(read_resource_pressure(path, PRESSURE_TYPE_FULL, &rp) == 0);
+ assert_se(read_resource_pressure(path, PRESSURE_TYPE_FULL, &rp) == 0);
assert_se(LOADAVG_INT_SIDE(rp.avg10) == 0);
assert_se(LOADAVG_DECIMAL_SIDE(rp.avg10) == 23);
assert_se(LOADAVG_INT_SIDE(rp.avg60) == 0);
char ***l = userdata;
- assert(path);
- assert(de);
+ assert_se(path);
+ assert_se(de);
switch (event) {
int main(int argc, char *argv[]) {
_cleanup_strv_free_ char **list_recurse_dir = NULL;
const char *p;
+ usec_t t1, t2, t3, t4;
int r;
log_show_color(true);
p = "/usr/share/man"; /* something hopefully reasonably stable while we run (and limited in size) */
/* Enumerate the specified dirs in full, once via nftw(), and once via recurse_dir(), and ensure the results are identical */
+ t1 = now(CLOCK_MONOTONIC);
r = recurse_dir_at(AT_FDCWD, p, 0, UINT_MAX, RECURSE_DIR_SORT|RECURSE_DIR_ENSURE_TYPE|RECURSE_DIR_SAME_MOUNT, recurse_dir_callback, &list_recurse_dir);
+ t2 = now(CLOCK_MONOTONIC);
if (r == -ENOENT) {
log_warning_errno(r, "Couldn't open directory %s, ignoring: %m", p);
return EXIT_TEST_SKIP;
}
assert_se(r >= 0);
+ t3 = now(CLOCK_MONOTONIC);
assert_se(nftw(p, nftw_cb, 64, FTW_PHYS|FTW_MOUNT) >= 0);
+ t4 = now(CLOCK_MONOTONIC);
+
+ log_info("recurse_dir(): %s – nftw(): %s", FORMAT_TIMESPAN(t2 - t1, 1), FORMAT_TIMESPAN(t4 - t3, 1));
strv_sort(list_recurse_dir);
strv_sort(list_nftw);
for (;;) {
r = sd_hwdb_enumerate(hwdb, &key, &value);
- assert(IN_SET(r, 0, 1));
+ assert_se(IN_SET(r, 0, 1));
if (r == 0)
break;
- assert(key);
- assert(value);
+ assert_se(key);
+ assert_se(value);
log_debug("A: \"%s\" → \"%s\"", key, value);
len1 += strlen(key) + strlen(value);
}
copy = set_copy(s);
assert_se(copy);
- assert(set_equal(s, copy));
+ assert_se(set_equal(s, copy));
set_free(s);
set_free_free(copy);
info(SIGRTMAX);
/* We use signals SIGRTMIN+0 to SIGRTMIN+24 unconditionally */
- assert(SIGRTMAX - SIGRTMIN >= 24);
+ assert_se(SIGRTMAX - SIGRTMIN >= 24);
}
static void test_signal_to_string_one(int val) {
r = socket_address_print(&a, &out);
if (r < 0)
log_error_errno(r, "Printing failed for \"%s\": %m", in);
- assert(r >= 0);
+ assert_se(r >= 0);
assert_se(a.type == 0);
}
static void test_ifname_valid(void) {
log_info("/* %s */", __func__);
- assert( ifname_valid("foo"));
- assert( ifname_valid("eth0"));
-
- assert(!ifname_valid("0"));
- assert(!ifname_valid("99"));
- assert( ifname_valid("a99"));
- assert( ifname_valid("99a"));
-
- assert(!ifname_valid(NULL));
- assert(!ifname_valid(""));
- assert(!ifname_valid(" "));
- assert(!ifname_valid(" foo"));
- assert(!ifname_valid("bar\n"));
- assert(!ifname_valid("."));
- assert(!ifname_valid(".."));
- assert(ifname_valid("foo.bar"));
- assert(!ifname_valid("x:y"));
-
- assert( ifname_valid_full("xxxxxxxxxxxxxxx", 0));
- assert(!ifname_valid_full("xxxxxxxxxxxxxxxx", 0));
- assert( ifname_valid_full("xxxxxxxxxxxxxxxx", IFNAME_VALID_ALTERNATIVE));
- assert( ifname_valid_full("xxxxxxxxxxxxxxxx", IFNAME_VALID_ALTERNATIVE));
- assert(!ifname_valid_full("999", IFNAME_VALID_ALTERNATIVE));
- assert( ifname_valid_full("999", IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC));
- assert(!ifname_valid_full("0", IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC));
+ assert_se( ifname_valid("foo"));
+ assert_se( ifname_valid("eth0"));
+
+ assert_se(!ifname_valid("0"));
+ assert_se(!ifname_valid("99"));
+ assert_se( ifname_valid("a99"));
+ assert_se( ifname_valid("99a"));
+
+ assert_se(!ifname_valid(NULL));
+ assert_se(!ifname_valid(""));
+ assert_se(!ifname_valid(" "));
+ assert_se(!ifname_valid(" foo"));
+ assert_se(!ifname_valid("bar\n"));
+ assert_se(!ifname_valid("."));
+ assert_se(!ifname_valid(".."));
+ assert_se(ifname_valid("foo.bar"));
+ assert_se(!ifname_valid("x:y"));
+
+ assert_se( ifname_valid_full("xxxxxxxxxxxxxxx", 0));
+ assert_se(!ifname_valid_full("xxxxxxxxxxxxxxxx", 0));
+ assert_se( ifname_valid_full("xxxxxxxxxxxxxxxx", IFNAME_VALID_ALTERNATIVE));
+ assert_se( ifname_valid_full("xxxxxxxxxxxxxxxx", IFNAME_VALID_ALTERNATIVE));
+ assert_se(!ifname_valid_full("999", IFNAME_VALID_ALTERNATIVE));
+ assert_se( ifname_valid_full("999", IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC));
+ assert_se(!ifname_valid_full("0", IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC));
}
static void test_socket_print_unix_one(const char *in, size_t len_in, const char *expected) {
_cleanup_free_ char *out = NULL, *c = NULL;
- assert(len_in <= SUN_PATH_LEN);
+ assert_se(len_in <= SUN_PATH_LEN);
SocketAddress a = { .sockaddr = { .un = { .sun_family = AF_UNIX } },
.size = offsetof(struct sockaddr_un, sun_path) + len_in,
.type = SOCK_STREAM,
test_gid = getgid();
ngroups_max = sysconf(_SC_NGROUPS_MAX);
- assert(ngroups_max > 0);
+ assert_se(ngroups_max > 0);
test_gids = newa(gid_t, ngroups_max);
log_debug("> %s", path);
if (stat(path, &st) < 0) {
- assert(errno == ENOENT);
+ assert_se(errno == ENOENT);
log_notice("Path %s not found, skipping test", path);
return;
}
log_info("/* %s */", __func__);
char *x;
- x = strdupa("");
+ x = strdupa_safe("");
assert_se(streq(string_erase(x), ""));
- x = strdupa("1");
+ x = strdupa_safe("1");
assert_se(streq(string_erase(x), ""));
- x = strdupa("123456789");
+ x = strdupa_safe("123456789");
assert_se(streq(string_erase(x), ""));
assert_se(x[1] == '\0');
v = strv_new("xxx", "*\\*", "yyy");
assert_se(!strv_fnmatch_full(v, "\\", 0, NULL));
assert_se(strv_fnmatch_full(v, "\\", FNM_NOESCAPE, &pos));
- assert(pos == 1);
+ assert_se(pos == 1);
}
int main(int argc, char *argv[]) {
/* Ensure TIME_T_MAX works correctly */
uintmax_t x = TIME_T_MAX;
x++;
- assert((time_t) x < 0);
+ assert_se((time_t) x < 0);
return 0;
}
r = tempfn_random(p, extra, &s);
log_info_errno(r, "%s+%s → %s vs. %s (%i/%m vs. %i/%s)", p, strna(extra), strna(s), strna(expect), r, ret, strerror_safe(ret));
- assert(!s == !expect);
+ assert_se(!s == !expect);
if (s) {
const char *suffix;
assert_se(in_charset(suffix, HEXDIGITS));
assert_se(strlen(suffix) == 16);
}
- assert(ret == r);
+ assert_se(ret == r);
}
static void test_tempfn_random(void) {
r = tempfn_xxxxxx(p, extra, &s);
log_info_errno(r, "%s+%s → %s vs. %s (%i/%m vs. %i/%s)", p, strna(extra), strna(s), strna(expect), r, ret, strerror_safe(ret));
- assert(!s == !expect);
+ assert_se(!s == !expect);
if (s) {
const char *suffix;
assert_se(suffix = startswith(s, expect));
assert_se(streq(suffix, "XXXXXX"));
}
- assert(ret == r);
+ assert_se(ret == r);
}
static void test_tempfn_xxxxxx(void) {
*id,
&fragment,
&names);
- assert(r == 0);
+ assert_se(r == 0);
log_info("fragment: %s", fragment);
log_info("names:");
SET_FOREACH(name, names)
r = service_deserialize_exec_command(u, key, line);
log_debug("[%s] → %d (expected: %d)", line, r, expected);
- assert(r == expected);
+ assert_se(r == expected);
/* Note that the command doesn't match any command in the empty list of commands in 's', so it is
* always rejected with "Current command vanished from the unit file", and we don't leak anything. */
assert_se(make_salt(&t) == 0);
log_info("got %s", t);
- assert(!streq(s, t));
+ assert_se(!streq(s, t));
}
static void test_in_gid(void) {
- assert(in_gid(getgid()) >= 0);
- assert(in_gid(getegid()) >= 0);
- assert(in_gid(GID_INVALID) < 0);
- assert(in_gid(TTY_GID) == 0); /* The TTY gid is for owning ttys, it would be really really weird if we were in it. */
+ assert_se(in_gid(getgid()) >= 0);
+ assert_se(in_gid(getegid()) >= 0);
+ assert_se(in_gid(GID_INVALID) < 0);
+ assert_se(in_gid(TTY_GID) == 0); /* The TTY gid is for owning ttys, it would be really really weird if we were in it. */
}
static void test_gid_lists_ops(void) {
static int on_connect(VarlinkServer *s, Varlink *link, void *userdata) {
uid_t uid = UID_INVALID;
- assert(s);
- assert(link);
+ assert_se(s);
+ assert_se(link);
assert_se(varlink_get_peer_uid(link, &uid) >= 0);
assert_se(getuid() == uid);
/* Save the old $TZ */
tz = getenv("TZ");
if (tz)
- old_tz = strdupa(tz);
+ old_tz = strdupa_safe(tz);
/* Set the new $TZ */
tz_colon = strjoina(":", isempty(i->timezone) ? "UTC" : i->timezone);
int c;
- assert(argc >= 0);
- assert(argv);
+ assert_se(argc >= 0);
+ assert_se(argv);
while ((c = getopt_long(argc, argv, "r:", options, NULL)) >= 0)
switch(c) {
const struct dmi_header *h, uint8_t s) {
char *str;
- str = strdupa(dmi_string(h, s));
+ str = strdupa_safe(dmi_string(h, s));
str = strstrip(str);
if (!isempty(str))
printf("MEMORY_DEVICE_%u_%s=%s\n", slot_num, attr_suffix, str);
*/
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: called with no error",
- __FUNCTION__);
+ __func__);
log_debug("%s: sg_io failed status 0x%x 0x%x 0x%x 0x%x",
dev_scsi->kernel, io->driver_status, io->host_status, io->msg_status, io->status);
*/
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: called with no error",
- __FUNCTION__);
+ __func__);
log_debug("%s: sg_io failed status 0x%x 0x%x 0x%x",
dev_scsi->kernel, io->driver_status, io->transport_status, io->device_status);
if (!pos)
return NULL;
- base = strndupa(base, pos - base);
+ base = strndupa_safe(base, pos - base);
dir = opendir(base);
if (!dir)
return NULL;
p = endswith(s, "+");
if (p)
- s = strndupa(s, p - s);
+ s = strndupa_safe(s, p - s);
r = safe_atou(s, ret);
if (r < 0)
if (fd < 0)
return log_error_errno(r, "Failed to allocate AF_UNIX/SOCK_STREAM socket: %m");
- if (connect(fd, &sockaddr.un, sockaddr_len) < 0) {
+ if (connect(fd, &sockaddr.sa, sockaddr_len) < 0) {
no = strjoin("No (", errno_to_name(errno), ")");
if (!no)
return log_oom();
fprintf(f,
"[Unit]\n"
- "Description=Integrity Protection Setup for %%I\n"
+ "Description=Verity Protection Setup for %%I\n"
"Documentation=man:systemd-veritysetup-generator(8) man:systemd-veritysetup@.service(8)\n"
"SourcePath=/proc/cmdline\n"
"DefaultDependencies=no\n"
printf("%s attach VOLUME DATADEVICE HASHDEVICE ROOTHASH [OPTIONS]\n"
"%s detach VOLUME\n\n"
- "Attach or detach an integrity protected block device.\n"
+ "Attach or detach a verity protected block device.\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
program_invocation_short_name,
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
all setup run clean clean-again:
@TEST_BASE_DIR=../ ./test.sh --$@
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Basic systemd setup"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Run unit tests under containers"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Job-related tests"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Journal-related tests"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Resource limits-related tests"
+# SPDX-License-Identifier: LGPL-2.1-or-later
/usr/lib/systemd/tests/testdata/testsuite-06\.units(/.*)? system_u:object_r:systemd_unit_file_t:s0
+# SPDX-License-Identifier: LGPL-2.1-or-later
template(`systemd_test_base_template', `
gen_require(`
attribute systemd_test_domain_type;
+# SPDX-License-Identifier: LGPL-2.1-or-later
policy_module(systemd_test, 0.0.1)
# declarations
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="SELinux tests"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/1981"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2730"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2691"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2467"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/3166"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/3171"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="systemd-nspawn smoke test"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="/etc/machine-id testing"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Dropin tests"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="EXTEND_TIMEOUT_USEC=usec start/runtime/stop tests"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="UDEV"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="FailureAction= operation"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test cgroup delegation in the unified hierarchy"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test changing main PID"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Tmpfiles related tests"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test Type=exec"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="cryptsetup systemd setup"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test importd"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test setenv"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test StandardOutput=file:"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Ensure %j Wants directives work"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -e
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test OnClockChange= + OnTimezoneChange="
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="plugged -> dead -> plugged issue #11997"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test OOM killer logic"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -e
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test migrating state directory from DynamicUser=1 to DynamicUser=0 and back"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test NUMAPolicy= and NUMAMask= options"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -e
-#!/bin/bash
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test unit freezing and thawing via DBus and systemctl"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Test ExecReload= (PR #13098)"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test ExecXYZEx= service unit dbus hookups"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Test oneshot unit restart on failure"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test that ExecStopPost= is always run"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Test PrivateUsers=yes on user manager"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test log namespaces"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="testing homed"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Test that KillMode=mixed does not leave left over processes with ExecStopPost="
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -e
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test adding new BindPaths while unit is already running"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -e
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Test ExecCondition= does not restart on abnormal or failure"
-#!/bin/bash
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_REQUIRE_INSTALL_TESTS=0
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test timer units when initial clock is ahead"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test credentials"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="systemd-oomd Memory Pressure Test"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test OnSuccess= + Uphold= + PropagatesStopTo= + BindsTo="
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test systemd-repart"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Test auto restart of exited services which are stuck in reloading state"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Test that mount/unmount storms can enter/exit rate limit state and will not leak units"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Run unit tests under qemu"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test RestrictNetworkInterfaces="
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/17433"
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
# vi: ts=4 sw=4 tw=0 et:
#
# TODO:
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="test analyze"
--- /dev/null
+../TEST-01-BASIC/Makefile
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -e
+
+TEST_DESCRIPTION="test device isolation"
+TEST_NO_NSPAWN=1
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
+do_test "$@"
--- /dev/null
+/*.bin binary generated
-/*/* -whitespace
+/*/* -whitespace
+/fuzz-bus-match/* binary
+/fuzz-dhcp*/* binary
+/fuzz-dns-packet/* binary
+/fuzz-fido-id-desc/ binary
+/fuzz-lldp-rx/* binary
+/fuzz-ndisc-rs/* binary
+/*/* generated
--- /dev/null
+# Journal data in export format
+/*.txt binary generated
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for AmbientCapabilities
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for AmbientCapabilities
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for AmbientCapabilities (daemon)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for AmbientCapabilities
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for AmbientCapabilities
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for AmbientCapabilities (daemon)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for basic execution
ConditionKernelVersion=">=3.0"
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for BindPaths= and BindReadOnlyPaths=
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for CapabilityBoundingSet
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for CapabilityBoundingSet
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for CapabilityBoundingSet
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for CapabilityBoundingSet
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for exec condition that fails the unit
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for exec condition that triggers skipping
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for CPUAffinity (simple)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for CPUAffinity (reset)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for CPUAffinity (merge)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test DynamicUser with static User= whose uid and gid are different
# On Fedora, user adm has uid==3 and gid==4.
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test DynamicUser with static User= whose uid and gid are different
# On Ubuntu or Debian, user games has uid==5 and gid==60.
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test DynamicUser with User= and SupplementaryGroups=
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test DynamicUser with User=
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for RuntimeDirectory with RuntimeDirectoryPreserve=yes and DynamicUser=yes
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for RuntimeDirectory with RuntimeDirectoryPreserve=yes and DynamicUser=yes 2nd trial
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for RuntimeDirectory with DynamicUser=yes migrated from RuntimeDirectoryPreserve=yes
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test DynamicUser= migrate StateDirectory= (preparation)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test DynamicUser= migrate StateDirectory=
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test DynamicUser= with StateDirectory=
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test DynamicUser with SupplementaryGroups=
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for Environment
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for Environment
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for No Environment Variable Substitution
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for Environment
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for EnvironmentFile
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
ExecStart=/bin/sh -x -c 'test "$$PATH" = "/usr" && test "$$VAR1" = word3 && test "$$VAR2" = "\\$$word 5 6"'
Type=oneshot
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$PATH" = "/tmp:/bin"'
Type=oneshot
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for ExecSearchPath with EnvironmentFile where EnvironmentFile sets PATH
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for ExecSearchPath with EnvironmentFile where EnvironmentFile does not set PATH
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for PassEnvironment with ExecSearchPath with PATH set by user
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for PassEnvironment with ExecSearchPath with PATH not set by user
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for specifiers with exec search path
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
ExecStart=ls_temp
Type=oneshot
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for Group
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for Group
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for Group
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for Group (daemon)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for IgnoreSIGPIPE=no
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for IgnoreSIGPIPE=yes
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test to make sure that InaccessiblePaths= disconnect mount propagation
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test to make sure that mount namespace setup works properly with the 'InaccessiblePaths=/proc' option
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for IOSchedulingClass=best-effort
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for IOSchedulingClass=idle
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for IOSchedulingClass=none
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for IOSchedulingClass=realtime
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for find_executable() with MountAPIVFS=no
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for NoExecPaths=
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for OOMScoreAdjust
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for OOMScoreAdjust
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for PassEnvironment with variables absent from the execution environment
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for PassEnvironment and erasing the variable list
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for PassEnvironment with a variable name repeated
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for PassEnvironment
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for Personality=aarch64
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for Personality=ppc64
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for Personality=ppc64le
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for Personality=s390
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for Personality=x86-64
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for Personality=x86
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for PrivateDevices=yes with prefix
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test CAP_MKNOD capability for PrivateDevices=no
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test CAP_SYS_RAWIO capability for PrivateDevices=no
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for PrivateDevices=no
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test CAP_MKNOD capability for PrivateDevices=yes
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test CAP_SYS_RAWIO capability for PrivateDevices=yes
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test Group=group is applied after PrivateDevices=yes
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for PrivateDevices=yes
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for PrivateNetwork
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for PrivateTmp=yes with prefix
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for PrivateTmp=no
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for PrivateTmp=yes
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test ProtectHome=tmpfs vs ProtectSystem=strict
# Test for #11276
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test CAP_SYSLOG for ProtectKernelLogs=no
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test CAP_SYSLOG for ProtectKernelLogs=yes
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test CAP_SYS_MODULE ProtectKernelModules=no
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test CAP_SYS_MODULE for ProtectKernelModules=yes
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test to make sure that passing ProtectKernelModules=yes disconnect mount propagation
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test to make sure that passing ReadOnlyPaths= disconnect mount propagation
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for ReadOnlyPaths=
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for ReadOnlyPaths=
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for ReadOnlyPaths=
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test to make sure that passing ReadWritePaths= disconnect mount propagation
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test merging RestrictNamespaces= with all flags
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test merging RestrictNamespaces= with AND
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test merging RestrictNamespaces= with OR
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test RestrictNamespaces=~mnt
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test RestrictNamespaces=mnt
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test RestrictNamespaces=no
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test RestrictNamespaces=yes
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for RuntimeDirectoryMode
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for RuntimeDirectory owner (must not be the default group of the user if Group is set)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for RuntimeDirectory owner (must not be the default group of the user if Group is set)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for RuntimeDirectory owner (must not be the default group of the user if Group is set)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for RuntimeDirectory owner (must not be the default group of the user if Group is set)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for RuntimeDirectory
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=https://github.com/systemd/systemd/issues/2637
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for specifiers
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for specifiers (template unit)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for StandardInputText= and StandardInputData=
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for StandardInput=file:
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for StandardInput=file:
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for StandardOutput=append:
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for StandardOutput=file:
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for StandardOutput=truncate:
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for Supplementary Group with multiple groups without Group and User
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for Supplementary Group with multiple groups and Group=1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for Supplementary Group with multiple groups and Uid=1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for Supplementary Group with only one group and uid 1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for Supplementary Group with only one group
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for Supplementary Group
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for SystemCallErrorNumber
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for SystemCallErrorNumber
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for SystemCallFilter
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for SystemCallFilter
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for SystemCallFilter
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for SystemCallFilter
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for SystemCallFilter
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for SystemCallFilter
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for SystemCallFilter with specific kill action overriding default errno action
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for SystemCallFilter with specific errno action overriding default kill action
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for SystemCallFilter in system mode with User set
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for SystemCallFilter in system mode with User set
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for SystemCallFilter in system mode with User set (daemon)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for SystemCallFilter with errno name (for issue #18916)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for SystemCallFilter updating errno
# test for issue #9939 which is fixed by a5404992cc7724ebf7572a0aa89d9fdb26ce0b62 (#9942)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for SystemCallFilter with errno name
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for SystemCallFilter with errno number
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for TemporaryFileSystem with mount options
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for TemporaryFileSystem with read-only mode
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for TemporaryFileSystem
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for TemporaryFileSystem on /usr
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for UMask
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for UMask default
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for UnsetEnvironment
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for User
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for User
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for User (daemon)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for WorkingDirectory with trailing dot
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for WorkingDirectory
--- /dev/null
+* generated
--- /dev/null
+/conf/*.key generated
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=hoge
Kind=dummy
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=test1
Kind=dummy
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=test1
Kind=dummy
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
OriginalName=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=dummy98
Kind=dummy
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
OriginalName=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=dummy98
Kind=dummy
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
Property=INTERFACE=hoge
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
Property=INTERFACE=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=dropin-test
Kind=dummy
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=macvlan99
Kind=macvlan
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=macvtap99
Kind=macvtap
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=vlan99
Kind=vlan
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=vlan99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=bond199
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth-peer
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=sittun99
Kind=sit
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Kind=bareudp
Name=bareudp99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=batadv99
Kind=batadv
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=bond199
Kind=bond
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=bond98
Kind=bond
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=bond99
Kind=bond
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=bridge99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=bridge99
Kind=bridge
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=bridge99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=erspan98
Kind=erspan
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=erspan99
Kind=erspan
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=gretun96
Kind=gre
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=gretap96
Kind=gretap
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=ipiptun96
Kind=ipip
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=fou98
Kind=fou
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=fou99
Kind=fou
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=sittun96
Kind=sit
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=geneve99
Kind=geneve
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=gretun96
Kind=gre
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=gretun98
Kind=gre
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=gretun97
Kind=gre
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=gretun99
Kind=gre
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=gretap98
Kind=gretap
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=gretap99
Kind=gretap
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Kind=ifb
Name=ifb99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=ip6gretun96
Kind=ip6gre
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=ip6gretun98
Kind=ip6gre
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=ip6gretun97
Kind=ip6gre
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=ip6gretun99
Kind=ip6gre
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=ip6gretap98
Kind=ip6gretap
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=ip6gretap99
Kind=ip6gretap
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=ip6tnl98
Kind=ip6tnl
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=ip6tnl97
Kind=ip6tnl
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=ip6tnl99
Kind=ip6tnl
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=ipiptun96
Kind=ipip
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=ipiptun99
Kind=ipip
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=ipiptun99
Kind=ipip
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=ipiptun98
Kind=ipip
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=ipiptun97
Kind=ipip
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=ipiptun99
Kind=ipip
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=ipvlan99
Kind=ipvlan
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=ipvtap99
Kind=ipvtap
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=isataptun99
Kind=sit
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Kind=l2tp
Name=l2tp99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Kind=l2tp
Name=l2tp99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=l2tp-ses*
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=macsec99
Kind=macsec
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=macsec99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=gretun97
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=ip6gretun97
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Kind=nlmon
Name=nlmon99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
# This test cannot use a dummy interface: IPv6 addresses
# are added without having to go through tentative state
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=sittun96
Kind=sit
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=sittun98
Kind=sit
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=sittun97
Kind=sit
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=sittun99
Kind=sit
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=eni99np1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=tap99
Kind=tap
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=tun99
Kind=tun
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=*tun96
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=*tun98 *tap98 ip6tnl98 erspan98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=*tun97 ip6tnl97
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=*tun99 *tap99 ip6tnl99 erspan99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=vcan99
Kind=vcan
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth-peer
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=veth99
Kind=veth
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=vrf99
Kind=vrf
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=vrf99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=vtitun96
Kind=vti
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=vtitun98
Kind=vti
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=vtitun97
Kind=vti
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=vtitun99
Kind=vti
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=vti6tun98
Kind=vti6
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=vti6tun97
Kind=vti6
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=vti6tun99
Kind=vti6
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=vxcan99
Kind=vxcan
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=vxlan98
Kind=vxlan
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=vxlan97
Kind=vxlan
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=vxlan99
Kind=vxlan
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=wg98
Kind=wireguard
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=wg98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=wg97
Kind=wireguard
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=wg97
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=wg99
Kind=wireguard
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=wg99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Kind=xfrm
Name=xfrm99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Kind=xfrm
Name=xfrm99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=bridge99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=bridge99
Kind=bridge
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=bridge99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=bridge99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=bridge99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=bridge99
Kind=bridge
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=client-peer
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=client
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=server-peer
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=server
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=client
Kind=veth
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=server
Kind=veth
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98 test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=bond99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=bridge99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=bridge99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+++ /dev/null
-[Match]
-Name=veth99
-
-[Network]
-DHCP=ipv6
-
-[DHCPv6]
-RapidCommit=false
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth-peer
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth-peer
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth-peer
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth-peer
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth-peer
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth-peer
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth-peer
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth-peer
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth-peer
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth-peer
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth-peer
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=bareudp99
Name=batadv99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Network]
ManageForeignRoutes=no
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=vlan6
Kind=vlan
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=vlan6
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=vxlan97
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=test1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=vxlan99
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=dummy98
'dhcp-client-ipv4-only.network',
'dhcp-client-ipv4-use-routes-use-gateway.network',
'dhcp-client-ipv6-only.network',
- 'dhcp-client-ipv6-rapid-commit.network',
'dhcp-client-keep-configuration-dhcp-on-stop.network',
'dhcp-client-keep-configuration-dhcp.network',
'dhcp-client-listen-port.network',
self.assertNotRegex(output, r'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024')
def test_dhcp_client_ipv4_ipv6(self):
- copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network',
- 'dhcp-client-ipv4-only.network')
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv4-only.network')
start_networkd()
self.wait_online(['veth-peer:carrier'])
start_dnsmasq()
self.assertTrue(search_words_in_dnsmasq_log('client provides name: test-hostname'))
self.assertTrue(search_words_in_dnsmasq_log('26:mtu'))
- def test_dhcp6_client_settings_rapidcommit_true(self):
- copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network')
- start_networkd()
- self.wait_online(['veth-peer:carrier'])
- start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
-
- output = check_output('ip address show dev veth99')
- print(output)
- self.assertRegex(output, '12:34:56:78:9a:bc')
- self.assertTrue(search_words_in_dnsmasq_log('14:rapid-commit', True))
-
- def test_dhcp6_client_settings_rapidcommit_false(self):
- copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-rapid-commit.network')
- start_networkd()
- self.wait_online(['veth-peer:carrier'])
- start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
-
- output = check_output('ip address show dev veth99')
- print(output)
- self.assertRegex(output, '12:34:56:78:9a:bc')
- self.assertFalse(search_words_in_dnsmasq_log('14:rapid-commit', True))
-
def test_dhcp_client_settings_anonymize(self):
copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-anonymize.network')
start_networkd()
#!/bin/sh
+# SPDX-License-Identifier: LGPL-2.1-or-later
echo "$0 $*"
test "$(basename "$0")" = "script.sh" || exit 1
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# This file is part of systemd.
-#
-# systemd is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 2.1 of the License, or
-# (at your option) any later version.
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Basic System
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
[Unit]
Description=Test PathChanged
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
[Unit]
Description=Service Test for Path units
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
[Unit]
Description=Test DirectoryNotEmpty
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
[Unit]
Description=Service Test for Path units
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
[Unit]
Description=Test PathExists
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
[Unit]
Description=Service Test for Path units
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
[Unit]
Description=Test PathExistsGlob
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
[Unit]
Description=Service Test for Path units
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
[Unit]
Description=Test MakeDirectory & DirectoryMode
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
[Unit]
Description=Service Test for Path units
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
[Unit]
Description=Test PathModified
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
[Unit]
Description=Service Test for Path units
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
[Unit]
Description=Service Test Path Unit
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
[Unit]
Description=Test Path Unit=
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# This file is part of systemd.
-#
-# systemd is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 2.1 of the License, or
-# (at your option) any later version.
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Paths
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# This file is part of systemd.
-#
-# systemd is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 2.1 of the License, or
-# (at your option) any later version.
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=System Initialization
--- /dev/null
+/*.pkts binary generated
--- /dev/null
+/*.initial* generated
+/*.expected* generated
+# SPDX-License-Identifier: LGPL-2.1-or-later
# Trivial smoke test that covers the most basic functionality
#
#Type Name ID GECOS HOMEDIR
+# SPDX-License-Identifier: LGPL-2.1-or-later
# check that 'm' lines do not conflicts 'u' line
#
#Type Name ID GECOS HOMEDIR
+# SPDX-License-Identifier: LGPL-2.1-or-later
#Type Name ID GECOS HOMEDIR
u u1 222 - -
g g1 111 - -
+# SPDX-License-Identifier: LGPL-2.1-or-later
u systemd-coredump 1 "systemd Core Dumper"
+# SPDX-License-Identifier: LGPL-2.1-or-later
# Ensure that the semantic for the uid:groupname syntax is correct
#
#Type Name ID GECOS HOMEDIR
+# SPDX-License-Identifier: LGPL-2.1-or-later
# Ensure that a preexisting system group can be used as primary
#
#Type Name ID GECOS HOMEDIR
+# SPDX-License-Identifier: LGPL-2.1-or-later
# Test generation of ID dynamically based on SYSTEM_UGID_MAX and
# replacement of all fields up to the login shell.
#
+# SPDX-License-Identifier: LGPL-2.1-or-later
# Ensure that the semantic for the uid:gid syntax is correct
#
#Type Name ID GECOS HOMEDIR
+# SPDX-License-Identifier: LGPL-2.1-or-later
# Ensure that already created groups are used when using the uid:gid syntax
#
#Type Name ID GECOS HOMEDIR
+# SPDX-License-Identifier: LGPL-2.1-or-later
# Reproduce the base-passwd master.{passwd,group} from Debian
#
#Type Name ID GECOS Home directory
+# SPDX-License-Identifier: LGPL-2.1-or-later
# Ensure that existing IDs are not reused by default. I.e. the existing
# ID 111 from g1 will cause u1 to get a new and different ID (999 on most
# systems).
+# SPDX-License-Identifier: LGPL-2.1-or-later
# Issue #8315
#
#Type Name ID GECOS HOMEDIR
+# SPDX-License-Identifier: LGPL-2.1-or-later
g groupname 300
u username -:300
+# SPDX-License-Identifier: LGPL-2.1-or-later
u user1 300
u user2 -:300
+# SPDX-License-Identifier: LGPL-2.1-or-later
# Ensure invalid uids are detected
#
#Type Name ID GECOS HOMEDIR
+# SPDX-License-Identifier: LGPL-2.1-or-later
# Ensure it is not allowed to create groups implicitly in the uid:gid syntax
#
#Type Name ID GECOS HOMEDIR
+# SPDX-License-Identifier: LGPL-2.1-or-later
# Ensure it is not allowed to create groups implicitly in the uid:groupname syntax
#
#Type Name ID GECOS HOMEDIR
--- /dev/null
+* generated
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=ForeverPrintHola service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Silent successful service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
Type=oneshot
ExecStart=/bin/echo Start Hola
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Load systemd-test module
DefaultDependencies=no
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Before=local-fs.target
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
DefaultDependencies=no
Conflicts=shutdown.target
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Requires=test10.socket
ConditionPathExistsGlob=/tmp/nonexistent
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Socket]
ListenStream=/run/test.ctl
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Fail on restart
StartLimitIntervalSec=1m
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Testsuite: Fail Runtime (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after RuntimeSecMax.)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Testsuite: Fail Start (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after TimeoutStartSec.)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Testsuite: Fail Stop (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after TimeoutStopSec.)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Testsuite: EXTEND_TIMEOUT_USEC Success - extend timeout on all services
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Testsuite: Success Runtime (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < RuntimeMaxSec)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Testsuite: Success Start (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < TimeoutStartSec)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Testsuite: Success Stop (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < TimeoutStopSec)
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Dependent service for percent-j specifier
After=testsuite-28-pre.service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Wants with percent-j specifier
Wants=specifier-j-depends-%j.service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
ExecStart=rm -f /failed /testok
Type=oneshot
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
[Service]
WatchdogSec=10min
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Honor First Shutdown feature
After=multi-user.target
#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
echo "Honor first shutdown test script"
sleep infinity;
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Path]
PathExists=/tmp/test63
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
ConditionPathExists=!/tmp/nonexistent
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=A conjugate
Requires=a.service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=A
Requires=b.service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Relabel all filesystems
DefaultDependencies=no
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=B
Wants=f.service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=C
Requires=a.service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=D:Cyclic
After=b.service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Daughter Service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML discard empty service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML discard set ml service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML discard slice
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML override empty service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML override slice
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML passthrough empty service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML passthrough set DML service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML passthrough set ML service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML passthrough slice
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=DML slice
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=E:Cyclic
After=b.service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=End the test
After=testsuite.target
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=F
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=G
Conflicts=e.service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Grandchild Service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=H
Wants=g.service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Sleep for a minute, then say hello.
Wants=sleep.service hello.service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Hello World
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=I
Conflicts=a.service d.service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
ExecStart=/bin/true
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
BindsTo=loopy2.service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
ExecStart=/bin/true
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
ExecStart=/bin/true
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
ExecStart=/bin/true
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Nomem Parent Slice
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Nomem Leaf Service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Deeper Parent Slice
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Parent Slice
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Bad sched priority for Idle
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Sched idle with prio 0
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Bad sched priority for RR
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Change prio
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Default prio for RR
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Sleep for 1 minute
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Son Service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-01-BASIC
After=multi-user.target
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-02-UNITTESTS
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-03-JOBS
After=multi-user.target
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-04-JOURNAL
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-05-RLIMITS
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-06-SELINUX
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-07-ISSUE-1981
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-08-ISSUE-2730
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-09-ISSUE-2691
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-10-ISSUE-2467
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-11-ISSUE-3166
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-12-ISSUE-3171
After=multi-user.target
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-13-NSPAWN-SMOKE
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
# shellcheck disable=SC2016
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-14-MACHINE-ID
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-15-DROPIN
-#!/bin/bash
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-16-EXTEND-TIMEOUT
# Testsuite: Assess all other testsuite-*.services worked as expected
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -ex
set -o pipefail
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -ex
set -o pipefail
#!/usr/bin/env bash
-
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -ex
test_rule="/run/udev/rules.d/49-test.rules"
run_test() {
since="$(date +%T)"
- udevadm trigger -w --action add /dev/null
+ SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action add /dev/null
for _ in {1..20}; do
if coredumpctl --since "$since" --no-legend --no-pager | grep /bin/udevadm ; then
#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -ex
set -o pipefail
EOF
udevadm control --reload
-udevadm trigger --settle --action add /dev/null
+SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action add /dev/null
test -f /run/udev/tags/added/c1:3
test ! -f /run/udev/tags/changed/c1:3
udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*' && { echo 'unexpected TAGS='; exit 1; }
udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*' && { echo 'unexpected CURRENT_TAGS='; exit 1; }
-udevadm trigger --settle --action change /dev/null
+SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action change /dev/null
test -f /run/udev/tags/added/c1:3
test -f /run/udev/tags/changed/c1:3
udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*'
udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*'
-udevadm trigger --settle --action add /dev/null
+SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action add /dev/null
test -f /run/udev/tags/added/c1:3
test -f /run/udev/tags/changed/c1:3
#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -ex
set -o pipefail
EOF
udevadm control --reload
-udevadm trigger --settle --action add /dev/null
+SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action add /dev/null
test -f /run/udev/data/c1:3
udevadm info /dev/null | grep -q 'E: HOGE=aa\\x20\\x20\\x20bb'
#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -ex
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-17-UDEV
#!/usr/bin/env bash
-
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-18-FAILUREACTION
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-19-DELEGATE
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-20-MAINPIDGAMES
Before=getty-pre.target
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
#
# With "e" don't attempt to set permissions when file doesn't exist, see
# https://github.com/systemd/systemd/pull/6682.
-#
-
set -eux
set -o pipefail
#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
#
# Basic tests for types creating directories
-#
-
set -eux
set -o pipefail
#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
#
# Basic tests for types creating/writing files
-#
-
set -eux
set -o pipefail
#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
#
# Basic tests for types creating fifos
-#
-
set -eux
set -o pipefail
#! /bin/bash
-
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
#
# Inspired by https://github.com/systemd/systemd/issues/9508
-#
-
set -eux
set -o pipefail
#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
#
# Verifies the issues described by https://github.com/systemd/systemd/issues/10191
-#
-
set -eux
set -o pipefail
#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
#
# Verify tmpfiles can run in a root directory under a path prefix that contains
# directories owned by unprivileged users, for example when a root file system
# is mounted in a regular user's home directory.
#
# https://github.com/systemd/systemd/pull/11820
-#
-
set -eux
set -o pipefail
#!/usr/bin/env bash
-
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
#!/usr/bin/env bash
-
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
-#! /bin/bash
-
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
set -x
-#! /bin/bash
-
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
# Test the "Age" parameter (with age-by) for systemd-tmpfiles.
-
set -e
set -x
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-22-TMPFILES
After=systemd-tmpfiles-setup.service
#!/usr/bin/env bash
-
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-23-TYPE-EXEC
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-24-CRYPTSETUP
After=multi-user.target
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-25-IMPORT
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-26-SETENV
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-27-STDOUTFILE
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-28-PERCENTJ-WANTEDBY
# Testsuite: Ensure %j Wants directives work
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-29-PORTABLE
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -eux
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-30-ONCLOCKCHANGE
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-31-DEVICE-ENUMERATION
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-32-OOMPOLICY
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-33-CLEAN-UNIT
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -eux
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-34-DYNAMICUSERMIGRATE
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-36-NUMAPOLICY
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-37-RUNTIMEDIRECTORYPRESERVE
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -eux
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
ExecStart=/bin/sleep 3600
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-38-FREEZER
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-39-EXECRELOAD
#!/usr/bin/env bash
-
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-40-EXEC-COMMAND-EX
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-41-ONESHOT-RESTART
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-42-EXECSTOPPOST
Before=getty-pre.target
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
systemd-analyze log-level debug
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-43-PRIVATEUSER-UNPRIV
After=systemd-logind.service user@4711.service
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TESTSUITE-44-LOG-NAMESPACE
Before=getty-pre.target
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
systemd-analyze log-level debug
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-46-HOMED
Wants=getty-pre.target
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Issue 14566 Repro
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
sleep infinity &
echo $! >/leakedtestpid
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-47-ISSUE-14566
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-48-START-STOP-NO-RELOAD
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -eux
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
RuntimeMaxSec=300
# Adding a new mounts at runtime works if the unit is in the active state,
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
RuntimeMaxSec=10
Type=notify
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-49-RUNTIME-BIND-PATHS
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
echo "MARKER_FIXED" >/run/testservice-49-fixed
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-50-DISSECT
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -eux
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Issue 16115 Repro with on-abnormal
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Issue 16115 Repro with on-failure
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-51-ISSUE-16115
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Testsuite service
-#!/bin/bash
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -ex
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-53-ISSUE-16347
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TESTSUITE-54-CREDS
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
# shellcheck disable=SC2016
set -eux
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
# Don't use set -x here, since it generates a lot of output and slows
# the script down, causing unexpected test fails.
set -eu
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Create a lot of memory pressure
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=No memory pressure
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Create some memory pressure
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test slice for memory pressure kills
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TESTSUITE-55-OOMD
After=user@4711.service
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Unit with BindsTo=
BindsTo=testsuite-57-bound-by.service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Unit with BoundBy=
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Failing unit
OnFailure=testsuite-57-uphold.service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Stop Propagation Receiver
Wants=testsuite-57-prop-stop-two.service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Stop Propagation Sender
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Shortlived Unit
StopWhenUnneeded=yes
#!/usr/bin/env bash
-
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -ex
if [ -f /tmp/testsuite-57.counter ] ; then
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Succeeding unit
OnSuccess=testsuite-57-fail.service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Upholding Unit
Upholds=testsuite-57-short-lived.service
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-57-ONSUCCESS-UPHOLD
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-58-REPART
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-59-RELOADING-RESTART
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-60-MOUNT-RATELIMIT
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-61-UNITTESTS-QEMU
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-62-RESTRICT-IFACES-all-pings-work
[Service]
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-62-RESTRICT-IFACES-allow-list
[Service]
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-62-RESTRICT-IFACES-deny-list
[Service]
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-62-RESTRICT-IFACES-empty-assigment
[Service]
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-62-RESTRICT-IFACES-invert-assigment
[Service]
+# SPDX-License-Identifier: LGPL-2.1-or-later
Description=TEST-62-RESTRICT-IFACES
[Service]
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
set -ex
set -o pipefail
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-63-ISSUE-17433
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-64-UDEV
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
# vi: ts=4 sw=4 tw=0 et:
set -eux
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-65-ANALYZE
#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
# shellcheck disable=SC2016
set -eux
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Service that uses device isolation
+
+[Service]
+DevicePolicy=strict
+DeviceAllow=/dev/null r
+StandardOutput=file:/tmp/testsuite66serviceresults
+ExecStartPre=rm -f /tmp/testsuite66serviceresults
+ExecStart=/bin/bash -c "while true; do sleep 0.01 && echo meow > /dev/null && echo thisshouldnotbehere; done"
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TESTSUITE-66-DEVICEISOLATION
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+RESULTS_FILE=/tmp/testsuite66serviceresults
+
+systemd-analyze log-level debug
+systemd-analyze log-target console
+
+systemctl start testsuite-66-deviceisolation.service
+
+sleep 5
+grep -q "Operation not permitted" "$RESULTS_FILE"
+
+systemctl daemon-reload
+systemctl daemon-reexec
+
+systemctl stop testsuite-66-deviceisolation.service
+
+grep -q "thisshouldnotbehere" "$RESULTS_FILE" && exit 42
+
+systemd-analyze log-level info
+
+echo OK >/testok
+
+exit 0
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Testsuite target
Requires=multi-user.target
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=override0
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Documentation=man:override1
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Documentation=man:override2
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Documentation=man:override3
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=A unit with multiple dashes
Documentation=man:test
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=override4
+# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
Type=oneshot
RemainAfterExit=yes
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Local Integrity Protected Volumes (Pre)
+Documentation=man:systemd.special(7)
+RefuseManualStart=yes
+Before=integritysetup.target
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Local Integrity Protected Volumes
+Documentation=man:systemd.special(7)
['veritysetup-pre.target', 'HAVE_LIBCRYPTSETUP'],
['veritysetup.target', 'HAVE_LIBCRYPTSETUP',
'sysinit.target.wants/'],
+ ['integritysetup-pre.target', 'HAVE_LIBCRYPTSETUP'],
+ ['integritysetup.target', 'HAVE_LIBCRYPTSETUP',
+ 'sysinit.target.wants/'],
['dev-hugepages.mount', '',
'sysinit.target.wants/'],
['dev-mqueue.mount', '',
# (at your option) any later version.
[Unit]
-Description=Remote Verity Integrity Protected Volumes
+Description=Remote Verity Protected Volumes
Documentation=man:systemd.special(7)
After=remote-fs-pre.target veritysetup-pre.target
DefaultDependencies=no
Restart=on-failure
RestartKillSignal=SIGUSR2
RestartSec=0
-RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 AF_PACKET AF_ALG
+RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 AF_PACKET
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
# (at your option) any later version.
[Unit]
-Description=Local Verity Integrity Protected Volumes (Pre)
+Description=Local Verity Protected Volumes (Pre)
Documentation=man:systemd.special(7)
RefuseManualStart=yes
Before=veritysetup.target
# (at your option) any later version.
[Unit]
-Description=Local Verity Integrity Protected Volumes
+Description=Local Verity Protected Volumes
Documentation=man:systemd.special(7)